Click here to Skip to main content
15,883,960 members
Articles / Desktop Programming / WPF

Fixing WPF ComboBox Colors Without Extending or Templating

Rate me:
Please Sign up or sign in to vote.
2.33/5 (2 votes)
16 Nov 2007CPOL2 min read 37.8K   317   7   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

Image 1

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

Image 2

The background color of the drop down is still White.

Image 3

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

Using the Code

Here is the important class that does all the work:

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralInteresting approach. Pin
Patrick Etc.16-Nov-07 10:20
Patrick Etc.16-Nov-07 10:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.