65.9K
CodeProject 正在变化。 阅读更多。
Home

在不扩展或设置模板的情况下修复 WPF ComboBox 颜色

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.33/5 (2投票s)

2007年11月16日

CPOL

2分钟阅读

viewsIcon

38818

downloadIcon

340

本文演示了一种非常简单的技术,可用于轻松修改标准的 Windows Presentation Foundation 控件,而无需创建扩展类或使用数据模板。

引言

如果您使用过 WPF 并使用了非标准配色方案,您可能会遇到 ComboBox 或其他 WPF 控件的问题。我使用的是深色配色方案,文字为白色。但是,我的应用程序中的 ComboBox 忽略了样式中的前景色设置。我唯一能让 ComboBox 前景色改变的方法是为每个 ComboBox 显式设置它。WPF 中的 ComboBox 样式已损坏。

我尝试了不同的技术来解决这个问题,但所有这些都需要比我喜欢的更多的代码。

  • 第一次尝试:**扩展 ComboBox** 类,使用隐藏前景色属性并手动设置它们的自定义类。这可行,但它要求我在 XAML 代码和样式中的 TargetType 中使用不同的类名。
  • 第二次尝试:创建一个包含 ComboBox 并手动设置颜色的**数据模板**。这不起作用,因为数据模板的前景色从未设置。
  • 最终尝试:监听 Foreground **依赖属性的 Changed 事件**(实际上是一个回调),并在那里修复颜色。**这有效了!**而且它不需要更改任何 ComboBox 声明。

示例

第一个组合框没有更改前景色,而第二组是正确的。

下拉菜单的背景颜色仍然是白色。

下拉菜单的背景颜色与背景颜色匹配。

Using the Code

以下是完成所有工作的关键类

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 );
  }
}

必须在创建任何 ComboBox 之前调用 Intialize 方法。将 ComboBoxFix.Iniatilize() 调用放在主窗口构造函数的首行是最佳位置。

关注点

依赖属性系统非常强大。只需少量代码,就可以注册到包含该属性的特定类型的所有对象的 Changed 事件。

但是,它比标准的 .NET 属性更复杂。重要的是要记住,WPF 绑定、动画等直接设置依赖属性。它们不会通过 .NET 属性 setter。

似乎此问题的根源与此差异有关。

因此,如果您创建任何依赖属性,请记住通过在属性元数据中注册回调方法来处理该值的更改。

© . All rights reserved.