如何使WPF DrawingImage元素的颜色动态?

9

我们需要在运行时更改某些矢量图形的颜色。使用静态或动态资源值设置这些颜色似乎行不通。我们想要有多个版本的相同图形,每个版本都可以将某些图形元素(路径、线条等)设置为不同的颜色,因此我认为动态资源方法行不通。那就只能使用数据绑定,这似乎是正确的方法。我们更新图形以使用数据绑定表达式而不是硬编码的刷子颜色,如下所示:

<DrawingImage x:Key="Graphic1">
  <DrawingImage.Drawing>
    <DrawingGroup>
      <DrawingGroup.Children>
        <GeometryDrawing Geometry="F1 M 8.4073,23.9233L">
          <GeometryDrawing.Pen>
            <Pen LineJoin="Round" Brush="{Binding Line1Brush, Mode=OneWay}"/>
          </GeometryDrawing.Pen>
        </GeometryDrawing>
        <GeometryDrawing Geometry="F1 M 3.6875,2.56251L">
          <GeometryDrawing.Pen>
            <Pen LineJoin="Round" Brush="{Binding Line2Brush, Mode=OneWay}"/>
          </GeometryDrawing.Pen>
        </GeometryDrawing>
      </DrawingGroup.Children>
    </DrawingGroup>
  </DrawingImage.Drawing>
</DrawingImage>

然后我们为每个Graphic1实例创建一个支持INotifyPropertyChanged的视图模型对象,并确保它具有Line1BrushLine2Brush属性。听起来不错,但我无法让其工作。我假设这个图形本身是在一个资源字典中定义为Image对象的并尝试设置它们的DataContext,但我的Debug窗口输出数据绑定错误。以下是Image XAML:

<Image x:Name="Pulse1" Grid.Column="0" Source="{StaticResource Graphic1}"/>
<Image x:Name="Pulse2" Grid.Column="1" Source="{StaticResource Graphic1}"/>

然后在 Windows 的 Initialize 方法中,我像这样设置它们的数据上下文:

 public MainWindow()
 {
     InitializeComponent();
     this.DataContext = this;
     this.PulseImage1 = new PulseImageViewModel();
     this.PulseImage2 = new PulseImageViewModel();
     this.PulseImage2.Line1Brush = Brushes.Green;
     this.PulseImage2.Line2Brush = Brushes.Red;
     this.Pulse1.DataContext = this.PulseImage1;
     this.Pulse2.DataContext = this.PulseImage2;
}

PulseImageViewModel(如下所示)定义了两个属性Line1BrushLine2Brush,每个属性都会触发PropertyChanged事件。

public class PulseImageViewModel : INotifyPropertyChanged
{
    private Brush _line1Brush = Brushes.Yellow;
    private Brush _line2Brush = Brushes.Black;

    public event PropertyChangedEventHandler PropertyChanged;

    public Brush Line1Brush
    {
        get { return _line1Brush; }
        set
        {
            if (_line1Brush != value)
            {
                _line1Brush = value;
                NotifyPropertyChanged("Line1Brush");
            }
        }
    }
    public Brush Line2Brush
    {
        get { return _line2Brush; }
        set
        {
            if (_line2Brush != value)
            {
                _line2Brush = value;
                NotifyPropertyChanged("Line2Brush");
            }
        }
    }

    private void NotifyPropertyChanged(string propertyName)
    {
        var del = PropertyChanged;
        if (del != null)
        {
            del(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

我遇到了数据绑定错误,提示WPF在顶层MainWindow对象上查找Line1Brush而不是我的PulseImageViewModel对象。你认为我做错了什么或者有没有更好的方法来实现动态可变颜色的矢量图形?此外,如果用户没有连接视图模型对象,图形默认使用一组漂亮的静态颜色将会很好。

1个回答

6
StaticResource将从Window继承DataContext,使用DynamicResource会改变吗? 编辑我得到了和你一样的结果,Snoop也没有什么帮助。即使将DataContext移到XAML中也没有改变任何东西。为了好玩,我将一个TextBox放入Label的内容中,这样就可以正常工作了... 不知道其中的区别。
<Window.Resources>
    <TextBlock x:Key="Text1"
               Text="{Binding Line1Brush}" />
</Window.Resources>
<Grid>
    <!-- Confusingly enough, this works -->
    <Label Content="{StaticResource Text1}"
           DataContext="{Binding PulseImage1}" />
</Grid>

编辑2 以下内容有效:

<DataTemplate x:Key="Graphic1">
    <Image>
        <Image.Source>
            <DrawingImage>
                <DrawingImage.Drawing>
                    <DrawingGroup>
                        <DrawingGroup.Children>
                            <GeometryDrawing Geometry="F1 M 8.4073,23.9233L">
                                <GeometryDrawing.Pen>
                                    <Pen LineJoin="Round"
                                         Brush="{Binding Line1Brush, Mode=OneWay}" />
                                </GeometryDrawing.Pen>
                            </GeometryDrawing>
                            <GeometryDrawing Geometry="F1 M 3.6875,2.56251L">
                                <GeometryDrawing.Pen>
                                    <Pen LineJoin="Round"
                                         Brush="{Binding Line2Brush, Mode=OneWay}" />
                                </GeometryDrawing.Pen>
                            </GeometryDrawing>
                        </DrawingGroup.Children>
                    </DrawingGroup>
                </DrawingImage.Drawing>
            </DrawingImage>
        </Image.Source>
    </Image>
</DataTemplate>

使用以下XAML代码:

<ContentPresenter ContentTemplate="{StaticResource Graphic1}"
                  Content="{Binding PulseImage1}"
                  Grid.Column="0" />
<ContentPresenter ContentTemplate="{StaticResource Graphic1}"
                  Content="{Binding PulseImage2}"
                  Grid.Column="1" />

不,改为使用DynamicResource并没有帮助。在输出窗口仍然会得到相同的数据绑定错误 - BindingExpression路径错误:'Line1Brush'属性在'object' ''MainWindow' (Name='')'上未找到。 - Keith Hill
嗯,我想知道为什么这需要一个DataTemplate/ContentPresenter才能工作?我猜这对于任何需要图像的地方都可以使用,例如按钮、菜单项等? - Keith Hill
是的,这对它们中的任何一个都适用。由于某种原因,在直接的“DrawingImage”资源中,绑定会被急切地评估。但在“DataTemplate”中,它们会在使用时被评估。 - user7116

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接