Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Windows Ribbon for WinForms, Part 15 – Use Ribbon as External DLL

5.00/5 (12 votes)
19 Mar 2010Ms-PL3 min read 1   2.9K  
In this article, I'll present how to load ribbon resources from external DLLs.

This series of CodeProject articles is based on a series of posts I've first published on my blog.

Using Windows Ribbon for WinForms just got a lot easier.

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

Introduction

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, class names and interfaces have changed. Rest assured that every single change I made is making the library a little more 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 has changed?

Class names

I've added a "Ribbon" prefix to all the ribbon control helper classes. This is to prevent collision with the standard WinForms controls, like Button, ComboBox, etc.

Yes, 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 happens, the user can't add "using RibbonLib.Controls;", so almost every other line is cluttered with the "RibbonLib.Controls" prefix, for example:

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

Instead of:

C#
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 the following code in the Form class anymore:

C#
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:

C#
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;
}

_mapRibbonControls is an internal dictionary that contains all the ribbon control helper classes the user has 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 the Ribbon external DLL

Now you can have your ribbon resources 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, will it 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 a 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 the previous steps. So the pre-build events for the projects that use the ribbon library are now:

"%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" 
  /v "$(ProjectDir)RibbonMarkup.rc"

cmd /c "("$(DevEnvDir)..\..\VC\bin\vcvars32.bat") && 
  ("$(DevEnvDir)..\..\VC\bin\link.exe" /VERBOSE /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 YourAppName.ribbon.dll

I've updated all the ribbon library samples to use the ribbon resource DLL.

That's it for now.

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)