WPF Setting on a Control Template - wpf

General question. I have a ControlTemplate that is reasonably complex. Several TextBoxes etc.
I can't use TemplateBinding to bring all the properties to the surface so that I can set all the styles.
Is there a way for a Style to 'delv' into the controls within a control to set values?
Hope my question is clear without an example.
Thanks

The short answer is no. The ControlTemplate is essentially a black box, at least where XAML is concerned (there are ways to dig down the visual tree in code).
When you say you "can't use TemplateBinding", why not? If you just don't have enough available properties that can be fixed by creating some attached properties for the values you want to pass through. This is assuming you're templating a control that you can't change, otherwise you can just add new dependency properties.
Attached property:
public static class CustomProps
{
public static readonly DependencyProperty MyNewBrushProperty = DependencyProperty.RegisterAttached(
"MyNewBrush",
typeof(Brush),
typeof(CustomProps),
new UIPropertyMetadata(Brushes.Green));
public static Brush GetMyNewBrush(DependencyObject target)
{
return (Brush)target.GetValue(MyNewBrushProperty);
}
public static void SetMyNewBrush(DependencyObject target, Brush value)
{
target.SetValue(MyNewBrushProperty, value);
}
}
And usage in Style and Template:
<Style TargetType="{x:Type Button}">
<Setter Property="local:CustomProps.MyNewBrush" Value="Red" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:CustomProps.MyNewBrush)}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Using this method also still allows overriding values on individual instances.

Related

WPF: Making Properties of embedded control from template in custom control available

I have a Custom Control IconMD with the Properties IconName,OverlayName and OverlayPosition
I have embedded this Control in another Custom Control IconButton like so:
<ControlTemplate TargetType="{x:Type local:IconButton}">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Opacity" Value="0.7"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<local:IconMD
x:Name="_ButtonIcon"
OverlayPosition="{TemplateBinding OverlayPosition}"
IconName="{TemplateBinding IconName}"
OverlayName="{TemplateBinding OverlayIconName}"
/>
</Grid>
</Border>
</ControlTemplate>
The only purpose of the Dependency Properties OverlayPosition, IconName and OverlayIconName of this IconButton Control are simply to be passed through to the embedded Icon - the same idea as BorderBrush etc.
Now, as I also have IconToggleButton and IconRepeatButton (which inherit from the respective Base Classes and cannot inherit from IconButton!?), i must repeat this pattern for each of them. Should the capabilities of my IconMD Class grow, i would have to expand this pattern in every Custom Control that uses it.
How can I simply make the (properties of the) named IconMD Control "_ButtonIcon" available outside of my IconButton?
I would imagine instead of this
<imCC:IconButton
IconName="mdi-account-card-details"
OverlayIconName="mdi-multiplication-box"/>
writing something like this
<imCC:IconButton
_ButtonIcon.IconName="mdi-account-card-details"
_ButtonIcon.OverlayName="mdi-multiplication-box"/>
You can use attached properties instead of normal dependency properties (create in visual studio with snipped propa).
If you create IconName as attached property in class IconButton, you set it as follows:
<imCC:IconButton
imCC:IconButton.IconName="mdi-account-card-details"
...
And use in the ControlTemplate like this:
<local:IconMD
x:Name="_ButtonIcon"
IconName="{Binding Path=(local:IconButton.IconName),RelativeSource={RelativeSource TemplatedParent}}"
...
You could define the properties as attached properties that you can set on any UIElement or Button element.
Attached Properties Overview: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/attached-properties-overview
namespace WpfApplication1
{
public static class AttachedProperties
{
public static readonly DependencyProperty IsBubbleSourceProperty = DependencyProperty.RegisterAttached(
"IconName", typeof(string), typeof(AttachedProperties));
public static void SetIconName(UIElement element, string value)
{
element.SetValue(IsBubbleSourceProperty, value);
}
public static string GetIconName(UIElement element)
{
return (string)element.GetValue(IsBubbleSourceProperty);
}
}
}
<imCC:IconButton xmlns:local="clr-namespace:WpfApplication1"
local:AttachedProperties.IconName="mdi-account-card-details" />

How to style child of a UserControl in XAML in Silverlight?

I have a UserControl MyParentControl which has another control inside (TreeView). I expose this control as a dep property say TreeView MyChildControl.
Then in XAML which uses MyParentConrol I want to access all the TreeView properties, for example Style.
I want to write something like:
<my:MyParentControl>
<my:MyParentControl.MyChildControl.Style>
<Style />
</my:MyParentControl.MyChildControl.Style>
</my:MyParentControl>
Is there a way to achieve that?
By exposing the DependencyProperty for your inner control you have solved half of the problem - ie you can set individual properties in xaml.
The next step is to have those property setters affect the child control.
There are two options to achieve that.
In your control template, define your child control and use Bindings on each property you want to set.
Define a container element in your parent control template and set it's content to your child whenever the dependency property changes.
Although both of these methods could work, you may find that the solution involving the least amount of code, and the greatest amount of flexibility, is to expose a Style property for your child control and apply that in the control template.
public class ParentControl : Control
{
public Style ChildControlStyle
{
get { return (Style)GetValue(ChildControlStyleProperty); }
set { SetValue(ChildControlStyleProperty, value); }
}
public static readonly DependencyProperty ChildControlStyleProperty =
DependencyProperty.Register("ChildControlStyle",
typeof(Style),
typeof(ParentControl),
new PropertyMetadata(null));
}
<Style TargetType="ParentControl">
<Setter Property="ChildControlStyle">
<Setter.Value>
<Style TargetType="ChildControl">
<!-- setters -->
</Style>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ParentControl">
<Grid>
<ChildControl Style="{TemplateBinding ChildControlStyle}" />
<!-- other stuff -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You would get that effect by writing XAML like this:
<my:MyParentControl>
<my:MyParentControl.Resources>
<Style TargetType="my:MyChildControl">
<Setter Property="Background" Value="Red"/>
</Style>
</my:MyParentControl.Resources>
<my:MyParentControl>
In this example, this XAML creates a MyParentControl in which all the children of type MyChildControl have red backgrounds.

How to set size of CustomControl in Silverlight using TemplateBinding?

I am developing a Silverlight CustomControl, that defines a dependency property named SpinnerSize. Now I want to set the width and height of a Border inside of the default template to the SpinnerSize-property using TemplateBinding:
<Style TargetType="local:MyCustomControl">
<Setter Property="SpinnerSize" Value="12" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:MyCustomControl">
<Border
Width="{TemplateBinding SpinnerSize}"
Height="{TemplateBinding SpinnerSize}"
Background="Red" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The SpinnerSize reference in the above example is defined as follows:
public static readonly DependencyProperty SpinnerSizeProperty =
DependencyProperty.Register(
"SpinnerSize",
typeof(int),
typeof(MyCustomControl),
new PropertyMetadata(default(int)));
public int SpinnerSize
{
get { return (int)this.GetValue(SpinnerSizeProperty); }
set { this.SetValue(SpinnerSizeProperty, value); }
}
The result is that I don't see the border at all. If I set width and height of the border manually to a value everything works fine.
Is TemplateBinding a valid way to achieve that or do I have to set the width and height manually in the OnApplyTemplate()-method in the control?
Your XAML looks fine, and it is valid to use TemplateBinding like this, so the problem must be in your DependencyProperty.
Height and Width are Doubles. The Binding engine doesn't deal in implicit casts.
Change your DP to that type and it should work fine.

How to override base style control template in derived style in WPF?

I have a style for button. That style contains the ControlTemplate for Button. The ControlTemplate contains an Image with name "ImgButton".
I want to make this style as base style for other Buttons and want to override the "Source" property of Image control for different buttons.
Any ideas?
You may create attached behavior that will offer a property to assign Source. You should bind your image to this property in a template using TemplatedParent as RelativeSource. In derived styles you can simply use Setter(s) to specify a different Source.
Attached behavoir:
public static class ImageSourceBehavior
{
public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached(
"Source", typeof(ImageSource), typeof(ImageSourceBehavior),
new FrameworkPropertyMetadata(null));
public static ImageSource GetSource(DependencyObject dependencyObject)
{
return (ImageSource)dependencyObject.GetValue(SourceProperty);
}
public static void SetSource(DependencyObject dependencyObject, ImageSource value)
{
dependencyObject.SetValue(SourceProperty, value);
}
}
Styles:
<Style x:Key="Style1"
TargetType="Button">
<Setter Property="local:ImageSourceBehavior.Source"
Value="..."/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Image Source="{Binding Path=(local:ImageSourceBehavior.Source),RelativeSource={RelativeSource TemplatedParent}}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="Style2"
BasedOn="{StaticResource Style1}"
TargetType="Button">
<Setter Property="local:ImageSourceBehavior.Source"
Value="..."/>
</Style>

How to Inherit a Control Template

I working on a WPF project where I've over-ridden the CheckBox control for some special operations. That is working correctly.
My problem is that the ControlTemplate that was applied from the theme (shinyred.xaml from codeplex), is not applied to my over-ridden control. Is there a way to inherit the CheckBox ControlTemplate for use by my new control?
All the samples the I can find are focused on inheriting the style for the CheckBox, but nothing about the ControlTemplate.
No, as you said it is possible to 'inherit' a style by using the BasedOn property, but it's not possible to directly 'inherit' a template. This is understandable though, what would be the semantics of template inheritance? How would the derived template be able to somehow add or change elements in the base template?
With styles it's entirely possible, since you can simply add Setters, Triggers, etc. The only thing that would conceivably be possible with template inheritance is adding Triggers to the base template. However, in that case you'd have to have intimate knowledge of the element names in the base template, and an element name change in the base template could break your derived one. Not to mention an issue with readability, where you refer to a name in your derived template, which is defined somewhere else entirely.
Belated Addition Having said all that, it is possible to resolve your particular problem (although I doubt by now it is still yours, or even a problem). You simply define a style for your control with a setter for the Template property thus:
<Style TargetType="<your type>">
<Setter Property="Template" Value="{StaticResource <existing template resource name>}"/>
</Style>
Keeping in mind what said by #Aviad, the following is a work around:
say you have a Button that define a template that you want to ihnerit, define your CustomButton as Custom Control like this:
public class CustomButton : Button
{
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
typeof(string), typeof(CustomButton), new UIPropertyMetadata(null));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
Then go to your Generic.xaml and define the following:
<Style
x:Key="CustomButtonStyle" TargetType="{x:Type local:CustomButton}">
<Setter Property="FontSize" Value="18" /> <!--Override the font size -->
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomButton}">
<Button Style="{StaticResource ButtonStyleBase}"
Height="{TemplateBinding Height}"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:CustomButton}}, Path=Command}"
CommandParameter="{Binding}"
Width="{TemplateBinding Width}">
<Grid>
<StackPanel>
<Image Source="Image/icon.jpg" />
<TextBlock Text="{TemplateBinding Text}"></TextBlock>
</StackPanel>
</Grid>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Note that the button we want to inherit the template is wrapped inside my new template, and the style is set to the existing button. go the same way with the checkbox and organize the checkbox and label for instance vertically inside the new ControlTemplate of the CustomCheckBox

Resources