WPF resource path resolution is all about context

Say you wanted to defined a Menu and Toolbar in a XAML file that associated with any code behind. One reason why you would do this is to be able to support a generic data grid view that you pass it a menu, toolbar and status bar with with its asssociated view model. As you can image, such a framework can allow you use a single DatagridView implementation for many different types of view.

Such a menu can be defined as follows:

<Menu
    DockPanel.Dock="Top"
    MinHeight="25"
    Background="Transparent"   
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:utils="clr-namespace:Pidac.Controls.Dictionary.Utils;assembly=Pidac.Controls.Dictionary">
    <Menu.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <utils:SharedResourceDictionary Source="../Resources/Styles.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Menu.Resources>
    <MenuItem 
        Header="File"                  
        Margin="5,1,5,0" >
        <MenuItem 
            Header="Copy"   
            Command="{Binding Commands[CopyCmd]}" CommandParameter="{Binding ViewID}"  
            Icon="{StaticResource CopyImg16}"/>
        <Separator />
        <MenuItem 
            Header="Export" 
            Command="{Binding Commands[ExportToFileCmd]}" 
            CommandParameter="{Binding ViewID}"
            Icon="{StaticResource DocumentExportImg16}"/>
        <MenuItem
            Header="Forward as Attachment" 
            CommandParameter="{Binding ViewID}"
            Command="{Binding Commands[AttachToEmailCmd]}"
            Icon="{StaticResource DocumentAttachImg16}"/>
        <MenuItem 
            Header="Print" 
            Command="{Binding Path=Commands[PrintCmd]}" 
            CommandParameter="{Binding ViewID}"
            Icon="{StaticResource PrintImg16}"/>
        <Separator/>                       
        <MenuItem 
            Header="Refresh Now" 
            Command="{Binding Path=Commands[RefreshViewCmd]}"
            CommandParameter="{Binding ViewID}" 
            Icon="{StaticResource RefreshImage16}"/>   
        <MenuItem 
            Header="Close" 
            Command="{Binding Path=Commands[CloseCmd]}" 
            CommandParameter="{Binding ViewID}"
            Icon="{StaticResource CloseOrExitImage16}"/>
            
    
    <MenuItem Header="Tools" Margin="5,1,5,0" >           
        <MenuItem 
            Header="Query" 
            Command="{Binding Path=Commands[QueryCmd]}" 
            CommandParameter="{Binding ViewID}"
            Icon="{StaticResource SearchImage16}"/>
        <Separator/>
        <MenuItem 
            Header="Select columns" 
            Command="{Binding Path=Commands[SelColumnsCmd]}" 
            CommandParameter="{Binding ViewID}"
            Icon="{StaticResource TableSelectColumnImage16}"/>
        <MenuItem 
            Header="Set Options" 
            Command="{Binding Path=Commands[PropertiesCmd]}" 
            CommandParameter="{Binding ViewID}"
            Icon="{StaticResource SetOptionsImage16}"/>
        </MenuItem>
    </MenuItem>
</Menu>

and the code that will parse this XAML into an menu instance that will be added to the object tree is as follows:

   public static TObject CreateObjectFromResource<TObject>(string resourceUrl, string szBaseUri) //where TObject : UIElement
        {
            var context = new ParserContext();
            if (szBaseUri != null)
                context.BaseUri = new Uri(szBaseUri);

            object Object = null;
            using (Stream resource = ResourceUtils.GetResourceStream(resourceUrl))
                Object = XamlReader.Load(resource, context);

            return (TObject)Object;
        }

Now this is the thing. You MUST absolutely have your resource paths right, otherwise, this you will scratch head for a couple of hours just to get this simple part of the framework in place. The two lines of pain are:

    xmlns:utils="clr-namespace:Pidac.Controls.Dictionary.Utils;assembly=Pidac.Controls.Dictionary"

and

       <utils:SharedResourceDictionary Source="../Resources/Styles.xaml"/>

In the first line, I am using a custom SharedResourceDictionary implementation to re-use styles. Even though this class is implemented in the same assembly that has the XAML resource, I need to still provide the assembly attribute value as well. Doing this:

    xmlns:utils="clr-namespace:Pidac.Controls.Dictionary.Utils"

throws the following exception and this is possibly due to the fact that the XAML parser code is not contained in the same assembly as the SharedResourceDictionary implementation.

On the second line, however, providing an absolute path is not necessary. Reason here is that XAML resource file is embedded within an assembly, which implies that the WPF style resolving process will look for the style relative to the resource location in its containing assembly. An absolute path never hurts but its not necessary.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s