Dean Chalk
Dean Chalk

Reputation: 20471

Metro XAML - Issues With TemplateBinding and SolidColorBrush

Here is a simple custom control to illustrate my issue

public sealed class TestControl : Control
{
    public static DependencyProperty TestColorProperty = DependencyProperty.Register("TestColor", typeof(Brush), typeof(TestControl), new PropertyMetadata(new SolidColorBrush(Colors.Blue)));

    public Brush TestColor
    {
        get { return (Brush)GetValue(TestColorProperty); }
        set { SetValue(TestColorProperty, value); }
    }

    public TestControl()
    {
        this.DefaultStyleKey = typeof(TestControl);
    }
}

As you can see, it has a single Brush dependency property, with a default value of Blue (set in the PropertyMetaData as shown above.

Here is the XAML for my control in Generic.xaml

<Style TargetType="local:TestControl">
        <Setter Property="TestColor" Value="Red" />
        <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:TestControl">
                <Border
                    Background="{TemplateBinding TestColor}"
                    BorderBrush="{TemplateBinding BorderBrush}"
                    BorderThickness="{TemplateBinding BorderThickness}">
                        <TextBlock Text="TEST"  />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

As you can see, I set the TestColor Brush dependency property to Red in a Style setter - overriding the default value of Blue as declared in my PropertyMetaData. Notice that my Border in my Template uses TemplateBinding to set the background to the brush as discussed.

So what color do you think the border background gets set ? Red or Blue ?

The answer is neither.

If I set a breakpoint in my control somewhere where this value should be available (e.g. OnApplyTemplate as an example) then the value is null, rather than Red (default) as expected. In fact I have set breakpoints at all of the lifecycle points in the control and the default value in ProprtyMetaData is never used.

Setting the value within the style does nothing either (it doesn't get set to Blue as per my style setter delaration. This suggests that the style setter is failing for SolidColorBrush somehow.

However, this works

public BlankPage()
{
    this.InitializeComponent();
    testcont.TestColor = new SolidColorBrush(Colors.Orange);
}

and this works as well:

<Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
    <local:TestControl  TestColor="Green" />
</Grid>

but TemplateBinding just doesn't work, and this is important as Im trying to write re-useable custom controls.

Is this a bug ?

Dean

Upvotes: 5

Views: 2119

Answers (5)

Joy Rex
Joy Rex

Reputation: 618

You can try the below code snippet to bind the TestColor,

 Background="{Binding  RelativeSource={RelativeSource TemplatedParent}, Path=TestColor}"

I hope it will be helpful.

Regards, Rex

Upvotes: 0

razblack
razblack

Reputation: 131

I have a working solution that uses a custom control, which in turn is placed multiple times into a user control (which is nice a reusable in my frames).

(in generic.xaml, recall I created a custom control)

<Style TargetType="local:myPad">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:myPad">
                <Path Data="..." Height="60" Width="30" Fill="{TemplateBinding myPadColor}"/>

(in myPad.cs)

    public sealed class myPad : Control
    {
        public myPad()
        {
            this.DefaultStyleKey = typeof(myPad);
        }

        public SolidColorBrush myPadColor
        {
            get { return (SolidColorBrush)GetValue(PadColorProperty); }
            set { SetValue(PadColorProperty, value); }
        }

        public static readonly DependencyProperty PadColorProperty =
            DependencyProperty.Register("myPadColor", typeof(SolidColorBrush), typeof(myPad), new PropertyMetadata(null));
}

in my user control, I then drop the template control and can have multiple "color" versions of the same...

<UserControl
    x:Class=....
    <Canvas Width="235" Height="235">
        <local:myPad x:Name="thisPad" myPadColor="White">

...

I cut out much of the excess stuff (...), but I think you get the idea here. I also believe now too, that you can use yet another binding for myPadColor in the user control or in code-behind to get creative.

btw, i'm using VS2012 with SP3.. so perhaps things have been fixed now.

also, i'm pretty new to programming XAML, but at Tim Heuer's blog I found the right way to do the dependency property (thanks Tim!)

if you replace the (null) value in the propertymetadata value with your (new SolidColorBrush(Colors.Black)) you will get a default value in design mode.

:)

hope that helps.

Upvotes: 0

d.k.
d.k.

Reputation: 21

Seems it isn't yet fixed. If you develop something like custom button and PointerOver effect is just lighter background, a trick that works is to have invisible PointerOverVisual with white background on top of the content. Then PointerOver animation will make it slightly visible. That's what I use:

<VisualStateManager.VisualStateGroups>
                        <VisualStateGroup x:Name="CommonStates">
                            <VisualState x:Name="Normal"/>
                            <VisualState x:Name="PointerOver">
                                <Storyboard>
                                    <ObjectAnimationUsingKeyFrames   Storyboard.TargetProperty="Opacity" Storyboard.TargetName="pointerOverVisual">
                                        <DiscreteObjectKeyFrame KeyTime="0" Value="0.15"/>
                                    </ObjectAnimationUsingKeyFrames>

.......

<Border x:Name="Border" BorderThickness="0" Background="{TemplateBinding Background}" Margin="3">   
        content here                                                    
</Border>
    <Rectangle x:Name="pointerOverVisual" Fill="White" Opacity="0" Margin="3"/>
    <Rectangle x:Name="FocusVisualWhite" IsHitTestVisible="False" Opacity="0" StrokeDashOffset="1.5" StrokeEndLineCap="Square" Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}" StrokeDashArray="1,1"/>
    <Rectangle x:Name="FocusVisualBlack" IsHitTestVisible="False" Opacity="0" StrokeDashOffset="0.5" StrokeEndLineCap="Square" Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}" StrokeDashArray="1,1"/>

Upvotes: 0

notacat
notacat

Reputation: 691

Personally, I think it's a bug somewhere in xaml parser. Try something like this, it should work:

    <Setter Property="SelectedDayBrush">
        <Setter.Value>
            <SolidColorBrush>#7F7F7F7F</SolidColorBrush>
        </Setter.Value>
    </Setter>
    <Setter Property="SelectedDayBrush">
        <Setter.Value>
            <SolidColorBrush Color="Orange"/>
        </Setter.Value>
    </Setter>

Upvotes: 0

Tim Heuer
Tim Heuer

Reputation: 4311

this is an issue we're looking to address. In the mean time, set it up like this:

<SolidColorBrush x:Key="DefaultTestColorBrush">Red</SolidColorBrush>

and then in your template:

<Setter Property="TestColor" Value="{StaticResource DefaultTestColorBrush}" />

Then you should be able to clear this hurdle for now.

Upvotes: 0

Related Questions