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.
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" />
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With