This series of CodeProject articles is based on a series of posts I've first published on my blog.
CheckBox and ToggleButton
In short, I've added support for CheckBox and ToggleButton ribbon controls. A new sample, named 10-CheckBox has been added to the project site. The result looks like this:
Using CheckBox and ToggleButton - Ribbon Markup
Commands and Views sections:
='1.0'='utf-8'
<Application xmlns='http://schemas.microsoft.com/windows/2009/Ribbon'>
<Application.Commands>
<Command Name="cmdToggleButton"
Id="1002"
LabelTitle="Toggle Button">
<Command.LargeImages>
<Image>Res/Open32.bmp</Image>
</Command.LargeImages>
<Command.SmallImages>
<Image>Res/Open16.bmp</Image>
</Command.SmallImages>
</Command>
<Command Name="cmdCheckBox"
Id="1003"
LabelTitle="Check Box">
<Command.LargeImages>
<Image>Res/Save32.bmp</Image>
</Command.LargeImages>
<Command.SmallImages>
<Image>Res/Save16.bmp</Image>
</Command.SmallImages>
</Command>
</Application.Commands>
<Application.Views>
<Ribbon>
<Ribbon.Tabs>
<Tab>
<Group>
<ToggleButton CommandName="cmdToggleButton" />
</Group>
<Group CommandName="cmdGroupCheckBox">
<CheckBox CommandName="cmdCheckBox" />
</Group>
</Tab>
</Ribbon.Tabs>
</Ribbon>
</Application.Views>
</Application>
Using CheckBox and ToggleButton - Code-Behind
Initializing:
private Ribbon _ribbon;
private RibbonToggleButton _toggleButton;
private RibbonCheckBox _checkBox;
public Form1()
{
InitializeComponent();
_ribbon = new Ribbon();
_toggleButton = new RibbonToggleButton(_ribbon,
(uint)RibbonMarkupCommands.cmdToggleButton);
_checkBox = new RibbonLib.Controls.RibbonCheckBox(_ribbon,
(uint)RibbonMarkupCommands.cmdCheckBox);
_button.OnExecute += new OnExecuteEventHandler(_button_OnExecute);
}
Connect the IUICommandHandler
implementation with the helper classes implementations: [If this the first time you see this, you should really check the previous posts..]
public HRESULT Execute(uint commandId, RibbonLib.Interop.UI_ExecutionVerb verb,
ref PropertyKey key, ref PropVariant currentValue,
IUISimplePropertySet commandExecutionProperties)
{
switch (commandId)
{
case (uint)RibbonMarkupCommands.cmdToggleButton:
_toggleButton.Execute(verb, ref key, ref currentValue,
commandExecutionProperties);
break;
case (uint)RibbonMarkupCommands.cmdCheckBox:
_checkBox.Execute(verb, ref key, ref currentValue,
commandExecutionProperties);
break;
}
return HRESULT.S_OK;
}
public HRESULT UpdateProperty(uint commandId, ref PropertyKey key,
ref PropVariant currentValue,
ref PropVariant newValue)
{
switch (commandId)
{
case (uint)RibbonMarkupCommands.cmdToggleButton:
_toggleButton.UpdateProperty(ref key, ref currentValue, ref newValue);
break;
case (uint)RibbonMarkupCommands.cmdCheckBox:
_checkBox.UpdateProperty(ref key, ref currentValue, ref newValue);
break;
}
return HRESULT.S_OK;
}
Update (18.11.2009): The updated version of the Ribbon class provides an implementation for IUICommandHandler
, so the user doesn't need to implement the Execute
and UpdateProperty
methods anymore.
Get / Set checkbox status:
void _button_OnExecute(ref PropertyKey key, ref PropVariant currentValue,
IUISimplePropertySet commandExecutionProperties)
{
MessageBox.Show("checkbox check status is: " + _checkBox.BooleanValue.ToString());
}
Windows Ribbon for the WinForms Library - Internal Design Issues
The rest of this post is extremely boring. It discusses internal details of my ribbon library implementation. This doesn't change anything for the user of the library. Also, it has nothing to do with the checkbox feature.
Now that I'm the only one left, I can discuss some internal details of the library. I've just made a major refactoring of the ribbon library.
The ribbon library is composed of:
- Windows Ribbon Framework API wrappers
- Main ribbon class
- Helper classes for different ribbon controls, such as Button, ComboBox, DropDownGallery etc.
In the old version of RibbonLib, the control helper classes had lots of duplicated code. For instance, each control that had images attached to it (LargeImage
, SmallImage
, ... properties) needed to handle those images in the same way (manipulating internal variables, notifying the parent ribbon that an image has invalidated etc.). Now, I don't know about you, but whenever I copy-paste code, I always get this strange feeling that something is wrong. So after several sample projects, I couldn't take it anymore, and decided to redesign this section.
What I've done was encapsulate common code in classes, so they can be reused in several controls without duplicating code.
So now, every ribbon control is composed of several property providers (like ImagePropertiesProvider
and TooltipPropertiesProvider
) and event providers (like ExeciteEventsProvider
and PreviewEventsProvider
).
Note: The following sentence is hard, but there is an example right after, so be strong.
Each provider component has its own interface which the using-control also implements by delegating the execution to the component.
For example, I have an ImagePropertiesProvider
class that implements the interface IImagePropertiesProvider
, which exposes four image properties (LargeImage
, SmallImage
, ..). In my Button helper class, I create a member variable of type ImagePropertiesProvider
and make the Button class also implement IImagePropertiesProvider
by calling the corresponding methods on the member variable.
This is one of those cases where multiple inheritance is really missing. As a substitute, what I did was use multiple inheritance of interfaces, aggregation, and delegation pattern. So the controls still have some duplicated code (the delegation code), but this code is really simple and has no logic in it.
Also, each ribbon control has Execute
and UpdateProperty
methods it needs to implement according to the properties and events it exposes. So I've made a general implementation which resides in theBaseRibbonControl
class that searches the implementation of a property or event inside one of the registered providers, thus simplifying this code section in every control (now it doesn't exist, the simplest way I know).
The results of all these changes are:
- Shorter code with less duplications.
- Developing of new helper classes is extremely simple.
- Cool class diagrams..
Property Providers Class Diagram
Events Providers Class Diagram
Ribbon Controls Class Diagram
That's it for now.