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
Related
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.
I want to create a custom Button inside WPF. Of course, the button will be an UserControl and it will contain many visual elements (like stroke, highlight, shadow, glow, images etc.).
The problem is that if I use DependencyProperties and bind them in XAML I won't be able to see the result at DesignTime (I've tried to implement IsInDesignMode method but for a certain reason that I can't understand my VS just crashes when I use this method on UserControls, otherwise it works just fine) and this is definitely not good.
So I am thinking about not using XAML at all and do all my work in the code behind.
What do you guys think?
Like you, when I was getting started and wanted to understand how / what was going on and working with templates, it took a lot of trial and error. Hopefully my research and some step-by-step components can help you customize to your liking and KNOWING where things are coming from.
First, when trying to understand how a new "template style" will work, I created a simple stand-alone WPF app ("AMS") for my Any Manipulating Styles. This way, I don't have to wait forever to see what something will look like during trial / error with the rest of my primary project and themes.
From that, I created a new WPF Window called "TestingStyles". Save/Compile, run, no problem.
Now, in the "VIEW CODE" of the TestingStyles window, I have put whatever I am playing with for a custom class... To help show the step-by-step, I've created the following:
namespace AMS
{
/// <summary>
/// Interaction logic for TestingStyles.xaml
/// </summary>
public partial class TestingStyles : Window
{
public TestingStyles()
{
InitializeComponent();
}
}
// Enumerator for a custom property sample...
public enum HowToShowStatus
{
ShowNothing,
ShowImage1
}
public class YourCustomButtonClass : Button
{
public YourCustomButtonClass()
{
// auto-register any "click" will call our own custom "click" handler
// which will change the status... This could also be done to simplify
// by only changing visibility, but shows how you could apply via other
// custom properties too.
Click += MyCustomClick;
}
protected void MyCustomClick(object sender, RoutedEventArgs e)
{
if( this.ShowStatus == HowToShowStatus.ShowImage1 )
this.ShowStatus = HowToShowStatus.ShowNothing;
else
this.ShowStatus = HowToShowStatus.ShowImage1;
}
public static readonly DependencyProperty ShowStatusProperty =
DependencyProperty.Register("ShowStatus", typeof(HowToShowStatus),
typeof(YourCustomButtonClass), new UIPropertyMetadata(HowToShowStatus.ShowNothing));
public HowToShowStatus ShowStatus
{
get { return (HowToShowStatus)GetValue(ShowStatusProperty); }
set { SetValue(ShowStatusProperty, value); }
}
}
}
As you can see, the custom "Button" class, I have at the bottom outside the default TestingStyles : Window declaration... so its all in the same "Project".
In this XAML sample, I make reference to a "TaskComplete.png" graphic file (which should just for sample purposes, add directly to the project... Even if a simple smiley face for sample purposes).
So, create such a simple .png file... even by using Microsoft Paint and drawing a circle with eyes and smile. Save into the project at the root (get into pathing stuff later, get it working first).
Save and recompile the project, so the project knows publicly what the new "class" (button) is when you start to define the XAML template.
Now, back to the TestingStyles designer and get it into split screen so you can see both the designer and the XAML markup... and Just replace with the following...
<Window x:Class="AMS.TestingStyles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:my="clr-namespace:AMS"
Title="TestingStyles" Height="300" Width="300" >
<Window.Resources>
<!-- Build a "Style" based on an anticpated target control type of YourCustomButtonClass.
per the "my:" reference, the "my" is an "alias" to the xmlsn:my in the declaration above,
so the XAML knows which library to find such control. In this case, I've included within
the actual forms's 'View Code' as a class at the bottom.
As soon as you assign an "x:Key" reference, its like its telling XAML to make this a PRIVATE
style so you don't reference it explicitly (yet)
-->
<Style TargetType="my:YourCustomButtonClass" x:Key="keyYourCustomButtonClass">
<!-- put whatever normal "settings" you want for your common look / feel, color -->
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0,0,1,1"/>
<Setter Property="Width" Value="100" />
<Setter Property="Height" Value="30" />
<!-- Now, for the template of the button. Things can get really crazy here
as you are now defining what you want the "button" to look like, borders,
content, etc. In this case, I have two borders to give the raise/sunken effect
of a button and it has its own colors -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button" >
<!-- The x:Name references used during triggers to know what it is "applying" changes to -->
<Border x:Name="BorderTopLeft"
BorderBrush="Gainsboro"
BorderThickness="0,0,1.5,1.5">
<Border x:Name="BorderBottomRight"
BorderBrush="Gray"
BorderThickness="1.5,1.5,0,0">
<!-- Now, what control type do you want the button to have...
Ex: You could use a grid (as I have here), stack panels, etc -->
<Grid Background="LightBlue" >
<!-- I'm defining as two columns wide, one row tall.
First column fixed width 20 pixels example for an image -->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20px" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<!-- Now, create the controls I want available within my "template".
when assigned with "x:Name", thats like a property withing the template
that triggers can associate and update to. -->
<Image x:Name="btnImage"
Grid.Row="0" Grid.Column="0"
Stretch="None"
VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Source="TaskComplete.png"
Visibility="Visible" />
<!-- and also have the text for the button to show the user -->
<TextBlock x:Name="txtNewBtn"
Grid.Row="0" Grid.Column="1"
Padding="5"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Text="{TemplateBinding Content}" />
<!-- The "{TemplateBinding Content}" means to set the text based on
the "CONTENT" property of the original button and not use a fixed value -->
</Grid>
</Border>
</Border>
<!-- Now, some triggers for the button itself... some can be property based, others data-based -->
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<!-- What properties do we want to change when user CLICKS
on the button, give the "EFFECT" of click down/up by
changing the "Margin" and border thicknesses... -->
<Setter Property="Margin" Value="1,1,0,0"/>
<!-- Notice the "TargetName" below referring to the x:Name I've applied in template above
so when the user clicks on the button, it changes the border thickness properties of
each to give the effect of a normal button clicking. I'm widening one border, shrinking other -->
<Setter TargetName="BorderTopLeft" Property="BorderThickness" Value="2.5,2.5,0,0"/>
<Setter TargetName="BorderBottomRight" Property="BorderThickness" Value="0,0,.5,.5"/>
</Trigger>
<!-- Here, I have a custome property on the class for "ShowStatus". The binding is to itself
regardless of how many instances of this type of "button" are on a given form
First trigger happens when the value is changed to "ShowNothing", but can also change
when set to "ShowImage1" or other as you may need applicable
-->
<DataTrigger Binding="{Binding Path=ShowStatus, RelativeSource={RelativeSource Self}}" Value="ShowNothing">
<Setter TargetName="btnImage" Property="Visibility" Value="Hidden"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=ShowStatus, RelativeSource={RelativeSource Self}}" Value="ShowImage1">
<Setter TargetName="btnImage" Property="Visibility" Value="Visible"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- NOW, we can expose any instance of "YourCustomButtonClass" button to use the style based on definition above
any instance of such YourCustomButtonClass will automatically reflect this style / look -->
<Style TargetType="my:YourCustomButtonClass" BasedOn="{StaticResource keyYourCustomButtonClass}" />
</Window.Resources>
<Grid>
<my:YourCustomButtonClass Content="Button" VerticalAlignment="Top" ShowStatus="ShowImage1" />
</Grid>
</Window>
This should give you a great jump-start to defining your own templates and how the elements start to tie together. Once this sample is running, as you change any colors, margins, padding, etc to the template, you'll immediately see the visual impact that component has on the control.
Have fun and don't bang your head too much against the wall...
BTW, once this is working, then you can take the style element stuff within the
<Window.Resources>
</Window.Resources>
and put it into a Windows Resource Dictionary to make it global to your project instead of just this test form.
I have a dropdownlist control and its ItemsSource is a collection of items which of type T_LookupTable, which is a table in the db, and one of it's columns is 'isEnabled'.
How do I bind the IsEnabled property of the ComboBoxItem in the XAML to this value in the collection?
Further, I have numerous drop-downs in the application which employ this same method, so I would like to somehow make this a global feature if possible, through a static resource, is something like that possible? I found this piece of XAML, which will work, but I want the items to be greyed out in the drop-down, and this method only disables them where you can't click them, but there is no visual indicator which says the item is disabled:
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<ContentPresenter x:Name="ContentPresenter" IsHitTestVisible="{Binding Path=isEnabled}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
I had similar problem with TreeViewItems...
Basically, you have to inherit ComboBox class, override GetContainerForItemOverride method like this:
protected override DependencyObject GetContainerForItemOverride()
{
var result = new ComboBoxItem();
result.SetBinding(Control.IsEnabledProperty, new Binding("IsEnabled"));
return result;
}
It hard codes data binding to IsEnabled property of your data object.
I have buttons on a toolbar in WPF.
When I do the XAML:
<ToolBar.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Width" Value="21"></Setter>
<Setter Property="Height" Value="21"></Setter>
</Style>
</ToolBar.Resources>
None of the buttons on the toolbar set their sizes accordingly.
I have to go to each button and manually set their widths and heights to the desired values.
Any idea why the Style on the toolbar does not work?
This occurs because ToolBar applies the style identified by ToolBar.ButtonStyleKey to buttons, instead of leaving them with the default style. (That's why buttons in Toolbars are flat even though the default style is raised.) Reference.
You need to "hijack" this style, instead of the default style:
<ToolBar.Resources>
<Style x:Key="{x:Static ToolBar.ButtonStyleKey}" TargetType="Button">
<Setter Property="Width" Value="100" />
</Style>
</ToolBar.Resources>
Note the x:Key in the Style declaration.
If you are adding hardcoded buttons to your toolbar, you can set ToolBar.ItemContainerStyle to a custom style to get the effect you want.
<ToolBar.ItemContainerStyle>
<Style
TargetType="Button">
<Setter
Property="Width"
Value="21" />
<Setter
Property="Height"
Value="21" />
</Style>
</ToolBar.ItemContainerStyle>
If you are using ToolBar.ItemsSource you can instead use ToolBar.ItemTemplate to define a template for your toolbar data.
<ToolBar.ItemTemplate>
<DataTemplate>
<Button
Width="21"
Height="21"
Content="{Binding}" />
</DataTemplate>
</ToolBar.ItemTemplate>
Note that in some cases, both of these can be used at the same time for additional flexibility.
This applies not only to toolbar, but to all derivatives of ItemsControl.
Best of luck,
As noted in the other answer the Toolbar will automatically apply its own styles to many/most typical controls added to it.
As an alternative to hijacking its style keys or applying your own styles to the controls manually, you can instead override its method which sets its internal styles in the first place. Simple example:
public class LessIsMoreToolbar : ToolBar
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
// Nada
}
}
and then use <local:LessIsMoreToolbar> in your XAML instead of <Toolbar>.
Note that here PrepareContainerForItemOverride() specifically does NOT call base.PrepareContainerForItemOverride()`. This is what eliminates the setting of styles. You can view the base's version of this method yourself to verify this doesn't eliminate anything you need.
One caveat is that PrepareContainerForItemOverride is defined by ItemsControl, which is like a great-grandparent of Toolbar. Its version of this method kicks off some other Prepare... cases which you also should be careful won't break anything. You can't (or perhaps shouldn't) just call that version of the method directly.
But in the end if it works for you then this is a nice simple approach.
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.