22.05.2009, 09:22 | #1 |
Участник
|
Копирование колонок Excel
Всем привет. Чота никак не могу разобраться как должен выглядеть копирующий колонки метод класса ComExcelDocument_RU...
|
|
22.05.2009, 09:52 | #2 |
Участник
|
запишите макрос в экселе того, что хотите сделать, и повторите в аксапте, при этом смотрите как находятся нужные объекты в других методах
Последний раз редактировалось ice; 22.05.2009 в 09:54. |
|
|
За это сообщение автора поблагодарили: decoder (1). |
22.05.2009, 10:41 | #3 |
int 20h
|
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 |
Moderator
|
Очередное разглагольство про Excel
Сразу оговорюсь: словеса мои нижеследующие ни в коем случае не претензии к участникам этой короткой дискуссии, если кому-то так может показаться в связи с тем, что я использую цитаты. Просто существует устоявшаяся точка зрения по вопросу, а мне опять хочется призвать вас посомневаться и призадуматься
И не пренебрегайте изучением самого Excel! Макрорекордер пишет неоптимально - и это вполне естественно! Хотя, тем не менее, огромное ему спасибо за то, что он вообще пишет. Вот здесь в очередной раз и развернусь Ребята, не ограничивайте себя подходом класса 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 |
Administrator
|
и еще мааленькое добавление.
В Аксапте еще с 3.0 есть очень хорошее семейство классов SysExcel*. Начинать следует с SysExcelApplication. Это семейство очень упрощает работу с Excel, особенно если требуется учесть разные версии, в отличие от ComExcelDocument_RU. Конечно - это семейство далеко неполное и возможно его захочется расширить. Но у этого семейства классов есть пожалуй самый главный плюс - везде есть метод comObject - который всегда позволяет вернуться к COM-объектам и без лишнего модифицирования объектов - сделать свою задачу.
__________________
Возможно сделать все. Вопрос времени |
|
22.05.2009, 18:22 | #6 |
int 20h
|
to Gustav без обид:
Можно было конечно рассказать о существовании вывода в Ексель через механизмы прямого обращения через COM, используя операции с текстовым буфером и вставкой всех данных за раз, но за чем. Ведь человек просто спросил как должен был выглядеть метод копирования колонок. Он не требовал механизма оптимизации и прочего чудо программирования где можно было безусловно раскрыть Ваши способности.(кои не раз спасали жизнь мне, скажу честно пользовался Вашими советами) но для примера и начала разбора с механизмами подойдет способ предложенный ice и простенький метод копирования. Далее он сам все найдет. Мы же не сразу со знаниями рождаемся, а приобретаем их по ходу жизни. А вообще Вы как всегда правы в своем посте.
__________________
It's just my Unhopelessnessabilityerism Trying to debug my mind |
|
22.05.2009, 19:58 | #7 |
Moderator
|
Абсолютно. Я ж сразу предупредил - ничего личного. Мы просто делаем тихую революцию
Цитата:
Спасибо, очень приятно Цитата:
Ну вот и смотрит человек и видит, аналогично как и в других методах этого класса, что чтобы выполнить простую операцию надо эвона сколько строк понаписать. Они все правильные - иначе ж бы не работало. Но они - лишние... Вот как уже достаточно (бог с ними пока с букмарками): 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); } } X++: void copy(COM range1, COM range2) { if (m_comDocument) { range1.copy(range2); } } Цитата:
Спасибо еще раз и простите, что еще раз тут повыступал. |
|
|
За это сообщение автора поблагодарили: zZ_TOP_Zz (1). |
25.05.2009, 13:03 | #8 |
Участник
|
Цитата:
Сообщение от 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 |
Moderator
|
Что мешает в самом начале создать 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 В Аксапту мы, к сожаление, не можем напрямую перенести эту строку кода 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 |
Участник
|
Вы исходите из очень странной предпосылки, что при работе с экземпляром Excel предполагается многократное обращение к одним и тем же диапазонам. Почему, собственно?
Да, такое возможно, но, вообще-то, подобной стратегии работы с файлами Excel нужно всячески избегать. Своеобразная "нормализация" работы - обращение к одному диапазону только один раз за сеанс. По возможности, разумеется. Но даже если подобного дублирования обращения избежать не удалось, то действительно ли подобных дублей будет настолько много, что это серьезно замедлит весь процесс работы с Excel? Сомневаюсь, что именно ЭТО будет существенным тормозом. Вопрос целей и средств (цены). Как мне кажется, цена подобного решения не оправдано высока по сравнению с достугнутыми целями Ведь получается вместо того, чтобы пользоваться готовым методом (пусть и избыточным) необходимо каждый раз заново писать процедуру выполнения этого процесса. Работа программиста существенно усложняется. А смысл? Незначительный выигрыш в производительности? Стоит ли "овчинка выделки"? |
|
27.05.2009, 16:19 | #11 |
Moderator
|
Да я не за скорость бьюсь, а, скорее, за последующее удобство, что ли...
Грубо говоря, я выступаю за такую замену заголовка метода (еще раз ссылаюсь на ComExcelDocument_RU по именнованной ячейки вывести номер ее строки.): вместо X++: void copyAndInsertRange(str _bookMark, int _workSheet = 1) // здесь мы уже ограничены текущим workbook, ну или надо его добавлять третьим параметром X++: void copyAndInsertRange(COM _comRange) // здесь такого ограничения нет. Конечно, ломать существующее не надо. Уже написанное пусть работает. Но для новых методов - как добавляемых в ComExcelDocument_RU, так и для независимых статических, считаю целесообразным переход на использование объектных параметров. |
|
27.05.2009, 17:33 | #12 |
Участник
|
Ну, не получается так просто. Не получается. Получаем усложнение, а не упрощение. Не в самом методе копирования, а в его использовании. Как с этим работать-то?
Чтобы использовать объектные параметры их предварительно надо создать. Значит, озаботится их хранением где-то во вне. Значит, в общем случае код получается примерно такой ComSource = создатьCom(BookMark1) ComTarget = создатьCom(BookMark2) copyAndInsertRange(ComSource, ComTarget) Это альтернатива существующему copyAndInsertRange(BookMark1, BookMark2) Имеем минимум 3 строчки кода против 1 (а реально больше, поскольку надо озаботится описанием переменных и дополнительными проверками). Хуже того, это придется постоянно повторять (писанину кода) при написании новых отчетов. Новый отчет - новые 3 строчки, вместо 1. Какие уж тут последующие удобства! Когда такое будет оправдано? Только при многократном использовании, хотя бы одного из Com-объектов. Или же при копировании между разными файлами Excel. Это действительно настолько часто встречающаяся ситуация, чтобы создавать себе проблемы? |
|
28.05.2009, 10:13 | #13 |
Moderator
|
Я написал постом выше, что можно не создавать отдельные переменные диапазонов для разового вызова. Диапазон как 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) ); Что касается проверок, то их мы уже однажды пообсуждали: О проверках в классе ComExcelDocument_RU Владимир, предлагаю на этом прервать нашу "битву парадигм" до будущих встреч в новых темах |
|