Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can you make the color of elements of a WPF DrawingImage dynamic?

Tags:

binding

wpf

We have a need to make certain vector graphic images change the color of certain elements within the graphic at runtime. It would seem that setting those colors based on either static or dynamic resource values wouldn't work. We want to have multiple versions of the same graphic, each with the abilty to set certain graphic elements (Path, Line, etc) to different colors so I don't think that a dynamic resource approach would work. That leaves data binding which seems like the right approach. We update the graphic to use a data binding expr instead of a hard-coded brush color like so:

<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>

Then we create a view model object (supporting INotifyPropertyChanged) instance for each instance of Graphic1 in this case and make sure it has both a Line1Brush and a Line2Brush property. Sounds good, but I can't get it to work. I assume this graphic, which is itself defined in a resource dictionary to Image objects and I attempt to set their DataContext and I get data binding error output in my Debug window. Here's the Image XAML:

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

And then in the Window's Initialize method I set their data context like so:

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

Where PulseImageViewModel (shown below) defines two properties Line1Brush and Line2Brush, each of which fire the PropertyChanged event.

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

Yet I get data binding errors indicating that WPF is looking for Line1Brush on the top level MainWindow object instead of on my PulseImageViewModel object. Any thoughts on what I'm doing wrong or if there's a better way to accomplish my goal of dynamically changeable colors on vector graphics? Also, it would be nice if the graphics could default to a nice, static set of colors if the user didn't hook up the view model object.

like image 208
Keith Hill Avatar asked Feb 09 '11 18:02

Keith Hill


1 Answers

The StaticResource is going to have the DataContext from the Window I believe. Does this change if you use DynamicResource?

Edit I get the same results as you, and Snoop is not being helpful. Even moving the DataContext to the XAML changes nothing. For fun I switched to pulling a TextBox into a Label's content, and lo and behold THAT works... No idea what the difference is.

<Window.Resources>
    <TextBlock x:Key="Text1"
               Text="{Binding Line1Brush}" />
</Window.Resources>
<Grid>
    <!-- Confusingly enough, this works -->
    <Label Content="{StaticResource Text1}"
           DataContext="{Binding PulseImage1}" />
</Grid>

Edit 2 The following works:

<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>

With the XAML looking like:

<ContentPresenter ContentTemplate="{StaticResource Graphic1}"
                  Content="{Binding PulseImage1}"
                  Grid.Column="0" />
<ContentPresenter ContentTemplate="{StaticResource Graphic1}"
                  Content="{Binding PulseImage2}"
                  Grid.Column="1" />
like image 160
user7116 Avatar answered Sep 17 '22 11:09

user7116