Author |
Topic |
|
Евгений
88 Posts |
Posted - 01/20/2005 : 16:50:51
|
Как это сделать? Скажем, юзер перенес шейп туда, куда не положено. Высвечиваю в этот момент messageBox, что так нельзя, и хочу вернуть все в прежнее положение. Просто Application.Undo бросает непонятный exception. Подскажите. |
|
Евгений
88 Posts |
Posted - 01/20/2005 : 16:58:02
|
В reference написано следующее: quote: The number of times that code can call the Undo method depends on whether or not the code is executing in the scope of an open undo unit. Code runs in the scope of an open undo unit if it is:
a macro or add-on invoked by the Visio user interface. In an event handler responding to a Visio event other than the VisioIsIdle event. In a user-created undo scope. If code is not executing in the scope of an open undo unit, it can call the Undo method for each undo unit presently on the Visio undo stack. The maximum number of units on the undo stack is set in the Options dialog box on the General tab (20 is the default). If the number of calls to the Undo method exceeds the number of undo units on the stack, no action is taken and the Undo method raises no exception.
То есть, мне желательно или создать заранее user-created scope, или сделать так, чтобы мой код запускался извне open unit code. Как это возможно? |
|
|
Евгений
88 Posts |
Posted - 01/20/2005 : 18:49:54
|
Блеснула надежда выполнить Application.DoCmd (1017), но ответ, что данная команда currently disabled... |
|
|
Tumanov
Russia
1198 Posts |
Posted - 01/20/2005 : 20:49:26
|
Нехорошую операцию Вы пытаетесь сделать... Когда изменяется формула, то CellChanged срабатывает на открытом Undo Unit по изменению этой самой формулы. В этом случае Вы можете отменять не Undo, находящиеся в стеке, а только операции, выполняемые в данном макросе. А их тут нет - вот и идет exception. Более того, даже если бы и удалось отменить изменение формулы, то это действие тоже является изменением (хоть и обратным) и в свою очередь вызвало бы то же событие. То есть было бы зацикливание. Там в хелпе на тему BeginUndoScope method есть хороший примерчик, поясняющий подобную вложенность. Вот если бы дождаться завершения этого Scope, то Undo прошло бы нормально (при условии, что не вклинится какое-то незапланированное изменение). Короче, в данном случае я посоветовал бы Вам не связываться с Undo, а просто по какому-то другому событию, например, по SelectionChanged (это когда пользователь ткнет мышкой в шейп) запомнить координаты шейпа, а в CellChanged отработать их обратно. Шейп естественно вернется на прежнее место. |
|
|
Евгений
88 Posts |
Posted - 01/20/2005 : 23:23:21
|
quote: Короче, в данном случае я посоветовал бы Вам не связываться с Undo, а просто по какому-то другому событию, например, по SelectionChanged (это когда пользователь ткнет мышкой в шейп) запомнить координаты шейпа, а в CellChanged отработать их обратно. Шейп естественно вернется на прежнее место.
Все гениальное - просто! :) |
|
|
Евгений
88 Posts |
Posted - 01/21/2005 : 11:34:34
|
Все не так просто, как хотелось бы. Юзер может обозначить более чем один шейп одновременно - и сдвинуть их всех. Как теперь запомнить позиции всех обозначенных шейпов?... В качестве глобальной переменной теперь придется взять не пару x, y, а целую коллекцию пар. Как это сделать? Поискал в инете, нашел Queue, SortedList, но это видимо в VB, а как то же получить в VBA - без понятия... |
|
|
Евгений
88 Posts |
Posted - 01/21/2005 : 17:45:11
|
Проблема тут еще такая, что если юзер введет неправильное значение проперти, я должен это отловить. То есть, при любом изменении проперти проверяю правильность значении, и если оно некорректно, я должен... что сделать? Вернуть прежнее! Неужели единственным выходом тут является записывание в переменные всех значений пропертей шейпа, которых много? |
|
|
Tumanov
Russia
1198 Posts |
Posted - 01/21/2005 : 19:50:18
|
Тогда Вам не повезло. Придется разбираться с техникой Undo по всем правилам... Кстати, если бы Вы изменяли Custom Properties не подручными средствами Visio, а на своей форме, то там проверять на допустимость введенного значения было бы проще. Значение вводится в другом месте (на форме), а в Properties Вы его пишете только в том случае, если оно допустимо. В Вашем же случае можно попытаться, еще находясь в открытом Undo Unit, выставить какой-нибудь флаг, который можно проверить за пределами этого юнита, и уже там провести Application.Undo. Важно найти событие, по которому можно проверять этот флаг. Напрашивается VisioIsIdle, но как-то уж очень мне не нравится это событие... А другие происходят несколько попозже, и пользователю может показаться, будто изменение произошло, а когда он куда-то переключится, то изменение откатится, но пользователь этого уже не будет видеть... Короче, экспериментировать надо. А чисто теоретически VisioIsIdle работает. Вот если в упомянутом примере BeginUndoScope в Private Sub Visio_Application_CellChanged вставить If Visio_Application.IsInScope(m_lngScopeID) Then Flag = 1 Debug.Print Cell.Name & " changed in scope "; m_lngScopeID End If и добавить обработчик Private Sub Visio_Application_VisioIsIdle(ByVal app As IVApplication) If Flag = 1 Then Flag = 0 Application.Undo End If End Sub то при выполнении макроса Sub ScopeActions() мы успеваем заметить, как изображение появляется на рисунке, а затем исчезает - это срабатывает Undo.
|
|
|
Евгений
88 Posts |
Posted - 01/21/2005 : 20:57:27
|
Не понимаю, чем Вам не нравится этот способ? Только что сделал пару тестов - все работает на ура, именно это и было нужно. Хотя когда появляется процедура XXX_VisioIsIdle, окно редактора VB начинает неприятно мигать (видимо, из-за постоянной обработки этой процедуры). Но юзер этого не увидит, так что все замечательно! В который раз огромное спасибо! |
|
|
|
Topic |
|
|
|