Я подыскиваю папку (каталог) для хранения временных .txt файлов (эти файлы будут создаваться и удаляться только на время выполнения процедуры). Остановил выбор на папке Пользователя.
Почему?
Загвоздка в том, что некоторые процедуры - подразумевают работу с .txt файлами не только в выбранном каталоге, но и всех его ПОДкаталогах (выбор осуществляется через диалог выбора каталога).
Т.е., нужен такой каталог, в котором ничего интересного обычно не хранится (при выборе такого каталога предлагается обработка всех его подкаталогов; ну, или можно вернуться к диалогу и выбрать любые другие каталоги\подкаталоги вручную). Но это всё лирика))
Основная причина в том, что папка Пользователя разрешает создание файлов (!!!) - по крайней мере в Win 8.1 (да, я люблю Win 8.1).
Этот факт (!!!) теперь мне нужно проверить в Win XP - хотя бы ещё и потому что там совсем другая структура каталогов (я подглядывал здесь: https://habr.com/ru/post/70922/).
Код
Option Explicit
Private Sub Workbook_Open()
Const zQcQ1q As Double = 100000000000000#
Const zQcQ2q As Double = 999999999999999#
Dim zQ0q As String
Dim zQ1q As String
Dim zQFq As Variant
Dim zQsq As Variant
Set zQsq = CreateObject("WScript.Shell").SpecialFolders: zQ0q = _
zQsq("MyDocuments")
Dim zQiq As Variant
Dim zQSplitq() As String
zQSplitq = _
Split(zQ0q, "\")
zQ0q = "" '!!!
For zQiq = 0 To UBound( _
zQSplitq) - 1: zQ0q = zQ0q & _
zQSplitq(zQiq) & "\"
Next
zQ1q = _
zQ0q & Application.WorksheetFunction.RandBetween(zQcQ1q, zQcQ2q) & ".txt"
Set zQFq = CreateObject("Scripting.FileSystemObject").CreateTextFile( _
zQ1q)
For Each _
zQsq In zQsq
zQFq.WriteLine _
zQsq
Next: zQFq.Close '!!!
Shell "explorer.exe" & " " & zQ0q, vbNormalFocus
Shell "notepad.exe " & " " & zQ1q, vbNormalFocus
Kill zQ1q '!!!
Beep
End Sub
Что код делает:
Находит SpecialFolders("MyDocuments") - которая примечательна тем, что во всех версиях винды она лежит прямо в папке Пользователя.
Находит папку Пользователя - через Split по "\" и через "UBound - 1".
Создаёт в папке Пользователя .txt файл со случайным именем.
Записывает в этот .txt все возможные SpecialFolders.
Открывает этот .txt и папку Пользователя через Shell.
Удаляет этот .txt.
Передаёт на секретную веб-страницу данные фашего паспорта и дебетовой карты (на самом деле нет)))
В общем, я вполне уверен, что папка Пользователя в Win XP тоже разрешает создание файлов. Но я буду признателен, если кто-то из владельцев хрюши здесь это подтвердит)) И ещё мне не помешает список "всех возможных SpecialFolders" (см. п. 4).
Public zQQCancel As Boolean
'--------------------------------------------------
Option Explicit
'--------------------------------------------------
Sub zQCallingSub()
Call zQSubQ0(Array( _
Array("zQSubQ1", 1, Application, "String", Array(1, 2, 3, 4)), _
Array("zQSubQ2", 1, Application, "String", Array(1, 2, 3, 4)), _
Array("zQSubQ3", 1, Application, "String", Array(1, 2, 3, 4)), _
Array("zQSubQ4", 1, Application, "String", Array(1, 2, 3, 4)) _
))
End Sub
'--------------------------------------------------
Sub zQSubQ0(zQArgs As Variant)
Dim zQEach As Variant
For Each zQEach In zQArgs
If zQQCancel = True Then zQQCancel = False: Exit Sub
Select Case UBound(zQEach)
Case 0: Application.Run zQEach(0)
Case 1: Application.Run zQEach(0), zQEach(1)
Case 2: Application.Run zQEach(0), zQEach(1), zQEach(2)
Case 3: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3)
Case 4: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3), zQEach(4)
Case 5: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3), zQEach(4), zQEach(5)
Case 6: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3), zQEach(4), zQEach(5), zQEach(6)
Case 7: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3), zQEach(4), zQEach(5), zQEach(6), zQEach(7)
Case 8: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3), zQEach(4), zQEach(5), zQEach(6), zQEach(7), zQEach(8)
Case 9: Application.Run zQEach(0), zQEach(1), zQEach(2), zQEach(3), zQEach(4), zQEach(5), zQEach(6), zQEach(7), zQEach(8), zQEach(9)
Case Else: MsgBox "!!!", vbCritical: Exit Sub
End Select
Next
Beep
End Sub
'--------------------------------------------------
'Runеные сабы оставил за кадром.
Собственно, вопрос: можно ли вот этот Select Case как-то, ну чтобы Run глотал zQEach целиком; но НЕ как массив (как например Run глотает его 5й элемент, zQEach(5); который сам по себе массив Array(1, 2, 3, 4)), а нужна какая-то херня, которая бы каждый элемент zQEach - пихнула бы автоматически в поле аргумента Run. ??? есть или нет такая буква
Edit: на другом форуме мне подсказали, что VBA так не умеет.
Я написал макрос сохранения копии файла, который работает и в Excel, и в Word. Проблема в том, что сейчас он хранится в двух разных файлах-шаблонах: • Normal.dotm для Word; • PERSONAL.XLSB для Excel.
Соответственно, если я улучшаю один - мне приходится дублировать изменения в другом.
ВОПРОС: есть ли способ подключения какой-то общей библиотеки, или я не знаю чего, чтобы макросы загружались оттуда? Чтобы это был один общий файл, к которому обращались бы и Word, и Excel.
__________________________________________ РЕШЕНИЕ (2020 - никак руки не доходили написать):
Оказалось, всё проще)) Берёшь код, и пишешь его где-то в одном месте (проекте) - либо в Word, либо в Excel; и уже из него - вызываешь какие тебе надо документы (из Экселя - Вордовские, почему бы и нет), как угодно их форматируешь и т.д.; события всякие тоже там будут работать (т.е. события Ворда - пишешь тоже в Экселе; в общем, всё в одном проекте).
))) Можно, конечно, и фигню 2019го года допилить - мелкомягким назло XD но пока оказалось не обязательно)
__________________________________________ РЕШЕНИЕ (2019):
Код
Sub zSubQ1()
Dim QPathQ0 As String
Dim QPathQ1 As String
QPathQ0 = "" 'путь к общей папке с .bas \ .cls и т.п. файлами.
Dim zQ0 As Object: Set zQ0 = CreateObject("Scripting.FileSystemObject")
Dim zQ1 As Object: Set zQ1 = zQ0.GetFolder(QPathQ0).Files
Dim zQ(0 To 1) As Object
For Each zQ(0) In zQ1 'просматривает каждый файл в общей папке.
QPathQ1 = QPathQ0 & zQ(0).Name
Set zQ(1) = ThisWorkbook.VBProject.VBComponents.Import(QPathQ1) '*
'''Select Case ?..
zQ(1).Name = Replace(zQ(0).Name, ".", "") '*
Next
'*: без этого - VBA импортирует их с именем по умолчанию, типа "Module1".
End Sub
Ещё не тестировал, но идея в том, чтобы при каждом запуске документа (Excel \ Word) - подгружать в его проект файлы из общего каталога; а при выходе из документа - удалять их (пока тоже за кадром).
Ремарка "'''Select Case ?.." - это недописанное условие проверки.
ДА, и у Word'а проблемы с Document_Open - когда создаёшь документ путём запуска WINWORD.EXE (т.е., при первом запуске приложения); мне помогла такая вот простенькая заплатка (не пугайтесь тонны текста - там, в общем-то, готовая копипаста): https://wordmvp.com/FAQs/MacrosVBA/PseudoAutoMacros.htm
У меня на единственном листе событие Worksheet_Change примерно следующего содержания:
Код
Private Sub Worksheet_Change(ByVal zChange As Range)
Application.EnableEvents = False
If Columns(zChange.Column) = 1 _
Then
Application.Undo: Exit Sub
Else
End If
Application.EnableEvents = True
End Sub
- т.е., попытка изменения данных внутри колонки 1 приводит к отмене изменений. Так я защищаю ячейки с данными.
Было замечено, что если вызвать стандартное окно "Найти", и произвести замену - события Worksheet_Change начнут выполняться последовательно для ВСЕХ ячеек, которые подошли условию замены (для 3х ячеек - 3 отдельных события Worksheet_Change, подряд). Т.е., zChange.Count в таком случае ВСЕГДА = 1 - даже если "изменённых заменой" ячеек больше одной (!!!), что отличает данный случай Worksheet_Change от случая удаления\переноса строк, например, где zChange.Count был бы кратен 16384.
Проблема оказалась в том, что если "изменённых заменой" ячеек получится ЧЁТНОЕ число - чётное число раз подряд выполненный Application.Undo, в рамках каждого отдельного Worksheet_Change, приведёт к тому, что изменения НЕ будут отменены (из-за отмены "отмены" изменений).
И если в случае переноса\удаления строк, я бы мог видоизменить код следующим образом:
Код
Private Sub Worksheet_Change(ByVal zChange As Range)
Application.EnableEvents = False
If Columns(zChange.Column) = 1 _
And zChange.Count = 1 _ '<<< добавленное условие.
Then
Application.Undo: Exit Sub
Else
End If
Application.EnableEvents = True
End Sub
- позволяя, тем самым, переносить\удалять строки, то для окна замены это снова приведёт к озвученной проблеме.
И т.д., и т.п.
...Вопрос: есть ли способ узнать, воспользовался ли пользователь окном замены - ???
__________________________________________ Для решения проблемы достаточно "перевоткнуть" (в макросе!) любую ячейку на Листе, после Application.Undo: тогда история изменений документа обнулится, и следующую отмену выполнить не удастся!
А чтобы система не ругалась на невозможность отмены - перед отменой поместите On Error Resume Next.
Примерно в таком виде:
Код
Private Sub Worksheet_Change(ByVal zChange As Range)
Application.EnableEvents = False
On Error Resume Next
Application.Undo (внутри условий \ как угодно)
Cells(1, 1). Value = Cells(1, 1).Value
Application.EnableEvents = True
End Sub
В упрощённом виде, у меня UserForm1 содержит MultiPage1 из 2х страниц ("1" и "2"):
Код
Private Sub UserForm_Initialize()
блаблабла
MultiPage1.Value = 1
End Sub
Private Sub MultiPage1_Change
Select Case MultiPage1.Value
Case 1: блаблабла
Case 2: блаблабла
End Select
End Sub
- т.е. я хочу, чтобы при каждой (новой) инициализации, UserForm переключал MultiPage1 на страницу "1", чтобы второе событие - MultiPage1_Change - сделало своё не-важно-какое дело.
Проблема вылезла совершенно ЛЕВОГО характера: оказалось, VBA отказывается запускать MultiPage1_Change, если MultiPage1 УЖЕ находится на странице "1", ДО запуска UserForm! И при том - НЕ где-то в памяти, а в ОКНЕ РЕДАКТИРОВАНИЯ UserForm1, открываемом из списка модулей (не знаю, как это окно правильно называется; "View Object", а НЕ "View Code" которое; "Shift + F7" - оно же).
!!!???..
Т.е., если я в этом окне редактирования открою страницу "2", ПЕРЕД запуском UserForm - переключение сработает! Но это [цензура]-как неудобно, ясное дело - выставлять MultiPage на "правильную" страницу, каждый раз, в условиях огромного количества страниц и объектов.
Переключение также сработает, если я создам 3ю, НЕНУЖНУЮ (пустую) страницу для MultiPage1 (и для многих других), и буду скрывать её при каждом открытии, соответственно.
Приготовлю файл, если потребуется, но... может, кто-то уже знает, как ПРИНУДИТЬ MultiPage принимать значение, которому она уже равна?
Вот, есть у нас Union(), да? Он объединяет несколько объектов типа Range (строго говоря, Union увеличивает количество ячеек в диапазоне, путём объединения двух изначально меньших диапазонов).
А есть ли в VBA функция уменьшения диапазонов (путём исключения меньших из большего) - ??? Было бы круто, если так же как в Union можно было бы указать условно-неограниченное число элементов. Кроме манипуляций с Intersect, само собой.
Чтобы получилось что-то вроде:
Код
Set zRange = Colimns(1).EntireColumn
Set zRange = "UnUnion"(zRange, Cells(1, 1))
- т.е., чтобы эксель сам понял, что zRange у нас теперь = Range(Cells(2, 1), Cells(1048576, 1)).
Придумал хитрый план - подглядев технологию у объектов типа Range:
Код
Private Sub MultiPage2_Change()
Dim zTextBox As TextBox
Select Case MultiPage2.Value
Case 1: Set zTextBox = TextBox1
Case 2: Set zTextBox = TextBox2
Case 3: Set zTextBox = TextBox3
Case 4: Set zTextBox = TextBox4
End Select
With zTextBox
.SetFocus
.SelStart = 0
End With
End Sub
...но он не сработал: с Set'ом - возвращает "Type mismatch"; без Set'a - "Object variable or With block variable not set" (eye roll).
Хотелось, чтобы при переключении MultiPage2 - срабатывал фокус на тот, или иной TextBox, расположенный (само собой) на соответственном листе. Ну и чтобы код был симпатичным.
Изменить "TextBox1" на "UserForm1.TextBox1" пробовал. Пробовал также и на "Me.TextBox1": ничего не изменилось.
В Locals вообще какая-то ахинея, но типы данных вроде соответствуют.
Public zStr As String = "" (при открытии файла). Кнопочно-нажимной макрос присваивает zStr значение = ActiveCell.Value
Public Function возвращает значение zStr. В теории.
На практике же ДАЖЕ при включённом Calculation = xlAutomatic Public Function возвращает zStr ТОЛЬКО если нырнуть в ячейку вывода. Т.е., автоматический пересчёт не работает.
Файл прилагаю. Незамысловатый код:
Код
Public zStr As String
Код
Sub Q1()
zStr = ActiveCell.Value
Beep
End Sub
Код
Public Function zStr_Value() As String
zStr_Value = zStr
End Function
Application.Volatile не помогает. Видимо, поможет только Worksheet_Change.
Я передал несколько переменных из Worksheet_Change Листа:
Код
Private Sub Worksheet_Change(ByVal zChange as Range)
...
Dim zRng As Range
Dim zRngCount As Integer
Dim zArr(1 to zRngCount - предположим, что она здесь уже определена как конкретное число)
Call zCall(zArr, zRng, zRngCount)
...
((после выполнения zCall - продолжается выполнение Worksheet_Change))
End Sub
в Sub отдельного модуля:
Код
Sub zCall(zArr, zRng As Range, zRngCount As Integer)
For i = 1 to zRngCount
zArr(i) = ((присваиваю некое значение из области zRng))
Next i
End Sub
В действительности, каждой из представленных переменных несколько десятков, и для каждой производится не одна, а множество процедур в Sub'e (в частности - не один "For", а десятки).
Проблема в том, что выполнение zCall (вызванного из Worksheet_Change) непостижимым образом прерывается, прямо посреди одного из ничем не примечательных циклов. Даже сам цикл не завершается! Выполнение Worksheet_Change продолжается, но результаты "передачи" переменных получаются неверными.
Факты могу привести следующие: - без переноса, код работает идеально (перенос потребовался из-за возникшей на днях "Procedure too large"); - прерывание происходит тем позже, чем меньше длина массива zRng (зависит от порядкового номера строки, изменение ячейки\ячеек которой инициировало Worksheet_Change); но если редактировать одну и ту же ячейку (при прочих равных условиях), прерывание происходит в одном и том же месте; - часть кода, которую Sub "успевает" отработать полностью (до прерывания) - даёт верный результат; - в режиме дебаггера видно, что на некоторых "не отработанных" массивах (при наведении курсором, уже после возвращения в Worksheet_Change) висит статус "Subscript out of range"; - прерывание (в Sub'e) происходит примерно на 240-280 строке кода; - Sub после вызова из Worksheet_Change выполняется беспрерывно, не считая причины публикации вопроса: никаких других процедур не предусмотрено, никаких условий выхода \ вызова других процедур, ничего такого; - Excel 2013, 32 bit, сборка стандартная: плагинов \ расширений нет; - ни на одном этапе, никаких ошибок Excel при этом не выдаёт.
Может я не верно произвожу процедуру передачи? Но об этом можно судить из оформления метода Call. Может у Sub'ов какое-то ограничение (ну там, не знаю, по количеству итераций)? Хотя я пробовал прогнать переданную таким же образом переменную через 30000 итераций в одном-единственном цикле: всё прошло успешно.
Странно то, что прерывание может произойти ПРЯМО посреди цикла! For i, Next i, Next, Next... А потом - БАЦ! И не дойдя до следующего "Next" (не говоря про остальные строки кода), его выкинет обратно в Worksheet_Change. И так каждый раз, и зависит - такое впечатление - только от длины zRng (т.е., в разных случаях обрываются разные циклы; хотя все они идентичны: лишь порядковые номера у заполняемых массивов разные).
К сожалению, не могу привести код целиком.
2018.05.03 Товарищи, прошу простить грешного: ошибка оказалась моя личная. Длина одного из массивов, используемых внутри цикла, была на 1цу меньше по сравнению с длиной цикла (это правильно, не вдаваясь в подробности, с точки зрения внутренней логики). Уж не знаю, почему, но при передаче в модуль - это вызывало крах процедуры. До передачи же (когда ещё не было ошибки "too large") - он, судя по всему, просто дальше продолжал.
Всем большое спасибо за участие! Ваши советы и опыт всегда мне очень помогали!
2018.05.24 А ещё это говно лечится простой резолюцией где-нибудь в начале процедуры (или перед проблемным местом):
Код
On Error Resume Next
- ибо действует только в пределах процедуры, в отличие от Application.EnableEvents (что логично, конечно, но всё равно вымораживает), и при разделении одной процедуры на несколько - соответственно, теряется; потому он, собственно, и "продолжал" до разделения.
Private Sub Worksheet_Change (ByVal zChange As Range)
(более 5000 строк)
End Sub
Сегодня получил ошибку "Procedure too large" при попытке запуска. Узнал об ограничении.
Суть кода в том, что если я изменяю на листе ячейку из определённой колонки (например, из колонки "1"), то такая ячейка будет обработана (назовём её "zCell"). В частности - на основе данных этой изменённой ячейки, будет рассчитан ряд переменных:
Код
zCellUPRng As Range
- все ячейки колонки "1" ВЫШЕ zCell.
zCellDNRng As Range
- все ячейки колонки "1" НИЖЕ zCell.
zArrUPRowS(1 To zCellUPRng.Count)
- массив номеров строк, соответствующих ячейкам из zCellUPRng;
такой же - для zCellDNRng.
zArrUPValueS(1 To zCellUPRng.Count)
- массив значений, соответствующих ячейкам из zCellUPRng;
такой же - для zCellDNRng.
после этого, я разбиваю их на все ДОПУСТИМЫЕ значения (которые перед этим ограничиваю),
и составляю из них следующие списки:
Dim zArrUP8(1 To zCellUPRng.Count)
- список номеров строк, значения ячеек которых = 8 (всего 8 списков, от 1 до 8);
получаю я его следующим образом:
For i = 1 to zCellUPRng.Count
If zArrUPValueS(i) = 8 _
Then
zArrUP8(i) = zArrUPRowS(i)
Else
End If
Next i
и далее происходит ещё много-много чего.
Таким образом, я столкнулся с проблемой перегрузки процедуры.
Пожалуйста: НЕ предлагайте введения циклов и прочих агрегаторов, вроде "With": ни один из них для меня не новость.
Я пытаюсь решить проблему путём ПЕРЕДАЧИ переменных, в связи с чем и возник мой ВОПРОС.
Товарищ vikttur в своё время пролил мне свет на то, как можно передавать переменные между процедурами, и\или из одного модуля в другой - за что ему отдельное спасибо, ибо с тех пор качество моей жизни значительно улучшилось))) Однако в случае Worksheet_Change у меня возникли трудности.
Насколько я понимаю данную процедуру, весь "перенос" сводится к работе с командой "Call". Например, в отдельном модуле определяем:
Код
Sub z_Call1(zAnotherCell As Range)
Set zAnotherCell = Cells(1, 1)
End Sub
- тогда в Private Sub Worksheet_Change я бы написал:
Код
Dim zAnotherCell As Range
Call z_Call1(zAnotherCell)
Здесь команда "Call" позволила бы мне оперировать свойствами ячейки (1, 1).
Но как мне быть в случае Worksheet_Change, когда отдельным модулям необходимо оперировать свойствами ИЗМЕНЯЕМОЙ ячейки - ???..
Как, например, мне перенести в отдельный модуль процесс создания одного из упомянутых выше списков:
Код
Dim zArrUP8(1 To zCellUPRng.Count)
- список номеров строк, значения ячеек которых = 8 (всего 8 списков, от 1 до 8);
получаю я его следующим образом:
For i = 1 to zCellUPRng.Count
If zArrUPValueS(i) = 8 _
Then
zArrUP8(i) = zArrUPRowS(i)
Else
End If
Next i
- ведь вместе с ним нужно будет перенести и прочие переменные!
Код
zArrUPValueS(1 To zCellUPRng.Count)
- массив значений, соответствующих ячейкам из zCellUPRng;
такой же - для zCellDNRng.
zArrUPRowS(1 To zCellUPRng.Count)- массив номеров строк, соответствующих ячейкам из zCellUPRng;
такой же - для zCellDNRng.
zCellUPRng As Range - все ячейки колонки "1" ВЫШЕ zCell.
- и так вплоть до "zCell", с изменения которой и началось выполнение Worksheet_Change.
Ведь это бесконечный цикл какой-то, разве нет? Может, есть какой-то пример, как люди справлялись с "Procedure too large", ИМЕННО в случае Worksheet_Change?
Изменено: RazorBaze - 30.04.2018 07:24:37(приложил файл примера с работающим кодом и пояснениями к вопросу)
Dim _
zArr1Max as Integer, _
zArr2Max as Integer, _
zArr3Max as Integer, _
zArr4Max as Integer
Есть ли способ обратиться к ОДНОЙ из этих переменных при условии значения ячейки от 1 до 4 ??? То бишь, если значение "1" - обращаемся к "zArr1Max"; если "2" - "zArr2Max", и т.д.
Я знаю только как вызвать конкретный ЭЛЕМЕНТ массива. Например: "Msgbox zAnotherArr(1)". Или можно ещё так:
Код
For i = 1 to Ubound(zAnotherArr)
Msgbox zAnotherArr(i)
Next i
Вот, вопрос, собственно: можно ли как-то похожим образом обратиться к ОДНОЙ из четырёх вышеуказанных переменных, исходя из ОДНОГО из четырёх возможных значений несвязанной с ними (например) ячейки? Чтобы получилась конструкция, вида "zArr(i)Max" - ???
Имею текст в ячейке, вида: 249234792762\\12412584935374\\1231242195982184\\3242374235882358
Он служит неким адресом строки, и я использую его в макросе.
Нужно средствами VBA (Mid? InStr? но я затрудняюсь сочинить формулу) получить кусок строки от ПЕРВОГО знака "\\" (включительно) до ВТОРОГО "\\" (не включительно), чтобы на выходе получилось: \\12412584935374
вообще строки у меня бывают разные, но во всех нужно именно от первого "\\" до второго "\\".
предполагаемый шаблон кода:
Код
Sub aaa()
Dim _
StrOld as String, _
StrNew as String
StrOld = 249234792762\\12412584935374\\1231242195982184\\3242374235882358 (или ссылка на ячейку - не важно)
StrNew = ...
Sub a()
'
Cells(Selection.Row, 11).value = 3
End Sub
Есть Private_Sub типа Worksheet_Change на Листе (Лист в Книге всего 1):
Код
Private Sub Worksheet_Change (ByVal zChange As Range)
'
Dim _
zRng as Range, _
zCell as Range
Set zRng = [вся колонка 11]
For Each zCell in zRng
If Intersect (zCell, zChange) Is Nothing _
Then
Else
MsgBox "!!!"
End If
Next zCell
Как заставить Worksheet_Change срабатывать при выполнении макросом Sub a() - ???
учусь работать с массивами. в приложенном файле имеется столбец из двоек, троек и единиц.
нужно, чтобы "For each Cell in Stolbec" (или наподобие), макрос работал с "Selection = 3" (при "Selection.Count = 1" - эти условия уже прописаны), возвращая "MsgBox" с текстом, как в образце: - номер строки с выделенной тройкой; - номер строки "ближайшей сверху" двойки; - номер строки "ближайшей сверху" единицы.
предполагается объявление одного, или нескольких массивов, которые работали бы с диапазоном D1:D44, перебирая значения сверху\вниз, и каким-то образом находили именно ту двойку и ту единицу, у которых было бы максимальное значение ".Row", но не больше, чем "Selection.Row", и меньше значений ".Row" для любых других единиц и двоек идущих после "Selection").
во вложенном файле, имеем прототип базы данных. нужно средствами VBA найти способ автоматически вставлять строку ниже той, уровень группировки которой увеличивается.
целесообразность описана в файле. если сформулировать задачу совсем просто (в качестве альтернативы чтению файла) - нужно, чтобы при изменении уровня группировки ЛЮБОЙ строки на листе, Excel возвращал MsgBox "уровень строки № (номер строки) изменён".
Sub dNOhotkey5()
'
Dim r, rng, cell As Range
Set rng = Selection
For Each r In rng
If r.Value Like "" Then
If cell Is Nothing Then
Set cell = Rows(r.Row)
Else
Set cell = Union(cell, Rows(r.Row))
End If
Else
End If
Next r
If cell Is Nothing Then
Beep
MsgBox "0"
Else
If cell.Count = 1 Then
Beep
MsgBox cell.Count
Else
Beep
MsgBox cell.Count / 16384
End If
End If
Beep
End Sub
он в пределах выделения (Selection) считает количество строк (Row), которые содержат хотя бы одну пустую ячейку.
в коде есть условие (If cell Is Nothing Then), которое повторяется 2 раза.
ВОПРОС. можно ли переписать код так, чтобы это условие 2 раза не повторять? файл прилагаю.
я хочу выбрать некоторое количество ячеек (одну, две, или все) в столбце B (VBA: "Selection"). пусть будет макрос, который проверит значение соседней ячейки в столбце А (VBA: Offset 0, -1).
это могут быть и другие столбцы, поэтому нужны именно Offset и Selection (или их эквиваленты, мне неизвестные). тогда:
ЕСЛИ ячейка столбца А, соответствующая ячейке столбца B, равна "1" - вся строка удаляется целиком, и так для каждой строки (ячейки столбца B) в Selection.
т.е., на выходе, это аналогично фильтрации (как если бы я в столбце А выбрал только нули), но мне нужно, чтобы эксель напрочь удалял такие строки.
мой макрос работает, но только для одной ячейки, а не всего Selection хотя Next вроде на месте. файл прилагаю.
я собираюсь выбирать (VBA: "Selection") любое значение из колонки B. нужно, чтобы: если Selection.Row < 10 - значение Selection копировалось в соседнюю ячейку колонки А. в противном случае - значение Selection копировалось в соседнюю ячейку колонки B.
и чтобы после копирования (в колонку А, или колонку B - в зависимости от Selection.Row), к результату был применён Replace (допустим, была удалена цифра "0").
Если выделение (ячейка) находится ниже десятой строки - ячейка справа от неё объявляется переменной "а". Если выше десятой строки - ячейка слева.
Нужно, чтобы переменная "а" приняла значение выделенной ячейки, а затем - в этой же "а" выполнилась некая подстановка (например, исчезли запятые).
мой неработающий код:
Код
Sub a()
If Selection.Row > 10 Then
a = Selection.Offset(0, 1)
Else
a = Selection.Offset(0, -1)
End If
a.Formula = Selection
a.Replace What:=",", Replacement:=""
End Sub
я понимаю, что без файла не интересно)) но всё же, подскажите, что не так?
подозреваю, что "a.Formula" и "a.Replace" некорректны для поставленной задачи.
в приложенном файле (можно с нуля создать свой) ячейка a1 содержит формулу, длина которой НЕ умещается в формульной строке fx
имеется кнопка, вызывающая макрос (ЛЮБОЙ макрос; мой выполняет команду ctrl + home) проблема возникает при выполнении макроса нажатием экранной кнопки ТОГДА, когда выбрана ячейка, формула или текст которой не умещаются в формульной строке: содержимое формульной строки становится искажённо-жирным.
проблема возникает даже тогда, когда макрос ПУСТОЙ! вида:
Код
sub a()
end sub
проблемы НЕ возникает, если: 1) развернуть формульную строку до ширины ДОСТАТОЧНОЙ, чтобы увидеть её содержимое целиком; 2) изначально нажать на кнопку, стоя там, где содержимое формульной строки видно без расширения; 3) перейти в режим редактирования ячейки (F2).
кроме того, проблемы НЕ возникает, если выполнить макрос не кнопкой, а из меню макросов. или сочетанием клавиш (у меня Ctrl + й). ???
одна из ячеек в файле примера после применения условного форматирования (макросом) должна окраситься в красный цвет, потому что её текст содержит текст в формате юникод (грустный смайлик).
однако при попытке вставить юникод в VBA - он автоматически заменяется на "?".
при этом если узнать код конкретного символа =КОДСИМВ() - в моём случае это "63" - и попытаться заменить "?" в VBA на "Chr(63)" - условное форматирование этого не понимает, и вместо юникода - ищет "Chr(63)".
Стандартная функция Excel "Заполнить" > "Выровнять" (она же в VBA: "Selection.Justify") работает только с первыми 255 символами в ячейке, как и множество других подлых функций (например, "СЧЁТЕСЛИ").
Возможно ли обойти это ограничение? Возможно ли написать пользовательскую функцию? Может есть вообще другой способ?
Задача: преобразовать текст в ячейке таким образом, чтобы рядом с ним создалось количество строк, равное количеству слов в ячейке (по факту - количеству пробелов между словами, +1). Чтобы у каждого слова из первоначальной ячейки была своя собственная строка. Вот так: Чтобы у каждого слова из первоначальной ячейки была своя собственная строка. Вот так: (а потом я ещё всякие символы лишние, вроде ":" и "." убираю). "Текст по столбцам" не предлагать. Спасибо.
Таблица примерно 10,000 строк на 1,000 столбцов. С каждым годом будет примерно вдвое больше. Ячейки 800 из 1,000 столбцов так или иначе ссылаются друг на друга. У формул очень сложные условия (до десятков "ЕСЛИ"), причём я задавал их таким образом, чтобы каждый столбец сперва проверял значение ПЕРВОГО - самого главного в иерархии - столбца. Выглядит это примерно так: =ЕСЛИ(А1="";"";ЕСЛИ(ЕСЛИ(ЕСЛИ(...))), - т.е., если ячейка первого столбца пустая, то формула возвращает пустое же значение, и ресурсы на проверку остальных условий не расходуются.
Всё это делалось с целью уменьшения времени пересчёта всех этих десятков условий. Однако проверка специальным калькулятором пересчёта показала, что эксель дольше всего "думал" не над "формульными" ячейками, а над тем самым "главным" столбцом А1, на который ссылается большинство формул. Причём ячейки столбца А1 не содержат вообще никаких формул. Только текстовые значения (или их отсутствие, "пустоту").
"Application.Calculation = xlManual" помогает, но при включении "автомата" (или там, сохранении) пересчёт происходит слишком долго. Может быть есть какое-то более сильное колдунство?
Все знают, что если начать ячейку с "=", а следом ввести букву (например, "Е") - появится список функций, начинающихся на эту букву: - ЕОШИБКА - ЕСЛИ - ЕСЛИПУСТО - ЕССЫЛКА - ЕТЕКСТ - ЕЧИСЛО, и т.д.
При этом, если после "Е" поставить букву "С" - все несовпадения отсеются, и останутся только: - ЕСЛИ - ЕСЛИПУСТО - ЕССЫЛКА, и т.д. Такой же список, только с другими вариантами выбора, выпадает в VBA, когда начинаешь ввод любой его функции.
Вопрос. Можно ли средствами VBA, или любыми другими средствами, сделать похожий список для другого символа? Например, чтобы при вводе кавычки (") - выпадал бы список неких вариантов, и при наборе букв - несовпадения автоматически отсеивались бы, аналогично списку формул? А перечень вариантов чтобы задавался диапазоном ячеек, как при "проверке данных". При этом хотелось бы, чтобы такой список выпадал при всяком вводе кавычек ("), а не только в начале.
В приложенном файле представлены объекты "А", "Б" и "ШАБЛОН". Каждый из них характеризуется свойствами 1 и 2. Задача: при добавлении строки "Свойство 3" для объекта "ШАБЛОН", такая же строка должна появиться у объектов "А" и "Б".
Дополнительные условия: 1) Любые изменения свойств объекта "ШАБЛОН" (например, изменение названия "Свойство 3" на "Продолжительность") должны "синхронизироваться" с остальными объектами. 2) При удалении строки "Свойство 3" у объекта "ШАБЛОН", а также при удалении любой другой строки, должны удаляться соответствующие строки и у остальных объектов. 3) Иерархия документа пострадать не должна. Желательно.