I'd like to understand which properties of an xaml Control are applied to the ControlTemplate of that Control. F.e. If I create a Control based on the Window Class like this:
(This is very simplified — It doesn't make sense in the current state I know...)
public class BaseWindow : Window {
public BaseWindow() { }
}
And the Template:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
xmlns:local="clr-namespace:Arctic">
<Style TargetType="{x:Type local:BaseWindow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BaseWindow}">
<Grid Background="Transparent">
<Border Background="{TemplateBinding Background}"/>
<ContentPresenter/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Now, when I specify a BaseWindow Control in my app the Margin Property is applied to the BaseWindow without specifying a TemplateBinding. The Background isn't, I have to declare the TemplateBinding in the Template in order to achieve that. Can you explain to me why some properties are applied to the ControlTemplate by default and others are not?
My guess is, that the Window.xaml (Default Window Template of WPF) binds to some properties like the Margin but ignores some like Background. If that is true, then I do not understand why I can set the Background in a Window Control and it is applied to it. Seems like the Window binds to some properties and stops doing that when you derive from it…
This is probably completely wrong — I just wanted to explain my thoughts.
Window class inherit FrameworkElement and all its properties including FrameworkElement.Margin. Same goes for Control.Background. Your question is why you have to do something to have Control.Background working.
Answer is simple:
Margin is used in layouting, its functionality is implemented/provided by FrameworkElement and it happens always, invisible for you and disregarding of ControlTemplate (because all framework elements participate in layouting and use margin).
Background, in turn, is provided to be use by visuals. It's up to you how to use it, because only you know how control will looks like. Control doesn't know what to do with that property.
So, you have to use TemplateBinding to bind Background to some color in your ControlTemplate, but Margin works without need to do anything in control template.
Related
I'm trying to create library with components that have an extended appearance configuration.
Instead of creating more styles and templates, I can inherit style from default style for custom component, and override few values.
I created CustomizableContextMenu and CustomizableMenuItem classes inherited from default ContextMenu and MenuItem and extended them by additional constructor. For example for CustomizableContextMenu.
static CustomizableContextMenu()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomizableContextMenu),
new FrameworkPropertyMetadata(typeof(CustomizableContextMenu)));
}
Styles and templates are in separate files that are referenced in the file "Themes/Generic.xaml".
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/FluentControls;component/Themes/CustomizableContextMenuStyle.xaml"/>
<ResourceDictionary Source="pack://application:,,,/FluentControls;component/Themes/CustomizableMenuItemStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style BasedOn="{StaticResource CustomizableContextMenuStyle}" TargetType="{x:Type components:CustomizableContextMenu}"/>
<Style BasedOn="{StaticResource CustomizableMenuItemStyle}" TargetType="{x:Type components:CustomizableMenuItem}"/>
Until I try to make style and template in the same app (without overriding DefaultStyleKeyProperty) and making reference to style in MainWindow.xaml everything works fine. But now (except other components such Button, TextBox, ComboBox, CheckBox, etc.) I have a problem with it.
I'm trying to create CustomizableButton (the same problem appears when I'm using standard Button) with CustomizableContextMenu and CustomizableMenuItem, but it didn't work. The message I've got from the IDE was "Cannot set OverridesDefaultStyle property in the default Style"
This is implementation code of CustomizableContextMenu.
<Components:CustomizableButton
...
Content="I have ContextMenu!">
<Button.ContextMenu>
<Components:CustomizableContextMenu
Background="#C0000000"
BorderBrush="#C00078D7"
BorderThickness="1"
Foreground="White">
<Components:CustomizableMenuItem
Background="#C0000000"
BorderBrush="#C00078D7"
Foreground="White"
Header="Item Test 1"
InputGestureText="Ctrl+1"
Margin="0,1"/>
</Components:CustomizableContextMenu>
</Button.ContextMenu>
</Components:CustomizableButton>
It's possible to do it in easy way, or not? And I must remove override of DefaultStyleKeyProperty, and import style in the traditional way to MainWindow.xaml and use it by setting property in component definition?
Remove the following <Setter> from the default style:
<Setter Property="OverridesDefaultStyle" Value="True" />
Since the style in themes/generic.xaml is the default style by defintion, it cannot override another default style.
Most of the tutorials and questions I see are about restyling the listbox to look different, but I'm interested in adding additional controls to make it behave differently. I initially started out trying to make the list builder control out of a checkbox list, but found myself too deep. I decided to abstract and start with a smaller problem.
What I am looking to do first, to get a better understanding of how this works is add "up" and "down" buttons next to the control. I think this can all be done in xaml, so to try and pressure myself to stick to that I'm working in Kaxaml.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<!-- ListBox Order Button Style
Col 1
Listbox
Col 2
Buttons Up and Down
-->
<Style x:Key="{x:Type ListBox}" TargetType="{x:Type ListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Rectangle Fill="Yellow"/>
<!--<ListBox></ListBox>-->
</Grid>
<StackPanel Grid.Column="1">
<Button>Up</Button>
<Button>Down</Button>
</StackPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid>
<ListBox>
<TextBlock>Value 1</TextBlock>
<TextBlock>Value 2</TextBlock>
<TextBlock>Value 3</TextBlock>
<TextBlock>Value 4</TextBlock>
</ListBox>
</Grid>
</Page>
I am currently hung up on a few things.
1) When I try to use a ListBox where the Yellow Rectangle is I start getting infinite loop problems.
2) I'm not sure how to connect the buttons to the listbox once it is there. I think Triggers is the answer, but I don't have much experience with them.
Your infinite loop can be addressed by not relying on the TargetType to apply the style. Instead, apply the style explicitly via a named key (i.e. something other than {x:Type Listbox}). That way the style is applied only when you specifically want it to be applied.
"Connecting" the buttons can be done a variety of ways. The simplest would be to handle the Button.Click event and perform whatever action you want there.
All that said, I think you're going about this the wrong way. Let a ListBox be a ListBox; don't try to make it into something it's not. If you want a reusable control that adds functionality around a ListBox, like buttons to control the contents of the ListBox, you should probably be authoring a UserControl, which is essentially a composite control made up of whatever you want.
Doing so will give you a lot more control over the appearance of the control. You'll also have the opportunity to declare dependency properties on your control that are specific to exactly what that control needs to support (something you can't do just with a Style). Yes, it also means you'll have to expose properties of contained elements via new properties in your UserControl that effectively delegate to the contained elements, but that's a small price to pay for the flexibility and relative simplicity of creating the UserControl in the first place.
I've been tinkering around with WPF MDI, which sets the control template for MDI Child objects. So when you add an MdiChild object and set its Content to a UserControl it looks good, but if you inherit from MdiChild then it doesn't work.
The template code looks something like this:
<Style TargetType="{x:Type local:MdiChild}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MdiChild}">
<!-- ... -->
I'd like this template to apply not just to MdiChild, but also to anything that derives from it. How can I go about this? The only way I can think of is to create a style targeting each derived class that is based on the MdiChild style, but that's not very desirable.
You have to declare style for each derived type but with WPF you have power to inherit from base style using BasedOn.
<Style TargetType="{x:Type local:DerivedMdiChild}"
BasedOn="{StaticResource {x:Type local:MdiChild}}">
......
</Style>
This way all setters, triggers etc. will be inherited and you don't have to redefine them again for each derived style. Moreover, it gives you power to override setter of base style in case you want some different behaviour in derived version.
I'm having a problem when changing a flow direction in a usercontrol by code, it changes all controls flowdirection.
How do i avoid some of thouse controls of changing flowdirection, maintaining allways LTR flowdirection.
Best Regards
You can declare default style in your resource for each and every control, so that it gets applied to respective control by default in you user control.
the xaml code is:
<Window.Resources>
<Style x:Key="TextBoxTemplate" TargetType="{x:Type TextBox}">
<Setter Property="FlowDirection" Value="RightToLeft"/>
</Style>
</Window.Resources>
the code behind is:
public void Init_Style()
{
Style style = this.FindResource("TextBoxTemplate") as Style;
textBox1.Style = style;
}
In this way you can avoid changes of all controls flow direction.
I've been using the same bit of code for several versions of my app with no problems, but I'm now mysteriously receiving NullRerefenceExceptions with the following:
this.Loaded += delegate {
deleteBrush = new DeleteBrushAdorner( background );
AdornerLayer al = AdornerLayer.GetAdornerLayer( background );
al.Add( deleteBrush ); // null ref here??
};
background is just a Border element.
My two thoughts on what could be causing it are a) switching to .NET 4.0, and b) placing instances of the above element (which is a UserControl) in an ItemsControl.
Oddly this doesn't happen all the time, and it's hard to predict when it will happen, so it's not reliable.
In my case I had a class that is based on Window and GetAdornerLayer() returned null. It turned out that the ControlTemplate for my derived class did not contain the AdornerDecorator. Adding that as the top level in the ControlTemplate solved the issue.
<Style TargetType="my:MyWindow" BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="my:MyWindow">
<AdornerDecorator>
<DockPanel ...>
</DockPanel>
</AdornerDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The docs for AdornerLayer.GetAdornerLayer specify:
If no adorner layers are found, the method returns null.
So my guess is that there are no adorner layers... do you have any reason to believe that this shouldn't be the case? What guarantee are you currently relying on that there will be an adorner layer in the visual tree?
I'm curious as to whether or not this was really solved. An AdornerDecorator provides an AdornerLayer for element below it -- and everything will be below it. It is a decorator, meaning it has a Child that is the content. That content is being provided with an AdornerLayer. So, if you put an AdornerDecorator in your XAML and the child is the border, the border does have an AdornerLayer.
Furthermore, Window defines an AdornerDecorator as the top of the visual tree so any element in a Window will have an AdornerLayer above it. So, if your conent above was in a Window...