I have a DependencyProperty of type ImageSource named "Image".
public static readonly DependencyProperty ImageProperty = DependencyProperty.Register(nameof(Image), typeof(ImageSource), typeof(CellView), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public ImageSource Image
{
get
{
return (ImageSource)GetValue(ImageProperty);
}
set
{
SetValue(ImageProperty, value);
}
}
And I bind two elements to this DependencyProperty using TemplateBinding
<Image x:Name="_1" Source="{TemplateBinding Image}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Height="30" Width="30"/>
<Ellipse x:Name="_2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stretch="Uniform">
<Ellipse.Fill>
<ImageBrush ImageSource="{TemplateBinding Image}" Stretch="Fill"></ImageBrush>
</Ellipse.Fill>
</Ellipse>
The assignment works for <Image> element's Source property (Successfully seeing the image in the UI), while the assignment doesn't work for <Ellipse.Fill.ImageBrush>'s ImageSource property. (Returning NULL as I checked using the debugger output)
Here's how I implemented a simple debugging method:
public override void OnApplyTemplate()
{
image = Template.FindName("_1", this) as Image;
ellipse = Template.FindName("_2", this) as Ellipse;
button = Template.FindName("_3", this) as Button;
button.Click += Button_Click;
base.OnApplyTemplate();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var y = image.Source.ToString();
var x = ((ImageBrush)ellipse.Fill).ToString();
var z = ((ImageBrush)ellipse.Fill).ImageSource;
Debug.WriteLine(y);
Debug.WriteLine(x);
Debug.WriteLine(z == null);
}
Debugger Output (On Button Click)
pack://application:,,,/MVVM-TrafficLight;component/Resources/Images/Yellow.png
System.Windows.Media.ImageBrush
True
I can fix this by implementing a separate DependencyProperty for the Ellipse.Fill.ImageBrush's ImageSource property. However, what I do want to know is why the assignment does not work, because as far as I know you need to supply an ImageSource object to an ImageSource property, which is exactly what I am doing. Is there anything that I am missing here?
Note: TemplateBinding works.
Because the object that defines the binding is nested into a property (property element syntax) TemplateBinding
can't resolve the source at compiletime.
Instead, replace {TemplateBinding}
with {RelativeSource TemplatedParent}
:
<Ellipse>
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Image}" />
</Ellipse.Fill>
</Ellipse>
Sometimes we have to wait until the template is completely applied in order to resolve a property path. In this case the binding has to be evaluated at runtime (RelativeSource
markup extension) and not compiletime (TemplateBinding
markup extension).