In the third trimester release of Component One Studio for WinForms in 2013, ComponentOne Themes for WinForms made their debut, offering WinForms developers the opportunity to offer their end users a little more variety when it came to styling their application’s look and feel. Several themes have now been added to the collection that ships with the studio (at the time of writing this there are over thirty) and of course there is nothing to stop the creative amongst you creating your own themes, especially as more and more of the C1 controls in the WinForms suite support the themes. Now that the end user can chose anything from a bright vivid pink to a dark format not dissimilar to expression Blend it became obvious that the humble message box, which is still incredibly useful, was rather letting the side down. dom11 What we need now is something that actively matches the theme that our end users have decided they wish to use. dom12 A few years ago I had a stab at rectifying this issue in relation to the Visual Styles that Studio for WinForms controls had been supporting for a while but whilst my solution worked it was really only of benefit to those of you who code in VB and even then lacked the true simplicity of the built in message box. With the advent of ComponentOne Themes I decided that the time had come to have another go, This time my goals were to have it work in exactly the same way as a conventional message box, be of benefit to both C1 users coding in C# (probably the majority of you I would guess (although I might argue that you’re all wrong!)) and VB and finally to ensure that it didn’t contravene the EULA for C1 controls. This time around I believe all there conditions have been met, furthermore there are a few little extras that are of immediate benefit with more to come. Before we go any further let’s take a quick look at C1 Themes, specifically one aspect of them that has really made the implementation of a C1 Themed message box so much easier to achieve.

C1 Themes

There is an excellent blog on C1 Themes written by the man behind them which, if you haven’t read it, I would urge you to do (you can find it here). To me the really interesting aspect of this was mention of the C1ThemeLocator. It occurred to me that this was the answer to a C1 Themed message box. In fact, as it turned out, the C1Themelocator is actually The way to use C1 Themes …. full stop. As is suggested by the name the C1ThemeLocator is all about locating theme resources, but it goes deeper than that because when combined with the c1ThemeController and specifically the controller’s ApplyThemeToControlTree method you suddenly have a means to apply themes entirely in code, and more specifically in very little code at all. Successful use of this is based on two simple premisses:

  1. You store the Theme definitions in a standard location.
  2. You store the theme that is currently in use in a centralized location (your project’s settings for example).

What you end up with by following these two simple rules is the ability to write code along the following lines: VB:


Private Sub LoadTheme()  
        Dim lThemeName As String = My.Settings.ThemeToUse  
        If Not String.IsNullOrEmpty(lThemeName) Then  
            Dim lThemeLocator As New C1ThemeLocator(locationType:=C1ThemeLocator.LocationType.ThemesFolder, themeName:=lThemeName)  
            Dim lTheme As C1Theme = New C1ThemeLocator(lThemeLocator).GetTheme()  
            C1ThemeController.ApplyThemeToControlTree(Me, lTheme)  
        End IF  
 End Sub  

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load  
LoadTheme()  
End Sub  

C#:


private void LoadTheme()  
{  
string lThemeName = Properties.Settings.Default.ThemeToUse;  
if (!string.IsNullOrEmpty(lThemeName)) {  
C1ThemeLocator lThemeLocator = new C1ThemeLocator(locationType: C1ThemeLocator.LocationType.ThemesFolder, themeName: lThemeName);  
C1Theme lTheme = new C1ThemeLocator(lThemeLocator).GetTheme();  
C1ThemeController.ApplyThemeToControlTree(this, lTheme);  
}  
}  

private void Form1_Load(System.Object sender, System.EventArgs e)  
{  
LoadTheme();  
}  

In both of these examples the assumption is that the theme definitions are stored in a sub directory of the application directory called Themes and that you have chosen to store a reference to the theme that is currently in use in the projects settings, specifically in a setting of type string called ‘ThemeToUse’. Applying c1 themes to forms is quite genuinely as simple as that. Create a code snippet (or a new template if you happen to be a fan of CodeRush) and it gets even easier. This logic can also be extended to the process of allowing your end user to set the theme of their choice. Typically you can achieve everything that you need to with the presence of one combo box on your form and a couple of handlers, as illustrated below (where the combo box has been named rbnCmbThemes): VB:


Private Sub rbnCmbThemes_DropDown(sender As Object, e As EventArgs) Handles rbnCmbThemes.DropDown  
        LoadThemeCombo()  
    End Sub  

    Private Sub rbnCmbThemes_ChangeCommitted(sender As Object, e As EventArgs) Handles rbnCmbThemes.ChangeCommitted  
        My.Settings.ThemeToUse = rbnCmbThemes.Text  
        LoadTheme()  
    End Sub  

    Private Sub LoadThemeCombo()  
        Dim lThemeList As New List(Of String)  
        lThemeList = GetListOfAvailableC1Themes()  
        For i As Integer = 1 To lThemeList.Count - 1  
            rbnCmbThemes.Items.Add(lThemeList.ElementAt(i))  
        Next  
    End Sub  

C#:


private void rbnCmbThemes_DropDown(object sender, EventArgs e)  
{  
LoadThemeCombo();  
}  

private void rbnCmbThemes_ChangeCommitted(object sender, EventArgs e)  
{  
Properties.Settings.Default.ThemeToUse = rbnCmbThemes.Text;  
LoadTheme();  
}  

private void LoadThemeCombo()  
{  
List<string> lThemeList = new List<string>();  
lThemeList = GetListOfAvailableC1Themes();  
for (int i = 1; i <= lThemeList.Count - 1; i++) {  
rbnCmbThemes.Items.Add(lThemeList.ElementAt(i));  
}  
}  

One point of note from the above snippets. GetListOfAvailableC1Themes() is contained within a helper class which checks for the presence of a themes directory in the first instance and if it finds it returns a list of all the available themes for display in the combo. The next decision that had to be made was what the actual message box text should be displayed in. The standard message box uses a text box control. Rich text was an option, but it doesn’t reflect C1Themes. That steered me to the C1Superlabel.

C1SuperLabel

The first reason behind choosing this came about because I had had immense difficulty in determining the size of rich text boxes constructed dynamically, probably my fault but none the less I was having trouble. Bernardo suggested at the time I tried this previously that I look at the SuperLabel and indeed that did make it easier. It did one more thing though, it added the possibility to have true html rendered in the message box. Try doing this with a standard message box…… dom13 For those of you who happen to code in VB (and who are reasonably proficient in HTML) this is an absolute joy because of its inbuilt support for xml literals. Now you have the possibility of really quite complex messages, containing tables and images., and because the C1 Superlabel supports C1 Themes the text colour should render correctly in relation to the theme that your end user has selected. It does mean however that that you need to use html tags to apply formatting though. A case in point; “this is a line” & environment.newline & “this is a second line” will not produce two lines of text one under the other. “this is a line
this is a second line” on the other hand will.

Image handling

One advantage of using the SuperLabel is that you can also embed images into you messages. The VtlMessageBox publically exposes a Dictionary (of String, Image) VtlMessageBoxImages. If you want to add images to your messages then you first need to add them to this dictionary. When you go on to create a messageBox calling VtlMessageBox.ShowWithImages the contents of the VtlMessageBoxImages will be transferred to the SuperLabel’s Images collection. This means that you can add images using the tag <img src = ‘res://KEY’>. One important point to note. The VtlMessageBoxImages dictionary is always cleared after use. Anytime that you want to use images you must populate the dictionary first. You must also use the ShowWithImages method rather than plain Show (sepite the fact that their signatures are identical) as the internal transfer only takes place when ShowWithImages is called. This may change at some point in the future rendering the ShowWithImages method obsolete (although it won’t be removed to ensure backwards compatability). However the logic to either render or not render images needs to be absolutely rock solid before the change occurs.

The VtlMessageBox

Finally we come to the message box itself. The VtlMessageBox itself is a small assembly, supplied in both .net2 and .Net4 versions. It is available via NuGet (search for Vtl Message Box) and as a result you should also get a notification when updates are available. dom14 By default VtlMessageBox produces a message box based on a C1Ribbon Form designed for use in applications which you have designed around the C1Ribbon form. However there may well be those of you who have simply chosen to use C1 controls and themes in applications based on standard windows forms. A special property (UseC1RibbonFormAsMessageBoxBase) is provided for use in these scenarios. As well as the two variants on the message box the dll also contains the helper class to facilitate the easy creation of a themes combo control in your applications. There are currently four overloads of the standard Show() function included with the VtlMessageBox which should cover most of the most common message boxes that you are likely to want to create. These are matched by four overloaded ShowWithImages() functions which are essentially duplicates of the Show() functions but with the added facility for you to include images within the message text. Rather than relying on the standard system icons (which are starting to look a little dated) VtlMessageBox ships with four newer variants on the same lines.

Some Illustrative Examples

Here you have an example of a VtlMessageBox in ExpressionDark Theme whose message is set out in tabular form with images along with a stand icon to the left. dom1 Exactly the same in Violette: dom2 And finally in Office2013White. dom3 The one proviso that you must ensure is met when you use this is that the main project which calls the VtlMessageBox contains references to c1Input, C1SuperToolTip, C1Themes and C1Ribbon.

Using the VtlMessageBox

Ribbon / Standard forms

To allow for the fact tha many of you may well be using Standard forms rather than C1 Ribbon forms the VtlMessageBOx can be configured so that it produces message boxes based on Standard forms. By default VtlMessageBoxes will always use C1 Ribbon Forms unless you tell it differently. You only need do so once and it involves setting the UseC1RibbonFormAsMessageBoxBase property to False

Adding binding redirects to your app.config file

I’d said earlier that one of my objectives with this was to ensure that not only did this respect the C1 EULA but that I did not end up having to constantly rebuild this assembly everytime that the C1 assemblies upon which it has a dependence were changed. One option would have been to use ILMerge but given that the C1Ribbon and Themes assemblies come to nearly 24mb in size I considered that this was not an option (despite the fact that it would have made it easier). In the end the obvious choice appeared to be bindingRedirect and s a result of that some additional code needs to be added to your application’s configuration file. This is the main reason why the VtlMessageBox is being distributed via NuGet because the necessary additions will be done automatically. A typical example of the addtioin that needs to be made is shown below.


&lt;runtime&gt;  
    &lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&gt;  
      &lt;dependentAssembly&gt;  
        &lt;assemblyIdentity name=&quot;C1.Win.C1Ribbon.4&quot; culture=&quot;neutral&quot; publicKeyToken=&quot;79882d576c6336da&quot;/&gt;  
        &lt;bindingRedirect oldVersion=&quot;4.0.20141.570&quot; newVersion=&quot;4.0.20142.582&quot;/&gt;  
      &lt;/dependentAssembly&gt;  
    &lt;/assemblyBinding&gt;  
    &lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&gt;  
      &lt;dependentAssembly&gt;  
        &lt;assemblyIdentity name=&quot;C1.Win.C1Themes.4&quot; culture=&quot;neutral&quot; publicKeyToken=&quot;594a0605db190bb9&quot;/&gt;  
        &lt;bindingRedirect oldVersion=&quot;4.0.20141.51&quot; newVersion=&quot;4.0.20142.52&quot;/&gt;  
      &lt;/dependentAssembly&gt;  
    &lt;/assemblyBinding&gt;  
    &lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&gt;  
      &lt;dependentAssembly&gt;  
        &lt;assemblyIdentity name=&quot;C1.Win.C1SuperTooltip.4&quot; culture=&quot;neutral&quot; publicKeyToken=&quot;79882d576c6336da&quot;/&gt;  
        &lt;bindingRedirect oldVersion=&quot;4.0.20141.137&quot; newVersion=&quot;4.0.20142.140&quot;/&gt;  
      &lt;/dependentAssembly&gt;  
    &lt;/assemblyBinding&gt;  
      &lt;assemblyBinding xmlns=&quot;urn:schemas-microsoft-com:asm.v1&quot;&gt;  
        &lt;dependentAssembly&gt;  
          &lt;assemblyIdentity name=&quot;C1.Win.C1Input.4&quot; culture=&quot;neutral&quot; publicKeyToken=&quot;7e7ff60f0c214f9a&quot;/&gt;  
          &lt;bindingRedirect oldVersion=&quot;4.0.20141.33362&quot; newVersion=&quot;4.0.20142.33409&quot;/&gt;  
        &lt;/dependentAssembly&gt;  
    &lt;/assemblyBinding&gt;  
  &lt;/runtime&gt;  

Unfortunately all is not yet sweetness and light, you will need to make some adjustments to this section in your app.config file to ensure that the oldVersion matches what it should be (you’ll find the relevant value in a comment above the line where it needs to be altered, and the newVersion value needs to be set to the current version of the C1 assembly in question. This is a prime canditate for some sort of automation, and the fact that this is delivered via NuGet means that it should be possible to incorportae that as well.

Step list

  1. Add the VtlMb Nuget package to your project ( I would suggest doing so after you have added references to C1Ribbon, C1Themes, C1Input and C1SuperTooltip if you don’t already have them)
  2. Open up the app.config file and make the necessary adjustments to Old and NewVersions. If you added the NuGet package after adding the references to C1 assemblies then you should only need to adjust the oldVersion. NB you will need to adjust the newVersion if you run C1 live and add even newer versions of the C1 assemblies.
  3. Add a using or imports ViewToLear.Vtlmb statement to any class or module in which you wish to access the VtlMessagebox or its properties
  4. Somewher at the start of your application set the theme that the VtlMessageBox should use (CurrentC1ThemeInUse) and whetehr the messagebox should be drawn with standard forms.
  5. Call VtlMessageBox.Show or VtlMessageBox.ShowWithImages whenever you need it.

If you encounter issues with the VtlMessageBox, or require support then please submit a ticket at www.vtlsoftware.co.uk/support .

Future Enhancements

This whole project came about because I wanted an implementation of this for my own projects, and I wanted something that was simple to use and worked in pretty much the same way as the standard message box. As a result there are likely to be additions to this as it gets extended to offer the additional functionality that I seek. As I gradually got this to work I began to think that it might just be of some use to others of you still targetting winforms, so I took the decission to try and do the job properly. Consequently it has taken longer to acomplish than I had originally envisaged and led to my having to solve some problems which I would never have in normal circumstances have given any thought to. I have however learnt a few new tricks and that is always a satisfying outcome. The first goal is to automate the handling and updating of the additions that need to be made to the app.config file. Currently this is proving a little difficult but the choice of NuGet as a delivery pltform should make its delivery easy. If there are things which you believe would improve this or additional features that you’d like to see feel free to e mail me at support at vtlsoftware dot co dot uk.

License

There is no specific license, on my part at least, with which you must comply in order to make use of this. It does however go without saying that you need to have a valid C1 License. If you wish to acknowledge its use in your code that would be very kind but it is not a requirement.

Acknowledgements

As is ever the case I owe an immense debt of gratitude to a number of individuals. To Andrey Dryazgov who patiently answered some ridiculous questions on my part and most importantly provided the vital clue that ensured that this really would work the way that I had intended it to. To Dmitry Yaitskov whose original blog on the subject both awakened me to a better way to use C1 themes and thus the possibility that this whole thing might work. To Bernardo Castilho who as ever very patiently answered a number of questions for me. Finally but by no means last to Greg Lutz who has had the uneviable job of being on the receiving end of numerous e mails from me as I worked my way through writing and amending this, building various installers that worked and then didn’t and then eventually having to convert it all into the end result that you’re reading now. Gentlemen, thank you one and all.