Welcome to RenEvo Sign in | Join | Help

Your first WPF Ribbon Application

Well, in the spirit of those “great” first programs, I thought that I would post this one up as a follow up to my last news post about how I found this little gem in the WPF Futures site.

So, without further ado, lets get started.

First things first, you need to head over to codeplex and download the WPF Ribbon Preview, this will require you to fill out an Office 2007 licensing agreement, it is free, and basically says that you aren’t going to compete with Microsoft if you use this UI, and that you will adhere to the Ribbon Standards, which the control does a really good job of enforcing for you.

In the download, the file you are going to be most worried about is the “RibbonControlsLibrary.dll”, this is the assembly that contains all the Ribbon goodness. Extract that to a location where you can easily find it, and startup Visual Studio 2008.

We want to create a new WPF Application, the language you choose at this point is totally irrelevant, as most of this article is going to be in XAML. I named mine “RibbonSample”.

image

First things first, add a reference to the RibbonControlsLibrary.dll. You will need to browse to the file, the place you extracted it above. Also be sure that the “Copy Local” property for the reference is set to True.

Now lets dig into some XAML, I prefer in Visual Studio to just remove the preview pane all together when starting my XAML work.

We are going to have to add a few new schema references to the XAML, specifically for the Ribbon Control.  Lets prefix this with an “r” to keep our XAML sane. If you have never added any XAML references, it is done by simply declaring a new CLR schema reference. The code below has the added code italic and bolded.

<Window x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="Window1" Height="300" Width="300">
    <Grid>
        
    </Grid>
</Window>

Now some major changes, we are going to change the main window class to a ribbon window, modify the size, startup location, resize mode, min height, and min width. For easier layout, I am going to also change the Grid to a DockPanel. Inside the DockPanel, we also need to add an actual Ribbon, or else our app will just show up as a big black mass. This is done by simply placing a Ribbon control inside of the DockPanel. And finally, to fill up the area that we don’t need with the ribbon, lets just dump in a RichTextBox control.

After doing all of the above, the XAML to get a Ribbon on a Form up and running is only this amount of XAML.

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto"></RichTextBox>
    </DockPanel>
</r:RibbonWindow>

image

Pretty simple eh?

It doesn’t really do much at this point, as we haven’t added anything to it, but that will come in the next step.

Adding some images
Ribbon controls are very graphical, so in order to fill this thing up, lets add some images to our projects, create an images directory and find some nice png files, or use the ones provided in the download below. I used 48x48 PNG files with transparency.

Starting at the top, we are going to want to start placing some images on our form. Just like regular forms, you can add the form icon in the root window declaration.

Next we want to add a button to the QAT (Quick Access Toolbar) that is located on the form’s title bar. This is done by simply adding to the QuickAccessToolBar element for the Ribbon. For this one, we will simply add a command button that has an icon and is clickable. This part is a bit new to me, but we need to create some static resources in the form (or you can link them) for the Ribbon Commands, this makes them very re-usable, and easier to work with.  Below is the XAML for the new QAT button and its placement. You will however have to implement the “CanExecute” event in order for the button to be clickable, simply return True for this function.

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
        </ResourceDictionary>
    </r:RibbonWindow.Resources>
    
    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto"></RichTextBox>
    </DockPanel>
</r:RibbonWindow>

image

Now that we have implemented a QAT button, lets add an image to our “Start Button” for the Ribbon, this is pretty straightforward. For this image I used a 24x24 as it doesn’t streatch into the area.

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
        </ResourceDictionary>
    </r:RibbonWindow.Resources>

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu>
                    <r:RibbonApplicationMenu.Command>
                        <r:RibbonCommand SmallImageSource="Images\box.png" LargeImageSource="Images\box.png" />
                    </r:RibbonApplicationMenu.Command>
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto"></RichTextBox>
    </DockPanel>
</r:RibbonWindow>

The next step will be to add a few menu item commands, and the first one we will add a few sub-items to so you can see how the rollout menus work. As part of a work around, we will need to also add a sized rectangle to the RecentItemList of the application menu so that it will draw large enough for us to have sub items without scrolling (similar to how office works).

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 1" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 1" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\files.png" LargeImageSource="Images\files.png" />
            <r:RibbonCommand x:Key="MenuItem2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 2" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 2" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 3" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 3" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\print.png" LargeImageSource="Images\print.png" />
            <r:RibbonCommand x:Key="MenuItem4" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 4" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 4" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\diagnostic.png" LargeImageSource="Images\diagnostic.png" />
            
        </ResourceDictionary>
    </r:RibbonWindow.Resources>

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu>
                    <r:RibbonApplicationMenu.Command>
                        <r:RibbonCommand SmallImageSource="Images\box.png" LargeImageSource="Images\box.png" />
                    </r:RibbonApplicationMenu.Command>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem1}">
                        <TextBlock Text="Item 1 in the list" />
                        <TextBlock Text="Item 2 in the list" />
                        <TextBlock Text="Item 3 in the list" />
                        <TextBlock Text="Item 4 in the list" />
                    </r:RibbonApplicationMenuItem>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem2}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem3}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem4}" />
                    <r:RibbonApplicationMenu.RecentItemList>
                        <Rectangle Height="300" />
                    </r:RibbonApplicationMenu.RecentItemList>
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto"></RichTextBox>
    </DockPanel>
</r:RibbonWindow>

image

Now we can move along to the fun parts, and add a few ribbon tabs, and a few groups of buttons on those tabs.

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 1" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 1" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\files.png" LargeImageSource="Images\files.png" />
            <r:RibbonCommand x:Key="MenuItem2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 2" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 2" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 3" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 3" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\print.png" LargeImageSource="Images\print.png" />
            <r:RibbonCommand x:Key="MenuItem4" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 4" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 4" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\diagnostic.png" LargeImageSource="Images\diagnostic.png" />
            
            <r:RibbonCommand x:Key="HomeButton1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calculator" LabelDescription="Calc This!" ToolTipTitle="Calculator" ToolTipDescription="Used to do math and stuff" SmallImageSource="Images\calculator.png" LargeImageSource="Images\calculator.png" />
            <r:RibbonCommand x:Key="HomeButton2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calendar" LabelDescription="Schedule This!" ToolTipTitle="Calendar" ToolTipDescription="Schedule and remind yourself of stuff" SmallImageSource="Images\calendar.png" LargeImageSource="Images\calendar.png" />
            <r:RibbonCommand x:Key="HomeButton3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Computer" LabelDescription="Format This!" ToolTipTitle="Computer" ToolTipDescription="Where you store your naked pictures" SmallImageSource="Images\computer.png" LargeImageSource="Images\computer.png" />
            
            <r:RibbonCommand x:Key="MediaEject" CanExecute="RibbonCommand_CanExecute" LabelTitle="Eject" LabelDescription="Eject" ToolTipTitle="Eject" ToolTipDescription="Open the cup holder" SmallImageSource="Images\bt_eject.png" LargeImageSource="Images\bt_eject.png" />
            <r:RibbonCommand x:Key="MediaBackward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Previous" LabelDescription="Previous" ToolTipTitle="Previous" ToolTipDescription="Previous Tune" SmallImageSource="Images\bt_skip_backward.png" LargeImageSource="Images\bt_skip_backward.png" />
            <r:RibbonCommand x:Key="MediaPlay" CanExecute="RibbonCommand_CanExecute" LabelTitle="Play" LabelDescription="Play" ToolTipTitle="Play" ToolTipDescription="Play Tune" SmallImageSource="Images\bt_play.png" LargeImageSource="Images\bt_play.png" />
            <r:RibbonCommand x:Key="MediaStop" CanExecute="RibbonCommand_CanExecute" LabelTitle="Stop" LabelDescription="Stop" ToolTipTitle="Stop" ToolTipDescription="Stop the music" SmallImageSource="Images\bt_stop.png" LargeImageSource="Images\bt_stop.png" />
            <r:RibbonCommand x:Key="MediaForward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Next" LabelDescription="Next" ToolTipTitle="Next" ToolTipDescription="Next Tune" SmallImageSource="Images\bt_skip_forward.png" LargeImageSource="Images\bt_skip_forward.png" />
        </ResourceDictionary>
    </r:RibbonWindow.Resources>

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu>
                    <r:RibbonApplicationMenu.Command>
                        <r:RibbonCommand SmallImageSource="Images\box.png" LargeImageSource="Images\box.png" />
                    </r:RibbonApplicationMenu.Command>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem1}">
                        <TextBlock Text="Item 1 in the list" />
                        <TextBlock Text="Item 2 in the list" />
                        <TextBlock Text="Item 3 in the list" />
                        <TextBlock Text="Item 4 in the list" />
                    </r:RibbonApplicationMenuItem>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem2}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem3}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem4}" />
                    <r:RibbonApplicationMenu.RecentItemList>
                        <Rectangle Height="300" />
                    </r:RibbonApplicationMenu.RecentItemList>
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>
            <r:RibbonTab Label="Home">
                <r:RibbonGroup>
                    <r:RibbonButton Command="{StaticResource HomeButton1}" />
                    <r:RibbonButton Command="{StaticResource HomeButton2}" />
                    <r:RibbonButton Command="{StaticResource HomeButton3}" />
                </r:RibbonGroup>
            </r:RibbonTab>
            <r:RibbonTab Label="Media">
                <r:RibbonGroup>
                    <r:RibbonButton Command="{StaticResource MediaEject}" />
                    <r:RibbonButton Command="{StaticResource MediaBackward}" />
                    <r:RibbonButton Command="{StaticResource MediaPlay}" />
                    <r:RibbonButton Command="{StaticResource MediaStop}" />
                    <r:RibbonButton Command="{StaticResource MediaForward}" />
                </r:RibbonGroup>
            </r:RibbonTab>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto">
            <FlowDocument>
                <Paragraph>
                    <Hyperlink NavigateUri="http://www.renevo.com">RenEvo Software &amp; Designs</Hyperlink>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </DockPanel>
</r:RibbonWindow>

And the Preview:

image image

Now, lets work on those groups a bit, lets add a title to both groups, and make the icons for the media in a group.

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 1" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 1" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\files.png" LargeImageSource="Images\files.png" />
            <r:RibbonCommand x:Key="MenuItem2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 2" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 2" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 3" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 3" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\print.png" LargeImageSource="Images\print.png" />
            <r:RibbonCommand x:Key="MenuItem4" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 4" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 4" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\diagnostic.png" LargeImageSource="Images\diagnostic.png" />

            <r:RibbonCommand x:Key="HomeButton1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calculator" LabelDescription="Calc This!" ToolTipTitle="Calculator" ToolTipDescription="Used to do math and stuff" SmallImageSource="Images\calculator.png" LargeImageSource="Images\calculator.png" />
            <r:RibbonCommand x:Key="HomeButton2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calendar" LabelDescription="Schedule This!" ToolTipTitle="Calendar" ToolTipDescription="Schedule and remind yourself of stuff" SmallImageSource="Images\calendar.png" LargeImageSource="Images\calendar.png" />
            <r:RibbonCommand x:Key="HomeButton3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Computer" LabelDescription="Format This!" ToolTipTitle="Computer" ToolTipDescription="Where you store your naked pictures" SmallImageSource="Images\computer.png" LargeImageSource="Images\computer.png" />

            <r:RibbonCommand x:Key="MediaEject" CanExecute="RibbonCommand_CanExecute" LabelTitle="Eject" LabelDescription="Eject" ToolTipTitle="Eject" ToolTipDescription="Open the cup holder" SmallImageSource="Images\bt_eject.png" LargeImageSource="Images\bt_eject.png" />
            <r:RibbonCommand x:Key="MediaBackward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Previous" LabelDescription="Previous" ToolTipTitle="Previous" ToolTipDescription="Previous Tune" SmallImageSource="Images\bt_skip_backward.png" LargeImageSource="Images\bt_skip_backward.png" />
            <r:RibbonCommand x:Key="MediaPlay" CanExecute="RibbonCommand_CanExecute" LabelTitle="Play" LabelDescription="Play" ToolTipTitle="Play" ToolTipDescription="Play Tune" SmallImageSource="Images\bt_play.png" LargeImageSource="Images\bt_play.png" />
            <r:RibbonCommand x:Key="MediaStop" CanExecute="RibbonCommand_CanExecute" LabelTitle="Stop" LabelDescription="Stop" ToolTipTitle="Stop" ToolTipDescription="Stop the music" SmallImageSource="Images\bt_stop.png" LargeImageSource="Images\bt_stop.png" />
            <r:RibbonCommand x:Key="MediaForward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Next" LabelDescription="Next" ToolTipTitle="Next" ToolTipDescription="Next Tune" SmallImageSource="Images\bt_skip_forward.png" LargeImageSource="Images\bt_skip_forward.png" />
        </ResourceDictionary>
    </r:RibbonWindow.Resources>

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu>
                    <r:RibbonApplicationMenu.Command>
                        <r:RibbonCommand SmallImageSource="Images\box.png" LargeImageSource="Images\box.png" />
                    </r:RibbonApplicationMenu.Command>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem1}">
                        <TextBlock Text="Item 1 in the list" />
                        <TextBlock Text="Item 2 in the list" />
                        <TextBlock Text="Item 3 in the list" />
                        <TextBlock Text="Item 4 in the list" />
                    </r:RibbonApplicationMenuItem>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem2}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem3}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem4}" />
                    <r:RibbonApplicationMenu.RecentItemList>
                        <Rectangle Height="300" />
                    </r:RibbonApplicationMenu.RecentItemList>
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>
            <r:RibbonTab Label="Home">
                <r:RibbonGroup>
                    <r:RibbonGroup.Command>
                        <r:RibbonCommand LabelTitle="Programs" />
                    </r:RibbonGroup.Command>
                    <r:RibbonGroup.GroupSizeDefinitions>
                        <r:RibbonGroupSizeDefinitionCollection>
                            <r:RibbonGroupSizeDefinition>
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                            </r:RibbonGroupSizeDefinition>
                        </r:RibbonGroupSizeDefinitionCollection>
                    </r:RibbonGroup.GroupSizeDefinitions>

                    <r:RibbonButton Command="{StaticResource HomeButton1}" />
                    <r:RibbonButton Command="{StaticResource HomeButton2}" />
                    <r:RibbonButton Command="{StaticResource HomeButton3}" />
                </r:RibbonGroup>
            </r:RibbonTab>
            <r:RibbonTab Label="Media">
                <r:RibbonGroup>
                    <r:RibbonGroup.Command>
                        <r:RibbonCommand LabelTitle="Media Controls" />
                    </r:RibbonGroup.Command>
                    <r:RibbonControlGroup>
                        <r:RibbonButton Command="{StaticResource MediaEject}" />
                        <r:RibbonButton Command="{StaticResource MediaBackward}" />
                        <r:RibbonButton Command="{StaticResource MediaPlay}" />
                        <r:RibbonButton Command="{StaticResource MediaStop}" />
                        <r:RibbonButton Command="{StaticResource MediaForward}" />
                    </r:RibbonControlGroup>
                </r:RibbonGroup>
            </r:RibbonTab>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto">
            <FlowDocument>
                <Paragraph>
                    <Hyperlink NavigateUri="http://www.renevo.com">RenEvo Software &amp; Designs</Hyperlink>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </DockPanel>
</r:RibbonWindow>

image image

And now lets add a single extra item to the QAT Drop down.

image

<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 1" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 1" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\files.png" LargeImageSource="Images\files.png" />
            <r:RibbonCommand x:Key="MenuItem2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 2" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 2" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 3" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 3" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\print.png" LargeImageSource="Images\print.png" />
            <r:RibbonCommand x:Key="MenuItem4" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 4" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 4" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\diagnostic.png" LargeImageSource="Images\diagnostic.png" />

            <r:RibbonCommand x:Key="HomeButton1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calculator" LabelDescription="Calc This!" ToolTipTitle="Calculator" ToolTipDescription="Used to do math and stuff" SmallImageSource="Images\calculator.png" LargeImageSource="Images\calculator.png" />
            <r:RibbonCommand x:Key="HomeButton2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calendar" LabelDescription="Schedule This!" ToolTipTitle="Calendar" ToolTipDescription="Schedule and remind yourself of stuff" SmallImageSource="Images\calendar.png" LargeImageSource="Images\calendar.png" />
            <r:RibbonCommand x:Key="HomeButton3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Computer" LabelDescription="Format This!" ToolTipTitle="Computer" ToolTipDescription="Where you store your naked pictures" SmallImageSource="Images\computer.png" LargeImageSource="Images\computer.png" />

            <r:RibbonCommand x:Key="MediaEject" CanExecute="RibbonCommand_CanExecute" LabelTitle="Eject" LabelDescription="Eject" ToolTipTitle="Eject" ToolTipDescription="Open the cup holder" SmallImageSource="Images\bt_eject.png" LargeImageSource="Images\bt_eject.png" />
            <r:RibbonCommand x:Key="MediaBackward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Previous" LabelDescription="Previous" ToolTipTitle="Previous" ToolTipDescription="Previous Tune" SmallImageSource="Images\bt_skip_backward.png" LargeImageSource="Images\bt_skip_backward.png" />
            <r:RibbonCommand x:Key="MediaPlay" CanExecute="RibbonCommand_CanExecute" LabelTitle="Play" LabelDescription="Play" ToolTipTitle="Play" ToolTipDescription="Play Tune" SmallImageSource="Images\bt_play.png" LargeImageSource="Images\bt_play.png" />
            <r:RibbonCommand x:Key="MediaStop" CanExecute="RibbonCommand_CanExecute" LabelTitle="Stop" LabelDescription="Stop" ToolTipTitle="Stop" ToolTipDescription="Stop the music" SmallImageSource="Images\bt_stop.png" LargeImageSource="Images\bt_stop.png" />
            <r:RibbonCommand x:Key="MediaForward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Next" LabelDescription="Next" ToolTipTitle="Next" ToolTipDescription="Next Tune" SmallImageSource="Images\bt_skip_forward.png" LargeImageSource="Images\bt_skip_forward.png" />
        </ResourceDictionary>
    </r:RibbonWindow.Resources>

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                    <r:RibbonButton Command="{StaticResource MediaEject}" r:RibbonQuickAccessToolBar.Placement="InCustomizeMenu" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu>
                    <r:RibbonApplicationMenu.Command>
                        <r:RibbonCommand SmallImageSource="Images\box.png" LargeImageSource="Images\box.png" />
                    </r:RibbonApplicationMenu.Command>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem1}">
                        <TextBlock Text="Item 1 in the list" />
                        <TextBlock Text="Item 2 in the list" />
                        <TextBlock Text="Item 3 in the list" />
                        <TextBlock Text="Item 4 in the list" />
                    </r:RibbonApplicationMenuItem>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem2}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem3}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem4}" />
                    <r:RibbonApplicationMenu.RecentItemList>
                        <Rectangle Height="300" />
                    </r:RibbonApplicationMenu.RecentItemList>
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>
            <r:RibbonTab Label="Home">
                <r:RibbonGroup>
                    <r:RibbonGroup.Command>
                        <r:RibbonCommand LabelTitle="Programs" />
                    </r:RibbonGroup.Command>
                    <r:RibbonGroup.GroupSizeDefinitions>
                        <r:RibbonGroupSizeDefinitionCollection>
                            <r:RibbonGroupSizeDefinition>
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                            </r:RibbonGroupSizeDefinition>
                        </r:RibbonGroupSizeDefinitionCollection>
                    </r:RibbonGroup.GroupSizeDefinitions>

                    <r:RibbonButton Command="{StaticResource HomeButton1}" />
                    <r:RibbonButton Command="{StaticResource HomeButton2}" />
                    <r:RibbonButton Command="{StaticResource HomeButton3}" />
                </r:RibbonGroup>
            </r:RibbonTab>
            <r:RibbonTab Label="Media">
                <r:RibbonGroup>
                    <r:RibbonGroup.Command>
                        <r:RibbonCommand LabelTitle="Media Controls" />
                    </r:RibbonGroup.Command>
                    <r:RibbonControlGroup>
                        <r:RibbonButton Command="{StaticResource MediaEject}" />
                        <r:RibbonButton Command="{StaticResource MediaBackward}" />
                        <r:RibbonButton Command="{StaticResource MediaPlay}" />
                        <r:RibbonButton Command="{StaticResource MediaStop}" />
                        <r:RibbonButton Command="{StaticResource MediaForward}" />
                    </r:RibbonControlGroup>
                </r:RibbonGroup>
            </r:RibbonTab>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto">
            <FlowDocument>
                <Paragraph>
                    <Hyperlink NavigateUri="http://www.renevo.com">RenEvo Software &amp; Designs</Hyperlink>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </DockPanel>
</r:RibbonWindow>

Finally, if you want to switch to the Office 2007 look & feel, instead of the Windows 7 look & feel, simply change the Resource Dictionary (you can also create customized ones). you will want to remove the form’s icon if you use the office 2007 theme though.

<!-- Remove the Icon Property if you are going to use the Office 2007 themes-->
<r:RibbonWindow x:Class="Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:r="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
    Title="My First Ribbon Form" ResizeMode="CanResizeWithGrip" WindowStartupLocation="CenterScreen"
    Icon="Images\app.png"
    Height="600" Width="800" MinHeight="300" MinWidth="400">

    <r:RibbonWindow.Resources>
        <ResourceDictionary>
            <r:RibbonCommand x:Key="QATButton" CanExecute="RibbonCommand_CanExecute" LabelTitle="QAT Button" LabelDescription="This is a sample QAT Button" ToolTipTitle="QAT Button" ToolTipDescription="This is a sample QAT Button, it doesn't do anything" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 1" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 1" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\files.png" LargeImageSource="Images\files.png" />
            <r:RibbonCommand x:Key="MenuItem2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 2" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 2" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\save.png" LargeImageSource="Images\save.png" />
            <r:RibbonCommand x:Key="MenuItem3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 3" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 3" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\print.png" LargeImageSource="Images\print.png" />
            <r:RibbonCommand x:Key="MenuItem4" CanExecute="RibbonCommand_CanExecute" LabelTitle="Menu Item 4" LabelDescription="This is a sample menu item" ToolTipTitle="Menu Item 4" ToolTipDescription="This is a sample menu item" SmallImageSource="Images\diagnostic.png" LargeImageSource="Images\diagnostic.png" />

            <r:RibbonCommand x:Key="HomeButton1" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calculator" LabelDescription="Calc This!" ToolTipTitle="Calculator" ToolTipDescription="Used to do math and stuff" SmallImageSource="Images\calculator.png" LargeImageSource="Images\calculator.png" />
            <r:RibbonCommand x:Key="HomeButton2" CanExecute="RibbonCommand_CanExecute" LabelTitle="Calendar" LabelDescription="Schedule This!" ToolTipTitle="Calendar" ToolTipDescription="Schedule and remind yourself of stuff" SmallImageSource="Images\calendar.png" LargeImageSource="Images\calendar.png" />
            <r:RibbonCommand x:Key="HomeButton3" CanExecute="RibbonCommand_CanExecute" LabelTitle="Computer" LabelDescription="Format This!" ToolTipTitle="Computer" ToolTipDescription="Where you store your naked pictures" SmallImageSource="Images\computer.png" LargeImageSource="Images\computer.png" />

            <r:RibbonCommand x:Key="MediaEject" CanExecute="RibbonCommand_CanExecute" LabelTitle="Eject" LabelDescription="Eject" ToolTipTitle="Eject" ToolTipDescription="Open the cup holder" SmallImageSource="Images\bt_eject.png" LargeImageSource="Images\bt_eject.png" />
            <r:RibbonCommand x:Key="MediaBackward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Previous" LabelDescription="Previous" ToolTipTitle="Previous" ToolTipDescription="Previous Tune" SmallImageSource="Images\bt_skip_backward.png" LargeImageSource="Images\bt_skip_backward.png" />
            <r:RibbonCommand x:Key="MediaPlay" CanExecute="RibbonCommand_CanExecute" LabelTitle="Play" LabelDescription="Play" ToolTipTitle="Play" ToolTipDescription="Play Tune" SmallImageSource="Images\bt_play.png" LargeImageSource="Images\bt_play.png" />
            <r:RibbonCommand x:Key="MediaStop" CanExecute="RibbonCommand_CanExecute" LabelTitle="Stop" LabelDescription="Stop" ToolTipTitle="Stop" ToolTipDescription="Stop the music" SmallImageSource="Images\bt_stop.png" LargeImageSource="Images\bt_stop.png" />
            <r:RibbonCommand x:Key="MediaForward" CanExecute="RibbonCommand_CanExecute" LabelTitle="Next" LabelDescription="Next" ToolTipTitle="Next" ToolTipDescription="Next Tune" SmallImageSource="Images\bt_skip_forward.png" LargeImageSource="Images\bt_skip_forward.png" />

            <!-- Uncomment below for Office 2007 Blue -->
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/RibbonControlsLibrary;component/Themes/Office2007Blue.xaml" />
            </ResourceDictionary.MergedDictionaries>
            <!-- Uncomment below for Office 2007 Silver -->
            <!--<ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/RibbonControlsLibrary;component/Themes/Office2007Silver.xaml" />
            </ResourceDictionary.MergedDictionaries>-->
            <!-- Uncomment below for Office 2007 Black -->
            <!--<ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/RibbonControlsLibrary;component/Themes/Office2007Black.xaml" />
            </ResourceDictionary.MergedDictionaries>-->
        </ResourceDictionary>
    </r:RibbonWindow.Resources>

    <DockPanel>
        <r:Ribbon DockPanel.Dock="Top" Title="My First Ribbon Form" x:Name="mainRibbon">
            <r:Ribbon.QuickAccessToolBar>
                <r:RibbonQuickAccessToolBar>
                    <r:RibbonButton Command="{StaticResource QATButton}" />
                    <r:RibbonButton Command="{StaticResource MediaEject}" r:RibbonQuickAccessToolBar.Placement="InCustomizeMenu" />
                </r:RibbonQuickAccessToolBar>
            </r:Ribbon.QuickAccessToolBar>
            <r:Ribbon.ApplicationMenu>
                <r:RibbonApplicationMenu>
                    <r:RibbonApplicationMenu.Command>
                        <r:RibbonCommand SmallImageSource="Images\box.png" LargeImageSource="Images\box.png" />
                    </r:RibbonApplicationMenu.Command>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem1}">
                        <TextBlock Text="Item 1 in the list" />
                        <TextBlock Text="Item 2 in the list" />
                        <TextBlock Text="Item 3 in the list" />
                        <TextBlock Text="Item 4 in the list" />
                    </r:RibbonApplicationMenuItem>
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem2}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem3}" />
                    <r:RibbonApplicationMenuItem Command="{StaticResource MenuItem4}" />
                    <r:RibbonApplicationMenu.RecentItemList>
                        <Rectangle Height="300" />
                    </r:RibbonApplicationMenu.RecentItemList>
                </r:RibbonApplicationMenu>
            </r:Ribbon.ApplicationMenu>
            <r:RibbonTab Label="Home">
                <r:RibbonGroup>
                    <r:RibbonGroup.Command>
                        <r:RibbonCommand LabelTitle="Programs" />
                    </r:RibbonGroup.Command>
                    <r:RibbonGroup.GroupSizeDefinitions>
                        <r:RibbonGroupSizeDefinitionCollection>
                            <r:RibbonGroupSizeDefinition>
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                                <r:RibbonControlSizeDefinition ImageSize="Large" IsLabelVisible="True" />
                            </r:RibbonGroupSizeDefinition>
                        </r:RibbonGroupSizeDefinitionCollection>
                    </r:RibbonGroup.GroupSizeDefinitions>

                    <r:RibbonButton Command="{StaticResource HomeButton1}" />
                    <r:RibbonButton Command="{StaticResource HomeButton2}" />
                    <r:RibbonButton Command="{StaticResource HomeButton3}" />
                </r:RibbonGroup>
            </r:RibbonTab>
            <r:RibbonTab Label="Media">
                <r:RibbonGroup>
                    <r:RibbonGroup.Command>
                        <r:RibbonCommand LabelTitle="Media Controls" />
                    </r:RibbonGroup.Command>
                    <r:RibbonControlGroup>
                        <r:RibbonButton Command="{StaticResource MediaEject}" />
                        <r:RibbonButton Command="{StaticResource MediaBackward}" />
                        <r:RibbonButton Command="{StaticResource MediaPlay}" />
                        <r:RibbonButton Command="{StaticResource MediaStop}" />
                        <r:RibbonButton Command="{StaticResource MediaForward}" />
                    </r:RibbonControlGroup>
                </r:RibbonGroup>
            </r:RibbonTab>
        </r:Ribbon>
        <RichTextBox Height="auto" Width="auto">
            <FlowDocument>
                <Paragraph>
                    <Hyperlink NavigateUri="http://www.renevo.com">RenEvo Software &amp; Designs</Hyperlink>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>
    </DockPanel>
</r:RibbonWindow>

image

And that is it, a very simple look into creating and using the new WPF Ribbon Control.  There are a lot more features, but this is just a taste.

You can download the code used in this article if you like which includes a whole host of images to play with.  You will however need to get the Ribbon Control and re-adjust the reference to your location before compiling (licensing and all).

Also, Microsoft has posted an introduction to this which I kind of learned from on windowsclient.net

kick it on DotNetKicks.com

Posted by Tom Anderson | 3 Comments

Removing My Namespace from VB.Net

Recently a question arose on Stackoverflow that asked if you could remove the My namespace from vb.net.

So, before I get going with this article, I want to state that the My Namespace does have a few uses, it provides instant access to resources, settings, and quick environment settings.

I also want to state that THIS IS NOT A REQUIRED FEATURE IN VB.NET. Did I stress that yet? It is however a default feature in VB.Net.

Anyway, lets get on with it.

First things first, backup your project directory, I don’t want to be responsible for you deleting any of your settings or resources because you want to remove something you are using.

Now that you did that (right?) lets move to the solution explorer and click on the option to “Show all files”.

image

Expand the “My Project” Node and select the “Application.myapp”, “Resources.resx”, and “Settings.settings” nodes. When I say nodes, that means items below “My Project”, this is a treeview.

image

Now, hit delete. This will remove any of the “My” code that has automatically already been added to your project. Go ahead and click on the “Show all files” button again to get back to a clean view.

Next, double click on “My Project” and navigate to the Compile tab, and click on the “Advanced Compile Options”. This dialog has all kinds of fun stuff in it, but we are only worried about one particular setting. Go ahead and click on “Enable Optimizations”.

image

Click “OK” and we are about 50% done.

Now, in the solution explorer, right click on your project and select “Unload Project”.

image

This will unload your project from the IDE, but retain the reference to it, another great thing about it is it allows us to edit the .vbproj file directly instead of through the UI, which is what is required for us to do the next step.

image

Look for the <MyType> xml tag, we need to set this to “Empty”, not Empty, but with the value of “Empty”. You may also need to change the <StartupObject> tag to reflect your main form if it is currently set to “My.Application”.

Below is the XML from the first PropertyGroup after modifying it.

   1:    <PropertyGroup>
   2:      <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
   3:      <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
   4:      <ProductVersion>9.0.30729</ProductVersion>
   5:      <SchemaVersion>2.0</SchemaVersion>
   6:      <ProjectGuid>{99D23E3F-D6D5-467F-AB1D-A594E40F4378}</ProjectGuid>
   7:      <OutputType>WinExe</OutputType>
   8:      <StartupObject>RemoveMyNamespace.Form1</StartupObject>
   9:      <RootNamespace>RemoveMyNamespace</RootNamespace>
  10:      <AssemblyName>RemoveMyNamespace</AssemblyName>
  11:      <FileAlignment>512</FileAlignment>
  12:      <MyType>Empty</MyType>
  13:      <TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
  14:      <OptionExplicit>On</OptionExplicit>
  15:      <OptionCompare>Binary</OptionCompare>
  16:      <OptionStrict>Off</OptionStrict>
  17:      <OptionInfer>On</OptionInfer>
  18:    </PropertyGroup>

I bolded the change.

Save the file and close it, now right click on the project in the solution explorer and reload the project.

image

Compile it, and you can now view it in Reflector to see that the “My” namespace is completely removed.

image

We have now successfully remove the “My” namespace.

image

The keyword still exists, but it is now removed completely from your project.

kick it on DotNetKicks.com
Posted by Tom Anderson | 1 Comments

Custom Configuration Sections in Application Config files

A colleague of mine approached me the other day on how to build custom configuration section handlers in .Net.  As I have said in previous articles, I have been using “real” .config files now instead of pseudo .config, .cfg, or .xml files for complex application settings, and this has proven very portable for the configurations.

When creating custom configuration sections, there are essentially three steps.

  1. Provide a way to load the configuration section
  2. Define properties for the section
  3. Read/Write to the base object’s collection

A bit of knowledge here.  ConfigurationSection and ConfigurationElement are essentially property bags, you will be directing all of your properties in your classes to read and write to the Item collection where your property name is the key, and the value is, you guessed it, the value.

So lets start off easy, lets create the configuration section first and lets make it something simple, like a configuration section for our application windows.

   1:  Imports System.Configuration
   2:   
   3:  Public Class WindowSettings
   4:      Inherits ConfigurationSection
   5:   
   6:  End Class

Basic class, yes, now we want to define some primary properties for the section, lets add “applicationKey” so this is re-usable.

Above I mentioned that we are simply creating wrappers for the internal property bag, we also need to add an attribute to the property specifying the name of the attribute in the actual config file.

   1:      <ConfigurationProperty("applicationKey")> _
   2:      Public Property ApplicationKey() As String
   3:          Get
   4:              Return MyBase.Item("applicationKey")
   5:          End Get
   6:          Set(ByVal value As String)
   7:              MyBase.Item("applicationKey") = value
   8:          End Set
   9:      End Property

Pretty simply so far?

Now is an optional step that I like to use for testing, it really helps me work with these sections, below are some helper methods (both static and instanced) that I build for any custom section handlers, a base class is also recommended if you are going to be doing a lot of these.

   1:  #Region " Base Implementation For Load/Save "
   2:   
   3:      'only initialized internally
   4:      Protected Sub New()
   5:   
   6:      End Sub
   7:   
   8:      ''' <summary>
   9:      ''' Internal placeholder for configuration file
  10:      ''' </summary>
  11:      ''' <remarks></remarks>
  12:      Protected m_BaseConfiguration As Configuration = Nothing
  13:   
  14:      ''' <summary>
  15:      ''' Gets the custom configuration by path specifying a custom section name
  16:      ''' </summary>
  17:      ''' <param name="path">Path to the configuration file</param>
  18:      ''' <param name="section">Section in configuration file to load</param>
  19:      Public Shared Function LoadConfigSection(ByVal path As String, _
  20:                                               ByVal section As String) As WindowSettings
  21:          Dim retVal As New WindowSettings(), config As Configuration = Nothing
  22:   
  23:          Dim configFileMap As New ExeConfigurationFileMap()
  24:   
  25:          configFileMap.ExeConfigFilename = path
  26:          config = ConfigurationManager.OpenMappedExeConfiguration( _
  27:                                                              configFileMap, _
  28:                                                              ConfigurationUserLevel.None)
  29:   
  30:          If config.Sections(section) Is Nothing Then
  31:              config.Sections.Add(section, New WindowSettings)
  32:          End If
  33:   
  34:          retVal = TryCast(config.GetSection(section), WindowSettings)
  35:          retVal.m_BaseConfiguration = config
  36:   
  37:          Return retVal
  38:      End Function
  39:   
  40:      ''' <summary>
  41:      ''' Gets the custom configuration by path
  42:      ''' </summary>
  43:      ''' <param name="path"></param>
  44:      ''' <returns></returns>
  45:      ''' <remarks></remarks>
  46:      Public Shared Function LoadConfigSection(ByVal path As String) As WindowSettings
  47:          Return LoadConfigSection(path, "WindowSettings")
  48:      End Function
  49:   
  50:      ''' <summary>
  51:      ''' Gets the custom configuration
  52:      ''' </summary>
  53:      Public Shared Function LoadConfigSection() As WindowSettings
  54:          Return LoadConfigSection(Application.ExecutablePath & ".config")
  55:      End Function
  56:   
  57:      ''' <summary>
  58:      ''' Saves the current configuration
  59:      ''' </summary>
  60:      ''' <remarks></remarks>
  61:      Public Sub Save()
  62:          m_BaseConfiguration.Save(ConfigurationSaveMode.Minimal)
  63:      End Sub
  64:   
  65:  #End Region

This is the big bread and butter code that makes these easy to work with. First it makes the class construtor protected which prevents the class from accidentally being created, adds three overloaded methods for loading the config section, and then provides a save routine.

I am sure somewhere down the road I will be creating a generic implementation of this class so you can simply inherit from ConfigurationSection(of T).

So currently we have implemented all three steps required to create a basic configuration section in a standard .config file.

To load, set, and save simply do the following line of code.

   1:          Dim config As WindowSettings= WindowSettings.LoadConfigSection()
   2:          config.ApplicationKey = Application.ProductName
   3:          config.Save()

To retrieve the value, simply use the following one liner.

   1:          MessageBox.Show(WindowSettings.LoadConfigSection().ApplicationKey)

Well, all that is fine and dandy, but what if we need to do some collections in our settings, like saving each forms top, left, width, and height values?

Then we will create a ConfigurationElement and ConfigurationElementCollection.  First with the ConfigurationElement, it is almost identical to the ConfigurationSection as far as wrapping the property bags.  Although since we are setting form settings, lets add some default values to these properties so our forms are 0,0 location and 0,0 sizes.

   1:      Public Class WindowSetting
   2:          Inherits ConfigurationElement
   3:   
   4:          <ConfigurationProperty("id")> _
   5:          Public Property ID() As String
   6:              Get
   7:                  Return MyBase.Item("id")
   8:              End Get
   9:              Set(ByVal value As String)
  10:                  MyBase.Item("id") = value
  11:              End Set
  12:          End Property
  13:   
  14:          <ConfigurationProperty("top", DefaultValue:=100)> _
  15:          Public Property Top() As Integer
  16:              Get
  17:                  Return MyBase.Item("top")
  18:              End Get
  19:              Set(ByVal value As Integer)
  20:                  MyBase.Item("top") = value
  21:              End Set
  22:          End Property
  23:   
  24:          <ConfigurationProperty("left", DefaultValue:=100)> _
  25:          Public Property Left() As Integer
  26:              Get
  27:                  Return MyBase.Item("left")
  28:              End Get
  29:              Set(ByVal value As Integer)
  30:                  MyBase.Item("left") = value
  31:              End Set
  32:          End Property
  33:   
  34:          <ConfigurationProperty("height", DefaultValue:=600)> _
  35:          Public Property Height() As Integer
  36:              Get
  37:                  Return MyBase.Item("height")
  38:              End Get
  39:              Set(ByVal value As Integer)
  40:                  MyBase.Item("height") = value
  41:              End Set
  42:          End Property
  43:   
  44:          <ConfigurationProperty("width", DefaultValue:=800)> _
  45:          Public Property Width() As Integer
  46:              Get
  47:                  Return MyBase.Item("width")
  48:              End Get
  49:              Set(ByVal value As Integer)
  50:                  MyBase.Item("width") = value
  51:              End Set
  52:          End Property
  53:      End Class

 

This class is about as straight forward as they come, define properties, route to property bag, define attribute name, set default value.  We also added an ID property which will act as our key for our collection.

Which brings us to our Collection. ConfigurationElementCollections have two must overrides, CreateNewElement() and GetElementKey()  both of these methods are pretty easy to implement, and below is the most basic ConfigurationElementCollection implementation you can get by with. You must also specify an attribute to the class in order to name the element in the config file.

   1:      <ConfigurationCollection(GetType(WindowSetting), AddItemName:="windows")> _
   2:      Public Class WindowSettingCollection
   3:          Inherits ConfigurationElementCollection
   4:   
   5:          Protected Overloads Overrides Function CreateNewElement() _
   6:                                  As System.Configuration.ConfigurationElement
   7:              Return New WindowSetting
   8:          End Function
   9:   
  10:          Protected Overrides Function GetElementKey( _
  11:                                  ByVal element As System.Configuration.ConfigurationElement) _
  12:                                  As Object
  13:              Return DirectCast(element, WindowSetting).ID
  14:          End Function
  15:      End Class

That is it, but how much fun would it be if I just showed you that bit of code, and said deal with figuring out the rest on your own?  Personally, I like to add lots of helper methods to this class, again, this is another huge candidate for a generic.

   1:  <ConfigurationCollection(GetType(WindowSetting), AddItemName:="windows")> _
   2:      Public Class WindowSettingCollection
   3:          Inherits ConfigurationElementCollection
   4:   
   5:          ''' <summary>
   6:          ''' Creates a new element with default properties
   7:          ''' </summary>
   8:          Protected Overloads Overrides Function CreateNewElement() _
   9:                                          As System.Configuration.ConfigurationElement
  10:              Return New WindowSetting()
  11:          End Function
  12:   
  13:          ''' <summary>
  14:          ''' Creates a new element with the id specified
  15:          ''' </summary>
  16:          ''' <param name="id">id of the new element</param>
  17:          Protected Overloads Function CreateNewElement(ByVal id As String) _
  18:                                          As System.Configuration.ConfigurationElement
  19:              Return New WindowSetting() With {.ID = id}
  20:          End Function
  21:   
  22:          ''' <summary>
  23:          ''' Gets an element's key
  24:          ''' </summary>
  25:          ''' <param name="element">element to test</param>
  26:          Protected Overrides Function GetElementKey( _
  27:                                          ByVal element As System.Configuration.ConfigurationElement) _
  28:                                          As Object
  29:              Return DirectCast(element, WindowSetting).ID
  30:          End Function
  31:   
  32:          ''' <summary>
  33:          ''' Adds a new element to the collection
  34:          ''' </summary>
  35:          ''' <param name="element"></param>
  36:          ''' <remarks></remarks>
  37:          Public Sub Add(ByVal element As WindowSetting)
  38:              MyBase.BaseAdd(element)
  39:          End Sub
  40:   
  41:          ''' <summary>
  42:          ''' Adds a new element to the collection by id
  43:          ''' </summary>
  44:          ''' <param name="id">id of the new element</param>
  45:          Public Function AddNew(ByVal id As String) As WindowSetting
  46:              Dim newElement As WindowSetting = Me.CreateNewElement(id)
  47:              Add(newElement)
  48:              Return newElement
  49:          End Function
  50:   
  51:          ''' <summary>
  52:          ''' Adds a new element to the collection by form
  53:          ''' </summary>
  54:          ''' <param name="form">form for the new element</param>
  55:          Public Function AddNew(ByVal form As System.Windows.Forms.Form) As WindowSetting
  56:              Dim newElement As WindowSetting = Me.CreateNewElement(form.Name)
  57:              newElement.Top = form.Top
  58:              newElement.Left = form.Left
  59:              newElement.Height = form.Height
  60:              newElement.Width = form.Width
  61:              Add(newElement)
  62:              Return newElement
  63:          End Function
  64:   
  65:          ''' <summary>
  66:          ''' Removes an element by id
  67:          ''' </summary>
  68:          ''' <param name="id">id of the element to remove</param>
  69:          Public Sub Remove(ByVal id As String)
  70:              MyBase.BaseRemove(id)
  71:          End Sub
  72:   
  73:          ''' <summary>
  74:          ''' Clears all elements from the collection
  75:          ''' </summary>
  76:          Public Sub Clear()
  77:              MyBase.BaseClear()
  78:          End Sub
  79:   
  80:          ''' <summary>
  81:          ''' Retrieves an item from the collection by index
  82:          ''' </summary>
  83:          ''' <param name="index">index of the element</param>
  84:          Default Public Overloads ReadOnly Property Item(ByVal index As Integer) As WindowSetting
  85:              Get
  86:                  Return MyBase.BaseGet(index)
  87:              End Get
  88:          End Property
  89:   
  90:          ''' <summary>
  91:          ''' Retrieves an item from the collection by id
  92:          ''' </summary>
  93:          ''' <param name="id">id of the element</param>
  94:          Default Public Overloads ReadOnly Property Item(ByVal id As String) As WindowSetting
  95:              Get
  96:                  'auto-add if not exists
  97:                  If MyBase.BaseGet(id) Is Nothing Then
  98:                      Me.AddNew(id)
  99:                  End If
 100:   
 101:                  Return Me.BaseGet(id)
 102:              End Get
 103:          End Property
 104:   
 105:      End Class

All of those additional methods will give you all the tools you need to create a CRUD interface to the collection. I also added in an AddNew that simply takes a form object.

Finally to add the collection to the section, simply add a property for it.

   1:      <ConfigurationProperty("windows")> _
   2:      Public ReadOnly Property Windows() As WindowSettingCollection
   3:          Get
   4:              Return Me.Item("windows")
   5:          End Get
   6:      End Property

I also added IDisposable support to the WindowSettings class for ease of use as well as two helper methods for loading and saving form settings.

   1:      ''' <summary>
   2:      ''' Helper method to load form settings
   3:      ''' </summary>
   4:      ''' <param name="form">form to load</param>
   5:      Public Sub LoadFormSettings(ByVal form As System.Windows.Forms.Form)
   6:          With form
   7:              .Top = Me.Windows(form.Name).Top
   8:              .Left = Me.Windows(form.Name).Left
   9:              .Height = Me.Windows(form.Name).Height
  10:              .Width = Me.Windows(form.Name).Width
  11:          End With
  12:      End Sub
  13:   
  14:      ''' <summary>
  15:      ''' Helper method to save form settings
  16:      ''' </summary>
  17:      ''' <param name="form"></param>
  18:      ''' <param name="saveConfig">Optional parameter, when true will save the configuration file</param>
  19:      ''' <remarks></remarks>
  20:      Public Sub SaveFormSettings(ByVal form As System.Windows.Forms.Form, Optional ByVal saveConfig As Boolean = False)
  21:          With form
  22:              Me.Windows(form.Name).Top = .Top
  23:              Me.Windows(form.Name).Left = .Left
  24:              Me.Windows(form.Name).Height = .Height
  25:              Me.Windows(form.Name).Width = .Width
  26:          End With
  27:   
  28:          If saveConfig Then
  29:              Me.Save()
  30:          End If
  31:      End Sub

Now to use it!  In our forms Load event handler, add the following code:

   1:          Using config As WindowSettings = WindowSettings.LoadConfigSection()
   2:              config.ApplicationKey = Application.ProductName
   3:              config.LoadFormSettings(Me)
   4:          End Using

In our forms closing event handler, add the following code:

   1:          Using config As WindowSettings = WindowSettings.LoadConfigSection()
   2:              config.ApplicationKey = Application.ProductName
   3:              config.SaveFormSettings(Me, True)
   4:          End Using

 

And for the curious, here is the resulting app.config.

   1:  <configuration>
   2:      <configSections>
   3:          <section name="WindowSettings" type="SandBoxVB.WindowSettings, SandBoxVB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
   4:      </configSections>
   5:      <WindowSettings applicationKey="SandBoxVB">
   6:          <windows>
   7:              <windows id="Form1" top="215" left="313" height="334" width="479" />
   8:          </windows>
   9:      </WindowSettings>
  10:  </configuration>

 

Below is the full code for the WindowSettings Section Handler.

   1:  Imports System.Configuration
   2:   
   3:  Public Class WindowSettings
   4:      Inherits ConfigurationSection
   5:      Implements IDisposable
   6:   
   7:  #Region " Base Implementation For Load/Save "
   8:   
   9:      'only initialized internally
  10:      Protected Sub New()
  11:   
  12:      End Sub
  13:   
  14:      ''' <summary>
  15:      ''' Internal placeholder for configuration file
  16:      ''' </summary>
  17:      ''' <remarks></remarks>
  18:      Protected m_BaseConfiguration As Configuration = Nothing
  19:   
  20:      ''' <summary>
  21:      ''' Gets the custom configuration by path specifying a custom section name
  22:      ''' </summary>
  23:      ''' <param name="path">Path to the configuration file</param>
  24:      ''' <param name="section">Section in configuration file to load</param>
  25:      Public Shared Function LoadConfigSection(ByVal path As String, _
  26:                                               ByVal section As String) As WindowSettings
  27:          Dim retVal As New WindowSettings(), config As Configuration = Nothing
  28:   
  29:          Dim configFileMap As New ExeConfigurationFileMap()
  30:   
  31:          configFileMap.ExeConfigFilename = path
  32:          config = ConfigurationManager.OpenMappedExeConfiguration( _
  33:                                                              configFileMap, _
  34:                                                              ConfigurationUserLevel.None)
  35:   
  36:          If config.Sections(section) Is Nothing Then
  37:              config.Sections.Add(section, New WindowSettings)
  38:          End If
  39:   
  40:          retVal = TryCast(config.GetSection(section), WindowSettings)
  41:          retVal.m_BaseConfiguration = config
  42:   
  43:          Return retVal
  44:      End Function
  45:   
  46:      ''' <summary>
  47:      ''' Gets the custom configuration by path
  48:      ''' </summary>
  49:      ''' <param name="path"></param>
  50:      ''' <returns></returns>
  51:      ''' <remarks></remarks>
  52:      Public Shared Function LoadConfigSection(ByVal path As String) As WindowSettings
  53:          Return LoadConfigSection(path, "WindowSettings")
  54:      End Function
  55:   
  56:      ''' <summary>
  57:      ''' Gets the custom configuration
  58:      ''' </summary>
  59:      Public Shared Function LoadConfigSection() As WindowSettings
  60:          Return LoadConfigSection(Application.ExecutablePath & ".config")
  61:      End Function
  62:   
  63:      ''' <summary>
  64:      ''' Saves the current configuration
  65:      ''' </summary>
  66:      ''' <remarks></remarks>
  67:      Public Sub Save()
  68:          m_BaseConfiguration.Save(ConfigurationSaveMode.Minimal)
  69:      End Sub
  70:   
  71:  #End Region
  72:   
  73:      ''' <summary>
  74:      ''' Application key for the project, 
  75:      ''' Useful when loading multiples from the save file.
  76:      ''' </summary>
  77:      <ConfigurationProperty("applicationKey")> _
  78:      Public Property ApplicationKey() As String
  79:          Get
  80:              Return MyBase.Item("applicationKey")
  81:          End Get
  82:          Set(ByVal value As String)
  83:              MyBase.Item("applicationKey") = value
  84:          End Set
  85:      End Property
  86:   
  87:      ''' <summary>
  88:      ''' Retrieves a collection of windows in the application
  89:      ''' </summary>
  90:      <ConfigurationProperty("windows")> _
  91:      Public ReadOnly Property Windows() As WindowSettingCollection
  92:          Get
  93:              Return Me.Item("windows")
  94:          End Get
  95:      End Property
  96:   
  97:      ''' <summary>
  98:      ''' Helper method to load form settings
  99:      ''' </summary>
 100:      ''' <param name="form">form to load</param>
 101:      Public Sub LoadFormSettings(ByVal form As System.Windows.Forms.Form)
 102:          With form
 103:              .Top = Me.Windows(form.Name).Top
 104:              .Left = Me.Windows(form.Name).Left
 105:              .Height = Me.Windows(form.Name).Height
 106:              .Width = Me.Windows(form.Name).Width
 107:          End With
 108:      End Sub
 109:   
 110:      ''' <summary>
 111:      ''' Helper method to save form settings
 112:      ''' </summary>
 113:      ''' <param name="form"></param>
 114:      ''' <param name="saveConfig">
 115:      ''' Optional parameter, when true will save the configuration file
 116:      ''' </param>
 117:      ''' <remarks></remarks>
 118:      Public Sub SaveFormSettings(ByVal form As System.Windows.Forms.Form, _
 119:                                      Optional ByVal saveConfig As Boolean = False)
 120:          With form
 121:              Me.Windows(form.Name).Top = .Top
 122:              Me.Windows(form.Name).Left = .Left
 123:              Me.Windows(form.Name).Height = .Height
 124:              Me.Windows(form.Name).Width = .Width
 125:          End With
 126:   
 127:          If saveConfig Then
 128:              Me.Save()
 129:          End If
 130:      End Sub
 131:   
 132:      Public Class WindowSetting
 133:          Inherits ConfigurationElement
 134:   
 135:          <ConfigurationProperty("id")> _
 136:          Public Property ID() As String
 137:              Get
 138:                  Return MyBase.Item("id")
 139:              End Get
 140:              Set(ByVal value As String)
 141:                  MyBase.Item("id") = value
 142:              End Set
 143:          End Property
 144:   
 145:          <ConfigurationProperty("top", DefaultValue:=100)> _
 146:          Public Property Top() As Integer
 147:              Get
 148:                  Return MyBase.Item("top")
 149:              End Get
 150:              Set(ByVal value As Integer)
 151:                  MyBase.Item("top") = value
 152:              End Set
 153:          End Property
 154:   
 155:          <ConfigurationProperty("left", DefaultValue:=100)> _
 156:          Public Property Left() As Integer
 157:              Get
 158:                  Return MyBase.Item("left")
 159:              End Get
 160:              Set(ByVal value As Integer)
 161:                  MyBase.Item("left") = value
 162:              End Set
 163:          End Property
 164:   
 165:          <ConfigurationProperty("height", DefaultValue:=600)> _
 166:          Public Property Height() As Integer
 167:              Get
 168:                  Return MyBase.Item("height")
 169:              End Get
 170:              Set(ByVal value As Integer)
 171:                  MyBase.Item("height") = value
 172:              End Set
 173:          End Property
 174:   
 175:          <ConfigurationProperty("width", DefaultValue:=800)> _
 176:          Public Property Width() As Integer
 177:              Get
 178:                  Return MyBase.Item("width")
 179:              End Get
 180:              Set(ByVal value As Integer)
 181:                  MyBase.Item("width") = value
 182:              End Set
 183:          End Property
 184:      End Class
 185:   
 186:      <ConfigurationCollection(GetType(WindowSetting), AddItemName:="windows")> _
 187:      Public Class WindowSettingCollection
 188:          Inherits ConfigurationElementCollection
 189:   
 190:          ''' <summary>
 191:          ''' Creates a new element with default properties
 192:          ''' </summary>
 193:          Protected Overloads Overrides Function CreateNewElement() _
 194:                                          As System.Configuration.ConfigurationElement
 195:              Return New WindowSetting()
 196:          End Function
 197:   
 198:          ''' <summary>
 199:          ''' Creates a new element with the id specified
 200:          ''' </summary>
 201:          ''' <param name="id">id of the new element</param>
 202:          Protected Overloads Function CreateNewElement(ByVal id As String) _
 203:                                          As System.Configuration.ConfigurationElement
 204:              Return New WindowSetting() With {.ID = id}
 205:          End Function
 206:   
 207:          ''' <summary>
 208:          ''' Gets an element's key
 209:          ''' </summary>
 210:          ''' <param name="element">element to test</param>
 211:          Protected Overrides Function GetElementKey( _
 212:                                  ByVal element As System.Configuration.ConfigurationElement) _
 213:                                  As Object
 214:              Return DirectCast(element, WindowSetting).ID
 215:          End Function
 216:   
 217:          ''' <summary>
 218:          ''' Adds a new element to the collection
 219:          ''' </summary>
 220:          ''' <param name="element"></param>
 221:          ''' <remarks></remarks>
 222:          Public Sub Add(ByVal element As WindowSetting)
 223:              MyBase.BaseAdd(element)
 224:          End Sub
 225:   
 226:          ''' <summary>
 227:          ''' Adds a new element to the collection by id
 228:          ''' </summary>
 229:          ''' <param name="id">id of the new element</param>
 230:          Public Function AddNew(ByVal id As String) As WindowSetting
 231:              Dim newElement As WindowSetting = Me.CreateNewElement(id)
 232:              Add(newElement)
 233:              Return newElement
 234:          End Function
 235:   
 236:          ''' <summary>
 237:          ''' Adds a new element to the collection by form
 238:          ''' </summary>
 239:          ''' <param name="form">form for the new element</param>
 240:          Public Function AddNew(ByVal form As System.Windows.Forms.Form) _
 241:                                                                  As WindowSetting
 242:              Dim newElement As WindowSetting = Me.CreateNewElement(form.Name)
 243:              newElement.Top = form.Top
 244:              newElement.Left = form.Left
 245:              newElement.Height = form.Height
 246:              newElement.Width = form.Width
 247:              Add(newElement)
 248:              Return newElement
 249:          End Function
 250:   
 251:          ''' <summary>
 252:          ''' Removes an element by id
 253:          ''' </summary>
 254:          ''' <param name="id">id of the element to remove</param>
 255:          Public Sub Remove(ByVal id As String)
 256:              MyBase.BaseRemove(id)
 257:          End Sub
 258:   
 259:          ''' <summary>
 260:          ''' Clears all elements from the collection
 261:          ''' </summary>
 262:          Public Sub Clear()
 263:              MyBase.BaseClear()
 264:          End Sub
 265:   
 266:          ''' <summary>
 267:          ''' Retrieves an item from the collection by index
 268:          ''' </summary>
 269:          ''' <param name="index">index of the element</param>
 270:          Default Public Overloads ReadOnly Property Item(ByVal index As Integer) _
 271:                                                                      As WindowSetting
 272:              Get
 273:                  Return MyBase.BaseGet(index)
 274:              End Get
 275:          End Property
 276:   
 277:          ''' <summary>
 278:          ''' Retrieves an item from the collection by id
 279:          ''' </summary>
 280:          ''' <param name="id">id of the element</param>
 281:          Default Public Overloads ReadOnly Property Item(ByVal id As String) _
 282:                                                                      As WindowSetting
 283:              Get
 284:                  'auto-add if not exists
 285:                  If MyBase.BaseGet(id) Is Nothing Then
 286:                      Me.AddNew(id)
 287:                  End If
 288:   
 289:                  Return Me.BaseGet(id)
 290:              End Get
 291:          End Property
 292:   
 293:      End Class
 294:   
 295:  #Region " IDisposable Support "
 296:   
 297:      Private disposedValue As Boolean = False        ' To detect redundant calls
 298:   
 299:      ' IDisposable
 300:      Protected Overridable Sub Dispose(ByVal disposing As Boolean)
 301:          If Not Me.disposedValue Then
 302:              If disposing Then
 303:   
 304:              End If
 305:   
 306:          End If
 307:          Me.disposedValue = True
 308:      End Sub
 309:   
 310:      ' This code added by Visual Basic to correctly implement the disposable pattern.
 311:      Public Sub Dispose() Implements IDisposable.Dispose
 312:          ' Do not change this code.
 313:          Dispose(True)
 314:          GC.SuppressFinalize(Me)
 315:      End Sub
 316:   
 317:  #End Region
 318:   
 319:  End Class
 320:   
 321:   

 

C# Version provided by request.

 

*Sorry about formatting, I don’t normally use underscores to break lines other than for attributes, but the page is skinny and I didn’t want any overrun.

Posted by Tom Anderson | 0 Comments

Creating Compilable User Files in Visual Studio 2008

Back in my c++ days when working on projects with other people in source control etc… I would do all kinds of weird things, things like include files for coding only for me.

Example:

#ifdef (TOM)
include “Tom.h”;
#endif

This allowed me to call code that I didn’t quite want out of my grasp yet, but I wanted in source control.  Naturally the “Tom.h” and its associated files where not in source control, or even included in the project. I kind of missed that feature, and investigated today how to get the same functionality.

Behold the *.proj.user files in Visual Studio 2008 (might work in 2005, who knows, I didn’t try).

Normal source control will keep the .user files out of source control, as these are generally used to store debug information, local configurations, etc… There is nothing in the rulebook that stated I couldn’t use this file to also compile “user” code. You can create a .user file simply by adding it in the same directory as your *.csproj file with the <Project> tags.  The *.csproj files (and *.vbproj files) are simply msbuild file format, so you can check up on the MSDN documentation on how to work with it.

My “Sandbox.csproj.user” file:

   1:  <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   2:    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
   3:      <UserConfig>Tom</UserConfig>
   4:      <DefineConstants>DEBUG;TRACE;TOM</DefineConstants>
   5:    </PropertyGroup>
   6:    <ItemGroup Condition=" '$(UserConfig)' == 'Tom'">
   7:      <Compile Include="Tom.cs" />
   8:    </ItemGroup>
   9:  </Project>

So, basically what is going on here is that I added a new property in the Debug configuration for “UserConfig”, this is used later for a conditional, and thats about it.  I then added “TOM” to the defined constants.

I then added an ItemGroup with the condition of the UserConfig being “Tom”, clever eh?  Inside this ItemGroup I simply added a Compile for Tom.cs.  This file is pretty barebones, and does nothing, but here it is anyway.

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Linq;
   4:  using System.Text;
   5:   
   6:  namespace Sandbox
   7:  {
   8:      class Tom
   9:      {
  10:      }
  11:  }

Then in my main application, I simply do an #if on the TOM constant and insert my code in that block.

   1:  #if (TOM)
   2:              Tom t = new Tom();
   3:  #endif

Pretty nifty eh?

What is even better, if I switch to release mode Line 2 above grays out (letting me know it isn’t compiled) and my “Tom” class isn’t included in the compile either (verified with Reflector).

The only issue is that it doesn’t show up in the solution explorer, but neither did the c++ counter parts, this also has the nice beauty of not adding the file to source control, so now I can do all that “tom machine” specific coding I need to to bang out that gong feature.

image

So, if you want to do some code and keep it checked in without screwing everyone else up, or have your own set of tools you want to be stingy with, this method might work great for you!

 

*Warning: Code in the #if (TOM) blocks will be visible in source control, no one will have the compiled version of it, and unless they declare the constant, their code will be safe as code in conditional blocks that don’t meet the condition don’t get compiled.

Posted by Tom Anderson | 0 Comments

Proper Thread Work with WF

Most inexperienced developers tend to put workload heavy operations on the same thread that the UI runs on. While you can call ‘Application.DoEvents()’ to keep the windows messages flowing, this is not the proper way to keep your UI painted. In this article I will be explaining how to put your workload heavy operations on a separate thread, and how to properly update your form with information you want the user to see.

Let’s start with a simple snippet.

FileInfo[] files = new DirectoryInfo(@"C:\Windows").GetFiles();

Depending on the the size of the directory, this operation could potentially take several minutes or more. During this period, your UI cannot paint itself or receive windows messages. This means, your user thinks the application has locked up, or crashed. This is where threads come into play.

        private void MainForm_Shown(object sender, EventArgs e) {
            Thread thread = new Thread(new ThreadStart(ThreadProc));
            thread.IsBackground = true;
            thread.Start();
        }

The code above simply starts a new thread that runs on the ‘ThreadProc’ method. This will allow our form to run its own code, while the thread is running. Essentially with the result that we can run our workload heavy operations, without hindering the form. Below is the ‘ThreadProc’ method, which contains our workload heavy operation to simply read a directory and its files.       

        /// <summary>
        /// Method to process our workload
        /// </summary>
        private void ThreadProc() {
            FileInfo[] files = new DirectoryInfo(@"C:\Windows").GetFiles();
            UpdateActionDelegate uxad = new UpdateActionDelegate(UpdateAction);

            for (int i = 0; i < files.Length; i++) {
                DateTime time = DateTime.Now;

                if ((time - m_Time).Milliseconds >= 50) {
                    m_Time = time;

                    // Now we can update the thread since
                    // we waited the 50ms
                    uxAction.Invoke(uxad, files[ i ].Name);

                    // We could also invoke the mainform directly
                    // and have access to all of the controls
                    //this.Invoke(uxad, item.Name);

                    // Just an example since we are not really processing
                    // anything, so we want to see whats happening
                    Thread.Sleep(250);
                }
            }
        }

There are two key points to this method body.

1. We use a delegate called UpdateActionDelegate, which we use to properly invoke a label named ‘uxAction’ on the form, to safely update its text.


2. We use a private member named ‘m_DateTime’ of type DateTime, which we use to check if 50ms of time has passed between each iteration in the for-loop.

The 50ms is simply a delay. If you have no delay, the systems CPU will show it cycling at 100%, and depending on the clients system, they may have intense flickering on the form. This occurs because the operation would normally try to update the text of the label faster than the form can paint itself. This is why we implemented a 50ms delay before updating the label again. Below is the full code of the MainForm class, which demonstrates this in action. I have also attached the project files in a ZIP.

// *********************************************************************
// [RenEvo Software & Designs]
// [RenEvo], [Proper thread work with WF]
//
//   THIS FILE IS PROVIDED "AS-IS" WITHOUT ANY WARRANTY OF ANY KIND. ANY
//   MODIFICATIONS TO THIS FILE IN ANY WAY ARE YOUR SOLE RESPONSIBILITY.
//
// [Copyright (C) RenEvo Software & Designs  All rights reserved.]
// *********************************************************************

namespace ThreadUIExample {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Collections;
    using System.IO;
    using System.Threading;

    public partial class MainForm : Form {
        public MainForm() {
            InitializeComponent();
        }

        private void MainForm_Shown(object sender, EventArgs e) {
            Thread thread = new Thread(new ThreadStart(ThreadProc));
            thread.IsBackground = true;
            thread.Start();
        }

        /// <summary>
        /// Used to check if 50ms of time has passed since the last file was read
        /// </summary>
        private DateTime m_Time = DateTime.Now;

        /// <summary>
        /// Delegate used to safely invoke the action label
        /// </summary>
        /// <param name="action"></param>
        private delegate void UpdateActionDelegate(string action);

        /// <summary>
        /// Callback used to safely update the text of the action label
        /// when the label is properly invoked
        /// </summary>
        public void UpdateAction(string action) {
            uxAction.Text = string.Format("Action: {0}", action);
        }

        /// <summary>
        /// Method to process our workload
        /// </summary>
        private void ThreadProc() {
            FileInfo[] files = new DirectoryInfo(@"C:\Windows").GetFiles();
            UpdateActionDelegate uxad = new UpdateActionDelegate(UpdateAction);

            for (int i = 0; i < files.Length; i++) {
                DateTime time = DateTime.Now;

                if ((time - m_Time).Milliseconds >= 50) {
                    m_Time = time;

                    // Now we can update the thread since
                    // we waited the 50ms
                    uxAction.Invoke(uxad, files[ i ].Name);

                    // We could also invoke the mainform directly
                    // and have access to all of the controls
                    //this.Invoke(uxad, item.Name);

                    // Just an example since we are not really processing
                    // anything, so we want to see whats happening
                    Thread.Sleep(250);
                }
            }
        }
    }
}
Posted by Dave Anderson | 1 Comments

Attachment(s): ThreadUIExample.zip

New code snippet plugin for Live Writer

I am trying out a new code snippet plug-in for Live Writer.

Public Class CommitDB
    Public Function GetCommitStatusAll() As DataSet
        ' Create Instance of Connection and Command Object
        Dim myConnection As New SqlConnection(ConfigurationManager.AppSettings("NorthstarConnectionString"))
        Dim myCommand As New SqlDataAdapter("GetCommitStatusAll", myConnection)

        ' Mark the Command as a SPROC
        myCommand.SelectCommand.CommandType = CommandType.StoredProcedure

        ' Create and Fill the DataSet
        Dim myDataSet As New DataSet
        myCommand.Fill(myDataSet)

        ' Return the DataSet
        Return myDataSet
    End Function
End Class
Lets see how it looks on the blogs!
Posted by Tom Anderson | 0 Comments

Sharing configuration files

Derik Whittaker has posted a blog post that I found recently via stackoverflow.com that demonstrates a very unknown feature in the Add Existing Dialog in visual studio. Adding as a link.

This is… super useful.

Posted by Tom Anderson | 2 Comments

CAB – Part 5 Simple Module

In the previous articles on CAB, we have gone over some of the basics of getting our shell application ready to load up some modules.

In this article, we will actually create a simplistic Module to display a “SmartPart” in our primary workspace.

The first thing we need to do, is to add a new “Class Library” project to our current solution. Name the new project “RenEvo.Blogs.Cab.SimpleModule” and hit ok. Rename the “Class1.vb” to “SimpleModuleInit.vb”.  This class will be our entry point into the actual module.

At this point, we are going to make a few modifications to our current projects, as the build directories are kind of all over the place, and since we have a defined structure we need to adhere to (example: ./Modules/ for any modules loaded into the Shell).

In the RenEvo.Blogs.Cab project, set the Build output Path in the Compile tab of the project properties to “..\bin\Debug\” for Debug configuration, and “..\bin\Release\” for the Release configuration.  Do the same thing to the RenEvo.Blogs.Cab.Interfaces project.

For the new RenEvo.Blogs.Cab.SimpleModule, set the output paths to “..\bin\Debug\Modules\” and “..\bin\Release\Modules\” respectively.

Build the entire solution, and verify that there is a bin directory in the Solution folder that contains all of the projects output assemblies in the per-configuration folder.

Back to our Simple Module project, we need to add some key references. Add the following references, and be sure to set the “Copy Local” property to False for each reference.

  • Microsoft.Practices.CompositeUI
  • Microsoft.Practices.CompositeUI.WinForms
  • Microsoft.Practices.ObjectBuilder
  • System.Windows.Forms
  • System.Drawing

And then finally add a project reference to the RenEvo.Blogs.Cab.Interfaces, also settings the “Copy Local” property to false.

image

Next, create a few new folders inside of the Simple Module project.

  • Services
  • SmartParts
  • WorkItems

The first thing we need to do is to setup the SimpleModuleInit class so that the shell will load it up. The first thing we will do is to import a few namespaces, then add an assembly attribute “Module” which will be the attribute that the Shell will look for when loading modules via reflection. Next we will inherit ModuleInit in our class.  At this point, your code will look like this:

   1:  Imports Microsoft.Practices.CompositeUI
   2:  Imports Microsoft.Practices.CompositeUI.Services
   3:   
   4:  <Assembly: [Module]("Simple Module")> 
   5:   
   6:  Public Class SimpleModuleInit
   7:      Inherits ModuleInit
   8:   
   9:   
  10:  End Class

We will have to setup our Dependency Injection property in order to integrate into the Shell Application. If you are not familiar with Dependency Injection, I suggest you read up on it before continuing.  Essentially for this implementation of Injection, we will use Property Attributes to notify the base framework that we have a service dependency, and then the property declared type will provide the service type we are accessing, in this case “WorkItem” type.

   1:  #Region " Root Work Item Dependency Injection "
   2:   
   3:      Private m_WorkItem As WorkItem = Nothing
   4:      <ServiceDependency()> _
   5:      Public Property RootWorkItem() As WorkItem
   6:          Get
   7:              Return m_WorkItem
   8:          End Get
   9:          Set(ByVal value As WorkItem)
  10:              m_WorkItem = value
  11:          End Set
  12:      End Property
  13:   
  14:  #End Region

This is basically setting this property by doing the following line of code (not used in our code, but deep behind the scenes).

   1:  SimpleModuleInit.RootWorkItem = Services.Get(Of WorkItem)()

By using Dependency Injection, we don’t need to know what is going on behind the scenes, and in some later examples of implementing our own services, it is quite useful to just have what we need automatically populated, rather than trying to find it manually.

Moving along, the next step we will need to do, is to override the ModuleInit.Load() sub in our SimpleModuleInit. Within this Method, lets just simple do the MessageBox.Show(“SimpleModule Loaded”) to verify that our module is being loaded.

Be sure that the RenEvo.Blogs.Cab project is set as the Startup Project, and run the application.  While the Splash Screen is displayed, you should see a message box stating that your SimpleModule Loaded.

Ok, moving along quickly here, lets remove that message box line. Now in the SmartParts folder, add a new UserControl named "SimpleSmartPart.vb” and in the WorkItems folder, add a new Class named “SimpleWorkItem.vb”

Code for SimpleSmartPart.vb

   1:  Imports Microsoft.Practices.CompositeUI.SmartParts
   2:   
   3:  <SmartPart()> _
   4:  Public Class SimpleSmartPart
   5:   
   6:  End Class

What we are doing in the class above is just tagging this user control as a SmartPart, this allows the WorkSpace objects to communicate with them properly, which we will go over in a later article.

For the sake of being able to see the smart part easier, drop a label control in the design view on the SimpleSmartPart, name it “uxSmartPartName” and set its Text property to “Simple Smart Part Loaded”. Where you put the label is irrelevant, mine looks like this:

image 

Now, the code for the SimpleWorkItem.vb is as follows:

   1:  Imports Microsoft.Practices.CompositeUI
   2:  Imports Microsoft.Practices.CompositeUI.Commands
   3:  Imports RenEvo.Blogs.Cab.Interfaces.Common
   4:   
   5:  Imports System.Windows.Forms
   6:   
   7:  Public Class SimpleWorkItem
   8:      Inherits WorkItem
   9:   
  10:      Protected Overrides Sub OnRunStarted()
  11:          MyBase.OnRunStarted()
  12:   
  13:          Me.RootWorkItem.Workspaces(Constants.WorkSpaces.PrimaryWorkSpace).Show(New SimpleSmartPart)
  14:   
  15:      End Sub
  16:   
  17:  End Class

The code above inherits from WorkItem and overrides the OnRunStarted method, we then Show a new SimpleSmartPart in our PrimaryWorkSpace.

Finally, to get the work item to actually “run” we need to modify the SimpleModuleInit.vb by adding a workitem to the RootWorkItem property, and calling the “Run” method.  The code below goes in the Load() method in SimpleModuleInit.vb

   1:  Me.RootWorkItem.WorkItems.AddNew(Of SimpleWorkItem).Run()

In the above code we are using the “AddNew” method with a generic typing of SimpleWorkitem and then calling the method “Run” on the newly created WorkItem.

Save everything, and run the application.  When the Shell interface displays after the splash screen, you will see the label with the text “Simple Smart Part Loaded” in our interface.

image

And there you have it, we have loaded up a module into the Shell, and displayed our First Smart Part.

In the next article, we will be adding a Controller to the SmartPart for the MVC (Model View Controller) design pattern, and implementing a few commands within that Controller.

Download the Solution

Posted by Tom Anderson | 0 Comments
Filed under:

SQL Server 2008 Express Now Available

SQL Server Express 2008, which released on Monday, is now available from Microsoft.

New Features:

  • Support for LINQ, Entity Data Model and ADO.NET Entity Framework make it easy to create next generation data-enabled applications
  • New Date and Time data types with time zone support and .NET compatibility provides full control over temporal data
  • New T-SQL IntelliSense support in SQL Server Management Studio makes it easy to write accurate T-SQL code
  • Access a vast community of other SQL Server enthusiasts from beginners to experts via the SQL Server Express forum

Overview:

  • Tight integration with Visual Studio 2008 with SP1
  • SQL Server 2008 Express provides high-end database features
  • Support for new data types and features like spatial data, HierarchyID, and FileStream makes it easier to model complex data
  • Support for MERGE, GROUPING SETS, sparse columns and table-valued parameters makes it easier to write T-SQL code
  • New Import/Export wizard makes it easy to migrate data
Posted by Tom Anderson | 0 Comments

CAB - Part 4 Adding a WorkSpace

In the previous articles on CAB we have gone over creating the basics with a CAB, right now our application really has no "Composite" to it, just "Application Block".  In this article we will go over creating our first WorkSpace for modules to dock into, which will lead us to our next article on creating a basic module with a single workspace.

The first thing we need to do, in order to work with the CAB WinForms controls, is to create a new Tab on our Forms Toolbox, and load up the Microsoft.Practices.CompositeUI.WinForms.dll controls to it.  If you need help on this, you can see many articles on the web on how to add items to your toolbox, like this one.

You should now have 7 new controls, the one we are going to concentrate on for this article is the DeckWorkspace.  Simply drag it onto our form, set the Dock to Fill, name it uxPrimaryWorkspace, then clear the Text property.

Your form in designer should look like this:

image

The size of the form is un-important, later we will go about saving the forms settings, etc... but for now, lets leave it as is.

Now that we have our form setup, we need to add some ways to access this workspace, as well as register the WorkSpace with the CAB framework.  This is usually done via id strings, but since we want our users to properly access this workspace, we will want to create constants for access.  Instead of the modules accessing and referencing the main executable, we will add another project with the name ".Interfaces" that will contain all the constants for extension sites, workspaces, as well as internal services.

Add a Class Library project to the solution named "RenEvo.Blogs.Cab.Interfaces" with the default directory specified.  Next delete the Class1.vb that is automaticaly created.

Create a new folder called Common, then a new class file inside of that folder called WorkSpaces.  In the class file you need to implement a namespace, as well as a few constants inside of the class.  The namespace is only for better organization, and separation of objects.

   1:  Namespace Common.Constants
   2:   
   3:      ''' <summary>
   4:      ''' Class to store all of the workspace names
   5:      ''' </summary>
   6:      Public Class WorkSpaces
   7:          ''' <summary>
   8:          ''' The Primary Workspace for the CAB application
   9:          ''' </summary>
  10:          Public Const PrimaryWorkSpace As String = "PrimaryWorkSpace"
  11:   
  12:      End Class
  13:   
  14:  End Namespace

 

For now our class really doesn't cover a lot of constants, but in a larger application this could get quite huge.  Plus we no longer will have casing or mis-spelling issues with the other developers.

Now add a reference to the new project to the main Shell project, setting the copy to True.

Next, back in our form, we need to access the Constructor, and after the InitializeComponent, we want to reset the name of the uxPrimaryWorkspace in runtime to the name of the string in the preceding class.

   1:      Public Sub New()
   2:   
   3:          ' This call is required by the Windows Form Designer.
   4:          InitializeComponent()
   5:   
   6:          ' Add any initialization after the InitializeComponent() call.
   7:          Me.uxPrimaryWorkspace.Name = Interfaces.Common.Constants.WorkSpaces.PrimaryWorkSpace
   8:   
   9:      End Sub

 

This is a really easy implementation, probably the easiest in the entire application.  To test it, we can do the following in our Startup.vb class.

In the ShellShown event handlers, add the following lines of code to the bottom of the method.

   1:      Protected Sub ShellShown(ByVal sender As Object, ByVal e As EventArgs)
   2:          'remove the handler
   3:          RemoveHandler Shell.Shown, AddressOf ShellShown
   4:   
   5:          'set the cursor back to normal
   6:          m_Splash.Cursor = Cursors.Default
   7:   
   8:          'hide, dispose, and kill the splash 
   9:          m_Splash.Hide()
  10:          m_Splash.Dispose()
  11:          m_Splash = Nothing
  12:   
  13:          'test to see if we have a workspace
  14:          MessageBox.Show("Workspace Initialized: " & Boolean.Parse(Me.RootWorkItem.Workspaces(Interfaces.Common.Constants.WorkSpaces.PrimaryWorkSpace) IsNot Nothing))
  15:      End Sub

When ran, the application will display "Workspace Initialized: True" in a message box just after the splash screen closes.

Now remove that line of code, as we don't want that message box to continue displaying.

As stated before, in the next article I will be covering the creation of a very simplistic module that will load into this newly created workspace.

Download the Solution

Posted by Tom Anderson | 0 Comments
Filed under:

CAB - Part 3 Splash Screen

In the previous articles for CAB we have gone over creating our base form, as well as implementing a new way to load our modules (once we create some), in this article we are going to go over another simple implementation (that is specifically not covered in the documentation) to make our application much more user friendly.

Splash Screens.  As you know, in Visual Studio 2005 and 2008 you have the ability to enable the Application Framework, and simply select a Splash Screen from the project properties, in a CAB shell, you must run through the Sub Main() instead of simply using a Form for startup.  We will add one real fast that displays while the CAB is loading its assemblies and modules.

First, add a new form to our project (under Forms->Dialogs in our directory structure), select the Splash Screen form template, and name it CabSplashScreen.  For now we aren't going to modify the Splash Screen, just use the default template.

Just close the Splash Screen, and forget about it for now.

In our Startup class, we need to create a new private field for the splash screen, add creation to it in the sub new (non-shared instance), override the AfterShellCreated, as well as create an event handler for ShellShown.

Below is the code to do just that:

   1:      Private m_Splash As CabSplashScreen = Nothing
   2:   
   3:      Public Sub New()
   4:          'create the splash
   5:          m_Splash = New CabSplashScreen
   6:   
   7:          'show and update the splash
   8:          m_Splash.Show()
   9:          m_Splash.Update()
  10:   
  11:          'set the cursor to the hourglass
  12:          m_Splash.Cursor = Cursors.WaitCursor
  13:   
  14:          'let splash screen events process
  15:          Application.DoEvents()
  16:      End Sub
  17:   
  18:      Protected Overrides Sub AfterShellCreated()
  19:          MyBase.AfterShellCreated()
  20:   
  21:          'add an event handler for the ShellForm.Show event (this is when we will kill our splash)
  22:          AddHandler Shell.Shown, AddressOf ShellShown
  23:   
  24:      End Sub
  25:   
  26:      Protected Sub ShellShown(ByVal sender As Object, ByVal e As EventArgs)
  27:          'remove the handler
  28:          RemoveHandler Shell.Shown, AddressOf ShellShown
  29:   
  30:          'set the cursor back to normal
  31:          m_Splash.Cursor = Cursors.Default
  32:   
  33:          'hide, dispose, and kill the splash 
  34:          m_Splash.Hide()
  35:          m_Splash.Dispose()
  36:          m_Splash = Nothing
  37:      End Sub

The code is pretty straight forward, and I will leave you to the Comments to figure out what it is doing (I am sure that if you are venturing into creating a CAB shell then this is nothing new to you)

In our next article, we will go over creating our first WorkSpace.

Download the Solution

Posted by Tom Anderson | 0 Comments
Filed under:

CAB - Part 2 Loading Modules from a directory

In the first part of the CAB articles, I explained a bit about getting the CAB working in a generic shell, and simply initiating the framework, in this article I will explain a little known ability with the CAB to dynamically load all modules in a directory, instead of the "ProductCatalog.xml" that is used in the CAB samples.  This is something that I do personally in all 3 of our CAB shells, and have found it to be much easier to deploy new modules to customers.

In our Startup class, we simply need to override one of the base WorkItem methods for adding services "AddServices", remove teh IModuleEnumerator that is currently loaded (that productcatalog.xml loader) and replace it with a new ReflectionModuleEnumerator instead.

   1:      Protected Overrides Sub AddServices()
   2:          'setup base services
   3:          MyBase.AddServices()
   4:   
   5:          'remove the FileModule for ProductCatalog.xml
   6:          MyBase.RootWorkItem.Services.Remove(Of IModuleEnumerator)()
   7:   
   8:          'create a new reflection enumerator
   9:          Dim reflectionEnumerator As New ReflectionModuleEnumerator()
  10:   
  11:          'make sure our path exists for modules
  12:          Directory.CreateDirectory(Path.GetDirectoryName(Application.ExecutablePath) & "\Modules\")
  13:   
  14:          'set the working path to ./Modules/
  15:          reflectionEnumerator.BasePath = Path.GetDirectoryName(Application.ExecutablePath) & "\Modules\"
  16:   
  17:          'add it back to the services
  18:          MyBase.RootWorkItem.Services.Add(GetType(IModuleEnumerator), reflectionEnumerator)
  19:   
  20:      End Sub

In the next article, I will go over how to add a Splash Screen to the main form, since we have not enabled the "Application Framework".

Download Solution

Posted by Tom Anderson | 0 Comments
Filed under:

Combo Box Enumerations with Titles

Have you ever just wanted to populate a combo box with values from an enumeration, but hated the fact that it dealt with the name of the item in the enumeration, instead of some snazzy string? Take the following Enum as an example:

   1:  Public Enum TitledValues
   2:      FirstName
   3:      LastName
   4:      Address1
   5:      Address2
   6:      City
   7:      State
   8:      ZipCode
   9:      PhoneNumber
  10:      EmailAddress
  11:  End Enum

For some of those properties displaying "City" would be fine, but who wants to show a user "FirstName" ?

I have figured out a little technique using a custom type converter and attributes to be able at code time set the titles from the Enum instead of having to do huge select cases when handling the drop down events.

The first thing to do is to create a new attribute called "EnumTitleAttribute", this attribute is pretty straight forward, it simply contains a Title property.

   1:  <AttributeUsage(AttributeTargets.Field)> _
   2:  Public Class EnumTitleAttribute
   3:      Inherits Attribute
   4:   
   5:      Private m_Title As String = String.Empty
   6:      Public Property Title() As String
   7:          Get
   8:              Return m_Title
   9:          End Get
  10:          Set(ByVal value As String)
  11:              m_Title = value
  12:          End Set
  13:      End Property
  14:   
  15:  End Class

The AttributeUsage attribute is restricting usage of this attribute to fields, which is what enum values are stored as in the object.

The next step was to create a type converter to work with these new custom attributes, since we want this to be a global type of enum, we are going to implement it as a generic.

   1:  Public Class EnumTitleTypeConverter(Of T)
   2:      Inherits TypeConverter
   3:   
   4:      Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
   5:          If value.GetType Is GetType(T) AndAlso destinationType Is GetType(String) Then
   6:              If value.GetType.GetField(value.ToString).GetCustomAttributes(GetType(EnumTitleAttribute), True).Length > 0 Then
   7:                  Return DirectCast(value.GetType.GetField(value.ToString).GetCustomAttributes(GetType(EnumTitleAttribute), True)(0), EnumTitleAttribute).Title
   8:              Else
   9:                  Return value.ToString
  10:              End If
  11:          Else
  12:              Return MyBase.ConvertTo(context, culture, value, destinationType)
  13:          End If
  14:      End Function
  15:  End Class

The only functionality we really care about in this class is the "ConvertTo" method.  We want to check to see if the value being converted is the type of our generic (of T) and that the destination type is string.

The basics of this method are check the input parameters for the proper conversion types, if they match, see if the field in the type has the EnumTitleAttribute assigned to it, and if so, we are going to get the first one only and return the Title property, otherwise, we are simply going to return the default ToString on the value object.  If none of these match, we will simply let the standard type converter try to deal with it, where it generally just returns the ToString of the object.

Now, to apply the title's to an enumeration, we simply add the attributes to the enum fields and set the TypeConverter.

   1:  <TypeConverter(GetType(EnumTitleTypeConverter(Of TitledValues)))> _
   2:  Public Enum TitledValues
   3:      <EnumTitle(Title:="First Name")> _
   4:      FirstName
   5:      <EnumTitle(Title:="Last Name")> _
   6:      LastName
   7:      <EnumTitle(Title:="Address 1")> _
   8:      Address1
   9:      <EnumTitle(Title:="Address 2")> _
  10:      Address2
  11:      City
  12:      State
  13:      <EnumTitle(Title:="Zip Code")> _
  14:      ZipCode
  15:      <EnumTitle(Title:="Phone Number")> _
  16:      PhoneNumber
  17:      <EnumTitle(Title:="Email Address")> _
  18:      EmailAddress
  19:  End Enum

So the first blaring question is, how do you use it?  Create a form (or use an existing one), add a combo box (for this example, name it uxFieldNames) and set the DropDownStyle to DropDownList, then in the form load, do the following code.

   1:  Me.uxFieldNames.DataSource = [Enum].GetValues(GetType(TitledValues))

And then in the SelectedIndexChanged event for the ComboBox, you can find out the selected item with the following code

   1:          If Me.uxFieldNames.SelectedIndex > -1 Then
   2:              Select Case DirectCast(uxFieldNames.SelectedItem, TitledValues)
   3:                  Case TitledValues.FirstName
   4:                      'do something with the first name
   5:                  Case TitledValues.LastName
   6:                      'do something with the last name
   7:              End Select
   8:          End If

Without hardly any additional effort, especially from your UI coding, you end up with this result:

image

instead of this:

image

Good luck, and happy coding!

 

Full Source Code:

   1:  Imports System.ComponentModel
   2:  Imports System.Globalization
   3:   
   4:  <TypeConverter(GetType(EnumTitleTypeConverter(Of TitledValues)))> _
   5:  Public Enum TitledValues
   6:      <EnumTitle(Title:="First Name")> _
   7:      FirstName
   8:      <EnumTitle(Title:="Last Name")> _
   9:      LastName
  10:      <EnumTitle(Title:="Address 1")> _
  11:      Address1
  12:      <EnumTitle(Title:="Address 2")> _
  13:      Address2
  14:      City
  15:      State
  16:      <EnumTitle(Title:="Zip Code")> _
  17:      ZipCode
  18:      <EnumTitle(Title:="Phone Number")> _
  19:      PhoneNumber
  20:      <EnumTitle(Title:="Email Address")> _
  21:      EmailAddress
  22:  End Enum
  23:   
  24:  <AttributeUsage(AttributeTargets.Field)> _
  25:  Public Class EnumTitleAttribute
  26:      Inherits Attribute
  27:   
  28:      Private m_Title As String = String.Empty
  29:      Public Property Title() As String
  30:          Get
  31:              Return m_Title
  32:          End Get
  33:          Set(ByVal value As String)
  34:              m_Title = value
  35:          End Set
  36:      End Property
  37:   
  38:  End Class
  39:   
  40:  Public Class EnumTitleTypeConverter(Of T)
  41:      Inherits TypeConverter
  42:   
  43:      Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
  44:          If value.GetType Is GetType(T) AndAlso destinationType Is GetType(String) Then
  45:              If value.GetType.GetField(value.ToString).GetCustomAttributes(GetType(EnumTitleAttribute), True).Length > 0 Then
  46:                  Return DirectCast(value.GetType.GetField(value.ToString).GetCustomAttributes(GetType(EnumTitleAttribute), True)(0), EnumTitleAttribute).Title
  47:              Else
  48:                  Return value.ToString
  49:              End If
  50:          Else
  51:              Return MyBase.ConvertTo(context, culture, value, destinationType)
  52:          End If
  53:      End Function
  54:  End Class

 

 

*Edit: Added C# Code below

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Text;
   4:  using System.ComponentModel;
   5:  using System.Globalization;
   6:   
   7:  [TypeConverter(typeof(EnumTitleTypeConverter<TitledValues>))]
   8:  enum TitledValues
   9:  {
  10:      [EnumTitle(Title="First Name")]
  11:      FirstName,
  12:      [EnumTitle(Title = "Last Name")]
  13:      LastName,
  14:      [EnumTitle(Title = "Address 1")]
  15:      Address1,
  16:      [EnumTitle(Title = "Address 2")]
  17:      Address2,
  18:      City,
  19:      State,
  20:      [EnumTitle(Title = "Zip Code")]
  21:      ZipCode,
  22:      [EnumTitle(Title = "Phone Number")]
  23:      PhoneNumber,
  24:      [EnumTitle(Title = "Email Address")]
  25:      EmailAddress
  26:  }
  27:   
  28:  [AttributeUsage(AttributeTargets.Field)]
  29:  public class EnumTitleAttribute : Attribute
  30:  {
  31:      private string m_Title = string.Empty;
  32:      public string Title
  33:      {
  34:          get { return m_Title; }
  35:          set { m_Title = value; }
  36:      }
  37:  }
  38:   
  39:  public class EnumTitleTypeConverter<T> : TypeConverter
  40:  {
  41:      public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
  42:      {
  43:          if ((value.GetType() == typeof(T)) && (destinationType == typeof(String)))
  44:          {
  45:              if (value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(EnumTitleAttribute), true).Length > 0)
  46:              {
  47:                  return (value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof(EnumTitleAttribute),true)[0] as EnumTitleAttribute).Title;
  48:              }
  49:              else
  50:              {
  51:                  return value.ToString();
  52:              }
  53:          } 
  54:          else 
  55:          {
  56:              return base.ConvertTo(context, culture, value, destinationType);
  57:          }
  58:      }
  59:  }
Posted by Tom Anderson | 1 Comments

Correction for Vertigo Software post about debugging

Recently I read the article by Chris Idzerda over at Vertigo software that described how to Detect Visual Studio Debugging.  While the method he used was a bit of a "hacky workaround", below is a more sound way to detect it using built in .Net libraries, rather than a string lookup on the running executable.

VB.Net Version

   1:          If System.Diagnostics.Debugger.IsAttached Then
   2:              ' do code here for debugging with visual studio
   3:   
   4:          End If
 

C# Version

   1:              if (System.Diagnostics.Debugger.IsAttached)
   2:              {
   3:                  // do code here for debugging with visual studio
   4:              }

I hope this helps clear up any confusion.

Posted by Tom Anderson | 0 Comments

SWGEmu - .Net Stuffz

So a while back some of you may know that I was writing a pure .Net emulator.  That went "ok" and I had made zone in both pre-cu and nge based clients.  But after a while, the time constraints just couldn't be dealt with, and the project was, how do I say, huge?

One of the big issues that I had when writing the emulator in .Net was the encryption, decryption, and the crc calculations.  Originally I had just used a c++ api to pull it off.  This bothered me literally for some time as I just wanted a pure .Net solution.

So today, I spent a bit of time looking at the great documentation on the encryption/decryption algorigthm that the SWGEmu team has put up (if you recall, I used to be a part of that team for a very short lived time).

Below is the test app that I created and played around with to get these things working in pure (you got it) VB.Net.  I know the C# port would have been WAY easier using unsafe or even almost just duplicating the code, but I wanted a direct port that was spot on.

 

   1:  Public Class Program
   2:   
   3:      Public Shared Sub Main(ByVal args() As String)
   4:          Dim rawArray() As Byte = New Byte() {0, 9, 0, 1, 0, 2, &HAB, &H43, &HE3, &HD5, 0, &HFF, 0, &H11, &H45, &H32, &H76, &H43, &HD4, &HF1, 0, &HAB, &HCD}
   5:          Dim encryptedArray() As Byte
   6:          Dim decryptedArray() As Byte
   7:          Dim crcSeed As Integer = &H1D4E3287
   8:   
   9:          Console.WriteLine("Raw Bytes:")
  10:          For Each bt As Byte In rawArray
  11:              Console.Write("{0:X2} ", bt)
  12:          Next
  13:          Console.WriteLine()
  14:          Console.WriteLine()
  15:   
  16:   
  17:          encryptedArray = Encrypt(Strip(rawArray), crcSeed)
  18:          Console.WriteLine("Encrypted Bytes:")
  19:          For Each bt As Byte In encryptedArray
  20:              Console.Write("{0:X2} ", bt)
  21:          Next
  22:          Console.WriteLine()
  23:          Console.WriteLine()
  24:   
  25:          decryptedArray = Decrypt(encryptedArray, crcSeed)
  26:          Console.WriteLine("Decrypted Bytes:")
  27:          For Each bt As Byte In decryptedArray
  28:              Console.Write("{0:X2} ", bt)
  29:          Next
  30:          Console.WriteLine()
  31:          Console.WriteLine()
  32:   
  33:          Console.WriteLine("CRC Test")
  34:          Dim crc As Integer = CRC32(New Byte() {&H0, &H5, &HAA, &HBB, &HCC, &HDD, &H0, &H6, &H0}, 9, crcSeed)
  35:          Console.WriteLine("My CRC32:  {0:X4}", crc)
  36:          crc = API.GenerateCrc(New Byte() {&H0, &H5, &HAA, &HBB, &HCC, &HDD, &H0, &H6, &H0}, crcSeed)
  37:          Console.WriteLine("C++ CRC32: {0:X4}", crc)
  38:   
  39:          Console.ReadLine()
  40:      End Sub
  41:   
  42:      Private Shared Function Strip(ByVal inData() As Byte) As Byte()
  43:          Dim retVal As New List(Of Byte)
  44:          retVal.AddRange(inData)
  45:   
  46:          'strip channel and op code
  47:          If retVal(0) = 0 Then
  48:              retVal.RemoveAt(0)
  49:              retVal.RemoveAt(0)
  50:          End If
  51:   
  52:          'strip crc
  53:          retVal.RemoveAt(retVal.Count - 1)
  54:          retVal.RemoveAt(retVal.Count - 1)
  55:   
  56:          Return retVal.ToArray
  57:      End Function
  58:   
  59:      Public Shared Function Encrypt(ByVal inData() As Byte, ByVal seed As Integer) As Byte()
  60:          Dim count As Integer = Math.Floor(Decimal.Parse(inData.Length) / 4D)
  61:          Dim remainder As Integer = inData.Length - (count * 4)
  62:          Dim seedBytes() As Byte = BitConverter.GetBytes(seed)
  63:   
  64:          Array.Reverse(seedBytes)
  65:   
  66:          For i As Integer = 0 To count - 1
  67:              For b As Integer = 0 To 3
  68:                  inData((i * 4) + b) = inData((i * 4) + b) Xor seedBytes(b)
  69:              Next
  70:              For b As Integer = 0 To 3
  71:                  seedBytes(b) = inData((i * 4) + b)
  72:              Next
  73:          Next
  74:   
  75:          For i As Integer = 0 To remainder - 1
  76:              inData((count * 4) + i) = inData((count * 4) + i) Xor seedBytes(0)
  77:          Next
  78:   
  79:          Return inData
  80:      End Function
  81:   
  82:      Public Shared Function Decrypt(ByVal inData() As Byte, ByVal seed As Integer) As Byte()
  83:          Dim count As Integer = Math.Floor(Decimal.Parse(inData.Length) / 4D)
  84:          Dim remainder As Integer = inData.Length - (count * 4)
  85:          Dim seedBytes() As Byte = BitConverter.GetBytes(seed)
  86:   
  87:          Array.Reverse(seedBytes)
  88:   
  89:          For i As Integer = 0 To count - 1
  90:              Dim newSeed(3) As Byte
  91:   
  92:              For b As Integer = 0 To 3
  93:                  newSeed(b) = inData((i * 4) + b)
  94:              Next
  95:              For b As Integer = 0 To 3
  96:                  inData((i * 4) + b) = inData((i * 4) + b) Xor seedBytes(b)
  97:              Next
  98:              For b As Integer = 0 To 3
  99:                  seedBytes(b) = newSeed(b)
 100:              Next
 101:          Next
 102:   
 103:          For i As Integer = 0 To remainder - 1
 104:              inData((count * 4) + i) = inData((count * 4) + i) Xor seedBytes(0)
 105:          Next
 106:   
 107:          Return inData
 108:      End Function
 109:   
 110:      Public Shared Function CRC32(ByVal inData() As Byte, ByVal length As Integer, ByVal nCrcSeed As Integer) As Integer
 111:          'unsigned int nCrc = g_nCrcTable[(~nCrcSeed) & 0xFF];
 112:          Dim nCrc As UInt32 = m_CRCTable(Not nCrcSeed And &HFF)
 113:   
 114:          'nCrc ^= 0x00FFFFFF;
 115:          nCrc = nCrc Xor &HFFFFFF
 116:   
 117:          'unsigned int nIndex = (nCrcSeed >> 8) ^ nCrc;
 118:          Dim nIndex As UInt32 = (nCrcSeed >> 8) Xor nCrc
 119:   
 120:          'nCrc = (nCrc >> 8) & 0x00FFFFFF;
 121:          nCrc = (nCrc >> 8) And &HFFFFFF
 122:   
 123:          'nCrc ^= g_nCrcTable[nIndex & 0xFF];
 124:          nCrc = nCrc Xor m_CRCTable(nIndex And &HFF)
 125:   
 126:          'nIndex = (nCrcSeed >> 16) ^ nCrc;
 127:          nIndex = (nCrcSeed >> 16) Xor nCrc
 128:   
 129:          'nCrc = (nCrc >> 8) & 0x00FFFFFF;
 130:          nCrc = (nCrc >> 8) And &HFFFFFF
 131:   
 132:          'nCrc ^= g_nCrcTable[nIndex & 0xFF];
 133:          nCrc = nCrc Xor m_CRCTable(nIndex And &HFF)
 134:   
 135:          'nIndex = (nCrcSeed >> 24) ^ nCrc;
 136:          nIndex = (nCrcSeed >> 24) Xor nCrc
 137:   
 138:          'nCrc = (nCrc >> 8) &0x00FFFFFF;
 139:          nCrc = (nCrc >> 8) And &HFFFFFF
 140:   
 141:          'nCrc ^= g_nCrcTable[nIndex & 0xFF];
 142:          nCrc = nCrc Xor m_CRCTable(nIndex And &HFF)
 143:   
 144:          'for( short i = 0; i < nLength; i++ ) {
 145:          For i As Integer = 0 To length - 1
 146:              'nIndex = (pData[ i]) ^ nCrc;
 147:              nIndex = inData(i) Xor nCrc
 148:              'nCrc = (nCrc >> 8) & 0x00FFFFFF;
 149:              nCrc = (nCrc >> 8) And &HFFFFFF
 150:              'nCrc ^= g_nCrcTable[nIndex & 0xFF];
 151:              nCrc = nCrc Xor m_CRCTable(nIndex And &HFF)
 152:   
 153:          Next
 154:          '}
 155:   
 156:          'return ~nCrc;
 157:          Return Not nCrc
 158:      End Function
 159:   
 160:      '   void CRC_(unsigned char *data, int size, unsigned long &crc){
 161:      '        if (!size) return;
 162:      '        for (int i = 0; i < size; i++)
 163:      '            crc = (crc >> 8) ^ crc_table[(crc & 0xff) ^ data[ i]];
 164:      '   }
 165:   
 166:      Private Shared m_CRCTable() As UInt32 = New UInt32() { _
 167:                                                      &H0UL, &H77073096UL, &HEE0E612CUL, &H990951BAUL, &H76DC419UL, &H706AF48FUL, _
 168:                                                      &HE963A535UL, &H9E6495A3UL, &HEDB8832UL, &H79DCB8A4UL, &HE0D5E91EUL, &H97D2D988UL, _
 169:                                                      &H9B64C2BUL, &H7EB17CBDUL, &HE7B82D07UL, &H90BF1D91UL, &H1DB71064UL, &H6AB020F2UL, _
 170:                                                      &HF3B97148UL, &H84BE41DEUL, &H1ADAD47DUL, &H6DDDE4EBUL, &HF4D4B551UL, &H83D385C7UL, _
 171:                                                      &H136C9856UL, &H646BA8C0UL, &HFD62F97AUL, &H8A65C9ECUL, &H14015C4FUL, &H63066CD9UL, _
 172:                                                      &HFA0F3D63UL, &H8D080DF5UL, &H3B6E20C8UL, &H4C69105EUL, &HD56041E4UL, &HA2677172UL, _
 173:                                                      &H3C03E4D1UL, &H4B04D447UL, &HD20D85FDUL, &HA50AB56BUL, &H35B5A8FAUL, &H42B2986CUL, _
 174:                                                      &HDBBBC9D6UL, &HACBCF940UL, &H32D86CE3UL, &H45DF5C75UL, &HDCD60DCFUL, &HABD13D59UL, _
 175:                                                      &H26D930ACUL, &H51DE003AUL, &HC8D75180UL, &HBFD06116UL, &H21B4F4B5UL, &H56B3C423UL, _
 176:                                                      &HCFBA9599UL, &HB8BDA50FUL, &H2802B89EUL, &H5F058808UL, &HC60CD9B2UL, &HB10BE924UL, _
 177:                                                      &H2F6F7C87UL, &H58684C11UL, &HC1611DABUL, &HB6662D3DUL, &H76DC4190UL, &H1DB7106UL, _
 178:                                                      &H98D220BCUL, &HEFD5102AUL, &H71B18589UL, &H6B6B51FUL, &H9FBFE4A5UL, &HE8B8D433UL, _
 179:                                                      &H7807C9A2UL, &HF00F934UL, &H9609A88EUL, &HE10E9818UL, &H7F6A0DBBUL, &H86D3D2DUL, _
 180:                                                      &H91646C97UL, &HE6635C01UL, &H6B6B51F4UL, &H1C6C6162UL, &H856530D8UL, &HF262004EUL, _
 181:                                                      &H6C0695EDUL, &H1B01A57BUL, &H8208F4C1UL, &HF50FC457UL, &H65B0D9C6UL, &H12B7E950UL, _
 182:                                                      &H8BBEB8EAUL, &HFCB9887CUL, &H62DD1DDFUL, &H15DA2D49UL, &H8CD37CF3UL, &HFBD44C65UL, _
 183:                                                      &H4DB26158UL, &H3AB551CEUL, &HA3BC0074UL, &HD4BB30E2UL, &H4ADFA541UL, &H3DD895D7UL, _
 184:                                                      &HA4D1C46DUL, &HD3D6F4FBUL, &H4369E96AUL, &H346ED9FCUL, &HAD678846UL, &HDA60B8D0UL, _
 185:                                                      &H44042D73UL, &H33031DE5UL, &HAA0A4C5FUL, &HDD0D7CC9UL, &H5005713CUL, &H270241AAUL, _
 186:                                                      &HBE0B1010UL, &HC90C2086UL, &H5768B525UL, &H206F85B3UL, &HB966D409UL, &HCE61E49FUL, _
 187:                                                      &H5EDEF90EUL, &H29D9C998UL, &HB0D09822UL, &HC7D7A8B4UL, &H59B33D17UL, &H2EB40D81UL, _
 188:                                                      &HB7BD5C3BUL, &HC0BA6CADUL, &HEDB88320UL, &H9ABFB3B6UL, &H3B6E20CUL, &H74B1D29AUL, _
 189:                                                      &HEAD54739UL, &H9DD277AFUL, &H4DB2615UL, &H73DC1683UL, &HE3630B12UL, &H94643B84UL, _
 190:                                                      &HD6D6A3EUL, &H7A6A5AA8UL, &HE40ECF0BUL, &H9309FF9DUL, &HA00AE27UL, &H7D079EB1UL, _
 191:                                                      &HF00F9344UL, &H8708A3D2UL, &H1E01F268UL, &H6906C2FEUL, &HF762575DUL, &H806567CBUL, _
 192:                                                      &H196C3671UL, &H6E6B06E7UL, &HFED41B76UL, &H89D32BE0UL, &H10DA7A5AUL, &H67DD4ACCUL, _
 193:                                                      &HF9B9DF6FUL, &H8EBEEFF9UL, &H17B7BE43UL, &H60B08ED5UL, &HD6D6A3E8UL, &HA1D1937EUL, _
 194:                                                      &H38D8C2C4UL, &H4FDFF252UL, &HD1BB67F1UL, &HA6BC5767UL, &H3FB506DDUL, &H48B2364BUL, _
 195:                                                      &HD80D2BDAUL, &HAF0A1B4CUL, &H36034AF6UL, &H41047A60UL, &HDF60EFC3UL, &HA867DF55UL, _
 196:                                                      &H316E8EEFUL, &H4669BE79UL, &HCB61B38CUL, &HBC66831AUL, &H256FD2A0UL, &H5268E236UL, _
 197:                                                      &HCC0C7795UL, &HBB0B4703UL, &H220216B9UL, &H5505262FUL, &HC5BA3BBEUL, &HB2BD0B28UL, _
 198:                                                      &H2BB45A92UL, &H5CB36A04UL, &HC2D7FFA7UL, &HB5D0CF31UL, &H2CD99E8BUL, &H5BDEAE1DUL, _
 199:                                                      &H9B64C2B0UL, &HEC63F226UL, &H756AA39CUL, &H26D930AUL, &H9C0906A9UL, &HEB0E363FUL, _
 200:                                                      &H72076785UL, &H5005713UL, &H95BF4A82UL, &HE2B87A14UL, &H7BB12BAEUL, &HCB61B38UL, _
 201:                                                      &H92D28E9BUL, &HE5D5BE0DUL, &H7CDCEFB7UL, &HBDBDF21UL, &H86D3D2D4UL, &HF1D4E242UL, _
 202:                                                      &H68DDB3F8UL, &H1FDA836EUL, &H81BE16CDUL, &HF6B9265BUL, &H6FB077E1UL, &H18B74777UL, _
 203:                                                      &H88085AE6UL, &HFF0F6A70UL, &H66063BCAUL, &H11010B5CUL, &H8F659EFFUL, &HF862AE69UL, _
 204:                                                      &H616BFFD3UL, &H166CCF45UL, &HA00AE278UL, &HD70DD2EEUL, &H4E048354UL, &H3903B3C2UL, _
 205:                                                      &HA7672661UL, &HD06016F7UL, &H4969474DUL, &H3E6E77DBUL, &HAED16A4AUL, &HD9D65ADCUL, _
 206:                                                      &H40DF0B66UL, &H37D83BF0UL, &HA9BCAE53UL, &HDEBB9EC5UL, &H47B2CF7FUL, &H30B5FFE9UL, _
 207:                                                      &HBDBDF21CUL, &HCABAC28AUL, &H53B39330UL, &H24B4A3A6UL, &HBAD03605UL, &HCDD70693UL, _
 208:                                                      &H54DE5729UL, &H23D967BFUL, &HB3667A2EUL, &HC4614AB8UL, &H5D681B02UL, &H2A6F2B94UL, _
 209:                                                      &HB40BBE37UL, &HC30C8EA1UL, &H5A05DF1BUL, &H2D02EF8DUL _
 210:                                                  }
 211:   
 212:  End Class

 

There it is, in all the unfriendly nastyness.  The API class I am using is calling the previous c++ implementation that I had before, just to verify that they match.

Just thought I would gloat a bit, it was a tedious process, and a lot of learning about .Net binary actions that I wasn't aware of, sometimes you just look at the code, but don't really understand it, I finally have a really good understanding of how this all works.

*EDIT For Credit
SWGEmu.com
CRC Explained
Encryption Explained

Posted by Tom Anderson | 1 Comments
More Posts Next page »