Metro XAML - Issues With TemplateBinding and SolidColorBrush - xaml

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

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.

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>

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"/>

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.

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

Related

Getting a value from a higher databinding path

I have an object that contains a control defined in a control template, and an object which it gets notifications from.
These notification object keeps track of the status of the object, which translates on the screen to the colour in this instance, although they are used in other areas of the UI to reflect the state of these objects.
The rectangle colour changes fine on the status change, but I have another property which provides the foreground colour of the text.
However, because of the ContentPresenter getting the text from the control, I can no longer get the foreground colour with the TextBlock.Foreground setter in the style, as the binding is pointing elsewhere.
I could put the name of the object in the notification object as well which would get rid of this question and problem, but I have run up against a similar issue in other places, and as I am new to WPF would like to know the way to do it.
This is the main relevant part of the ControlTemplate 'AnimatedTemplate':
<Rectangle x:Name="ColourFill" Stroke="White" RadiusX="5" RadiusY="5">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Path=FillColour}"/>
</Rectangle.Fill>
<Rectangle.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Active}" Value="True" >
<DataTrigger.EnterActions>
<BeginStoryboard> ... </BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Margin="2"
TextBlock.FontFamily="Segoe UI"
TextBlock.FontWeight="Bold">
<ContentPresenter.Style>
<Style>
<Setter Property="TextBlock.FontSize"
Value="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent},
Converter={StaticResource PushPinContentSizeConverter}}" />
<Setter Property="TextBlock.Foreground"
Value="{Binding Path=TextColour}"/>
</Style>
</ContentPresenter.Style>
</ContentPresenter>
this is created with:
ControlTemplate template = (ControlTemplate)_window.FindResource("AnimatedTemplate");
Control c = new ContentControl();
c.Template = template;
c.SetValue(ContentControl.ContentProperty, displayName);
c.DataContext = <the notification object referred to as the first binding>
_window.d1overlay.Children.Add(c);

Select a different grid based on a converter's value for a UWP app

In a UWP app, I have a property that returns 3 values. I want to show a different grid based on this value using a converter. What is the best way to a achieve this? The direction I think I am going to head towards is to create 3 different grid templates, and then set the style to one of these 3 templates based on what the converter returns. Does anyone know if this will work? My grid doesn have to be a grid, it can be a contentcontrol or something like that. I basically want to show a different section of UI based on a property
Thanks
I would use the WindowsStateTriggers NuGet package.
Once installed you can reference at the top of your xaml
xmlns:triggers="using:WindowsStateTriggers"
and say you had a property in your Backend class called IsBusy
public bool IsBusy { get; set; } = false;
and for example you had 2 simple Grids in your xaml.
<StackPanel x:Name="root">
<Grid x:Name="RedGrid" Background="Red" Width="200" Height="100" />
<Grid x:Name="GreenGrid" Background="Green" Width="200" Height="100" Visibility="Collapsed"/>
</StackPanel>
You could setup a Visual State Group to show the Grids based on the IsBusy property. We want the Green Grid to be visible and the Red Grid to be collapsed when IsBusy = true
<StackPanel x:Name="root">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="IsBusy">
<VisualState.StateTriggers>
<triggers:EqualsStateTrigger EqualTo="True" Value="{Binding IsBusy, ElementName=root}" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RedGrid.Visibilty" Value="Collapsed"/>
<Setter Target="GreenGrid.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="RedGrid" Background="Red" Width="200" Height="100" />
<Grid x:Name="GreenGrid" Background="Green" Width="200" Height="100" />
</StackPanel>
NB The {Binding IsBusy, ElementName=root} depends on your DataContext and location of IsBusy property. Here its just in the code behind for the page.
Hope that gives you an idea.

ProgressBar Background Property not working in Windows Store Apps

I added a ProgressBar to my application and I wanted it to have a transparent background, so I did it like this:
<ProgressBar IsIndeterminate="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ProgressBar.Background>
<SolidColorBrush Color="Black" Opacity="0.5" />
</ProgressBar.Background>
</ProgressBar>
And in the Preview Window everything looks fine, however, when I run my app, the Background is simply not there. The solution I found to this is to put the ProgressBar in a Grid and set the Background property in the Grid, but since the Preview shows it right, and the property is there, shouldn't it work?
UPDATE:
Based on #Chris W. suggestion, I tried to override the default style of the ProgressBar element, like so:
<ProgressBar IsIndeterminate="True" Background="#FF000000" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Margin="50">
<ProgressBar.Style>
<Style TargetType="ProgressBar">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimation Storyboard.TargetName="DeterminateRoot"
Storyboard.TargetProperty="Opacity"
To="0.5"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ProgressBar.Style>
</ProgressBar>
But still, no juice.
Need to get rid of two (2) StoryBoard Animations
Document Outline > Right Click Progress Bar > Edit Template -> Edit A Copy
<!--
<FadeOutThemeAnimation Storyboard.TargetName="DeterminateRoot"/>
<DoubleAnimation Duration="0" To="0" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="DeterminateRoot"/>
-->
And as #ricochete suggested if using Opacity = 1 change up the Z-Order of DeterminateRoot to be on top of the EllipseGrid
<Border x:Name="DeterminateRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" MinHeight="{TemplateBinding MinHeight}">
<Rectangle x:Name="ProgressBarIndicator" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" Margin="{TemplateBinding Padding}"/>
</Border>
<Grid x:Name="EllipseGrid" Opacity="0">
<!-- ... more XAML Style -->
<Grid Background="#FFFF0000">
<ProgressBar IsIndeterminate="True" Style="{StaticResource ProgressBarStyle1}" Height="50" >
<ProgressBar.Background>
<SolidColorBrush Color="Black" Opacity="0.5"/>
</ProgressBar.Background>
</ProgressBar>
</Grid>
If you go look at the default template you'll see at the bottom of the template the Background only has a TemplateBinding in one spot for x:Name="DeterminateRoot" so that's the only place you'd see your color set from the Background property.
Then if you climb up through the Storyboard for the Indeterminate State you'll find;
<DoubleAnimation Storyboard.TargetName="DeterminateRoot"
Storyboard.TargetProperty="Opacity"
To="0"
Duration="0" />
...and you've got your ProgressBar set to IsIndeterminate="True" so you're setting the one place that accepts the Background property to a zero opacity explicitly.
So you could go pull that animation out of the Storyboard for that state, or put in your own new object to set your thing, or just do the workaround you mentioned by just throwing it in a Border or a Grid or something and doing it that way amongst other possibilities.
You might also try (once you've fixed your opacity setting issue from the storyboard) just flipping your SolidColorBrush with Opacity into just pure hex with the Alpha Channel set as 50% opacity equivalent. Making it just;
<ProgressBar Background="#80000000" IsIndeterminate="True" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
Anyhow, hope this helps, Cheers!

Windows 8 XAML RT toolkit Color palette - Column Charts

I'm building a windows store application and trying to use the chart component in XAML RT Toolkit. Now the problem is I want to represent each column bar with a specific colou. But then I'm not finding a way to do it. There's a similar question which is addressed for the pie chart color palette. But this doesn't seem to work in Column charts. Can somebody help ?
Hi Rajkumar,
We too have a similar problem. Finally succeeded. Please check the following link.
http://blogs.msdn.com/b/delay/archive/2009/02/04/columns-of-a-different-color-customizing-the-appearance-of-silverlight-charts-with-re-templating-and-mvvm.aspx
To give different color, the essence is following.
Step 1.
Create a style under Page.Resource as follows.
<Page.Resources>
<Style
x:Key="**ColorByPreferenceColumn**"
TargetType="charting:ColumnDataPoint">
<Setter Property="Background" Value="DarkGray"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="charting:ColumnDataPoint">
<Border
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="{Binding **ColorBackGround**}">
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush>
<GradientStop Color="#77ffffff" Offset="0"/>
<GradientStop Color="#00ffffff" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Border BorderBrush="#ccffffff" BorderThickness="1">
<Border BorderBrush="#77ffffff" BorderThickness="1"/>
</Border>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Step 2.
Apply the style to the chart control.
<charting:Chart x:Name="BuildStatusChart" Title="Build Status" Foreground="Black" Margin="20,20,20,20">
<charting:Chart.Series>
<Series:ColumnSeries Title="Build Status" ItemsSource="{Binding Items}"
IndependentValueBinding="{Binding Index}" HorizontalContentAlignment="Center"
DependentValueBinding="{Binding BuildTime}"
IsSelectionEnabled="False" SelectionChanged="OnSelectionChanged" DataPointStyle="{StaticResource ColorByPreferenceColumn}" >
</Series:ColumnSeries>
</charting:Chart.Series>
</charting:Chart>
Step 3:
Note : the style name is "ColorByPreferenceColumn" and color for each bar will be represented by "ColorBackGround". Search the above code segment , to know how it is applied. FInal thing is on code side have class with "ColorBackGround" peoperty.
public class Build : BindableBase
{
//Build Class
public Build() {}
private SolidColorBrush _colorBackGround;
public SolidColorBrush ColorBackGround
{
get { return _colorBackGround; }
set { _colorBackGround = value; }
}
// And your properties......
}
Step 5:
Ofcpourse as you know,
Set the the binding collection. In our case it was
((ColumnSeries)this.BuildStatusChart.Series[0]).ItemsSource = items; // items collection of individual objects.
Best Regards,
Anish.A.R

XAML style is applied to only the first rectangle. How to make it apply to all?

In a Windows 8 (WinRT) app, I am creating my own XAML style to get a dotted rectangle. In the setter for the style, I use Property="StrokeDashArray" Value="1,4". I then create a bunch of rectangles, and then explicitly set the style of those rectangles to this style I created. The first rectangle shows up with a dotted border - but the other two don't. However, if in addition to the Style={StaticResource myDottedStyle} I also specify the StrokeDashArray with each rectangle, then all them correctly show up with dotted borders.
Why is the dotted border only showing up for the first rectangle? How can I create a Style that is applied to all the rectangles without specifying the StrokeDashArray for each of them?
Here is a full code sample. In Windows 8 RTM, create a Blank XAML app project, and replace the Grid in the MainPage.xaml with the following:
<Page.Resources>
<Style x:Key="myDottedStyle" TargetType="Rectangle">
<Setter Property="Stroke"
Value="{StaticResource ApplicationForegroundThemeBrush}"/>
<Setter Property="StrokeThickness" Value="2"/>
<Setter Property="StrokeDashArray" Value="1,4"/>
</Style>
</Page.Resources>
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<Rectangle Style="{StaticResource myDottedStyle}" Width="40"
HorizontalAlignment="Left"/>
<Rectangle Style="{StaticResource myDottedStyle}" Width="40"
HorizontalAlignment="Center"/>
<Rectangle Style="{StaticResource myDottedStyle}" Width="40"
HorizontalAlignment="Right"/>
</Grid>
Here is a screenshot of the output of this
I found a related question that talks about DataTemplates here but I can't figure out how to translate that into my problem.
You could optimize things a bit by not requiring it to re-draw the rectangle per each instance and substitute for a ContentControl instead since they appear the same but with minor differences. So something for example like;
<Style x:Key="MyDottedStyle" TargetType="ContentControl">
<!-- Add additional Setters Here -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Rectangle Stroke="{StaticResource ApplicationForegroundThemeBrush}"
StrokeThickness="2"
StrokeDashArray="1,4"
Width="40" Height="40"
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
Margin="{TemplateBinding Margin}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- And now actually place it on your view -->
<ContentControl Style="{StaticResource MyDottedStyle}" HorizontalAlignment="Center"/>
This will allow you to not only clean things up because you can take your Style template and slap it over into say a Resource Dictionary to reduce clutter, but also makes it a little more efficient since you're not re-drawing your shape every time it's required. Hope this helps! Cheers!