.netwpfxamldependency-propertiesframeworkelement

Why does an Ellipse's Fill's ImageSource property, not accepting an ImageSource object type?


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.


Solution

  • 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).