Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Fixing WPF ComboBox Colors Without Extending or Templating

0.00/5 (No votes)
16 Nov 2007 1  
This article demonstrates a very simple technique which can be used to easliy modify the standard Windows Presentation Foundation controls without creating an extended class or using a data template.

Introduction

If you have worked with WPF and used a non-standard color scheme, you may have run into a problem with ComboBoxes or other WPF controls. I was using a dark color scheme with White text. However, the ComboBoxes in my application ignored the foreground setting in the style. The only way I could get the ComboBox foreground to change was to explicitly set it for each ComboBox. ComboBox styling in WPF was broken.

I tried different techniques to fix the problem, which all required more code than I liked:

  • First attempt: Extend the ComboBox class with my own class that hides the foreground properties and set them manually. This worked, but it required me to use a different class name everywhere in my XAML code and in the style's TargetType.
  • Second attempt: Create a data template that contained a ComboBox and set the colors manually. This did not work, as the data template's foreground was never set.
  • Final attempt: Listen to the Foreground Dependency Property's Changed event (actually a callback) and fix the colors there. This worked! And it required no changes to any ComboBox declarations.

Example

The first two combo boxes have not changed the foreground color, while the second set are correct.

The background color of the drop down is still White.

The background color of the drop down matches the background color.

Using the Code

Here is the important class that does all the work:

public static class ComboBoxFix
{
  private static bool _isInitialized = false;

  /// <summary>
  /// Initialize must be called 
  /// before any Combo boxes are created
  /// </summary>
  public static void Initialize()
  {
    if( !_isInitialized )
    {
      // Registed the callback methods 
      // when the properties change on a ComboBox class
      ComboBox.BackgroundProperty.OverrideMetadata(
          typeof( ComboBox ),
          new FrameworkPropertyMetadata( OnBackgroundChanged ) );

      ComboBox.ForegroundProperty.OverrideMetadata(
          typeof( ComboBox ),
          new FrameworkPropertyMetadata( OnForegroundChanged ) );

      _isInitialized = true;
    }
  }

  private static void OnBackgroundChanged( 
    DependencyObject d, DependencyPropertyChangedEventArgs e )
  {
    // Set the drop down background color to match the background
    SetDropDownBackground( d as ComboBox );
  }

  private static void OnForegroundChanged( 
    DependencyObject d, DependencyPropertyChangedEventArgs e )
  {
    // Manually set the foreground (to overcome bug)
    // Apparently the ComboBox does not listen 
    // when the Foreground DepencyProperty
    // is changed and therefore does not 
    // update itself unless the value is changed 
    // through the Foreground .net property
    (d as ComboBox).Foreground = e.NewValue as Brush;
  }

  private static void SetDropDownBackground( ComboBox comboBox )
  {
    // The drop down control uses 
    // the WindowBrush to paint its background
    // By overriding that Brush (just for this control)

    if( comboBox.Resources.Contains( SystemColors.WindowBrushKey ) )
    {
      comboBox.Resources.Remove( SystemColors.WindowBrushKey );
    }

    comboBox.Resources.Add( 
      SystemColors.WindowBrushKey, comboBox.Background );
  }
}

The Intialize method must be called before any ComboBoxes are created. The best place to put the ComboBoxFix.Iniatilize() call is on the first line of the main window's constructor.

Points of Interest

The Dependency Property system is very powerful. With very little code, it is possible to register to the Changed events of all objects of a specific type that contain that property.

However, it is more complex than a standard .NET property. It is important to remember that WPF binding, animations, etc., set the Dependency Property directly. They do not go through the .NET property setter.

It seems that the source of this problem is related to this difference.

Therefore, if you create any Dependency Properties, remember to handle the changes to that value by registering a callback method in the property metadata.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here