Why does template binded converter not work? - wpf

I have control template with a Border. I want to bind its background by template binding, but I also need to create an x:Name property for the SolidColorBrush. So I create a simple converter from Brush to Color, but it doesn't work and I don't understand why.
My XAML:
<ControlTemplate TargetType="Button" x:Key="appBtns">
<ControlTemplate.Resources>
<local:ColorConverter x:Key="ColorConverter"/>
</ControlTemplate.Resources>
<Border x:Name="BackRect" CornerRadius="15" BorderThickness="0" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
<Border.Background >
<SolidColorBrush x:Name="BackRectColor" Color="{TemplateBinding Background, Converter={StaticResource ColorConverter}}"/>
</Border.Background>
</Border>
</ControlTemplate>
My converter:
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((SolidColorBrush)value).Color;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}

The TemplateBinding markup extension is known to be very limited in some scenarios. However, you can simply use a regular binding with a RelativeSource of TemplatedParent instead.
<SolidColorBrush x:Name="BackRectColor" Color="{Binding Background, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource ColorConverter}}"/>
In fact, if you always use a SolidColorBrush, you do not even need a converter. Just specify the Color property of the Background SolidColorBrush as property path. It is resolved at runtime.
<ControlTemplate TargetType="Button" x:Key="appBtns">
<Border x:Name="BackRect" CornerRadius="15" BorderThickness="0" Height="{TemplateBinding Height}" Width="{TemplateBinding Width}">
<Border.Background >
<SolidColorBrush x:Name="BackRectColor" Color="{Binding Background.Color, RelativeSource={RelativeSource TemplatedParent}}"/>
</Border.Background>
</Border>
</ControlTemplate>

Related

How to create a RadioButton style with template binding for enumeration and images?

I'm trying to learn XAML Styles better.
In writing the XAML (WPF) for a project I'm working on, I have multiple uses of code like:
<RadioButton IsChecked="{Binding CurrentPen.CustomStrokeType, Converter={StaticResource StrokeTypeConverter}, ConverterParameter={x:Static e:PenEnum.LinePen}}"
Width="25" Height="25" Foreground="Blue" GroupName="CustomStroke"
>
<RadioButton.Style>
<Style TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Grid>
<Image x:Name="PART_Image" Source="/Doctor_Desk;component/Images/Line.png" Width="32" Height="32"/>
<ContentPresenter/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="PART_Image" Property="Source" Value="/Doctor_Desk;component/Images/Line-On.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</RadioButton.Style>
</RadioButton>
public class StrokeTypeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (PenEnum)parameter == (PenEnum)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return (bool)value ? parameter : null;
}
}
The above code works well to display a button with RadioButton behavior. The default "Off" image (i.e., "Line.png") is replaced with "Line-On.png" when the RadioButton is clicked. However, I have many of these RadioButton's -- each with a different On and Off image as appropriate for their respective e:PenEnum enumeration. Unfortunately, this basic style is repeated multiple times.
The only difference between all these buttons is the e:PenEnum value and the On and Off images.
Can this style be placed in the resources and reused for each button, allowing for the specific differences of each button?
How can this be done and still allow for different enumeration, on, and off images for each RadioButton?
TIA

Programmatically bind converter to column in DataGrid

I hope I can explain this clearly....
We are binding our DataGrid to a collection that comes from some datasource.
Attributes for each column are described in a different collection, so we create the columns at runtime and set properties on the column (readonly, for example) based on values in the attributes collection.
A new requirement is a 'required' attribute. For columns that are required, I'd like to bind a converter that sets the DataGridCell's background color based on the value. (The simplest case of converter would be some color if the cell were empty, and white if the user entered a value. I'm sure more sophisticated validation will be expected in the future.)
I think it can be done in something like what I'm tinkering with now :
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}">
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
(Still need to add the converter somewhere....)
Or will what I want to do have to be done in code-behind? Any pointers would be greatly appreciated...
Here is one way of doing it. IDK if it's the best way, but it works and it's been a couple hours since you asked so....
Your DataGridCell is filled with the border/textbox so I'm assuming you want to change the textbox's background color since you won't see the DataGridCell's background.
Since you mentioned there could be more complex scenarios in the future, I used a multibinding with a converter and passed in the textboxes datacontext (by using <Binding />) and it's text value.
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<TextBox Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content.Text}">
<TextBox.Resources>
<local:ValidationBGConverter x:Key="ValidationBGConverter" />
</TextBox.Resources>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Background">
<Setter.Value>
<MultiBinding Converter="{StaticResource ValidationBGConverter}">
<Binding />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Content.Text" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And here is the converter:
public class ValidationBGConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2)
return Brushes.Black;
var datacontext = values[0] as ViewData; // Or whatever the textbox's datacontext object is
if (datacontext != null) // If null, probably the new item row
{
var txt = values[1] as string; // Textbox text
if (string.IsNullOrWhiteSpace(txt))
return Brushes.Red;
if (txt.Length < 3)
return Brushes.Pink;
if (txt.Length > 5)
return new LinearGradientBrush(Colors.White, Colors.Blue, 90.0);
}
return Brushes.White;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And, a screenshot:

Lowercase and append a text from ressources in WPF

I want to use Label or TextBlock that will display a lower cased and appended with ":" character string that I get from resources.
For example something like this:
<Label Content="{x:Static Localization:Captions.Login}" />
where Captions.Login is the string "Login", and the output in my view should be: "login:".
I added a template for Label, that appends ":", but I cannot get to lowercase my string within this template:
<ControlTemplate x:Key="LabelControlTemplate" TargetType="{x:Type Label}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="True">
<TextBlock>
<Run Text="{TemplateBinding Content}"/>
<Run Text=":"/>
</TextBlock>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
The same I can get by using without the Controltemplate, the line of xaml:
<Label Content="{x:Static Localization:Captions.Login}" ContentStringFormat="{}{0}:" />
So to end, my question is how to bring lowercase functionality in this scenario(note I do not want to use TextBox and restylings to achieve this)
what about using binding and a converter?
<Label Content="{Binding Source="{x:Static Localization:Captions.Login}", Path=., Converter="{StaticResource MyToLowerWithDotConverter}"/>
something like that? i have no IDE atm so i dont know if the bindings are right.
Use a Converter to transform your string to lowercase.
public class LowerCaseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((string)value).ToLowerInvariant();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// unnecessary
throw new NotImplementedException();
}
}

How to customize Group Header in DataGrid Grouping?

I have a DataGrid that looks something like this.
I have grouped data by Gender. My GroupItem style is
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="True"
Background="White"
Foreground="Black">
<Expander.Header>
<TextBlock Text="{Binding Name}"/>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want my Group Header 'Male' and 'Female' to look like 'Gender : Male' and 'Gender : Female' instead of simple plain 'Male' and 'Female'. How can I modify my GroupItem style to achieve this so that every time I group my data in datagrid the group header can appear like GroupHeaderTitle : GroupHeaderValue? or Do I need to change anything other than GroupItem style to achieve this?
You can add a property GroupTitle which represents the desired group title at your view model if you are using MVVM or to your Window code-behind otherwise, then add another TextBlock at the Expander.Header which is bound to the GroupTitle property, see the following code snippet:
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="True"
Background="White" Foreground="Black">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=DataContext.GroupTitle}"/>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
When you add the grouping just supply a converter:
// Get the default view
ICollectionView view = CollectionViewSource.GetDefaultView(...);
// Do the grouping
view.GroupDescriptions.Clear();
view.GroupDescriptions.Add(new PropertyGroupDescription("Gender", new GenderConverter()));
// The converter
public class GenderConverter : IValueConverter
{
public object Convert(object value,
Type targetType, object parameter, CultureInfo culture)
{
return string.Format("Gender: {0}", value);
}
public object ConvertBack(object value,
Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}

Can you do "math" within WPF Styles that are data-bound

I have a button control style and I want to change the padding from whatever the data-bound version is to adjust for a glyph that needs a 2 pixel offset. I'll use SimpleButton from SimpleStyles.xaml as an example (... shows where the trigger code was removed for conciseness):
<Style x:Key="SimpleButton" TargetType="{x:Type Button}" BasedOn="{x:Null}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource SimpleButtonFocusVisual}"/>
<Setter Property="Background" Value="{DynamicResource NormalBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource NormalBorderBrush}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<!-- We use Grid as a root because it is easy to add more elements to customize the button -->
<Grid x:Name="Grid">
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Padding="{TemplateBinding Padding}"/>
<!-- Content Presenter is where the text content etc is placed by the control. The bindings are useful so that the control can be parameterized without editing the template -->
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Grid>
...
</Setter.Value>
</Setter>
</Style>
What I want to do is add some extra margin where Padding="{TemplateBinding Padding}". Something like Padding="{TemplateBinding Padding} + 2,0,0,0".
Is there a XAML syntax to that? If not, is there a best approach when doing this in code (Decorator?) ?
Currently XAML does not parse expressions in Binding syntax, etc. However, you can use an IValueConverter or IMultiValueConverter to help yourself out:
XAML:
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="Grid">
<Grid.Resources>
<local:ThicknessAdditionConverter x:Key="AdditiveThickness" />
</Grid.Resources>
<Border x:Name="Border">
<Border.Padding>
<Binding Path="Padding" RelativeSource="{RelativeSource TemplatedParent}"
Converter="{StaticResource AdditiveThickness}">
<Binding.ConverterParameter>
<Thickness>2,0,0,0</Thickness>
</Binding.ConverterParameter>
</Binding>
</Border.Padding>
</Border>
...
</Setter.Value>
IValueConverter code behind:
public class ThicknessAdditionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null) return new Thickness(0, 0, 0, 0);
if (!(value is Thickness)) throw new ArgumentException("Value not a thickness", "value");
if (!(parameter is Thickness)) throw new ArgumentException("Parameter not a thickness", "parameter");
var thickness = new Thickness(0, 0, 0, 0);
var t1 = (Thickness)value;
var t2 = (Thickness)parameter;
thickness.Left = t1.Left + t2.Left;
thickness.Top = t1.Top + t2.Top;
thickness.Right = t1.Right + t2.Right;
thickness.Bottom = t1.Bottom + t2.Bottom;
return thickness;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
There is a product available at Blendables.com called Eval Binding and Simple Binding does this now. (The product is not free) Check out the whitepaper here
For example for the bellow XAML code you need a converter to do the operation.
<Ellipse Fill="Blue" Height="50"
Width="{Binding RelativeSource={RelativeSource Self},
Path=Height, Converter={StaticResource MyConverter}}" />
But with EvalBinding you can do like bellow
<Ellipse Fill="Blue" Height="50"
Width="{blendables:EvalBinding [{Self}.Height]/2}" />
No, not in this version of XAML - use a Value Converter to do your math.
Check out the ExpressionConverter in this library.
You can do some simple math by taking advantage of transforms.
Check out this trick that Charles Petzold came up with a long time ago:
http://www.charlespetzold.com/blog/2006/04/060223.html
Unfortunately, it doesn't seem to help your particular scenario ... since you want only to change Left property of the Thickness type for the Padding ... and that is not a dependency property that you can bind to alone.
However, I felt compelled to add this answer in the case it helps others who find their way here via Google or another search engine.
Check out the MathConverter: http://rachel53461.wordpress.com/2011/08/20/the-math-converter/
There you can send in an expression as the converter-parameter, where #VALUE is the value you are binding to:
ConverterParameter=((#VALUE-15)*.2)

Resources