Binding an ImageSource to a DrawingImage - wpf

<Style
TargetType="{x:Type local:ObjectWindow}"
BasedOn="{StaticResource {x:Type Window}}">
<Setter
Property="Icon">
<Setter.Value>
<DrawingImage>
<DrawingImage.Drawing>
<DrawingGroup>
<ImageDrawing
Rect="0,0,32,32"
ImageSource="{Binding View.ImageSource, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ObjectWindow}}}" />
<ImageDrawing
Rect="16,16,16,16"
ImageSource="{DynamicResource View_Image}" />
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Setter.Value>
</Setter>
</Style>
You can see what I'm trying to do. Trying to overlay two images on top of each other for a Window's Icon. One image is a dynamic resource, which works fine, the other is the result of a Binding expression against a property of the Window.
This however does not work. ImageDrawing doesn't seem to support bindings. Is there something I should be using other than ImageDrawing to accomplish this?

Related

Changing DrawingImage brush binded to DynamicResource

I have a DrawingImage object as a resource which uses brush color from DynamicResource like this:
<DrawingImage x:Key="img_building">
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V512 H512 V0 H0 Z">
<DrawingGroup Opacity="1">
<GeometryDrawing Geometry="F1 M512,512z M0,0z M256.5,17.2L512,119.4 512,153.8 477.6,153.8C477.6,158.4 475.7,162.2 472,165.9 468.3,169.6 463.6,170.5 459,170.5L53,170.5C48.4,170.5 43.7,168.6 40,165.9 36.3,163.1 34.4,158.5 34.4,153.8L0,153.8 0,119.4 256.5,17.2z M68.8,188.2L136.6,188.2 136.6,392.6 171,392.6 171,188.2 238.8,188.2 238.8,392.6 273.2,392.6 273.2,188.2 341,188.2 341,392.6 375.4,392.6 375.4,188.2 443.2,188.2 443.2,392.6 459,392.6C463.6,392.6 468.3,394.5 472,397.2 475.7,400 477.6,404.6 477.6,409.3L477.6,426 35.3,426 35.3,409.3C35.3,404.7 37.2,400.9 40.9,397.2 44.6,393.5 49.3,392.6 53.9,392.6L69.7,392.6 69.7,188.2 68.8,188.2z M493.4,443.7C498,443.7 502.7,445.6 506.4,448.3 510.1,452 512,455.7 512,460.4L512,494.8 0.9,494.8 0.9,460.4C0.9,455.8 2.8,452 6.5,448.3 10.2,444.6 14.9,443.7 19.5,443.7L493.4,443.7z" >
<GeometryDrawing.Brush>
<SolidColorBrush Color="{DynamicResource Image.FillColor}" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
And then I'm using this resource in a template like this:
<DataTemplate x:Key="buildingImage">
<WrapPanel>
<WrapPanel.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="Margin" Value="0,0,4,0"></Setter>
<Setter Property="MaxHeight" Value="20"></Setter>
</Style>
</WrapPanel.Resources>
<Image Source="{DynamicResource img_building}" />
</WrapPanel>
</DataTemplate>
It's running fine but when I change the theme of my app, image remains same although the Image.FillColor changes.
I guess it's rendering the image for once and then uses the same rendered resource over and over again.
How to fix this? Is there a way to force a hard reload on DrawingImage in code behind?

How to set size of Image.Source which is a static resource in XAML?

I have the following Image in my GUI:
<Image Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="5" Source="{Binding SelectedExternalCameraDevice.LiveStreamSource, FallbackValue={StaticResource LivestreamDefaultImage}, TargetNullValue={StaticResource LivestreamDefaultImage}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stretch="Uniform"/>
The static resource for the fallback and targetnullvalue look as follows (This is basically taken from Windows's Image library - it is the icon "PlayVideo"):
<DrawingImage x:Key="LivestreamDefaultImage" >
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="{ui:DynamicColor SystemControlPageTextBaseMediumBrush}" Geometry="F1M16,16L0,16 0,0 16,0z" />
<GeometryDrawing Brush="{ui:DynamicColor SystemControlPageBackgroundChromeLowBrush}" Geometry="F1M16,4L16,12C16,13.103,15.103,14,14,14L2,14C0.897,14,0,13.103,0,12L0,4C0,2.897,0.897,2,2,2L14,2C15.103,2,16,2.897,16,4" />
<GeometryDrawing Brush="{ui:DynamicColor SystemControlPageTextBaseMediumBrush}" Geometry="F1M6,11L6,5 10.954,8z M14,3L2,3C1.448,3,1,3.448,1,4L1,12C1,12.552,1.448,13,2,13L14,13C14.552,13,15,12.552,15,12L15,4C15,3.448,14.552,3,14,3" />
<GeometryDrawing Brush="{ui:DynamicColor SystemControlPageBackgroundChromeLowBrush}" Geometry="F1M6,5L10.954,8 6,11z" />
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
As soon as the bound ImageSource in the ViewModel has a proper image, the Image control will stretch uniformly to the available space, keeping it's aspect ration. However, while the source is null the image takes the static resource as source, I have some default aspect ratio (which looks like 1:1). How can I set the width/height/aspect ration of the LivestreamDefaultImage, so that it will also stretch to the available space with my chosen dimensions?
If I understand you correctly, then try this Style:
<Image x:Name="image" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"
Grid.RowSpan="2" Margin="5"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="{Binding SelectedExternalCameraDevice.LiveStreamSource}"/>
<Setter Property="Stretch" Value="Uniform"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedExternalCameraDevice.LiveStreamSource}" Value="{x:Null}">
<Setter Property="Source" Value="{DynamicResource LivestreamDefaultImage}"/>
<Setter Property="Stretch" Value="Fill"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>

WPF RelativeSource not updating it's value

I'm trying to implement a button that uses XAML-defined icons to show a different state (like in the second comment here: https://codereview.stackexchange.com/questions/183871/wpf-button-with-xaml-defined-icon-that-changes-with-state). The icons are defined with a "Fill" property that uses a RelativeSource binding, like so:
<Canvas x:Key="icon_stop"
x:Shared="False"
Width="76"
Height="76"
Clip="F1 M 0,0L 76,0L 76,76L 0,76L 0,0">
<Rectangle
Width="28.5"
Height="28.5"
Canvas.Left="23.75"
Canvas.Top="23.75"
Stretch="Fill"
Fill="{Binding RelativeSource={
RelativeSource Mode=FindAncestor,
AncestorType=Control
}, Path=Foreground}"
/>
</Canvas>
The button is bound to a property in the window's ViewModel like this:
<Button Name="PlayStopButton"
Padding="20 0 20 0"
Command="{Binding Path=StartStopCommand}">
<ContentControl Style="{StaticResource style_icon}">
<Binding
Path="IsRunning"
UpdateSourceTrigger="PropertyChanged"
FallbackValue="{StaticResource icon_play}">
<Binding.Converter>
<converters:BoolToObjectConverter
TrueObject="{StaticResource icon_stop}"
FalseObject="{StaticResource icon_play}"
NullObject="{StaticResource icon_play}"
/>
</Binding.Converter>
</Binding>
</ContentControl>
</Button>
(I'm omitting the "style" part here and the "BoolToObjectConverter", that should be clear for the purpose)
The problem is that, when I run the program, the button gets correctly updated, but the icon is not visible. This is because the "icon_stop" resource is not updated with the correct "Fill" color. In fact, if I force it (for example to "Black") the icon shows correctly.
It seems to me that the "RelativeSource" bounded property gets not updated.
I think this is related to the fact that the resource is a "StaticResource" and thus gets created only once on the application loading.
I've found that there are also "DynamicResource" references in WPF, but I can't use them here (or at least I can't see how now as them must be bound to a dependency property and I have the value converter here).
How can I solve this?
Your XAML seems overly complicated. You may try something simple like this:
<Style TargetType="Button" x:Key="PlayButtonStyle">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Path Fill="{Binding Foreground,
RelativeSource={RelativeSource AncestorType=Button}}"
Data="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Content" Value="M5,0 L25,15 5,30Z"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsRunning}" Value="True">
<Setter Property="Content">
<Setter.Value>
<RectangleGeometry Rect="0,0,30,30"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
Try it like this:
<Button Style="{StaticResource PlayButtonStyle}" Foreground="Red" />

Bind to GeometryDrawing brush

I have 3 buttons in my user control, all 3 have little icons instead of text.
Image buttons
For example:
<controls:CheckButton Grid.Column="1" Style="{StaticResource ActionButtonStyle}">
<Image Source="{StaticResource Video}" Stretch="None"/>
</controls:CheckButton>
The image has this code to allow me to bind its color later:
<DrawingImage.Drawing>
<DrawingGroup ClipGeometry="M0,0 V24 H25 V0 H0 Z">
<GeometryDrawing Brush="{Binding Foreground, RelativeSource={RelativeSource FindAncestor, AncestorType=Control}}" Geometry="F0 M25,24z M0,0z M20.75,12C20.75,9.345,19.22,7.065,17,5.955L17,18.03C19.22,16.935,20.75,14.655,20.75,12L20.75,12z M0.5,7.5L0.5,16.5 6.5,16.5 14,24 14,0 6.5,7.5 0.5,7.5 0.5,7.5z" />
</DrawingGroup>
</DrawingImage.Drawing>
In order to set the color I use this style:
<Style x:Key="ActionButtonStyle" TargetType="ButtonBase">
<Setter Property="Foreground" Value="#FFE22880"/>
</Style>
I can see the pinkish color when in design time, but when running the app, this is what I get:
Black icons
*Some of the code is partial, but I've included everything important

Why does data binding my chart series color fail?

I am drawing simple line charts using the WPF toolkit. My goal is to set the line color of my series via Data Binding. This succeeds only partially. The question is: why?
Setup
Namespaces:
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit" x:Class="WpfApplication3.MainWindow"
xmlns:media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
Chart:
<chartingToolkit:Chart x:Name="chart">
<chartingToolkit:LineSeries x:Name="seriesEntries" IndependentValueBinding="{Binding Key}" DependentValueBinding="{Binding Value}" DataPointStyle="{StaticResource CommonLineSeriesDataPoint}">
<chartingToolkit:LineSeries.Tag>
<media:Brush>Green</media:Brush>
</chartingToolkit:LineSeries.Tag>
</chartingToolkit:LineSeries>
</chartingToolkit:Chart>
Ignore the Tag for now, it will be relevant later.
Notice the chart has a custom data point style, CommonLineSeriesDataPoint:
<Style x:Key="CommonLineSeriesDataPoint" TargetType="chartingToolkit:LineDataPoint">
<Setter Property="Background">
<Setter.Value>
<media:Brush>Red</media:Brush>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="chartingToolkit:LineSeries">
<Setter Property="DataPointStyle" Value="{StaticResource CommonLineSeriesDataPoint}" />
</Style>
As expected, this colors my line series red:
Breaking Change
Now I want to data bind my data point background. I make only one change. Instead of specifying the background brush directly, I bind it to the Tag property of my LineSeries, which is also a brush (see previous LineSeries declaration, it's a green one).
<Style x:Key="CommonLineSeriesDataPoint" TargetType="chartingToolkit:LineDataPoint">
<Setter Property="Background">
<Setter.Value>
<Binding Path="Tag" RelativeSource="{RelativeSource AncestorType={x:Type chartingToolkit:LineSeries}}" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="chartingToolkit:LineSeries">
<Setter Property="DataPointStyle" Value="{StaticResource CommonLineSeriesDataPoint}" />
</Style>
The result is this:
So the dots are green. But the line is gone.
My expectation is to see a green line as well! Where is it?
I found a solution after digging in the WPF Toolkit Sources.
Turns out the Stroke property of the series' Polyline is bound to a Background property via TemplateBinding. I suspect this doesn't go well with my try binding the Background property itself.
This answer on SO suggests that TemplateBinding is evaluated at compile time. So let's get rid of the TemplateBinding and bind the Stroke property directly to the Tag of my LineSeries (remember: the Tag contains the green brush).
From the WPF Toolkit Source \Source\DataVisualization\Themes\generic.xaml I copied part of the style definition for the LineSeries and added it to my ResourceDictionary:
<Style x:Key="CommonLineSeries" TargetType="chartingToolkit:LineSeries" BasedOn="{StaticResource {x:Type chartingToolkit:LineSeries}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="chartingToolkit:LineSeries">
<Canvas x:Name="PlotArea">
<Polyline Points="{TemplateBinding Points}" Stroke="{Binding Tag, RelativeSource={RelativeSource AncestorType={x:Type chartingToolkit:LineSeries}}}" Style="{TemplateBinding PolylineStyle}"/>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(If interested you can search for <!-- charting:LineSeries --> in generic.xaml to find the source I copied from.)
The only thing I modified is the binding of Stroke. Here I use the same binding I used for my data points.
Last thing to do: tell the LineSeries to use this style:
<chartingToolkit:LineSeries x:Name="seriesEntries" IndependentValueBinding="{Binding Key}" DependentValueBinding="{Binding Value}" DataPointStyle="{StaticResource CommonLineSeriesDataPoint}" Style="{StaticResource CommonLineSeries}">
And lo and behold, it works. The line is back and it's green:
(If you look closely you see that the legend entry for the series still has the wrong color. But I assume the solution will be quite similar to the above.)

Resources