I want to create a simple custom control that extends TextBox.
I create it by Add -> New Item... -> Custom Control and I make some change on the code generated automatically. I change the base class of the CustomControl into TextBox and delete the Template setter in Theme/Generic.xaml file.
But when I add it to MainWindow and run, it is blank. Here is my final code:
File Theme/Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="BorderThickness" Value="10"/>
</Style>
</ResourceDictionary>
File CustomControl.cs:
namespace Test
{
public class CustomControl : TextBox
{
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
}
}
There's nothing in it. It needs a template.
There are two ways to do that: First, easiest, base your Style on the default TextBox style. That'll give you the default template and everything else in the default style. Add setters at will to override the inherited ones, if you wish.
<Style
TargetType="{x:Type local:MyCustomControl}"
BasedOn="{StaticResource {x:Type TextBox}}"
>
<Setter Property="BorderThickness" Value="10"/>
<Setter Property="BorderBrush" Value="Black"/>
</Style>
Second, write your own template. If you find that you need to do anything that the default template won't do for you, you'll be doing it this way. But beware, control behavior always turns out to be much more complicated than you would naively assume. These can be deep waters at times.
Here's some documentation about retemplating a TextBox or a subclass of a TextBox.
You'll need to fill in a lot more properties than this, but here's a start:
<Style
TargetType="{x:Type local:MyCustomControl}"
BasedOn="{StaticResource {x:Type TextBox}}"
>
<Setter Property="BorderThickness" Value="10"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<Border
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
>
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Related
I have a application that already uses a style template to defining font family, font size, foregrounds colors and so on.
One funcionnality of that app is enable users to picker a particular color and then apply to all texts, including textblocks, listviews, buttons and etc.
I've already read this link (Find all controls in WPF Window by type), where I could find all objects' type of FrameworkElement and then apply the color to each element according with his type. However, I am not sure that is the best approach.
As you said, there are lots of way of doing this.
For starter, you can have a style template and then use StaticResource to get the style for it. For example:in your view mainwindow.xaml
<Window.Resources>
<Style TargetType="{x:Type Control}" x:Key="baseStyle">
<Setter Property="FontSize" Value="100" />
</Style>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource baseStyle}"></Style>
<Style TargetType="{x:Type ListView}" BasedOn="{StaticResource baseStyle}"></Style>
<!-- ComboBox, RadioButton, CheckBox, etc... -->
</Window.Resources>
OR Another way is:
View: mainwindow.xaml
<Window>
<Window.resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="FontSize"
Value="14" />
</Style>
</Window.resource>
<Grid>
<TextBox Text="blah"/>
</Grid>
</Window>
OR
you can do it from mainwindow constructor
Style = (Style)FindResource(typeof(Window));
and this, you will put this in the app.xaml
<Style TargetType="{x:Type Window}">
<Setter Property="FontSize"
Value="14" />
</Style>
i based my application on this
example
i need my own button-style (without mouseover animations etc.), so i made this in the app.xaml:
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border x:Name="Border"
CornerRadius="2" BorderThickness="1"
Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}">
<ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" />
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
my button: <Button IsEnabled="true"/>
now if i change the button to <Button IsEnabled="false"/> my app crashes at the start with an error like: "{DependencyProperty.UnsetValue}" is not a valid value for property "BorderBrush".
what am i doing wrong?
It has to do with your static reference.
In particular, XAML parsing is very touchy on order - you have to make sure that a brush with x:Key="DisabledForegroundBrush" is referenced before the parser gets to the line in the above style - even if your style above is in the same file as the DisabledForegroundBrush.
If you don't have a brush yet for the DisabledForegroundBrush, you can either remove the line referencing it in the above code if you don't require it, or, if you want it, you can create one as follows:
<SolidColorBrush x:Key="DisabledForegroundBrush" Color="Red" />
Where you can choose the color as appropriate. Alternatively, you can choose some other kind of brush here: http://msdn.microsoft.com/en-us/library/aa970904(v=vs.110).aspx
If you have a brush you'd like to use already, then if you could provide a little more information as to where the brush is in the code base (eg is it in a resource dictionary?) and where the DisabledForegroundBrush brush is, this might help me pinpoint an actual solution / best way of ensuring the brush is referenced.
Note: an alternative if you can't ensure that DisabledForegroundBrush is parsed first is to change the StaticResource to a DynamicResource, but this isn't recommended unless the resource's link might actually change at run time (see What's the difference between StaticResource and DynamicResource in WPF? )
An easier solution:
If you just want to hard-code the style in, instead of reference the foreground brush externally, then you could change the line:
<Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
to:
<Setter Property="Foreground" Value="[SOME COLOR]"/>
To get rid of the need to create a separate brush object for your font.
I have a WPF menu:
XAML:
<Style x:Key="{x:Type ContextMenu}" TargetType="{x:Type ContextMenu}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContextMenu}">
<Border Background="#FF171616" CornerRadius="5" BorderBrush="DarkGray" BorderThickness="5" Opacity="0.0">
<StackPanel ClipToBounds="True" Orientation="Vertical" IsItemsHost="True" Margin="5,4,5,4"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Am I using the correct x:Type ContextMenu to alter the drop down visualization? The menu item visual is altered because I've manually changed the style. But it is the context drop down on which I want to apply visuals.
How can I modify the context drop down itself?
Here is a paint sample of what I'm after:
To modify style for menu, you need to override Menu style.
Like below:
<Style TargetType="{x:Type Menu}">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="#FF171616" />
</Style>
Edit: you can actually modify the whole template also, if you like. But I think these properties should get you the visualization you are looking for.
I want to edit a the cell style of a DataGrid in WPF. So using Expression Blend I right go to - Objects and Timeline>>DataGrid>>Edit Additional Templates>>Edit CellStyle>>Edit a Copy
Here's what what appears on the page:
<SolidColorBrush x:Key="{x:Static DataGrid.FocusBorderBrushKey}" Color="#FF000000"/>
<Style x:Key="DataGridCellStyle1" TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.HighlightTextBrushKey}}"/>
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="True">
<Setter Property="BorderBrush" Value="{DynamicResource {x:Static DataGrid.FocusBorderBrushKey}}"/>
</Trigger>
</Style.Triggers>
</Style>
But I only want to change the padding and background. Instead it has given me 25 lines of code, including the cell template! Am I missing something, is there a better way of styling items like this without having to bring so much extra unnecessary code when I only want to change two items?
Check out the "BasedOn" attribute for Styles...
For example the following style takes everything from DataGridColumnHeader and only overrides the HorizontalContentAlignment property:
<Style x:Key="CenterAlignedColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}"
BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
<Setter Property="HorizontalContentAlignment" Value="Center"/>
</Style>
Overriding control templates in WPF requires you to completely replace the template. You may have wanted to change just the one aspect of the template but the result of that is Expression dumping a copy of the rest of the template so that it can be overridden. Make sure you're overriding the cell in the proper way (I'm not sure there's another way). Some controls (ListView comes to mind) will let you swap out data templates without overriding the entire control template, but I'm not sure that's what you want, or if it can be done with DataGrid.
See the answer to this: Replace part of default template in WPF
To do what you want to do, you would usually just set the background and Padding properties in a style:
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="10" />
<Setter Property="Background" Value="Green" />
</Style>
However in this case it seems that the default control template for DataGridCell ignores the padding value, so you will need to replace it with an implementation that doesn't. The following is based on the default template that you posted:
<Style TargetType="DataGridCell">
<Setter Property="Padding" Value="10" />
<Setter Property="Background" Value="Green" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Border BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="True">
<ContentPresenter
Margin="{TemplateBinding Padding}" <!-- this bit does the padding -->
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to use a setter to set a default margin of all elements in my stackpanel, not just buttons but also textboxes and labels.
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="0,10,0,0"/>
</Style>
</StackPanel.Resources>
...
When I try to change above Button to Control or FrameworkElement (a derived type of each element) it doesn't work.
How can I fix this without having to specify 2 different Style elements with the same content but different x:Types on the TargetType?
You can do this with inheritance via Style's BasedOn attribute:
<StackPanel.Resources>
<Style x:Key="BaseStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Margin" Value="0,10,0,0"/>
</Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}" />
</StackPanel.Resources>