Tag Archives: WPF

Solving cross-assembly WPF Resource problems once and for all

I wrote several posts about WPF weakness when it comes to sharing resources. Our use case, at my at work, where we load multiple versions of an assembly into a single app domain this problem has been felt most acutely. As I wrote previously, using the most documented way of loading resource dictionaries, unless you specify a version of an assembly when loading a resource dictionary you are running the risk of loading a wrong resource dictionary because you may have multiple versions of same library loaded into an app domain. And such problems are notoriously difficult to catch and understand.

At least now, it’s easy to solve. I am glad to announce that I found a definitive way to share resources across assembly boundaries without running into versioning issues. The key to doing this is to never load resource dictionary from another assembly using cross-assembly Uris. I did not know that there was another way until recently. All the examples of loading resource dictionaries, that I can recall, use the Source=”{PackUri}” method of loading, but there’s a way to reference resource dictionary using code.

A ResourceDictionary, just like any other class that can be defined in Xaml, can also contain the “code behind” portion, a source code file associated with the main Xaml file. If you already have resources defined in a resource dictionary somewhere in your assembly, you don’t have to change it. Instead you’ll create a brand new resource dictionary which you would use to export the resource for sharing. For example, let’s say you have a resource dictionary defining the colors of the stoplight. It lives in an assembly called ResourceSharing, in a namespace ResourceSharing.Resources in a file called Resources.xaml.


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <SolidColorBrush x:Key="Go" Color="Green"/>
    <SolidColorBrush x:Key="Stop" Color="Red"/>
    <SolidColorBrush x:Key="PrepareToStop" Color="Yellow"/>
    
</ResourceDictionary>

To export this ResourceDictionary, create a new Resource Dictionary for export. Let’s call it ResourceSharing.Resources.ExportResourceDictionary. I’ll consist of the xaml and the C# portions

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
                    x:Class="ResourceSharing.Resources.ExportResourceDictionary"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="Resources.xaml"/>
    </ResourceDictionary.MergedDictionaries>
    
</ResourceDictionary>

Notice the Class definition on line 2.

The code behind class looks like this

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ResourceSharing.Resources
{
    public partial class ExportResourceDictionary
    {
        //Expose it as singleton to avoid multiple instances of this dictionary
        private static readonly ExportResourceDictionary _instance = new ExportResourceDictionary();

        public static ExportResourceDictionary Instance
        {
            get { return _instance; }
        }

        public ExportResourceDictionary()
        {
            InitializeComponent();
        }
    }
}

Let’s also create a namespace we can use to make it easier to reference these resources from another assembly using namespaces

using System.Windows.Markup;

[assembly:XmlnsDefinitionAttribute("http://my.schemas.com/web/resources", "ResourceSharing.Resources")]

How to Share

Here’s how to consume the resources exported from the ResourceSharingAssembly. For this example, I had an executable assembly called ResourceSharingClient, where I am referencing exported resource right in the Main Window. Notice the use of the exported namespaces in line 2 and the way that I am referencing a static instance of the ExportResourceDictionary. Such references are version-proof because .NET knows the version of the dependent assemblies. There’s no need to dynamically discover the dependent assembly and the code actually looks a lot neater too. I know that such approach requires a bit more work, but the final result is a lot safer. I only wished that such approach had been advertised by Microsoft WPF evangelists from the beginning.

<Window
        x:Class="ResourceSharingClient.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:rs="http://my.schemas.com/web/resources"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
        mc:Ignorable="d"
        Title="MainWindow" d:DesignWidth="111.5" d:DesignHeight="109.5" Width="120" Height="130">
    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <x:Static Member="rs:ExportResourceDictionary.Instance"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>
    <Border BorderBrush="Black" BorderThickness="2" HorizontalAlignment="Center" VerticalAlignment="Center" Padding="2" CornerRadius="2" Background="Silver">
        <StackPanel Orientation="Vertical">
            <Ellipse Width="20" Height="20" Fill="{DynamicResource Stop}" Stroke="Black" StrokeThickness="1"/>
            <Ellipse Width="20" Height="20" Fill="{DynamicResource PrepareToStop}" Stroke="Black" StrokeThickness="1"/>
            <Ellipse Width="20" Height="20" Fill="{DynamicResource Go}"  Stroke="Black" StrokeThickness="1"/>
        </StackPanel>
    </Border>
</Window>

WPF Resource Key Collision

Our framework allows developers to create add-ins (modules), which are loaded at run time.  It’s possible for each developer to create custom resources and merge them into a Resource Dictionary on the Application object.  So what happens when two different add-ins define a resource with the same key?  How does WPF handle such key collision?

To answer this question, II created a small application which tests the ability of a single resource dictionary to load multiple merged dictionaries which have objects with same key. The code is below but I did discover that

  1. the app does not crash – WPF handles redifinition of the key gracefully
  2. the last one wins – whichever dictionary defining the entry with the same key loads last is the one that’s going to be resolved when app asks for such resource using the DynamicResource
  3. the original definition becomes effective when the override is removed – removing the duplicate definition makes the original value effective

It’s important to note that if the key already exists in the primary dictionary, the one into which other dictionaries are merged, the discovery rules will ignore any re-definition of the key, according to docs here.

Resources in a merged dictionary occupy a location in the resource lookup scope that is just after the scope of the main resource dictionary they are merged into. Although a resource key must be unique within any individual dictionary, a key can exist multiple times in a set of merged dictionaries. In this case, the resource that is returned will come from the last dictionary found sequentially in the MergedDictionaries collection. If the MergedDictionaries collection was defined in XAML, then the order of the merged dictionaries in the collection is the order of the elements as provided in the markup. If a key is defined in the primary dictionary and also in a dictionary that was merged, then the resource that is returned will come from the primary dictionary. These scoping rules apply equally for both static resource references and dynamic resource references.

<Application x:Class="Test.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="rd1.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <sys:String x:Key="MyString">Hello</sys:String>
    
</ResourceDictionary>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    
    <sys:String x:Key="MyString">Hello</sys:String>
    
</ResourceDictionary>
<Window x:Class="Test.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Click="HandleClick" Content="{DynamicResource MyString}"/>
    </Grid>
</Window>
using System;
using System.Windows;

namespace Test
{
    public partial class MainWindow : Window
    {
        private bool _remove;
        public MainWindow()
        {
            InitializeComponent();
        }

        private void HandleClick(object sender, RoutedEventArgs e)
        {
            if (_remove)
                Application.Current.Resources.MergedDictionaries.Remove(Application.Current.Resources.MergedDictionaries[1]);
            else
                Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("rd2.xaml", UriKind.Relative) });

            _remove = !_remove;
        }
    }
}

Microsoft mechanism to load WPF resources is broken. (looking for a better title)

I’ve been using WPF along with .NET and have been a staunch supported of the technology for years now. The pattern of separating presentation from logic gained a lot of traction. Technologies such as iOS’s cocoa touch, JavaFX and others follow same model. Even HTML5 forced all the pure presentation/layout markup out from HTML and into CSS. So the idea is sound. But what about execution.

WPF implements this pattern using XAML. XAML markup is used to design the visual this xaml is stored in the xaml file, whose name usually matches the name of the class. For example, MyWindow.xaml. During compilation, an auto-generated file marries the code that defines the visual (MyWindow.xaml.cs) and the xaml file itself (MyWindow.xaml.cs). The autogenerate file would be named MyWindow.g.i.cs and it would contain the implementation of the InitializeComponent() method that’s referenced in the main MyWindow.xaml.cs class. The contents of the method looks something like this:

[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute(&quot;PresentationBuildTasks&quot;, &quot;4.0.0.0&quot;)]
public void InitializeComponent() {
	if (_contentLoaded) {
		return;
	}
	_contentLoaded = true;
	System.Uri resourceLocater = new System.Uri(&quot;/MyLibrary;component/mainwindow.xaml&quot;, System.UriKind.Relative);
	
	#line 1 &quot;..\..\MainWindow.xaml&quot;
	System.Windows.Application.LoadComponent(this, resourceLocater);
	
	#line default
	#line hidden
}

So far so good right. OK, let’s talk about some of the advanced uses of .Net. Specifically support for having multiple versions of same assembly (dll) to be loaded in a single AppDomain. Such use case comes handy in a framework use case. A framework supports multiple plug-in modules, each can be developed by other developers who may rely on different versions of common libraries. I am not talking about things like log4net, I am talking about shared assemblies that define common logic and can be reused by each module. If 2 modules are loaded into a single app-domain and both depend on their ‘private’ version of the common assembly, a good framework should not force both modules to share latest version of the library, because

  • ‘latest’ does not mean backwards-compatible
  • ‘latest’ may have bugs that ‘earlier’ version did not
  • …I can go on for a while on this one. It’s 2014, dll hell is well known to software developers of the 20th century.

Now, let’s say that 2 different versions of the shared library called MyLibrary are loaded and one version is used by Module1 and another version is used by Module2. During compilation, assembly name along with the version are recorded in modules’ manifests so that when .NET encounters request to execute code defined in MyLibrary the source module will tell .Net which version of MyLibrary contains the code it wants executed. This is a basic building block of .NET and a very attractive feature.

Now, what about WPF? Well that’s not that clear is it? Look at Line #8 above: System.Uri resourceLocater = new System.Uri(“/MyLibrary;component/mainwindow.xaml”, System.UriKind.Relative). It defines what it calls a resourcelocator which the system will use to find the xaml resouce in an assembly called MyLibrary. I don’t know how WPF resolves the assembly, but if I were to write the logic for resolution, I would not have enough information to find a proper assembly MyLibrary, because I don’t know which version to load. I would have to guess. And as we know in any science guessing is bad. Computer Science is an exact… well… science. Guessing, hoping, holding fingers crossed or other techniques that do not produce determinate result will break at the worst possible moment! I can see how developers armed with inaccurate knowledge or with little experience make mistakes like this, but when Microsoft’s tools generated code that’s faulty, and make it impossible to intercept and fix, there is no other way to call it – they fucked up! How can they expect developers to trust their tooling if they, themselves, auto-generate code that sometimes breaks.

This breaks in a most bizarre way, too. MyLibrary version 2 was loading the MainWindow class, which then was running the InitializeComponent() method above, encountered the LoadComponent call, which asked to load the resource from the MyLibrary assembly without a version, and the code picked the wrong version of the MyLibrary assembly, which did not have the MyWindow class, nor the resource. What a way to blow up!

Such nonsense was reported to them too! This connect ticket marked as “Wont’t Fix” describes the problem: https://connect.microsoft.com/VisualStudio/feedback/details/668914/xaml-generated-code-uses-resource-uri-without-assembly-strong-name. StackOverflow also has references to this problem: http://stackoverflow.com/questions/1453107/how-to-force-wpf-to-use-resource-uris-that-use-assembly-strong-name-argh/6341678#6341678. Luckily one of the suggestions led me to the best solution.

The Fix


We were lucky to have our builds automated via scripts which rely on MSBuild to build the solutions. All I needed to do is to modify the script and pass the /p:AssemblyInfo=$version parameter to MSBuild. (http://stackoverflow.com/a/26689750/195275). This would tell the build system to add assembly version to the Resource reference in line 8. It now looks like the code below. Problem solved, but could have been avoided on the first place!

Shame on you, Microsoft!

[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute(&quot;PresentationBuildTasks&quot;, &quot;4.0.0.0&quot;)]
public void InitializeComponent() {
	if (_contentLoaded) {
		return;
	}
	_contentLoaded = true;
	System.Uri resourceLocater = new System.Uri(&quot;/MyLibrary;V1.0.23;component/mainwindow.xaml&quot;, System.UriKind.Relative);
	
	#line 1 &quot;..\..\MainWindow.xaml&quot;
	System.Windows.Application.LoadComponent(this, resourceLocater);
	
	#line default
	#line hidden
}

WPF: The real difference between ContentControl and ContentPresenter

Most of the resources on the web specify that ContentPresenter supports a special property called ContentSource, with a default value of “Content“, which makes it easy for it to automatically set the values of these properties used to render content:

    • Content
    • ContentTemplate
    • ContentTemplateSelector
    • ContentStringFormat (3.5 sp1 +)

Basically, the property specifies the string prefix used to bind to properties of the parent. If you change the value of the ContentSource property to something else, like “Header“, the ContentPresenter’s properties would auto-bind to bind to these properties of the control you are templating using ControlTemplate:

    • Header
    • HeaderTemplate
    • HeaderTemplateSelector
    • HeaderStringFormat (3.5 sp1 +)

The web is siltent, however, about one major difference in behavior that’s important to note. 

ContentPresenter’s DataContext is automatically set to the value of its Content property, while ContentControl’s DataContext is not.

Why is it important?  In one word – bindings.  Bindings are resolved relatived to the value of the  DataContext property.  If you declare a binding on the ContentPresenter, the moment its content is set, the binding would be re-evaluated.  For example, let’s say you are building a dual-content control like this:

<UserControl x:Class="ContentPresenterVsContentControl.DualContentControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ContentPresenter Grid.Row="0" 
                          Content="{Binding ContentOne}" 
                          ContentTemplate="{Binding ContentOneTemplate}" 
                          ContentTemplateSelector="{Binding ContentOneTemplateSelector}"/>
        <ContentPresenter Grid.Row="1" 
                          Content="{Binding ContentTwo}" 
                          ContentTemplate="{Binding ContentTwoTemplate}" 
                          ContentTemplateSelector="{Binding ContentTwoTemplateSelector}"/>
    </Grid>
</UserControl>

using System.Windows;
using System.Windows.Controls;

namespace ContentPresenterVsContentControl
{
    /// <summary>
    /// Interaction logic for DualContentControl.xaml
    /// </summary>
    public partial class DualContentControl : UserControl
    {
        public static readonly DependencyProperty ContentOneProperty = DependencyProperty.Register("ContentOne", typeof(object), typeof(DualContentControl));
        public static readonly DependencyProperty ContentOneTemplateProperty = DependencyProperty.Register("ContentOneTemplate", typeof(DataTemplate), typeof(DualContentControl));
        public static readonly DependencyProperty ContentOneTemplateSelectorProperty = DependencyProperty.Register("ContentOneTemplateSelector", typeof(DataTemplateSelector), typeof(DualContentControl));

        public static readonly DependencyProperty ContentTwoProperty = DependencyProperty.Register("ContentTwo", typeof(object), typeof(DualContentControl));
        public static readonly DependencyProperty ContentTwoTemplateProperty = DependencyProperty.Register("ContentTwoTemplate", typeof(DataTemplate), typeof(DualContentControl));
        public static readonly DependencyProperty ContentTwoTemplateSelectorProperty = DependencyProperty.Register("ContentTwoTemplateSelector", typeof(DataTemplateSelector), typeof(DualContentControl));


        public DualContentControl()
        {
            InitializeComponent();
            DataContext = this;
        }

        public object ContentOne
        {
            get { return GetValue(ContentOneProperty); }
            set { SetValue(ContentOneProperty, value); }
        }

        public DataTemplate ContentOneTemplate
        {
            get { return (DataTemplate) GetValue(ContentOneTemplateProperty); }
            set { SetValue(ContentOneTemplateProperty, value); }
        }


        public DataTemplateSelector ContentOneTemplateSelector
        {
            get { return (DataTemplateSelector) GetValue(ContentOneTemplateSelectorProperty); }
            set { SetValue(ContentOneTemplateSelectorProperty, value); }
        }

        public object ContentTwo
        {
            get { return GetValue(ContentTwoProperty); }
            set { SetValue(ContentTwoProperty, value); }
        }

        public DataTemplate ContentTwoTemplate
        {
            get { return (DataTemplate)GetValue(ContentTwoTemplateProperty); }
            set { SetValue(ContentTwoTemplateProperty, value); }
        }


        public DataTemplateSelector ContentTwoTemplateSelector
        {
            get { return (DataTemplateSelector)GetValue(ContentTwoTemplateSelectorProperty); }
            set { SetValue(ContentTwoTemplateSelectorProperty, value); }
        }

    }
}

Now, let’s use it in the project:

<Window x:Class="ContentPresenterVsContentControl.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:ContentPresenterVsContentControl"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <local:DualContentControl 
            ContentOne="Hello"
            ContentTwo="World">
            
            <local:DualContentControl.ContentOneTemplate>
                <DataTemplate>
                   <Label Background="Orange" Content="{Binding}" 
                          HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
                </DataTemplate>
            </local:DualContentControl.ContentOneTemplate>

            <local:DualContentControl.ContentTwoTemplate>
                <DataTemplate>
                    <Label Background="Green" Content="{Binding}" 
                           HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
                </DataTemplate>
            </local:DualContentControl.ContentTwoTemplate>
        </local:DualContentControl>
    </Grid>
</Window>

You would expect something like this,image below, right?

DualContentControlWindow

Well, if you used ContentPresenters in your UserControl, you would see this, instead:

DualContentControlWindowBad

You would also see strange binding errors in the console

System.Windows.Data Error: 40 : BindingExpression path error: 'ContentTwoTemplate' 
property not found on 'object' ''String' (HashCode=-1506748533)'. 
BindingExpression:Path=ContentTwoTemplate; DataItem='String' (HashCode=-1506748533); 
target element is 'ContentPresenter' (Name=''); target property is 'ContentTemplate' (type 'DataTemplate')

System.Windows.Data Error: 40 : BindingExpression path error: 'ContentTwoTemplateSelector' 
property not found on 'object' ''String' (HashCode=-1506748533)'. BindingExpression:Path=ContentTwoTemplateSelector; DataItem='String' (HashCode=-1506748533); 
target element is 'ContentPresenter' (Name=''); target property is 'ContentTemplateSelector' (type 'DataTemplateSelector')

System.Windows.Data Error: 40 : BindingExpression path error: 'ContentOneTemplate' 
property not found on 'object' ''String' (HashCode=-694847)'. 
BindingExpression:Path=ContentOneTemplate; DataItem='String' (HashCode=-694847); 
target element is 'ContentPresenter' (Name=''); target property is 'ContentTemplate' (type 'DataTemplate')

System.Windows.Data Error: 40 : BindingExpression path error: 'ContentOneTemplateSelector' 
property not found on 'object' ''String' (HashCode=-694847)'. BindingExpression:Path=ContentOneTemplateSelector; DataItem='String' (HashCode=-694847); 
target element is 'ContentPresenter' (Name=''); target property is 'ContentTemplateSelector' (type 'DataTemplateSelector')

Notice that for some reason the ContentOneTemplate, ContentOneTemplateSelector, ContentTwoTemplate and ContentTwoTemplateSelector are bound to an object of type ‘String’. Why? Because the moment you sent the ContentProperty of the ContentPresenter its DataContext was switch to match the value of the Content property. Since the Content of the both ContentPresenters are string, “Hello” and “World” all other bindings on the ContentPresenter are now resolved against these string values!

If you replace ContentPresenters with ContentControls, the system works as expected.

<UserControl x:Class="ContentPresenterVsContentControl.DualContentControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ContentControl Grid.Row="0" 
                          Content="{Binding ContentOne}" 
                          ContentTemplate="{Binding ContentOneTemplate}" 
                          ContentTemplateSelector="{Binding ContentOneTemplateSelector}"/>
        <ContentControl Grid.Row="1" 
                          Content="{Binding ContentTwo}" 
                          ContentTemplate="{Binding ContentTwoTemplate}" 
                          ContentTemplateSelector="{Binding ContentTwoTemplateSelector}"/>
    </Grid>
</UserControl>

How to customize column headings in DevExpress WPF GridControl

DevExpress is one of several 3rd party .NET control providers offering a full suite of controls for WPF.  Their library of widgets is widely used in many financial firms to speed up time to market and make UI development easier.  They look good and work relatively well.  But I don’t want to advertize them too much here. If you are working with their GridControl, you probably noticed how notoriously difficult it is to certain things you thought should be a lot easier.

I recently was asked to color the background of the Grid Headers and decided I would share my pain and the solution here. Unfortunately, this exercise is not for the faint-hearted, as it requires switching your WPF project to use a custom theme that you will create using their tool.

I will show how you can modify their grid’s column heading template to inject your own Attached Properties which you can then set in your Xaml. But first, you will need to switch to a custom theme which you will create using the DevEpxress Theme Editor, which you can download here: https://www.devexpress.com/Products/NET/Controls/WPF/Themes/theme-editor.xml. You can read instructions on using the tool here: https://documentation.devexpress.com/#WpfThemeEditor/CustomDocument10429. If you use their skins already then pick those that you use as a base when saving your own custom theme. Once you have your own custom theme project, and are successfully using it in your project, you are ready to proceed with the advanced part of this exercise.

Here’s an overview of what you will need to do.

  • Step 1. Create another dll project where you would define your own custom attached properties. YOu will need to compile the project and generate a DLL which you will later reference
  • Step 2. Open the theme projects that the DevExpress tool generated in Visual Studio and add references to the dll you created in step 1.
  • Step 3. Modify their Grid Header XAML templates to reference your new Attached Properties.
  • Step 4. Reference dll you had created in step 1 in your projects and set the Attached Properties you created in the GridColumn definitions.

DevExpress wants you to use 3 different brushes to color Grid Column Headers:

1. Normal Background Brush: used for normal state rendering
2. Hover Over Brush: used when mouse is over the column heading
3. Mouse Click Brush: used when mouse is clicked while over the column heading

To encapsulate all 3 states, we are going to create a custom Type called GridColHeaderBrushes in our custom dll project (step 1). I’ll be a DependencyObject defining 3 Attached properties.
Here’s the code.


    public class GridColHeaderBrushes : DependencyObject
    {
        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.RegisterAttached("Background",
            typeof(Brush),
            typeof(GridColHeaderBrushes),
            new UIPropertyMetadata(null));

        public static readonly DependencyProperty MouseOverProperty =
            DependencyProperty.RegisterAttached("MouseOver",
            typeof(Brush),
            typeof(GridColHeaderBrushes),
            new UIPropertyMetadata(null));

        public static readonly DependencyProperty MousePressedProperty =
            DependencyProperty.RegisterAttached("MousePressed",
            typeof(Brush),
            typeof(GridColHeaderBrushes),
            new UIPropertyMetadata(null));

        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }

        public Brush MouseOver
        {
            get { return (Brush)GetValue(MouseOverProperty); }
            set { SetValue(MouseOverProperty, value); }
        }

        public Brush MousePressed
        {
            get { return (Brush)GetValue(MousePressedProperty); }
            set { SetValue(MousePressedProperty, value); }
        }
    }

In addition we would need to create another class which would contain attached properties we would use on the grid. The two files must be in the same namespace to simplify things. Notice that I also added 2 more properties to control text alignment in column headers. This is a bonus 🙂

    public static class GridColumnHeaders
    {
        public static readonly DependencyProperty HeaderBackgroundsProperty =
            DependencyProperty.RegisterAttached("HeaderBackgrounds",
            typeof(GridColHeaderBrushes),
            typeof(GridColumnHeaders));

        public static readonly DependencyProperty HorizontalHeaderAlignmentProperty =
            DependencyProperty.RegisterAttached("HorizontalHeaderAlignment",
            typeof(HorizontalAlignment),
            typeof(GridColumnHeaders));

        public static readonly DependencyProperty VerticalHeaderAlignmentProperty =
            DependencyProperty.RegisterAttached("VerticalHeaderAlignment",
            typeof(VerticalAlignment),
            typeof(GridColumnHeaders));


        public static HorizontalAlignment GetHorizontalHeaderAlignment(DependencyObject obj)
        {
            return (HorizontalAlignment)obj.GetValue(HorizontalHeaderAlignmentProperty);
        }

        public static void SetHorizontalHeaderAlignment(DependencyObject obj, HorizontalAlignment value)
        {
            obj.SetValue(HorizontalHeaderAlignmentProperty, value);
        }

        public static VerticalAlignment GetVerticalHeaderAlignment(DependencyObject obj)
        {
            return (VerticalAlignment)obj.GetValue(VerticalHeaderAlignmentProperty);
        }

        public static void SetVerticalHeaderAlignment(DependencyObject obj, VerticalAlignment value)
        {
            obj.SetValue(VerticalHeaderAlignmentProperty, value);
        }

        public static GridColHeaderBrushes GetHeaderBackgrounds(DependencyObject obj)
        {
            return (GridColHeaderBrushes)obj.GetValue(HeaderBackgroundsProperty);
        }

        public static void SetHeaderBackgrounds(DependencyObject obj, GridColHeaderBrushes value)
        {
            obj.SetValue(HeaderBackgroundsProperty, value);
        }
    }

After you compile the dll, place it somewhere near the custom theme projects that DevExpress tool created for you, then open the custom theme project in Visual Studio. Open the regular project first – the one without the “ThemeEditor” in its name.

You will need to modify the ColumnHeader.xaml file located under the folder that matches your custom theme name, not under the “Generic” folder. You will need to do the following:

  1. Add a namespace reference to the namespace containing the classes above:
    <ResourceDictionary ... 
       xmlns:customui="clr-namespace:YourCustomAssembly.Namespace;assembly=YourCustomAssembly"/>
    
  2. Find the Border classes which are used as backgrounds for grid headers. They are named
    • BackgroundBorder
    • MouseOverElement
    • MousePressedElement

    and modify their “Background” Property so they look like this below. Make sure that you replace the with the hard-coded color that was in the code before you changed it.

    <Border x:Name="BackgroundBorder" Background="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HeaderBackgrounds).Background, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=<what was here before>}" cs:Name="Border_0004" />
    <Border x:Name="MouseOverElement" Background="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HeaderBackgrounds).MouseOver, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=<what was here before>}" Opacity="0" cs:Name="Border_0005" />
    <Border x:Name="MousePressedElement" Background="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HeaderBackgrounds).MousePressed, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=<what was here before>}" Opacity="0" cs:Name="Border_0006" /> 	
    
  3. As a bonus, add the HorizontalAlignment and VerticalAlignment properties to the Grid called “PART_Content” so it looks like this:
    <Grid x:Name="PART_Content"
      HorizontalAlignment="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.HorizontalHeaderAlignment), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=Left}"
      VerticalAlignment="{Binding Path=(dxg:BaseGridColumnHeader.GridColumn).(customui:GridColumnHeaders.VerticalHeaderAlignment), Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}, FallbackValue=Center}" >
    
  4. Compile and Save the project
  5. Replace the original dll that the DevExpress Theme Editor had created for your project with the new version you just compiled.

Customizing Grid Column Headers

You will need to reference the your custom dll containing the new Attached Properties in your project as well.  Once you do, you are ready to set the colors and alghment of your grid column headings. Don’t forget to add a customui namespace declaration at the top of your xaml file.

Here’s an example:

<dxg:GridControl ...>
 <dxg.GridControl.Columns>
   <dxg.GridColumn...>
      <customui:GridColumnHeaders.HeaderBackgrounds>
           <customui:DrigColHeaderBrushes Background="White" MouseOver="Green" MousePressed="Orange"/>
      </customui:GridColumnHeaders.HeaderBackgrounds>
   </dxg.GridColumn>
  ...

To control alignment you can do this:

<dxg:GridControl ...>
 <dxg.GridControl.Columns>
   <dxg.GridColumn customui:GridColumnHeaders.HorizontalHeaderAlignment="Right".../>
  ...