Monthly Archives: June 2014

My new release is out on Thursday.

Advertisements

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".../>
  ...