【问题标题】:WPF: Dynamically change dependency property value for all user controls of same typeWPF:动态更改相同类型的所有用户控件的依赖属性值
【发布时间】:2013-07-02 17:47:10
【问题描述】:

我对 WPF 比较陌生,在使用用户控件时遇到了一些困难。

请考虑以下情况: 我有一个带有用户控件的 WPF 应用程序,比如

MySpecialButtonControl

这个“按钮”有两个外观“oldStyle”和“newStyle”(由枚举“AppearanceStyle”指定),它们由一个名为的依赖属性控制

MyLayoutProperty

回调函数必须执行更改布局的代码。 现在这是我想做的: 我需要在运行时在代码隐藏文件中一次更改此窗口中用户控件的所有(!)实例的外观。

将属性绑定(例如)到 UC 的各个实例,例如

Binding binding = new Binding("AppearanceStyle");
binding.Source = myOptionsClass;
this.myButton.SetBinding(UserControls.MySpecialButtonControl.MyLayoutProperty, binding);

效果很好。但是,如何直接更改所有 UC 实例的依赖属性,而无需遍历 UC 的集合等?有没有办法在 WPF/C# 中实现这一点?

我尝试通过使用样式来解决这个问题,但是在运行时更改所有 UC 本身共享的样式是不可能的,因为它已经在使用中(并且使用该样式的 UC 已经被绘制)。

接下来,我尝试使用如下样式的动态资源:

  <uc:MySpecialButtonControl x:Key="myFakeButton" ></uc:MySpecialButtonControl >

    <Style x:Key="myButtonStyle" TargetType="uc:MySpecialButtonControl ">
        <Setter Property="MyLayoutProperty" Value="{DynamicResource myFakeButton}"></Setter>
    </Style>

这允许我在运行时更改“myFakeButton”的“MyLayoutProperty”,这是我想要的一半,但即使在谷歌搜索了一段时间后,我仍然找不到绑定“myFakeButton”的“MyLayoutProperty”的方法到我真正需要的二传手。

任何帮助将不胜感激!

更新: 我尝试实现 Michael 提供的解决方案,但不幸的是,我得到了以下异常:

PropertyMetadata is already registered for type 'MySpecialButtonControl'.

经过一番谷歌搜索(参见MSDN),我发现 OverrideMetadata 调用应该放在“MySpecialButtonControl”的静态构造函数中:

static MySpecialButtonControl()
{
    DefaultStyleKeyProperty.OverrideMetadata(
    typeof(MySpecialButtonControl),
    new FrameworkPropertyMetadata(typeof(MySpecialButtonControl)));
}

现在,应用程序编译。现在它完美地工作了。

【问题讨论】:

  • 如果您将所有控件绑定到 myOptionsClass 对象并且此对象实现为单例(只有一个类实例活动),您应该能够通过简单地更改 myOptionsClass 的绑定属性来更新所有控件。如果 myOptionsClass 实现 INotifyPropertyChanged 或使用依赖属性,则更改的值应自动复制到 UC 的每个实例(这就是数据绑定的全部内容)
  • 感谢您的想法 - 它可以手动将 UC 绑定到 Dependency 属性,但这不是我想要的:我不想为每个控件手动进行数据绑定。假设我在代码的某处动态创建了一个新的 UC(可能是一个非常复杂的场景)并“忘记”将它绑定到 myOptionClass。 => 那么它的外观不会通过改变 Dependcy 属性而改变。 => 没有好的代码维护。

标签: c# wpf styles


【解决方案1】:

我不完全确定我是否遵循,但我会尝试回答。请评论是否接近,我会编辑直到我们到达那里。

WPF 中的所有控件都有一个属性DefaultStyleKey。任何派生的自定义控件或用户控件都可以使用该属性来设置默认样式的键。在运行时,框架将尝试查找该键的资源。通常将默认样式键设置为类的运行时类型。

public MySpecialButtonControl()
{
    DefaultStyleKeyProperty.OverrideMetadata(
        typeof (MySpecialButtonControl),
        new FrameworkPropertyMetadata(typeof (MySpecialButtonControl)));

    InitializeComponent();
}

当控件放置到窗口上时,框架将在可用资源中查找具有DefaultStyleKey 定义的键的资源。可以在许多地方定义资源。谷歌“WPF 资源解析”了解更多信息。最简单的说明方法是显示 App.xaml 中定义的默认样式。

<Application.Resources>

    <!-- the default style for MySpecialButtonControls -->
    <Style x:Key="{x:Type uc:MySpecialButtonControl}"
           TargetType="{x:Type uc:MySpecialButtonControl}"
           BasedOn="{StaticResource {x:Type UserControl}}" >
        <Setter Property="Background" Value="Blue" />
    </Style>

</Application.Resources>

现在假设您想要在运行时切换两种不同的样式。您可以在 App.xaml 中定义这些样式。

<Application.Resources>

    <!-- the first style -->
    <Style x:Key="Style1"
           TargetType="{x:Type uc:MySpecialButtonControl}"
           BasedOn="{StaticResource {x:Type UserControl}}">
        <Setter Property="Background" Value="Blue" />
    </Style>

    <!-- the second style -->
    <Style x:Key="Style2"
           TargetType="{x:Type uc:MySpecialButtonControl}"
           BasedOn="{StaticResource {x:Type UserControl}}">
        <Setter Property="Background" Value="Red" />
    </Style>

    <!-- the default style, now based on Style1 -->
    <Style x:Key="{x:Type uc:MySpecialButtonControl}"
           TargetType="{x:Type uc:MySpecialButtonControl}"
           BasedOn="{StaticResource Style1}" />

</Application.Resources>

在运行时,您可以执行这样的操作来切换控件的默认样式。

private void Button_Click(object sender, RoutedEventArgs e)
{
    // get the style resources
    var style1 = FindResource("Style1") as Style;
    var style2 = FindResource("Style2") as Style;
    var defaultStyle = FindResource(typeof (MySpecialButtonControl)) as Style;
    if (style1 == null || style2 == null || defaultStyle == null)
        return;

    // create a new style based on the "other" style
    var newDefaultStyle = new Style(
        typeof (MySpecialButtonControl),
        (defaultStyle.BasedOn == style1 ? style2 : style1));

    // set the application-level resource to the new default style
    Application.Current.Resources[typeof (MySpecialButtonControl)] = newDefaultStyle;
}

这还差不多吗?

【讨论】:

  • 感谢迈克尔的回答!我相信这是实现我想要的一种方法 - 我明天会立即尝试。
  • 还有一个问题:如果您不能使用样式文件,您将如何解决问题(想象一个场景,UC 布局中的更改会非常复杂,并且有很多相互依赖,无法表示通过 XAML 中的简单样式,但需要在 C# 中实现)。有没有办法在不手动将“myOptionsClass”绑定到每个 UC 的情况下触发所有(!)UC 实例的依赖属性(MyLayoutProperty)值的更改?
  • 我想我现在可以自己回答我的问题了。 :) 我可以创建两种(或更多)不同的样式,它们只是依赖属性的值不同。更改样式将自动触发依赖属性回调,我可以在其中放置切换布局时必须执行的任何代码。
  • 尝试了您的解决方案,但遇到了一些问题 - 请参阅我的原始问题的更新。
  • 问题已解决。控件找不到样式资源,但现在它可以工作了。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-05-20
  • 2018-12-07
  • 2016-01-19
  • 2014-10-09
  • 2013-03-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多