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

18 בנובמבר 2009

4 תגובות

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 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 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 happens 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:

"%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,
Arik Poznanski.

kick it on DotNetKicks.com Shout it

הוסף תגובה
facebook linkedin twitter email

כתיבת תגובה

האימייל לא יוצג באתר. (*) שדות חובה מסומנים

4 תגובות

  1. Wuhi19 בנובמבר 2009 ב 17:45

    thats exactly the solution for my today´s problem :-)
    thx!

    להגיב
  2. Anonymous8 בדצמבר 2009 ב 15:54

    This is wonderful – in Visual Basic Express Edition I had to build my project manually because of creating and embedding the native resource in the .exe file every time. Now, I can build the project as usual and I can create the native resource file seperately, and only if needed. Thanks very much!

    I only had to install Visual C++ Express Edition, because I didn't have a copy of vsvars32.bat and other necessary files.

    להגיב
  3. João Pinho10 במאי 2010 ב 19:42

    Hi, can somebody tell me if the ribbon framework is compatible with windows XP ? i tried to run the samples into a machine with windows 7 and its ok but on my xp machine it trows an exception saying it cannot initialize the frwk..

    להגיב
  4. arik10 במאי 2010 ב 20:51

    Unfortunately the Windows Ribbon Framework is not compatible with Windows XP. Only Windows Vista and up.

    להגיב