14.10.2008, 13:13 | #21 |
Модератор
|
Gustav то что на картинках красное, она и есть объединенная ячейка.
|
|
14.10.2008, 13:36 | #22 |
Moderator
|
Цитата:
Код: Sub VBA_Macro4() Dim comRange As Range Dim comR As Range Dim addr As String Set comRange = Worksheets(1).Range("B2:D4") addr = comRange.Range("A1").MergeArea.Address 'здесь Range("A1") говорит о том, что из объединенной ячейки надо взять 'левую верхнюю ячейку диапазона - не путать с ячейкой A1 всего листа 'без этого просто comRange.MergeArea.Address вызовет ошибку '........................... Set comR = Worksheets(1).Range(addr) comR.Merge End Sub |
|
14.10.2008, 13:58 | #23 |
Moderator
|
Цитата:
Код: Sub Macro22() Dim rng As Range Set rng = Range("B2:C4") If rng.MergeCells Then Set rng = rng.Range("A1").MergeArea End If rng.Insert xlShiftDown 'где константа xlShiftDown = -4121 rng.Copy rng.Offset(-rng.Rows.Count) End Sub P.S. Соответственно, добавка в метод: X++: if (comRange.MergeCells()) { comRange = comRange.Range('A1'); comRange = comRange.MergeArea(); } |
|
14.10.2008, 14:16 | #24 |
Модератор
|
X++: void copyAndInsertBlock(str _bookMark, int _workSheet = 1) { #define.xlShiftDown(-4121) COM comRange, comRange1, comR; int rowsNumber, colsNumber; str s; ; if (! m_comDocument) throw error(strFmt("@DIS6401", this.getApplicationName())); comRange = this.findRange(_bookMark, _workSheet); if (! comRange) { return; } rowsNumber = any2int(COM::createFromObject( comRange.Rows() ).Count()); // comR=comRange.MergeArea(); //s=comRange.MergeArea.Address; comRange.Insert( #xlShiftDown ); comRange1 = comRange.Offset(-rowsNumber); /* if (comRange.MergeCells()) { comRange1 = comRange.Range(_bookMark); comRange1 = comRange.MergeArea(); } */ comRange.Copy(comRange1); } |
|
14.10.2008, 14:19 | #25 |
Moderator
|
Да уже всё распутано, вроде. Глаза постом выше поднимите
if (comRange.MergeCells()) перед Insert надо поставить и не comRangeОдин, а просто comRange там - идёт преобразование comRange по ходу. Вот ваш метод: X++: void copyAndInsertBlock(str _bookMark, int _workSheet = 1) { #define.xlShiftDown(-4121) COM comRange, comRange1; int rowsNumber; ; if (! m_comDocument) throw error(strFmt("@DIS6401", this.getApplicationName())); comRange = this.findRange(_bookMark, _workSheet); if (! comRange) { return; } if (comRange.MergeCells()) { comRange = comRange.Range('A1'); comRange = comRange.MergeArea(); } rowsNumber = any2int(COM::createFromObject( comRange.Rows() ).Count()); comRange.Insert( #xlShiftDown ); // и выше добавить: #define.xlShiftDown(-4121) comRange1 = comRange.Offset(-rowsNumber); comRange.Copy(comRange1); } |
|
|
За это сообщение автора поблагодарили: gl00mie (5). |
14.10.2008, 16:12 | #26 |
Модератор
|
какой интересный код получился Gustav Спасибо. Про 'A1' конечно забавно..
|
|
14.10.2008, 17:20 | #27 |
Moderator
|
Ага, c Excel вообще весело Любой прямоугольный диапазон из нескольких ячеек можно рассматривать как виртуальный лист внутри рабочего листа (worksheet) со своим локальным началом "A1" в левом верхнем углу этого диапазона. Причем диапазонам можно произвольно неограниченно "вкладываться" друг в друга (наверное, есть системные ограничения, я не проверял).
Поэтому запись Range("B2").Range("B2").Range("B2").Range("B2").Range("B2").Range("B2").Range("B2") - вовсе не бред сумасшедшего, а ссылка на ячейку H8: ? Range("B2").Range("B2").Range("B2").Range("B2").Range("B2").Range("B2").Range("B2").Address $H$8 |
|
14.10.2008, 18:08 | #28 |
Модератор
|
Gustav еще немного вопросов. (конечно понимаю что я тебя замучал, но все таки)
1) comRange.Insert( #xlShiftDown ); работает так же как и comRange.Insert(); Так зачем #xlShiftDown указывать? 2) Если ли какой нибудь метод в котором можно выставить по умолчание согласие на - "Данная операция приведет к отмене объединения ячеек". Чтоб сообщение не появлялось. 3) Как можно написать еще один метод на примере copyAndInsertBlock в котором именованная ячейка переползает в нижнею без вставки новой ячейки? Т.е. есть ячейка "test" в нее вставили значение, и имя ячейки переместилось вниз (хоть скопированием данных ячейки хоть без) |
|
14.10.2008, 18:40 | #29 |
Moderator
|
Цитата:
Сообщение от Poleax
Gustav еще немного вопросов. (конечно понимаю что я тебя замучал, но все таки)
1) comRange.Insert( #xlShiftDown ); работает так же как и comRange.Insert(); Так зачем #xlShiftDown указывать? 2) Если ли какой нибудь метод в котором можно выставить по умолчание согласие на - "Данная операция приведет к отмене объединения ячеек". Чтоб сообщение не появлялось. 3) Как можно написать еще один метод на примере copyAndInsertBlock в котором именованная ячейка переползает в нижнею без вставки новой ячейки? Т.е. есть ячейка "test" в нее вставили значение, и имя ячейки переместилось вниз (хоть скопированием данных ячейки хоть без) 2. Есть. Выражаюсь на VBA. Надо поставить Application.DisplayAlerts = False перед Insert и потом обязательно восстановить после него (ну или после всего цикла в другом методе) Application.DisplayAlerts = True 3. Не очень понял пока. У нас она и так съезжает. Зачем это надо? Чтобы все время в "test" писать? Типа не менять оператор doc.insertValue('test', ...) ? |
|
14.10.2008, 19:19 | #30 |
Модератор
|
Цитата:
3. Не очень понял пока. У нас она и так съезжает. Зачем это надо? Чтобы все время в "test" писать? Типа не менять оператор doc.insertValue('test', ...) ?
Победить то объединенную ячейку удалось. Но красота пропала в шаблоне. Фокус и задание в том, что есть одна строка в ней 4 именованных ячейки. Эту строку мы размножаем и по циклу заполняем последовательно столбцы. Если проявляется новое значение в 1 столбце надо вставить строку. ну и соответственно все значения для остальных столбцов. Идея с copyAndInsertBlock отрабатывает прекрасно кроме формата ниже лежащих строк :-( но если вы вставляем строку то и все 4 именованных ячейки переходят на строку ниже... :-) вызов расчета столбцов сделан отдельно. т.е. нельзя за один так цикла расчитать 4 значения для 4 ячеек. А получается что цикл пробегает только по одному столбцу. В итоге.. решение пока где то рядом. |
|
14.10.2008, 19:42 | #31 |
Модератор
|
Мысль в картинках...
Есть шаблон. В нем есть место где строчка. В данном примере она в Excel № 76 (картинка 1)
Работает вставка пока так с использованием copyAndInsertBlock и InsertRow. (картинка 2) Как видно текст сместился по всем столбцам вниз. НО форматирование осталось (Картинка 3) В конечном варианте должно появится такое. (Картинка 4) если использовать copyAndInsertBlock, то данные там где надо, то высота строк сползает. Может у кого есть светлая мысль? Последний раз редактировалось Poleax; 14.10.2008 в 19:55. |
|
14.10.2008, 19:48 | #32 |
Moderator
|
Размышляя, набросал тут 3 демонстрационных цикла вывода в Excel (чуть позже добавил четвертый):
X++: static void Job_TestFourLoops(Args _args) { ComExcelDocument_RU doc = new ComExcelDocument_RU(); COM xlApp; COM wbook; COM range, range1, range2; int i; #define.xlDown(-4121) #define.xlPasteFormats(-4122) ; doc.NewFile(); wbook = doc.getComDocument(); xlApp = wbook.Parent(); // ВАРИАНТ 1 // тестовая подготовка первой ячейки с именем и форматом range = xlApp.Range('B3'); range.Name('test'); COM::createFromObject( range.Font() ).Bold(true); // все время писать в test, смещая его вниз за счет вставки строк // и перекидывая формат на ячейку выше for (i=1;i<=10;i++) { doc.insertValue('test', i); doc.copyAndInsertRange('test'); } doc.insertValue('test',''); // ВАРИАНТ 2 // тестовая подготовка первой ячейки с именем и форматом range = xlApp.Range('B15'); range.Name('test2'); COM::createFromObject( range.Interior() ).ColorIndex(36); // прописывание значений range = xlApp.Range('test2'); // как если бы букмарк уже существовал в файле for (i=1;i<=10;i++) { // в цикле только пишем значения, не форматируем // адрес зависит от счетчика цикла range1 = COM::createFromVariant( range.Item(i,1) ); doc.insertValue( range1.Address() ,i); } // в конце скопировать формат первой ячейки на все остальные range2 = xlApp.Range(range, range.End(#xlDown)); range.Copy(); range2.PasteSpecial(#xlPasteFormats); xlApp.CutCopyMode(false); // ВАРИАНТ 3 // тестовая подготовка первой ячейки с именем и форматом range = xlApp.Range('B27'); range.Name('test3'); COM::createFromObject( range.Font() ).Bold(true); COM::createFromObject( range.Interior() ).ColorIndex(5); // прописывание значений range = xlApp.Range('test3'); // как если бы букмарк уже существовал в файле range1 = range; for (i=1;i<=10;i++) { // в цикле только пишем значения, не форматируем // адрес не зависит от счетчика цикла doc.insertValue( range1.Address(), i); range1 = range1.Offset(1,0); } // в конце скопировать формат первой ячейки на все остальные range2 = xlApp.Range(range, range.End(#xlDown)); range.Copy(); range2.PasteSpecial(#xlPasteFormats); xlApp.CutCopyMode(false); // ВАРИАНТ 4 // тестовая подготовка первой ячейки с именем и форматом range = xlApp.Range('B39'); range.Name('test4'); COM::createFromObject( range.Borders() ).LineStyle(1); COM::createFromObject( range.Font() ).Bold(true); // прописывание значений range = xlApp.Range('test4'); for (i=1;i<=10;i++) { // вставляем выше 'test4' строку с форматированием doc.copyAndInsertRangeNew('test4'); // получаем объектную переменную для строки на одну выше 'test4' range1 = range.Offset(-1,0); // пишем в строку выше 'test4' doc.insertValue( range1.Address(), i); // range c 'test4' на каждом шаге опускается ниже и ниже // а range1 следует за ним строкой выше, олицетворяя собой новую вставленную строку // которая рождается с исходными (пустыми) значениями 'test4', а потом переписывается текущими } // в конце остается пустая строка с 'test4', которую можно удалить doc.deleteRow(range.Row()); } |
|
14.10.2008, 20:25 | #33 |
Модератор
|
хм.. 3 вариант мне понравился. Сейчас с ним что то придумаю.. Спасибо за идею
|
|
14.10.2008, 23:47 | #34 |
Moderator
|
Думаю, совсем должен понравиться 4-й вариант:
X++: // ВАРИАНТ 4 // прописывание значений range = xlApp.Range('test'); for (i=1;i<=10;i++) { // вставляем выше 'test'а строку с форматированием doc.copyAndInsertRange('test'); // получаем объектную переменную для строки на одну выше 'test'а range1 = range.Offset(-1,0); // пишем в строку выше 'test'а doc.insertValue( range1.Address(), i); // range c 'test' на каждом шаге опускается ниже и ниже // а range1 следует за ним строкой выше, олицетворяя собой новую вставленную строку // которая рождается с исходными значениями 'test'а, а потом переписывается текущими } // в конце остается пустая строка с 'test', которую можно удалить |
|
15.10.2008, 10:15 | #35 |
Модератор
|
вчера вечерком, как и говорил покопался с 3 вариантом. Все удачно получилось.
Использовал InsertRow и смещение относительно вставки именованной ячейки. Отчет выглядит как я хотел. Gustav С меня пиво! Спасибо за терпение! |
|
15.10.2008, 10:39 | #36 |
Moderator
|
|
|
15.10.2008, 11:05 | #37 |
Модератор
|
4 я увидел сегодня утром.
в третий использовал приблизительно так: X++: static void Job107(Args _args) { ComExcelDocument_RU doc = new ComExcelDocument_RU(); COM xlApp; COM wbook; COM range, range1, range2; int i,n; ; doc.NewFile(); wbook = doc.getComDocument(); xlApp = wbook.Parent(); // ВАРИАНТ 3 // тестовая подготовка первой ячейки с именем и форматом range = xlApp.Range('B2'); range.Name('test'); range = xlApp.Range('test'); n=doc.getCellName_RowNum("test"); range1 = range; for (i=1;i<=10;i++) { // в цикле только пишем значения, не форматируем // адрес не зависит от счетчика цикла doc.insertRow(n,1,i); doc.insertValue( range1.Address(), i); range1 = range1.Offset(1,0); } } |
|
15.10.2008, 14:06 | #38 |
Moderator
|
Цитата:
Сообщение от Poleax
X++: range = xlApp.Range('test'); n=doc.getCellName_RowNum("test"); range1 = range; for (i=1;i<=10;i++) { // в цикле только пишем значения, не форматируем // адрес не зависит от счетчика цикла doc.insertRow(n,1,i); doc.insertValue( range1.Address(), i); range1 = range1.Offset(1,0); } X++: range = xlApp.Range('test'); //n=doc.getCellName_RowNum("test"); n = range.Row(); // раз уж переменную range определили, то чего так далеко за строкой лазить :) for (i=1;i<=10;i++) { // поменял операции местами для удобства понимания: // т.е. сначала записываем значение, а потом добавляем строку ниже doc.insertValue(any2str(COM::createFromVariant(range.Item(i, 1)).Address()), i); doc.insertRow(n, 1, i); } // удаляем последнюю строку - она всегда лишняя (здесь i=11) doc.deleteRow(any2int(COM::createFromVariant(range.Item(i, 1)).Row())); // после цикла ячейка test так и остается первой (верхней) ячейкой обработанного диапазона |
|