I’ve been working on a MMC v3.0 snap-in for our Oak City Software project to manage configuration settings (licensing, etc) and was having some problems getting it to respond to the standard action verbs.
These are the items you see over in the Actions pane (select Customize View, then check the Action Pane checkbox) that represent standard things you can do with/to a snapin. Stuff like Refresh, Cut, Paste, Properties.
I was trying to implement the Refresh verb and it just wasn’t working.
What it turns out is that there is a disconnect in the object hierarchy between your class derived from ScopeNode and your class that derives from UserControl and implements IFormViewControl (if you’re doing a WinForm view like I was). At runtime, there is one instance of your ScopeNode, but there could be multiple instances of your FormViewControl class because the MMC environment is MDI, not SDI. So the node has no way of knowing which instance of your FormViewControl to call to tell to refresh itself. And since MMC is the one creating the instance (you tell it the Type, and it creates it for you), there’s no way for you to stick a reference to your control into the node, or other tricky ways.
What you have to do is implement the Observer pattern so that your FormViewControl subscribes to an event notificaton from the Node, and thus would know when it has to refresh itself (or cut, paste, show the properties, etc).
Here’s a code sample. First, your Node class:
// Represents the license node in the MMC tree
public class OcsLicenseNode
: ScopeNode
{
// FormViewControl instances subscribe to this to know when to refresh themselves.
public event EventHandler RefreshEvent;
// Constructor
public OcsLicenseNode()
{
EnabledStandardVerbs = StandardVerbs.Refresh;
FormViewDescription rootView = new FormViewDescription(typeof(OcsRootNodeControl));
rootView.DisplayName = "OCS License"; // TODO: get from resource file
rootView.LanguageIndependentName = "OCS License";
rootView.ViewType = typeof(OcsRootNodeFormView);
rootView.ControlType = typeof(OcsLicenseNodeControl);
}
// Called when the user selects the Refresh action
protected override void OnRefresh(AsyncStatus status)
{
base.OnRefresh(status);
// Notify observers
if (RefreshEvent != null)
RefreshEvent(this, null);
}
}
Now the FormViewControl implementation:
// The usercontrol that is shown when the user selects the License node in the MMC tree
public partial class OcsLicenseNodeControl
: UserControl, IFormViewControl
{
private FormView _view;
// Constructor
public OcsLicenseNodeControl()
{
InitializeComponent();
}
// Forces the control to invalidate its client area and immediately redraw itself and any child controls.
public override void Refresh()
{
base.Refresh();
// See if they already have a license, and if so, display it.
//
// {snip}
//
}
// Initializes the specified view.
public void Initialize(FormView view)
{
Dock = DockStyle.Fill;
_view = view;
}
}
For production code, you’d use a try-catch block in the spot where we’re invoking the delegate — if one of the event implementations causes an exception to be thrown, then it would prevent any of the other observers in the delegate linked-list from being called.
Sidenote: posting code inside Wordpress is amazingly annoying. Enough so that I might need to consider changing my blog software after only using it for a few months. Getting just this little bit required me writing a VS.NET add-in to convert spaces to non-breaking space HTML entities, replace newlines with break tags, and several other unobvious tweaks. Not to mention Wordpress itself behaving erratically.