2

I'm trying to create a special button that colors an image based on Foreground color from the system. The solution seems to be in using the image as opacity mask to get the color and it works when I set the image directly like this:

<Grid>
  <Rectangle x:Name="ImageForeground" Height="48" Width="48" 
    Fill="{StaticResource PhoneForegroundBrush}" >
    <Rectangle.OpacityMask>
      <ImageBrush Stretch="Fill" ImageSource="/icons/play.png"/>
    </Rectangle.OpacityMask>
  </Rectangle>
</Grid>

But as soon as I try template this with a DependencyProperty for the image lite this:

public static readonly DependencyProperty ImageProperty  =
  DependencyProperty.Register("Image", typeof(ImageSource), 
                              typeof(RButton), null);  

And then in the XAML like this:

<Grid>
  <Rectangle x:Name="ImageForeground" Height="48" Width="48" 
    Fill="{TemplateBinding Foreground}" >
    <Rectangle.OpacityMask>
      <ImageBrush Stretch="Fill" ImageSource="{TemplateBinding Image}"/>
    </Rectangle.OpacityMask>
  </Rectangle>
</Grid>

I get an error saying:

object of type 'System.Windows.CustomDependencyProperty' 
  cannot be converted to type 'System.Windows.DependencyProperty'

The ImageProperty is ok, as I tested binding it to an image instead like this

<Image Source="{TemplateBinding Image}" Width="48" Height="48" />

Any ideas? My hunch says its in how I define my DependecyProperty, but I don't know how to move forward.

haqwin
  • 377
  • 3
  • 12

6 Answers6

7

The ImageBrush doesn't inherit from FrameworkElement so it can't be TemplateBound or Data Bound.

McGarnagle
  • 101,349
  • 31
  • 229
  • 260
Boryana Miloshevska
  • 2,730
  • 16
  • 17
  • Is there a source for this? Who says that a DependencyProperty of a DependencyObject can't be the target of a template-binding? – McGarnagle Mar 03 '14 at 16:19
3

I tried to replicate your problem in a test project based on the XAML you've given and have to admit that I haven't run into the problem that you describe. If you could provide the code for the RButton and some context as to how the control is used I might have more luck (or lack of!).

There was a great blog post All about Dependency Properties in Silverlight for WP7 posted today that you might find useful.

UPDATE: I just read this perfectly matched article that explains the problem fully and gives a solution. Basically, ImageBrush isn't a FrameworkElement, so you can't use the TemplateBinding with it.

Derek Lakin
  • 16,179
  • 36
  • 51
3

I marked It as answer although it wasn't the full answer. To get it all working I had to do some tweaking as I can't use a Converter in a TemplateBinding, sigh... anyway. Here is the code that solved it for me together with the converter in the article that Derek pointed out.

So to bind my Image DependencyProperty as an image brush in my Template I have to do this...

<Grid>
  <Grid.Resources>
    <controls:ImageBrushConverter x:Key="brushConverter"/>
  </Grid.Resources>
  <Rectangle 
    x:Name="ImageForeground"
    Height="48"
    Width="48" 
    Fill="{TemplateBinding Foreground}" 
        DataContext="{TemplateBinding Image}"
        OpacityMask="{Binding Converter={StaticResource brushConverter}}">
  </Rectangle>
</Grid> 

Not obvious but it did the trick, this has solved a lot for me. Thanks for the help

haqwin
  • 377
  • 3
  • 12
2

You can use a RelativeSource TemplatedParent binding instead of TemplateBinding. This is basically the same thing, but it will work in those odd cases where the TemplateBinding does not.

<ImageBrush
    ImageSource="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Image}" />
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
0

Another approach but without need of converter

<Grid>
<Rectangle x:Name="ImageForeground" DataContext="{TemplateBinding Image}" Height="48" Width="48" 
        Fill="{TemplateBinding Foreground}" >
    <Rectangle.OpacityMask>
        <ImageBrush Stretch="Fill" ImageSource="{Binding DataContext , RelativeSource={RelativeSource AncestorType=Rectangle}}"/>
    </Rectangle.OpacityMask>
</Rectangle>

//Or

<Rectangle DataContext="{TemplateBinding IconVisual}" x:Name="Icon" RadiusX="2" RadiusY="2" StrokeThickness="1" Margin="{TemplateBinding Padding}"  Fill="{TemplateBinding Foreground}" Stroke="{TemplateBinding Foreground}" >
                                <Rectangle.OpacityMask>
                                    <VisualBrush Visual="{Binding DataContext , RelativeSource={RelativeSource AncestorType=Rectangle}}"  Stretch="Uniform" />
                                </Rectangle.OpacityMask>
                            </Rectangle>
Toon Krijthe
  • 52,876
  • 38
  • 145
  • 202
0

Or if you are author of control then OnApplyTemplate, find the visual brush in template and set its value.

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    VisualBrush oVBrushBack = (VisualBrush)this.Template.FindName("rectVisual", this);

    oVBrushBack.Visual = this.IconVisual;
}
Juliano Alves
  • 2,006
  • 4
  • 35
  • 37