|
-
Jan 8th, 2009, 04:30 PM
#1
Thread Starter
Fanatic Member
Good undo routine?
Hello all.
I'm trying to make a good undo routine for a form very similar to ms word's. I have written a stack class that I'm using as the basis of my undo data, but I need to figure out a way to actually keep track of the changing data.
I've tried pushing the address to the location where the text or value property is stored then calling it up with the following code:
The first portion of data is the data size in bytes derived by using lenb(value or data variable here). the second part of the data is the memory location where the text or data property exists derived by using varptr(text od value property here), and the third part is the text or data values themselves.
vb Code:
30 vl_Unparsed_str = Split(vm_Undo_cls.Pop, "?")
40 vl_DataSize_lng = vl_Unparsed_str(0)
50 vl_DataPtr_lng = vl_Unparsed_str(1)
60 vl_Data_str = vl_Unparsed_str(2)
70 CopyMemory vl_DataPtr_lng, vl_Data_str, vl_DataSize_lng
I use this code, but the control never displays the new data. On the other hand, the IDE doesn't crash, so I know I'm not entirely off. Maybe there's some way to manually refresh a control?
If anyone has any better methods/ideas I'd be happy to hear them!
-
Jan 9th, 2009, 02:37 AM
#2
VB/Office Guru™ (AKA: Gangsta Yoda™ ®)
I dont answer coding questions via PM. Please post a thread in the appropriate forum. 
Microsoft MVP 2006-2011
Office Development FAQ (C#, VB.NET, VB 6, VBA)
Senior Jedi Software Engineer MCP (VB 6 & .NET), BSEE, CET
If a post has helped you then Please Rate it! 
• Reps & Rating Posts • VS.NET on Vista • Multiple .NET Framework Versions • Office Primary Interop Assemblies • VB/Office Guru™ Word SpellChecker™.NET • VB/Office Guru™ Word SpellChecker™ VB6 • VB.NET Attributes Ex. • Outlook Global Address List • API Viewer utility • .NET API Viewer Utility •
System: Intel i7 6850K, Geforce GTX1060, Samsung M.2 1 TB & SATA 500 GB, 32 GBs DDR4 3300 Quad Channel RAM, 2 Viewsonic 24" LCDs, Windows 10, Office 2016, VS 2019, VB6 SP6 
-
Jan 12th, 2009, 02:45 PM
#3
Thread Starter
Fanatic Member
Re: Good undo routine?
Awesome! That helped a lot for textboxes and stuff, I'm just not sure how to take those commands and make a global undo routine.
For example, if I were to just send that message to
Me.ActiveControl.hwnd
when the ActiveControl is a RichTextBox, TextBox, ComboBox, or ListBox, the last command performed on that control exclusively. So if I make one change in txtBox1 and then 5 changes in txtBox2, then send the undo message to textbox1, the changes are undone ONLY in textbox1, not textbox2 even though the changes in textbox 2 proceeded those in textbox1.
I want the changes to occur in the order they were performed.
I have absolutely no clue how to do this without using variant/object data types and attempted to overwrite the memory location of the object itself...
-
Jan 12th, 2009, 11:05 PM
#4
Re: Good undo routine?
I guess you would have to keep a list for each input control. Seems like it would be complicated to perform supporting all the different input controls available and all the third party apps etc.
VB/Office Guru™ (AKA: Gangsta Yoda™ ®)
I dont answer coding questions via PM. Please post a thread in the appropriate forum. 
Microsoft MVP 2006-2011
Office Development FAQ (C#, VB.NET, VB 6, VBA)
Senior Jedi Software Engineer MCP (VB 6 & .NET), BSEE, CET
If a post has helped you then Please Rate it! 
• Reps & Rating Posts • VS.NET on Vista • Multiple .NET Framework Versions • Office Primary Interop Assemblies • VB/Office Guru™ Word SpellChecker™.NET • VB/Office Guru™ Word SpellChecker™ VB6 • VB.NET Attributes Ex. • Outlook Global Address List • API Viewer utility • .NET API Viewer Utility •
System: Intel i7 6850K, Geforce GTX1060, Samsung M.2 1 TB & SATA 500 GB, 32 GBs DDR4 3300 Quad Channel RAM, 2 Viewsonic 24" LCDs, Windows 10, Office 2016, VS 2019, VB6 SP6 
-
Jan 13th, 2009, 01:33 AM
#5
Thread Starter
Fanatic Member
Re: Good undo routine?
Yeah...how does ms word or other such programs do it in that case?
I figured it would be simple, since it's such a common thing...
-
Jan 13th, 2009, 02:23 AM
#6
Re: Good undo routine?
Just spitballing here...
Use a module-level udt array in each form to hold that form's undo stack. (Even Word segregates its undo operations into document-specific stacks, yes?) That array holds the complete change log.
When the user does anything to change the value of any control, add a new element to the udt array. The udt might look something like:
Code:
' In a bas module
Public Type UndoType
ControlName As String
OldValue As Variant
NewValue As Variant
End Type
' In each form
Private mtypUndo() As UndoType
Private mtypRedo() As UndoType
Private mtypCurrent As UndoType
When a control gets the focus, its name and current contents get put into mtypCurrent. When a control loses focus, its value is checked against mtypCurrent.OldValue. If it's the same, nothing happens. If it's different, the new value is put in mtypCurrent and then mtypCurrent gets appended to the end of mtypUndo(), and mtypRedo() gets erased.
When the user does an Undo, go to the last element in mtypUndo, find that control, set its value to the OldValue. Now add that element to the Redo() array and redim it out of the Undo() array.
If the user selects Redo, do basically the same thing, except now you're setting the value of the control to NewValue and moving the element from the Redo to the Undo stack.
That's the basic idea, anyway. You can write a single global function to handle capturing and setting control values for any control type. That function will probably have a decent amount of logic in it, but it only has to be written once and then it will make all your GotFocus and LostFocus events into one-liners.
You'll also need to bypass the capture logic when your global function is changing control values, to differentiate it from the user's changes.
Last edited by Ellis Dee; Jan 13th, 2009 at 02:26 AM.
-
Jan 13th, 2009, 09:10 AM
#7
Thread Starter
Fanatic Member
Re: Good undo routine?
That's very similar to my initial design. However, i suppose the only way to use the ControlName would be to say Screen.ActiveForm.ActiveControl or something...as you can't use control names as a named object or anything. That being said, I'm not sure how one would simply get the object reference to change the value of the control without iterating through each control on the form EVERY time (something I want to avoid to time's sake). Actually, come to think of it, there will be some controls that are not user-modifyable. Perhaps I could just add those controls I want to monitor to a collection. Even then, though, I'd much prefer to somehow get an object reference directly from the name itself. Do you know of a way?
What I actually did in my initial design was simply store the value whenever the textbox (or combobox, or whatever) was changed, regardless of new value or old value. I had written a stack class awhile back for error tracing and debugging that I was going to just make an instance of in this case. The Push parameter of the stack class takes a variant member, so I suppose if I were to make an array with the first element being the control name and the second being the value that could work, but I still run into the problem of named object references. Got any ideas?
-
Jan 13th, 2009, 04:26 PM
#8
Re: Good undo routine?
You can directly refer to controls using their name, eg:
Code:
Form1.Controls("Command1").Caption = "hello"
However, what I would do is slightly change Ellis's UDT, so that instead of storing the name you store the control (with a data type of Control or Object).
In terms of reading/setting the values, I would use the TypeName function with a Select Case, eg:
Code:
Select Case TypeName(TheControl)
Case "TextBox", "ComboBox", "ListBox"
OldValue = TheControl.Text
Case "Label"
OldValue = TheControl.Caption
....
-
Jan 13th, 2009, 09:50 PM
#9
Re: Good undo routine?
 Originally Posted by drag0n_45
Actually, come to think of it, there will be some controls that are not user-modifyable.
If the user can't modify them, what is there to undo?
You'll need to add dependent logic to the global SetValue function. For example, option buttons present unique issues since they have values dependent on other controls. No doubt you have other examples of dependent control values. This can all be handled fairly simply, but it'll be a fair amount of code.
-
Jan 14th, 2009, 12:14 AM
#10
Thread Starter
Fanatic Member
Re: Good undo routine?
Well, I think these two responses have solved my problem.
Ellis, I will need a collection of modifyable controls, and then the user changes a control i will check if that control is aprt of the collection or not and if it is I will push the entry onto the stack. The reason I need to collection is because I can easily store the undo info in the keypress event for the form and have keypreview set to true that was there's only one processing routine.
si, I didn't even think of that controls thing. I'd probably just set an object variable to the object iself then use a case statement as such with typof to determine the proper property.
-
Jan 14th, 2009, 06:42 AM
#11
Re: Good undo routine?
I prefer to use TypeName rather than TypeOf, as not only does it allow you to use a Select Case, but it also allows you to write code that is more portable - as you can refer to any components that have been added to your project, and not worry about whether they will be in any other projects that use the same code.
-
Jan 14th, 2009, 09:23 AM
#12
Thread Starter
Fanatic Member
Re: Good undo routine?
I'm not sure that you meant by this:
it also allows you to write code that is more portable - as you can refer to any components that have been added to your project, and not worry about whether they will be in any other projects that use the same code.
Adding the components to the project allows you to be able to refer to them with TypeOf, doesn't it?
I do like the idea of being able to use select case - I guess I just use TypeOf so I can get the popup of all possible types to use.
-
Jan 14th, 2009, 11:24 AM
#13
Re: Good undo routine?
Yes it does, but now take that code and move it to a new project, that DOESN'T have the control referenced... now the TypeOf fails because the type you are comparing it to doesn't exist in the project any more.... but TypeName will continue to work (because it's just a string).
-tg
-
Jan 14th, 2009, 06:31 PM
#14
Re: Good undo routine?
Yeah, if you copy code that uses TypeOf to another project that doesn't have all the references, the code won't even compile. Which is annoying.
You can still use Intellisense with Select Case and TypeName. I almost always use this technique with Select Case because I too love the dropdowns.
I type out:
Code:
Select Case TypeName(ctl)
Case TypeOf ctl Is
Then pick the item I want from the dropdown that appears, then delete the "TypeOf ctl Is" part, then put whatever I picked from the dropdown in quotes.
You have to be careful not to leave the TypeOf in there or your code won't work, since then the Case will evaluate to a boolean instead of a string, meaning it will never match.
-
Jan 15th, 2009, 03:25 PM
#15
Thread Starter
Fanatic Member
Re: Good undo routine?
Yes it does, but now take that code and move it to a new project, that DOESN'T have the control referenced... now the TypeOf fails because the type you are comparing it to doesn't exist in the project any more.... but TypeName will continue to work (because it's just a string).
That makes a ton of sense, but don't you need the references to use the objects anyway? Unless of course you're using CreateObject, but I don't have the balls or trust myself the get the scripting right with that.
-
Jan 15th, 2009, 04:36 PM
#16
Re: Good undo routine?
Well, yes, if it's an object you're going to use, yes, you would have the reference. So, let's say that in proj A, you use GridXYZ.... but in Project B, you don't use GridXYZ.... if you use the TypeOF, you'll have to remove the lines that reference GridXYZ..... if you then add GridXYZ you would need to add them back in.... however, if you use TypeName, it's jsut a string, you can leave the GridXYZ lines in there.... and if/when you add the grid, it's already there. Gives you a little more flexibility.
-tg
-
Jan 15th, 2009, 04:38 PM
#17
Thread Starter
Fanatic Member
Re: Good undo routine?
oohhhh.. i see what you mean.. so you can make a class or something and compile the class or module in any project. (I was thinking project-dependend code).
-
Jan 15th, 2009, 04:56 PM
#18
Re: Good undo routine?
now you're getting it.... even if you don't compile it into an asdsembly or something... jsut simply being able to copy and paste the code w/o much (if any changes) is a bonus.
-tg
-
Jan 15th, 2009, 05:02 PM
#19
Thread Starter
Fanatic Member
Re: Good undo routine?
Actually, I just thought of a problem with this:
You can directly refer to controls using their name, eg:
Code:
Form1.Controls("Command1").Caption = "hello"
Some of my controls are in a controls array - how would I use those? Command1(2) in the string??
-
Jan 15th, 2009, 05:07 PM
#20
Re: Good undo routine?
As far as I know, you can't reference them at all that way - which is part of the reason I use the other method I suggested.
-
Jan 15th, 2009, 07:43 PM
#21
Re: Good undo routine?
 Originally Posted by drag0n_45
Some of my controls are in a controls array - how would I use those? Command1(2) in the string??
The index goes after the close ")" parenthesis.
So for example, let's say you have a control array of command buttons named cmdTest. If you wanted to access control 0 in the array by name, you'd use:
MsgBox Form1("cmdTest")(0).Caption
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|