2

简短版本:
WPF 似乎总是IValueConverter在绑定中使用时设置一个本地值,即使该转换器返回Binding.DoNothing.
我的问题是:我必须返回或做什么来告诉 WPF 使用继承的值?

请注意:我不想使用 DataTriggers,因为这会使我的代码显着膨胀,因为我需要一个数据触发器和一个转换器来处理当前转换器返回的每种颜色。


带复制的长版:

想象一下以下场景:
我有一个Buttona 位于其中TextBlock。存在Button设置Foreground属性的样式。此值由TextBlock. 现在我想创建一个值转换器,将 a 的值转换TextBlockBrush用作Foreground- 但仅在某些情况下。在我不想设置特殊颜色的情况下,我返回Binding.DoNothing. 我的理解是,这将使TextBlock继续使用继承的值。

不幸的是,我的理解并不正确。即使Binding.DoNothing设置了返回本地值。这已通过 Snoop 进行了验证。

这个简单的例子可以很容易地重现这个问题:

XAML:

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication1="clr-namespace:WpfApplication1"
        Title="MainWindow" Height="350" Width="525">
  <Window.Resources>
    <WpfApplication1:DummyConverter x:Key="DummyConverter" />
    <Style TargetType="{x:Type Button}">
      <Setter Property="Foreground" Value="Red" />
    </Style>
    <Style TargetType="{x:Type TextBlock}">
          <Setter Property="Foreground"
                  Value="{Binding Path=Text, RelativeSource={RelativeSource Self}, Converter={StaticResource DummyConverter}}" />
    </Style>
  </Window.Resources>
  <StackPanel>
    <Button><TextBlock>Text1</TextBlock></Button>
    <Button><TextBlock>Text2</TextBlock></Button>
  </StackPanel>
</Window>

转换器:

public class DummyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value.ToString() == "Text2")
            return Brushes.Cyan;
        return Binding.DoNothing;
    }
}

如您所见,第一个按钮的文本是黑色而不是红色。如果您删除TextBlock两个按钮的样式,则会有正确的红色文本。

问题:
我必须做些什么来防止这种情况发生?是否有一些返回值告诉引擎继续使用继承的值?

4

2 回答 2

3

回答你的问题:根据这个线程,不。一旦你给 TextBlock 一个样式设置器(#4),任何返回的值都将覆盖继承的属性(#7)。

相反,您可以像这样创建 MultiBinding:

public class DummyConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0].ToString() == "Text2")
            return Brushes.Cyan;

        return values[1];
    }
}

<Window x:Class="Spritefire.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Spritefire"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:DummyConverter x:Key="DummyConverter" />
        <Style TargetType="{x:Type Button}">
            <Setter Property="Foreground" Value="Red" />
        </Style>
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Foreground">
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource DummyConverter}">
                        <Binding Path="Text" RelativeSource="{RelativeSource Self}" />
                        <Binding Path="Foreground" ElementName="ExampleButton" />
                    </MultiBinding>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <StackPanel>
        <Button x:Name="ExampleButton">
            <TextBlock>Text1</TextBlock>
        </Button>
        <Button>
            <TextBlock>Text2</TextBlock>
        </Button>
    </StackPanel>
</Window>
于 2012-07-20T13:16:51.070 回答
1

我已经让它工作了,但这个解决方案并不好,而且有点味道......不过我会发布它。

像这样声明自定义附加属性:

 public static class CustomForeground
{
    public static readonly DependencyProperty CustomForegroundProperty = DependencyProperty.RegisterAttached(
                      "CustomForeground",
                      typeof(Brush),
                      typeof(CustomForeground),
                      new FrameworkPropertyMetadata(Brushes.Black, FrameworkPropertyMetadataOptions.AffectsRender, OnChange));

    public static void SetCustomForeground(UIElement element, Brush value)
    {
        element.SetValue(CustomForegroundProperty, value);
    }

    public static void OnChange(DependencyObject d, DependencyPropertyChangedEventArgs arg)
    {
        if (arg.NewValue != null)
            d.SetValue(TextBlock.ForegroundProperty, arg.NewValue);
    }
}

和文本框样式:

<Style TargetType="{x:Type TextBlock}">
        <Setter Property="WpfApplication1:CustomForeground.CustomForeground"
              Value="{Binding Path=Text, RelativeSource={RelativeSource Self}, Converter={StaticResource DummyConverter}}" />
    </Style>

这是我目前唯一想到的事情,假设你按钮的文本没有改变,它会起作用。如果是,您需要在另一个附加属性中记住 Foreground 的默认值并在 else 语句中设置它。

于 2012-07-20T13:00:42.270 回答