Skip to content

Use Ribbon as External DLL

harborsiem edited this page Apr 29, 2024 · 4 revisions

Use Ribbon as External DLL

Using Windows Ribbon for WinForms just got a lot easier.

Warning: Boring post. Talks about changes in the ribbon library and their reasons.

But first, let me start by asking for your forgiveness. I’m trying to create a library which will be easy to use and so the last change to the library wasn’t backward compatible. Namely, classes names and interfaces have changed. Rest assured that every single change I made is making the library a little easier to use. However, for all those who have started using the library, I say: sorry. [This is what you get for using a project in BETA]

So, what have changed?

Classes names I’ve added a “Ribbon” prefix to all the ribbon controls helper classes. This is to prevent collision with the standard WinForms controls, like Button, ComboBox, etc.

Yea, I know, this is why we have namespaces. However, having a WinForms project with both a button and a ribbon button is not a rare case. When this happen, the user can’t add “using RibbonLib.Controls;” so almost every other line is cluttered with “RibbonLib.Controls” prefix, for example:

RibbonLib.Controls.Button ribbonButton = new RibbonLib.Controls.Button(_ribbon, commandId);

Instead of:

RibbonButton ribbonButton = new RibbonButton(_ribbon, commandId);

IUICommandHandler Implementation I’ve added a general implementation of IUICommandHandler (Execute and UpdateProperty functions) to the Ribbon class, so that these methods should not be implemented anymore by the user. This means the user doesn’t need to write anymore the following code in its Form class:

public HRESULT Execute(uint commandId, ExecutionVerb verb, PropertyKeyRef key, PropVariantRef currentValue, IUISimplePropertySet commandExecutionProperties)
{
    switch (commandId)
    {
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerGroup:
            _groupColors.Execute(verb, key, currentValue, commandExecutionProperties);
            break;
        case (uint)RibbonMarkupCommands.cmdButtonsGroup:
            _groupButtons.Execute(verb, key, currentValue, commandExecutionProperties);
            break;
        case (uint)RibbonMarkupCommands.cmdButtonListColors:
            _buttonListColors.Execute(verb, key, currentValue, commandExecutionProperties);
            break;
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerThemeColors:
            _themeColors.Execute(verb, key, currentValue, commandExecutionProperties);
            break;
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerStandardColors:
            _standardColors.Execute(verb, key, currentValue, commandExecutionProperties);
            break;
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerHighlightColors:
            _highlightColors.Execute(verb, key, currentValue, commandExecutionProperties);
            break;
    }
    return HRESULT.S_OK;
}
public HRESULT UpdateProperty(uint commandId, ref PropertyKey key, PropVariantRef currentValue, ref PropVariant newValue)
{
    switch (commandId)
    {
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerGroup:
            _groupColors.UpdateProperty(ref key, currentValue, ref newValue);
            break;
        case (uint)RibbonMarkupCommands.cmdButtonsGroup:
            _groupButtons.UpdateProperty(ref key, currentValue, ref newValue);
            break;
        case (uint)RibbonMarkupCommands.cmdButtonListColors:
            _buttonListColors.UpdateProperty(ref key, currentValue, ref newValue);
            break;
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerThemeColors:
            _themeColors.UpdateProperty(ref key, currentValue, ref newValue);
            break;
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerStandardColors:
            _standardColors.UpdateProperty(ref key, currentValue, ref newValue);
            break;
        case (uint)RibbonMarkupCommands.cmdDropDownColorPickerHighlightColors:
            _highlightColors.UpdateProperty(ref key, currentValue, ref newValue);
            break;
    }
    return HRESULT.S_OK;
}

The new implementation, which resides in the Ribbon class just delegates the call to the correct ribbon control, according to the command ID:

public virtual HRESULT Execute(uint commandID, ExecutionVerb verb, PropertyKeyRef key, 
                               PropVariantRef currentValue, 
                               IUISimplePropertySet commandExecutionProperties)
{
    if (_mapRibbonControls.ContainsKey(commandID))
    {
        _mapRibbonControls[commandID].Execute(verb, key, currentValue, 
                                              commandExecutionProperties);
    }
    return HRESULT.S_OK;
}
public virtual HRESULT UpdateProperty(uint commandID, ref PropertyKey key, 
                                      PropVariantRef currentValue, 
                                      ref PropVariant newValue)
{
    if (_mapRibbonControls.ContainsKey(commandID))
    {
        _mapRibbonControls[commandID].UpdateProperty(ref key, currentValue, 
                                                     ref newValue);
    }
    return HRESULT.S_OK;
}

The _mapRibbonControls is an internal dictionary that contains all the ribbon controls helper classes the user have created in the main form.

!Note:
I’ve made these functions virtual so that if some user wants direct access to the notifications from the ribbon framework, he can still get them by deriving from the Ribbon class and overriding these methods.

Support for Ribbon External DLL Now you can have your ribbon resource reside in an external dll instead of inside the application executable, as a native resource. This issue was a problem for developers who needed the native resource for other uses (like setting the application icon).

In fact, I’ve made this the default behavior of the ribbon. So now when you call the simplest form of Ribbon.InitFramework, the ribbon library tries to load the ribbon from your_app_name.ribbon.dll and only if it fails to find this file it will revert back to the previous behavior, that is, try to load ribbon from your executable native resource.

Of course, you can provide your own dll name to load, or even load it yourself and pass the dll handle (=what returns from LoadLibrary) to the different overloads of Ribbon.InitFramework. This allows you to implement a custom ribbon loading mechanism which can be useful if you wish to load different ribbons on different scenarios, e.g. add localization support (different locale has different ribbon).

What this means for the user of the ribbon library is that he needs to add one more step to the pre-build event. This step will create the ribbon resource dll from the resource file created by previous steps. So, the pre-build events for projects that uses the ribbon library are now (example code, for building the RibbonMarkup.ribbon file use the RibbonTools):


"%PROGRAMFILES%\Microsoft SDKs\Windows\v7.0\Bin\UICC.exe" "$(ProjectDir)RibbonMarkup.xml" "$(ProjectDir)RibbonMarkup.bml" /res:"$(ProjectDir)RibbonMarkup.rc"
"%PROGRAMFILES%\Microsoft SDKs\Windows\v7.0\Bin\rc.exe" "$(ProjectDir)RibbonMarkup.rc"
cmd /c "("$(DevEnvDir)..\..\VC\bin\vcvars32.bat") && ("$(DevEnvDir)..\..\VC\bin\link.exe" /NOENTRY /DLL /OUT:"$(ProjectDir)$(OutDir)$(TargetName).ribbon.dll" "$(ProjectDir)RibbonMarkup.res")"

I know, this looks like a pile of junk, but actually what’s written is:

  • UICC – please convert RibbonMarkup.xml to RibbonMarkup.rc
  • rc – please convert RibbonMarkup.rc to RibbonMarkup.res
  • link – please convert RibbonMarkup.res to RibbonMarkup.ribbon

I’ve updated all the ribbon library samples to use the ribbon resource dll.

Table of contents

Clone this wiki locally