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)
Related
I have the following WPF Style for a custom control
<Style TargetType="{x:Type local:TransportControl}">
<Setter Property="MinorTickBrush" Value="{DynamicResource BlackBrush}"/>
<Setter Property="MajorTickBrush" Value="{DynamicResource BlackBrush}"/>
<Setter Property="IndicatorBrush" Value="{DynamicResource BlckBrush}"/>
<Setter Property="ProgressBorderBrush" Value="{DynamicResource BlackBrush}"/>
<Setter Property="ProgressBrush" Value="{DynamicResource HighlightBrush}"/>
<Setter Property="IndicatorSize" Value="16"/>
<Setter Property="IndicatorBrush" Value="{DynamicResource BlackBrush}"/>
<Setter Property="IndicatorGlow" Value="True"/>
<Setter Property="IndicatorGlowBrush" Value="GhostWhite"/>
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TransportControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{Binding Path=DataContext.IndicatorSize,
RelativeSource={RelativeSource AncestorType={x:Type local:TransportControl}},
Converter={StaticResource ValueToHorizontalPaddingConverter}}"
Margin="4,2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Canvas Name="PART_TimelineCanvas" Grid.Row="0" Height="20" ClipToBounds="False"/>
<Canvas Name="PART_ProgressCanvas" Grid.Row="1" ClipToBounds="False"/>
<Canvas Name="PART_IndicatorCanvas"
Grid.Row="0"
Grid.RowSpan="2"
ClipToBounds="False"
Panel.ZIndex="2"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
with the IValueConverter as
public class ValueToHorizontalPaddingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object format, CultureInfo culture)
{
double padding = System.Convert.ToDouble(value);
return new Thickness(padding, 0, padding, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I am attempting to set the padding of the control so my indicator can be centered correctly. I want the padding of the control to be half the IndicatorSize set in the parent style. Currently, I am just trying to get it to be the IndicatorSize, but the binding I am attempting does not work as expected.
Padding="{Binding Path=DataContext.IndicatorSize,
RelativeSource={RelativeSource AncestorType={x:Type local:TransportControl}},
Converter={StaticResource ValueToHorizontalPaddingConverter}}"
What am I doing wrong?
Thanks for your time.
You can use TemplateBinding to do that:
Padding="{TemplateBinding IndicatorSize, Converter={StaticResource ValueToHorizontalPaddingConverter}}"
Another way of centering the indicator would be to give the border a name, get a reference to it in your custom control's OnApplyTemplate(..) method and set its Padding in C# whenever the IndicatorSize property changes. This way you would not need the binding and converter.
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:
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();
}
}
I have a button control which its template is stilyzed in an external resource Theme.xaml. Below the controltemplate definition:
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type Button}">
<Grid x:Name="Grid">
<Border x:Name="Background" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="2,2,2,2" CornerRadius="2,2,2,2">
<Border x:Name="Hover" Background="{StaticResource HoverBrush}" CornerRadius="1,1,1,1" Height="Auto" Width="Auto" Opacity="0"/>
</Border>
<StackPanel Orientation="Horizontal" Margin="2,2,2,2">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" />
</StackPanel>
...
Now I added an item which is an ellipse that must be filled with red or green color (as a semaphore) depending on a custom property defined into my usercontrol:
<UserControl.Resources>
<ResourceDictionary Source="Themes/theme.xaml"/>
</UserControl.Resources>
<Grid>
<Button Click="Button_Click"></Button>
<Ellipse x:Name="ellipse1" Width="20" Height="20" Margin="5,40,45,5"></Ellipse>
</Grid>
and in the behind code I have:
private SolidColorBrush ButtonValue_;
public SolidColorBrush ButtonValue {
get { return ButtonValue_; }
set {
ButtonValue_ = value;
}
}
I'm trying to put into the CONTROLTEMPLATE this ellipse item, but i have some problems regarding how to BIND the Fill property of the ellipse with the ButtonValue custom property into the controlTemplate.
Any hints??
Thanks in advance
You can go to several directions:
Implement a custom control, that is your own class derived from an existing control (Button in your case). Add a dependency property (e.g. ButtonValue). Note - dependency property aren't standard .NET property - they have much more. Check out the following sample: http://msdn.microsoft.com/en-us/library/cc295235(v=expression.30).aspx (A custom button), or here: http://wpftutorial.net/HowToCreateACustomControl.html (A simpler sample, but without a property.
Have a data context for the control. Typically the data context is a separate class (a.k.a. the "View Model"), but if you aren't following the mvvm paradigm, it is OK the data context is self. Whatever data context you are using, it must derived from INotifyPropertyChanged, and it must file PropertyChanged event.
(Recommended!) Create a Control Template for CheckBox. When you come to think about it, logically your control is really a button with a binary state. Red/Green in your case, Checked/Unchecked for a CheckBox. So logically, you are looking for a checkbox, but you just want to present it differently.
So in your control template, draw the ellipse, and add a trigger for the IsChecked property:
<ControlTemplate x:Key="ButtonTemplate" TargetType="{x:Type CheckBox}">
<Grid>
... everything else in the control ...
<Ellipse x:Name="ellipse1" Width="20" Height="20" Margin="5,40,45,5" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="ellipse1" Property="Fill" Value="Red" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter TargetName="ellipse1" Property="Fill" Value="Green" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
This is a nice example for the difference between behavior and presentation of WPF.
While your control may look like a button, it behaves like a CheckBox, in the sense that it has two states.
EDIT: Use ToggleButton - this is the base class of CheckBox (and RadioButton), and it has exactly the functionality that you need, including the IsChecked property.
You have a couple of options:
1.The easiest one is to re-purpose an unused Brush or Color(with a converter) Button existing property:
<Window.Resources>
<ControlTemplate x:Key="repurposedProperty" TargetType="Button">
<Border Background="{TemplateBinding BorderBrush}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Window.Resources>
...
<Button Template="{StaticResource repurposedProperty}">Button</Button>
2.Other option is to define an attached property and use it in the ControlTemplate. On any Button that you apply the template to you have to set the attached property:
public static readonly DependencyProperty AttachedBackgroundProperty =
DependencyProperty.RegisterAttached("AttachedBackground", typeof (Brush), typeof (MainWindow),
new PropertyMetadata(default(Brush)));
public static void SetAttachedBackground(UIElement element, Brush value)
{
element.SetValue(AttachedBackgroundProperty, value);
}
public static Brush GetAttachedBackground(UIElement element)
{
return (Brush) element.GetValue(AttachedBackgroundProperty);
}
...
<
Window.Resources>
<ControlTemplate x:Key="attachedProperty" TargetType="Button">
<Border Background="{TemplateBinding WpfApplication1:MainWindow.AttachedBackground}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Window.Resources>
...
<Button Template="{StaticResource attachedProperty}">
<WpfApplication1:MainWindow.AttachedBackground>
<SolidColorBrush Color="Pink"></SolidColorBrush>
</WpfApplication1:MainWindow.AttachedBackground>
Button</Button>
PS: you can use a binding to set the value of the attached property.
A very simple question:
Why I cant see _ (underscore) in WPF content?
For instance the content of
<Label Content="test_t" Name="label2" />
is shown as "testt" (with the underscore not shown).
Labels support mnemonics (i.e. you can use ctrl+(key) to give them focus). You define the mnemonic key using an underscore.
http://www.charlespetzold.com/blog/2006/01/061004.html
If you want to see underscores, replace single underscores with double underscores.
This is because Label supports defining a mnemonic based on its content, which is done by prefixing the mnemonic with an underscore (the same thing that happens in Windows Forms with &).
Use a double underscore if you want a literal one to appear:
<Label Content="test__t" Name="label2" />
I know im late to the party but I believe that if you don't have the Label associated to a TextBox than you should use a TextBlock instead.
Changing your control to a TextBlock solves this issue since only Label has the mnemonic support
This style solves your problem:
<Style x:Key="{x:Type Label}"
TargetType="{x:Type Label}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Label}">
<Border Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="true">
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="False"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
I was binding to a data source which I didn't want to change, so I used a custom converter to create the missing underscore:
[ValueConversion(typeof(string), typeof(string))]
public class ShowUnderscoreConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
value is string text ? text.Replace("_", "__") : null;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
value is string text ? text.Replace("__", "_") : null;
}