Extend UserControl.Resources - wpf

I want to provide a simple default style for a UserControl, but still be able to extend or override the style when using the control. Below is a sample scenario with a simple UserControl and a Window containing the control. The intention is for the style for the Button provided in the Window to override the default style defined in the UserControl.
UserControl
<UserControl x:Class="Sample.TestControl" ... >
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="2" />
<Setter Property="Foreground" Value="Orange" />
</Style>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Background" Value="Black" />
</Style>
</UserControl.Resources>
<StackPanel>
<Button Content="Press Me" />
<Button Content="Touch Me" />
<Button Content="Tap Me" />
</StackPanel>
</UserControl>
Window
<Window x:Class="Sample.MainWindow" ... >
<Grid>
<local:TestControl>
<local:TestControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="2" />
<Setter Property="Foreground" Value="Green" />
</Style>
</local:TestControl.Resources>
</local:TestControl>
</Grid>
</Window>
Problem
The above code will result in:
Exception: Set property 'System.Windows.ResourceDictionary.DeferrableContent' threw an exception.
InnerException: Item has already been added.
The above code is trying to submit two styles with the same key into the same ResourceDictionary, so obviously it wasn't going to work. My guess that I am not going to be able to provide a default style for the buttons...

Insufficient Workaround: Override default ResourceDictionary
<Window x:Class="Sample.MainWindow" ... >
<Grid>
<local:TestControl>
<local:TestControl.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="2" />
<Setter Property="Foreground" Value="Green" />
</Style>
</ResourceDictionary>
</local:TestControl.Resources>
</local:TestControl>
</Grid>
</Window>
By putting the custom Button style in a ResouceDictionary, I can override the default one. However, its not just overriding the Button style, its overriding all of the resources. So, the StackPanel will no longer have a black background. (obviously I could add that to the overriding style as well, but thats not practical on a bigger scale.)

Related

How to make an exception for Application.Resources style in a specific Grid.Resources

I am doing small WPF app for my own using Visual Studio, C#, .NET Standard and WPF in this specific project.
I have defined style for all TextBlocks and TextBoxes in Applications.Resources like below.
<Application.Resources>
<Style TargetType="TextBox">
<Setter Property="FontSize" Value="10"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="10"/>
</Style>
</Application.Resources>
Then in main window I have a grid which contains some buttons.
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="FontSize" Value="50" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="50"/>
</Style>
</Grid.Resources>
<Button Grid.Column="0" Content="DASHBOARD" Command="local:CustomCommands.ShowDashboard"/>
</Grid>
I would like to set for the textblocks/textboxes in this specific buttons a wider font.
I tried for many different syntax but could not manage it. I tried also do define x:Key for this style in Grid.Resources and use it in this specific Button control. This wasn't work either.
Can anyone let me know which way should I let know my application that text in this buttons would have bigger font size?
The TextBlock created for string contents by the ContentPresenter inside the Button template doesn't apply the locally-defined resources, i.e. those in your Grid.
The easiest way to solve your problem would be to explicitly define a TextBlock as the Button's content.
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="50"/>
</Style>
</Grid.Resources>
<Button Grid.Column="0" Command="local:CustomCommands.ShowDashboard">
<TextBlock Text="DASHBOARD" />
</Button>
</Grid>

Is Style Inheritence with nested User Controls Possible In WPF?

I'm pretty sure it's not possible to use BasedOn with regards to a Style declaration in a nested UserControl, and be able to reference a Style declaration made in a parent UserControl - can anyone confirm this?
I've tried BasedOn="{StaticResource}" and BasedOn="{DynamicResource}" and neither work.
(Pseudo code sample below)
<Window>
<UserControl>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
</Style>
</StackPanel.Resources>
<UserControl2> <!--this will reside in a different .xaml file...-->
<Button Content="Hello World">
<Button.Style>
<Style TargetType="{x:Type Button}" BasedOn="????">
<!--is this possible to inherit the red background?-->
<Setter Property="Padding" Value="10" />
</Style>
</Button.Style>
</Button>
</UserControl2>
</StackPanel>
</UserControl

How do buttons style optimize in XAML code?

<Style x:Key="ToolBarButton" TargetType="{x:Type Button}">
<Setter Property="Control.Background" Value="#00000000"/>
<Setter Property="Control.Width" Value="25"/>
<Setter Property="Control.Height" Value="25"/>
</Style>
...
<Grid HorizontalAlignment="Left">
<Button Style="{StaticResource ToolBarButton}"/>
<Button Style="{StaticResource ToolBarButton}"/>
<Button Style="{StaticResource ToolBarButton}"/>
<Button Style="{StaticResource ToolBarButton}"/>
<Button Style="{StaticResource ToolBarButton}"/>
<Button Style="{StaticResource ToolBarButton}"/>
</Grid>
I wanna optimize my XAML code. I don't wanna assign a style to each button, but I wish every button to have my style.
Is it possible to do something like this? Only working ... :)
<Grid x:Name="gToolBar" Grid.Row="1" HorizontalAlignment="Left" Style="{StaticResource ToolBarButton}">
<Button/>
<Button/>
<Button/>
<Button/>
<Button/>
<Button/>
</Grid>
I don't use TargetType only, because I have other buttons with different styles.
I think it available, but i don't know how.
Thanks...
As i already said everything that is to be done in the comment, but for further clarification:
I wanna optimize my XAML code. I don't wanna assign a style to each button, but I wish every button to have my style.
Move the Style part to <Application.Resources> in App.xaml file, Like shown below:
<Application.Resources>
<Style TargetType="Button" >
<Setter Property="Control.Background" Value="#00000000"/>
<Setter Property="Control.Width" Value="25"/>
<Setter Property="Control.Height" Value="25"/>
</Style>
</Application.Resources>
Note: I've removed the x:Key part. Now this will apply to all the button's that is in the application.
I don't use TargetType only, because I have other buttons with different styles. I think it available, but i don't know how.
For this you would have to make a custom button as a UserControl, thus making them completely different from a usual Button. Apply styling to them in their own UserControl.Resources. Thus styling mentioned in App.Resources won't affect these custom made UserControls
You can create a default style for buttons within the grid:
<Window ...>
<Window.Resources>
<Style x:Key="ToolBarButton" TargetType="{x:Type Button}">
<Setter Property="Control.Background" Value="#00000000"/>
<Setter Property="Control.Width" Value="25"/>
<Setter Property="Control.Height" Value="25"/>
</Style>
</Window.Resources>
<Grid HorizontalAlignment="Left">
<Grid.Resources>
<Style x:Key="{x:Type Button}" BasedOn="{StaticResource ToolBarButton}" TargetType="{x:Type Button}"></Style>
</Grid.Resources>
<Button />
<Button />
<Button />
<Button />
<Button />
<Button />
</Grid>
</Window>
If you create a resource that uses a control type as key within a container, the style will get applied to all controls of the type within the container. If you want to define the original style somewhere else (for example because you use it in many place) you can base the local style off the global style.
You should use application resources to do that. add this code to there(app.xaml):
<Application.Resources>
<Style TargetType="Button" >
<Setter Property="Control.Background" Value="#00000000"/>
<Setter Property="Control.Width" Value="25"/>
<Setter Property="Control.Height" Value="25"/>
</Style>
</Application.Resources>
UPDATE
Or if you want this style apply just some part of your application such some special 'Grid' or special Window ..., just put it inside Resource of that element like this:
<Grid>
<Grid.Resources>
<Style TargetType="Button" >
<Setter Property="Control.Background" Value="#00000000"/>
<Setter Property="Control.Width" Value="25"/>
<Setter Property="Control.Height" Value="25"/>
</Style>
</Grid.Resources>
... inside code, and your buttons which we eant to apply style for them.
</Grid>

How to apply DependencyProperty values to child user controls?

I have multiple instances of the same custom user control in a StackPanel. Each instance needs the same DependencyProperty "ControlWidth". Instead of setting each user control with the same property, I want to set it only once in the parent StackPanel
<StackPanel>
<propwin:PropertyEditControl Label="First" ControlWidth="180" />
<propwin:PropertyEditControl Label="First" ControlWidth="180" />
...
</StackPanel>
I used to do this with Style properties
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type propwin:PropertyEditControl}">
<Setter Property="ControlWidth" Value="180" />
</Style>
</StackPanel.Resources>
<propwin:PropertyEditControl Label="First" />
<propwin:PropertyEditControl Label="Second" />
...
</StackPanel>
Update:
Thanks to Anatoliy, who mention that my code (the one I showed here) should work. I now tracked down the problem. Inside my PropertyEditControl.xaml I define a validation style:
<UserControl x:Class="MyModule.PropertyEditControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MyModule">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyModule;component/UI/ResourceDictionaries/ResourceLibrary.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style TargetType="local:PropertyEditControl">
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ValidationErrorTemplate}" />
</Style>
<Style x:Key="PropertyNameStyle" TargetType="DockPanel">
<Setter Property="Width" Value="{Binding ControlWidth}" />
<Setter Property="DockPanel.Dock" Value="Left" />
</Style>
...
If I remove the <Style TargetType="local:PropertyEditControl"> style it works!
It turned out that my original approach should have worked. The reason why it hasn't was a misplaced Style Resource inside the user control. Anyway, the correct answer to my initial question is: To set a DependencyProperty value for multiple controls only once, you have to set it inside the container style definition:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type propwin:PropertyEditControl}">
<Setter Property="ControlWidth" Value="180" />
</Style>
</StackPanel.Resources>
<propwin:PropertyEditControl Label="First" />
<propwin:PropertyEditControl Label="Second" />
...
</StackPanel>

WPF datagrid styling

I want to style a WPF datagrid and it seems to be really easy . As far as I understand I have to have code such as the following:
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type Custom:DataGridColumnHeader}" >
<Setter Property="Background" Value="#88800080" />
<Setter Property="Foreground" Value="White" />
</Style>
But my question is ..where do I place this code and how do I let the datagrid know to use the style above ?
Regards,
S
Put it in the resource of the xaml (local or global). The easiest is to put it in the local resource of the current xaml file:
<Page Name="SomeName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type Custom:DataGridColumnHeader}" >
<Setter Property="Background" Value="#88800080" />
<Setter Property="Foreground" Value="White" />
</Style>
</Page.Resources>
<!-- The rest of the xaml -->
</Page>
The best place to put styles is in a resource dictionary, referenced in App.xaml.
Resource dictionary ("StyleResources.xaml" in this example):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="TextBlockRightAlign" TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Right" />
</Style>
<Style x:Key="TextBlockTitle" TargetType="TextBlock">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ResourceDictionary>
Referencing the style dictionary in App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="StyleResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<ValueConverters:PriceConverter x:Key="PriceConverter"/>
</ResourceDictionary>
</Application.Resources>
Using the definition in a datagrid (column formatting here, but should work for headers as well):
<data:DataGridTextColumn Header="Charge" Width="100"
Binding="{Binding Charge, Mode=TwoWay, Converter={StaticResource PriceConverter}}"
ElementStyle="{StaticResource TextBlockRightAlign}" />
Note that the element inside the cell is a TextBlock, so you can use a style with a target type of TextBlock.
As for the "Type DataGridColumnHeader was not found": you need a second xml namespace entry since the DataGridColumnHeader is in the System.Windows.Controls.Primitives namespace. You need something like
xmlns:dg="clr-namespace:Microsoft.Windows.Controls.Primitives;assembly=WPFToolkit"
and then reference the new namespace in your style, e.g.
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type dg:DataGridColumnHeader}" >
Styles usually go:
<UserControl.Resources>
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type Custom:DataGridColumnHeader}" >
<Setter Property="Background" Value="#88800080" />
<Setter Property="Foreground" Value="White" />
</Style>
</UserControl.Resources>
Use the appropriate container if this isn't within a UserControl you may use "Window" or whatever container you're in.
Also you need to reference it in your datagrid with:
<Custom:DataGrid ColumnHeaderStyle="{StaticResource DataGridColumnHeaderStyle}"/>

Resources