Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There are a few options. The important thing is that you start designing it in early in the project. Trying to add it to an existing project can be <em>very</em> expensive if it wasn't designed with this capability in mind.</p> <p>There are a few fundamental patterns that you'll want to take advantage of:</p> <ol> <li><p><a href="http://en.wikipedia.org/wiki/Model-view-controller" rel="noreferrer">MVC</a> or <a href="http://en.wikipedia.org/wiki/Observer_pattern" rel="noreferrer">Observer</a> pattern. The first key isn't so much the religious or pattern-zealot implementation of your high-level architecture. What <em>is</em> important is that your software recognizes the difference between its current state and the displayed state, and decouples appropriately. There needs to be a common, clearly-defined coupling between your visual state and your application state. This provides you with the common architecture you need to create a command (see #2).</p></li> <li><p>The <a href="http://en.wikipedia.org/wiki/Command_pattern" rel="noreferrer">Command</a> pattern. You get a lot of quality with the command pattern (though it can come at the cost of some code that looks like it should be wizard-generated). The specific approach you take to the command pattern may vary (one class per command with implementation via overrides versus one class per many commands with implementation via event handlers, e.g.), but commands are the "action" class that @Ralph suggested structuring a stack around.</p> <p>This can be a bit tricky, but the general approach would be to listen for an event that would "commit" data from the visual state to the app state. The <a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.control.validated.aspx" rel="noreferrer"><code>Validated</code></a> event might be a good hook for a <code>Textbox</code>. The <a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.control.click.aspx" rel="noreferrer"><code>Click</code></a> event would make more sense for a <code>Button</code>. When that commit happens, you create the command associated with that control, you attempt to execute the command, and you add the command to your undo stack if the command completes successfully. Now you're tracking exactly what's happening and exactly the data it's happening with.</p></li> <li><p>The <a href="http://en.wikipedia.org/wiki/Memento_pattern" rel="noreferrer">Memento</a> pattern. @JP Pulled out the last piece of the puzzle. You can use a memento on the saved command to store what the affected control's state was before the command executed. This, combined with an <code>UnExecute()</code> member on your command's interface, should be the last core piece of design you need to perform your task.</p></li> </ol> <p>The nice thing about a structured approach like this is that you now have a natural extension point for additional behavior that needs to happen on a command-basis. Transactions are a natural fit, for example. In my current project, I'm using the WPF <a href="http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.aspx" rel="noreferrer"><code>ICommand</code></a> interface (in my winforms project) to provide feedback about whether a given command <a href="http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecute.aspx" rel="noreferrer"><code>CanExecute()</code></a> at any given time. This lets me enable and disable UI widgets appropriately in a purely command-driven way. :)</p> <p>The unfortunate thing is that there isn't a lot of support for this structure built-in to Winforms (as far as I know), so you'll need to build most of it from scratch. No single piece is particularly complicated, but you can find yourself generating a fair amount of mostly-boilerplate code for each command. It's also a pervasive design technique. For it to be effective, it has to be used consistently throughout the appropriate portions of the application. This makes retrofitting the functionality pretty expensive, especially if the original code is tightly coupled and incohesive.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload