AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 22.05.2009, 09:22   #1  
decoder is offline
decoder
Участник
Аватар для decoder
 
63 / 15 (1) ++
Регистрация: 19.12.2008
Адрес: Москва
Копирование колонок Excel
Всем привет. Чота никак не могу разобраться как должен выглядеть копирующий колонки метод класса ComExcelDocument_RU...
Старый 22.05.2009, 09:52   #2  
ice is offline
ice
Участник
Аватар для ice
Лучший по профессии 2014
 
1,689 / 405 (17) +++++++
Регистрация: 23.03.2006
запишите макрос в экселе того, что хотите сделать, и повторите в аксапте, при этом смотрите как находятся нужные объекты в других методах

Последний раз редактировалось ice; 22.05.2009 в 09:54.
За это сообщение автора поблагодарили: decoder (1).
Старый 22.05.2009, 10:41   #3  
zZ_TOP_Zz is offline
zZ_TOP_Zz
int 20h
Аватар для zZ_TOP_Zz
 
143 / 24 (1) +++
Регистрация: 26.02.2007
Адрес: Санкт -Петербург
X++:
void copy(MSOfficeBookMark_RU bookMark1, MSOfficeBookMark_RU bookMark2, int _workSheet = 1)
{
    Com Range1,Range2;
    Com workSheet;

    if (m_comDocument)
    {
        Range1 = this.findRange(bookMark1, _workSheet);
        Range2 = this.findRange(bookMark2, _workSheet);
        range1.copy();
        workSheet = this.getWorkSheet(_workSheet);
        workSheet.paste(range2);
    }

}
__________________
It's just my Unhopelessnessabilityerism
Trying to debug my mind
За это сообщение автора поблагодарили: decoder (1).
Старый 22.05.2009, 16:46   #4  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Очередное разглагольство про Excel
Сразу оговорюсь: словеса мои нижеследующие ни в коем случае не претензии к участникам этой короткой дискуссии, если кому-то так может показаться в связи с тем, что я использую цитаты. Просто существует устоявшаяся точка зрения по вопросу, а мне опять хочется призвать вас посомневаться и призадуматься

Цитата:
Сообщение от ice Посмотреть сообщение
запишите макрос в экселе того, что хотите сделать, и повторите в аксапте
И не пренебрегайте изучением самого Excel! Макрорекордер пишет неоптимально - и это вполне естественно! Хотя, тем не менее, огромное ему спасибо за то, что он вообще пишет.

Цитата:
Сообщение от ice Посмотреть сообщение
при этом смотрите как находятся нужные объекты в других методах
Вот здесь в очередной раз и развернусь

Ребята, не ограничивайте себя подходом класса ComExcelDocument_Ru! Он использует идеологию "закладок" (bookmark’ов) вместо идеологии диапазонов (COM range) и сам себе усложняет жизнь. Ещё он предназначен для работы только с одной рабочей книгой. И вы волей-неволей должны следовать этим "традициям" при создании новых методов.

Я же настоятельно рекомендую переходить от использования текстовых закладок к COM-переменным диапазонов и оперировать ими. Даже если у вас есть именованные диапазоны, сохраненные в шаблоне (т.е. не просто тривиальные "A1" или "D1:H5", а осмысленные имена типа "ВыручкаЗаМесяц"), то для них в самом начале выполнения можно создать переменные и дальше использовать их. Вы будете приятно поражены, насколько проще станет работать с объектами Excel и как взлетит ваша фантазия разработчика.

Ведь для чего в числе прочего создается осмысленное имя? Для того, чтобы при модификации рабочего листа это имя правильно пересчитывало свой адрес. Так если мы ссылаемся на ячейку A1 при помощи стринга "A1", то после вставки новой (первой) строки для ссылки на ту же ячейку нам придется писать уже "A2". Если же мы позаботились заранее и присвоили ячейке A1 "осмысленное" имя - стринг "MyCell", то и перед вставкой первой строки, и после нее обращение к "MyCell" будет ссылаться на одну и ту же ячейку: на A1 до вставки, на A2 - после.

Если же мы инициализируем переменную до вставки строки:

COM rngMyCell = Application().Workbooks(...).Worksheets(...).Range("A1")
или
COM rngMyCell = Application().Workbooks(...).Worksheets(...).Range("MyCell"),

то любой из этих двух вариантов будет правильно отслеживать все изменения структуры листа и всегда ссылаться на одну и ту же ячейку.

Теперь чтобы обратиться к нашему диапазону, его не надо искать на просторах листа или книги при помощи findRange - надо просто брать переменную диапазона и выполнять с ней требуемое действие: скопировать или вставить или очистить, сделать шрифт жирным или заливку синим или задать формат даты и т.п.

И о методе копирования "для ComExcelDocument_RU". Что, если вам придется копировать диапазон не в пределах одного листа и не между листами из одной рабочей книги, а из одной книги в другую? Всё, вы "попали". Прикиньте, хотя бы примерно, как надо будет извернуться.

На самом деле, копирование любого диапазона в Excel, начиная с единичной ячейки и заканчивая несколькими целыми строками или колонками, выполняется одним простым оператором:

исходныйДиапазон.Copy( диапазонВставки ); // и об этом надо почитать в хелпах и мануалах, макрорекордер так не пишет

Метод, соответственно, будет с двумя параметрами: static void копирование(COM исходныйДиапазон, COM диапазонВставки). Заметьте, что метод статический, а значит он может быть помещен в любой (!) подходящий класс, а не исключительно в ComExcelDocument_RU. Обратите также внимание (и это может вызвать смятение), что ему и Excel-то не нужен! Единственное, что этот метод не будет копировать диапазоны между разными сессиями Excel (разные объекты Excel.Application). Наверное... Хотя надо проверить!

Как вы поняли, предлагаемый метод с двумя параметрами будет содержать одну-единственную строку. Внимание - вопрос: нафига он тогда вообще нужен? Не проще ли, имея в распоряжении две COM-переменные диапазонов, написать эту строку кода прямо в caller-методе. Строка-то все равно одна. Зачем еще и лишний вызов?

И общее соображение: если уж делать класс для работы с Excel, то методы его должны отражать не элементарные операции, которые сам Excel как COM-объект и так выполняет замечательно, а некоторые комплексные действия в одном методе: например, вставить данные из буфера + сделать жирным первую строку заголовков + включить автофильтр. А когда 9 строк кода из 10 занимаются тем, что создают COM-переменные на основании предоставленной текстовой информации (букмарков, будь они неладны), а потом последняя строка строка наконец торжественно выполняет желаемое действие... Ну, не знаю, конечно... "Кого что возбуждает!" (с) фильм "Бин"

Еще о «чудесах оптимизации» кода ComExcelDocument_RU см. здесь: ComExcelDocument_RU по именнованной ячейки вывести номер ее строки.
За это сообщение автора поблагодарили: mazzy (2).
Старый 22.05.2009, 17:05   #5  
sukhanchik is offline
sukhanchik
Administrator
Аватар для sukhanchik
MCBMSS
Злыдни
Лучший по профессии 2015
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,275 / 3476 (122) ++++++++++
Регистрация: 13.06.2004
Адрес: Москва
и еще мааленькое добавление.
В Аксапте еще с 3.0 есть очень хорошее семейство классов SysExcel*. Начинать следует с SysExcelApplication. Это семейство очень упрощает работу с Excel, особенно если требуется учесть разные версии, в отличие от ComExcelDocument_RU.
Конечно - это семейство далеко неполное и возможно его захочется расширить. Но у этого семейства классов есть пожалуй самый главный плюс - везде есть метод comObject - который всегда позволяет вернуться к COM-объектам и без лишнего модифицирования объектов - сделать свою задачу.
__________________
Возможно сделать все. Вопрос времени
Старый 22.05.2009, 18:22   #6  
zZ_TOP_Zz is offline
zZ_TOP_Zz
int 20h
Аватар для zZ_TOP_Zz
 
143 / 24 (1) +++
Регистрация: 26.02.2007
Адрес: Санкт -Петербург
to Gustav без обид:

Можно было конечно рассказать о существовании вывода в Ексель через механизмы прямого обращения через COM, используя операции с текстовым буфером и вставкой всех данных за раз, но за чем.

Ведь человек просто спросил как должен был выглядеть метод копирования колонок. Он не требовал механизма оптимизации и прочего чудо программирования где можно было безусловно раскрыть Ваши способности.(кои не раз спасали жизнь мне, скажу честно пользовался Вашими советами) но для примера и начала разбора с механизмами подойдет способ предложенный ice и простенький метод копирования. Далее он сам все найдет. Мы же не сразу со знаниями рождаемся, а приобретаем их по ходу жизни.

А вообще Вы как всегда правы в своем посте.
__________________
It's just my Unhopelessnessabilityerism
Trying to debug my mind
Старый 22.05.2009, 19:58   #7  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Цитата:
Сообщение от zZ_TOP_Zz Посмотреть сообщение
to Gustav без обид:
Абсолютно. Я ж сразу предупредил - ничего личного. Мы просто делаем тихую революцию
Цитата:
Сообщение от zZ_TOP_Zz Посмотреть сообщение
Можно было конечно рассказать о существовании вывода в Ексель через механизмы прямого обращения через COM, используя операции с текстовым буфером и вставкой всех данных за раз, но за чем.
Вот это точно незачем, потому что вопрос был не про вывод, а про конкретную операцию уже внутри Excel. А что мы там делаем и как туда попали в задачке не говорится и не спрашивается.
Цитата:
Сообщение от zZ_TOP_Zz Посмотреть сообщение
скажу честно пользовался Вашими советами
Спасибо, очень приятно
Цитата:
Сообщение от zZ_TOP_Zz Посмотреть сообщение
но для примера и начала разбора с механизмами подойдет способ предложенный ice и простенький метод копирования.
Увы, он уже сразу не совсем простенький, потому что получен превращением кода макрорекордера, который операцию копирования (выделения типа Range.Select не учитываем) раскладывает на: копирование "от имени" range и последующую вставку от имени worksheet. К слову сказать, при выполнении Вашего метода еще, наверное, останется пунктирная рамка выделения, которую надо снимать операцией типа Application.CutCopyMode(false). Т.е. еще как минимум одна строка должна появиться.

Ну вот и смотрит человек и видит, аналогично как и в других методах этого класса, что чтобы выполнить простую операцию надо эвона сколько строк понаписать. Они все правильные - иначе ж бы не работало. Но они - лишние... Вот как уже достаточно (бог с ними пока с букмарками):
X++:
void copy(MSOfficeBookMark_RU bookMark1, MSOfficeBookMark_RU bookMark2, int _workSheet = 1)
{
    Com range1,range2;

    if (m_comDocument)
    {
        range1 = this.findRange(bookMark1, _workSheet);
        range2 = this.findRange(bookMark2, _workSheet);

        range1.copy(range2);
    }
}
А я предлагаю вот как (уже без букмарков и, заметьте, отпала необходимость в параметре worksheet, потому что range сам знает, с какого он worksheet'а и из какого он workbook'а):
X++:
void copy(COM range1, COM range2)
{
    if (m_comDocument)
    {
        range1.copy(range2);
    }
}
Ну и как-то уже и метод вроде не нужен в таком элементарном виде (но об этом я написал выше).
Цитата:
Сообщение от zZ_TOP_Zz Посмотреть сообщение
Далее он сам все найдет. Мы же не сразу со знаниями рождаемся, а приобретаем их по ходу жизни.
Иех, четвертый год я на форуме, время от времени возникаю с этой темой. А все равно, как ни вопрос по Excel, так очередной ответ с методом для ComExcelDocument_RU - и c теми же букмарками, с теми же проверками, с теми же findRange... А новичков учить-то, наверное, надо сразу правильно, чтобы потом мучительно не переучиваться.
Цитата:
Сообщение от zZ_TOP_Zz Посмотреть сообщение
А вообще Вы как всегда правы в своем посте.
Спасибо еще раз и простите, что еще раз тут повыступал.
За это сообщение автора поблагодарили: zZ_TOP_Zz (1).
Старый 25.05.2009, 13:03   #8  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,654 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Цитата:
Сообщение от Gustav
Метод, соответственно, будет с двумя параметрами: static void копирование(COM исходныйДиапазон, COM диапазонВставки). Заметьте, что метод статический, а значит он может быть помещен в любой (!) подходящий класс, а не исключительно в ComExcelDocument_RU. Обратите также внимание (и это может вызвать смятение), что ему и Excel-то не нужен! Единственное, что этот метод не будет копировать диапазоны между разными сессиями Excel (разные объекты Excel.Application). Наверное... Хотя надо проверить!

Как вы поняли, предлагаемый метод с двумя параметрами будет содержать одну-единственную строку. Внимание - вопрос: нафига он тогда вообще нужен? Не проще ли, имея в распоряжении две COM-переменные диапазонов, написать эту строку кода прямо в caller-методе. Строка-то все равно одна. Зачем еще и лишний вызов?
Подобные рассуждения хороши "вообще". В отрыве от конкретной задачи.

Да, конечно, получив ссылку на COM-объект дальше можно работать вне контекста ComExcelDocument_RU, но, разве вы будете организовывать копирование через COM-объекты? Нет, конечно. Вы будете организовывать копирование через АДРЕСА. Задача формулируется примерно так:

Скопировать ячейку "A1" в ячейку "A2". Ну, или через имена. Не важно.

Т.е. сама постановка задачи предполагает, что ссылок на COM-объекты нет. Есть только их адреса. А ссылки надо еще сформировать по этим адресам. Поэтому создание статического метода становится бессмысленным. Нужен "контекст". Среда ComExcelDocument_RU.

Разумеется, возможна ситуация, когда Com-объект со ссылкой на копируемые ячейки был сформирован раньше, для решения каких-то других задач. Но ведь очевидно, что это частный случай, обсуловленный логикой этой самой задачи. В общем случае далеко не факт, что это произойдет.

Т.е. работа с Com-объектами напрямую - это возможный вариант. Но именно как частный случай.
Старый 25.05.2009, 15:53   #9  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Что мешает в самом начале создать COM-переменные для всех необходимых в текущей процедуре "адресов"?

Я думаю, что работать с "АДРЕСАМИ" - вопрос привычки, традиции - не более. И происходит эта традиция именно от стиля записи макрорекордера. Ну, и, безусловно, от опыта ручной пользовательской работы в Excel.

В Excel мы можем предложить два следующих макроса для выполнения одной и той же задачи:
Код:
 
Sub Macro1()
    Range("D8").Copy Range("MyCell")
End Sub

Sub Macro2()
    Dim rngD8 As Range
    Dim rngMyCell As Range
    
    Set rngD8 = Application.Workbooks("Книга1").Worksheets(1).Range("D8")
    Set rngMyCell = Application.Workbooks("Книга1").Worksheets(1).Range("MyCell")

    rngD8.Copy rngMyCell
End Sub
Оба прекрасно работают в контексте Excel. И программисту в данном случае практически безразлично, что использовать с точки зрения трудозатрат и визуальной ясности кода: Range("D8") и Range("MyCell") или rngB8 и rngMyCell. А вот если бы в Excel не поддерживался контекст текущих объектов (workbook, worksheet) и ему пришлось бы в каждом месте использования этих своих диапазонов писать удручающую ссылку Application.Workbooks("Книга1").Worksheets(1).Range("D8")..., то он быстро бы смекнул, что определить rngD8 в самом начале лучше со всех сторон.

В Аксапту мы, к сожаление, не можем напрямую перенести эту строку кода Range("D8").Copy Range("MyCell"), но можем создать иллюзию, написав свой метод copy(from, to) и потом вызывать его: copy("D8","MyCell") - ну что ж, всё легко и просто и почти как в Excel. Снаружи! Но углубимся в реализацию этого copy. И что мы имеем? Неотвратимую необходимость создания двух COM-переменных в теле метода и в последующем выбрасывании их, извиняюсь, "на помойку" после выполнения собственно операции копирования. А если нам потребуется скопировать содержимое ячейки "D8" еще в 10 или 100 других ячеек? Ответ понятен: вызовами copy("D8",...) будет создано и тут же уничтожено 10 или 100 объектов Range("D8").
Старый 25.05.2009, 16:51   #10  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,654 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Вы исходите из очень странной предпосылки, что при работе с экземпляром Excel предполагается многократное обращение к одним и тем же диапазонам. Почему, собственно?

Да, такое возможно, но, вообще-то, подобной стратегии работы с файлами Excel нужно всячески избегать. Своеобразная "нормализация" работы - обращение к одному диапазону только один раз за сеанс. По возможности, разумеется.

Но даже если подобного дублирования обращения избежать не удалось, то действительно ли подобных дублей будет настолько много, что это серьезно замедлит весь процесс работы с Excel? Сомневаюсь, что именно ЭТО будет существенным тормозом.

Вопрос целей и средств (цены). Как мне кажется, цена подобного решения не оправдано высока по сравнению с достугнутыми целями

Ведь получается вместо того, чтобы пользоваться готовым методом (пусть и избыточным) необходимо каждый раз заново писать процедуру выполнения этого процесса. Работа программиста существенно усложняется. А смысл? Незначительный выигрыш в производительности? Стоит ли "овчинка выделки"?
Старый 27.05.2009, 16:19   #11  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Да я не за скорость бьюсь, а, скорее, за последующее удобство, что ли...

Грубо говоря, я выступаю за такую замену заголовка метода (еще раз ссылаюсь на ComExcelDocument_RU по именнованной ячейки вывести номер ее строки.):
вместо
X++:
void copyAndInsertRange(str _bookMark, int _workSheet = 1) 
// здесь мы уже ограничены текущим workbook, ну или надо его добавлять третьим параметром
пишем
X++:
void copyAndInsertRange(COM _comRange)
// здесь такого ограничения нет.
Соответственно, для второго случая, если мы не создаем переменной для диапазона и обращаемся к нему всего один раз за сеанс, можно писать вызовы вида copyAndInsertRange(worksheet.Range("A10:G15")), заранее позаботившись о создании переменной worksheet (уж одну-то можно создать?)

Конечно, ломать существующее не надо. Уже написанное пусть работает. Но для новых методов - как добавляемых в ComExcelDocument_RU, так и для независимых статических, считаю целесообразным переход на использование объектных параметров.
Старый 27.05.2009, 17:33   #12  
Владимир Максимов is offline
Владимир Максимов
Участник
КОРУС Консалтинг
 
1,654 / 1158 (42) ++++++++
Регистрация: 13.01.2004
Записей в блоге: 3
Ну, не получается так просто. Не получается. Получаем усложнение, а не упрощение. Не в самом методе копирования, а в его использовании. Как с этим работать-то?

Чтобы использовать объектные параметры их предварительно надо создать. Значит, озаботится их хранением где-то во вне. Значит, в общем случае код получается примерно такой

ComSource = создатьCom(BookMark1)
ComTarget = создатьCom(BookMark2)
copyAndInsertRange(ComSource, ComTarget)

Это альтернатива существующему

copyAndInsertRange(BookMark1, BookMark2)

Имеем минимум 3 строчки кода против 1 (а реально больше, поскольку надо озаботится описанием переменных и дополнительными проверками). Хуже того, это придется постоянно повторять (писанину кода) при написании новых отчетов. Новый отчет - новые 3 строчки, вместо 1. Какие уж тут последующие удобства!

Когда такое будет оправдано? Только при многократном использовании, хотя бы одного из Com-объектов. Или же при копировании между разными файлами Excel. Это действительно настолько часто встречающаяся ситуация, чтобы создавать себе проблемы?
Старый 28.05.2009, 10:13   #13  
Gustav is offline
Gustav
Moderator
Аватар для Gustav
SAP
Лучший по профессии 2009
 
1,858 / 1152 (42) ++++++++
Регистрация: 24.01.2006
Адрес: Санкт-Петербург
Записей в блоге: 19
Я написал постом выше, что можно не создавать отдельные переменные диапазонов для разового вызова. Диапазон как COM-объект можно передать методу при помощи волшебного слова Range с квалификатором родительского объекта, которым может выступать либо Worksheet, либо - и, возможно, это выглядит не столь естественно - Application (нужно помнить, что при использовании Application будет подразумеваться текущий активный Worksheet). В случае использования Worksheet выглядеть будет примерно так:
X++:
COM doc = new ComExcelDocument_RU();
COM worksheet;

worksheet = doc.getWorkSheet(1);

copyAndInsertRange( worksheet.Range(BookMark1), 
                    worksheet.Range(BookMark2) );
Интересно, что в нашей Аксапте (3.0 SP4) метод getWorkSheet на слое dis объявлен как protected, а на слое var копипастом "переписан" как public - т.е., видимо, тоже пошла трещина сомнения, что же первично: букмарки или объекты.

Что касается проверок, то их мы уже однажды пообсуждали: О проверках в классе ComExcelDocument_RU

Владимир, предлагаю на этом прервать нашу "битву парадигм" до будущих встреч в новых темах
Теги
columns, com connector, excel, колонки

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Копирование листа Excel целиком ax3.0 sp4 KR3 hlopez DAX: Программирование 1 20.04.2009 11:34
Копирование листов в Excel с шаблоном Zoe DAX: Программирование 8 20.11.2008 15:50
Копирование листов в Excel Андрей К. DAX: База знаний и проекты 12 30.08.2007 08:44
Вывод в Excel в формате XML и ширина колонок gl00mie DAX: Программирование 1 30.10.2006 10:45
Копирование из Грида в Excel ArturK DAX: Программирование 5 09.03.2004 19:55

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 00:10.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.