I'm working on a program with multiple UserControls that should follow the same visual guidelines, as they will function as TabPages of an application to show some data to the user.
Basically, all of the UserControls have two columns. In column #1 I have a label describing which value belongs into the actual control and in the second column follows the control (TextBox, DatePicker, Checkbox etc.) for the user to set:
When I designed the first of those UserControls I ended up with a bunch of Styles that I set in the UserControls resources like this, so all Labels would have the same width, right-aligned text and vice versa:
<UserControl.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Width" Value="250"/>
<Setter Property="DockPanel.Dock" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="DisplayMemberPath" Value="Value"/>
<Setter Property="SelectedValuePath" Value="Key"/>
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</UserControl.Resources>
Now, as I'm about to create all other 60+ UserControls I would like to avoid to add this exact same code over and over again to every single UserControl, just to achieve a consistency in visual style, like same fixed width for labels and so on, as this would be just horrible to maintain (imagine the customer requires all labels just to be 50px less wide...).
Is was wondering if there is any way of setting this formatting stuff once somewhere in the application and then just "import" those settings for my UserControls like this:
Master-file
<Master.Template1>
<Style TargetType="{x:Type Label}">
...
</Style>
<Style TargetType="{x:Type TextBox}">
...
</Style>
<Style TargetType="{x:Type ComboBox}">
...
</Style>
<Style TargetType="{x:Type DockPanel}">
...
</Style>
</Master.Template1>
Other UCs
<UserControl.Resources>
<using Style:Master.Template1/>
</UserControl.Resources>
I already read some stuff about this Application.ResourceDictionary, but I'm not sure if I'm on the right track with that, as I would prefer not to mess around with the code-behind files.
Or should I create my own Control that derives from a UserControl that already has all those values set in it's resources?
Add a new ResourceDictionary file to the project. Put your styles in the ResourceDictionary.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Label}">
<Setter Property="Width" Value="250"/>
<Setter Property="DockPanel.Dock" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="DisplayMemberPath" Value="Value"/>
<Setter Property="SelectedValuePath" Value="Key"/>
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</ResourceDictionary>
Then add the resource dictionary to your UserControl.Resources
<UserControl.Resources>
<ResourceDictionary Source="MyResources.xaml"/>
</UserControl.Resources>
Edit:
Glen Thomas and myself managed to answer the question at the same time. I still leave my own answer up, as the article on MSDN contains some useful further information and upcoming users might find it useful to know how to add a ResourceDictionary in the first place.
Of course this is possible, and as easy as I hoped: ResourceDictionarys were the way to go!
This article in the MSDN poked me into the right direction.
I just added a new ResourceDictionary to my program
and put the reference code into it:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Label}">
<Setter Property="Width" Value="250"/>
<Setter Property="DockPanel.Dock" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="HorizontalContentAlignment" Value="Right"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="DisplayMemberPath" Value="Value"/>
<Setter Property="SelectedValuePath" Value="Key"/>
<Setter Property="Margin" Value="2"/>
</Style>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</ResourceDictionary>
After that, I was able to "import" the Dictionary into my UserControl by replacing the UserControl.Resources with this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ResourceDictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
And voilà: formatted controls with the ability to choose if to apply the resource "template" or not and a central place to modify the shared values.
Perfect!
Related
I have read a lot of questions about this, but all of this questions was about VM models, and i dont have the VM model, i am trying to change the language of my application with a ResourceDictionary as is explained in this post.
How to change language in WPF/XAML
and also i saw a lot of examples without specify any key https://learn.microsoft.com/es-es/dotnet/framework/wpf/advanced/how-to-use-a-resourcedictionary-to-manage-localizable-string-resources
and i dont get what i am doing wrong, this is the xaml resource
<Window.Resources>
<Style x:Key="titulo" TargetType="TextBlock" BasedOn="{StaticResource Encabezado}">
<Setter Property="Margin" Value="20"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="tipoBase" TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Margin" Value="10"/>
</Style>
<Style x:Key="imagenTitulo" TargetType="Image">
<Setter Property="Height" Value="200"/>
<Setter Property="Source" Value="/images/asistente.jpg" />
</Style>
<Style x:Key="narracion" TargetType="TextBlock" BasedOn="{StaticResource tipoBase}">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="TextAlignment" Value="Center"/>
</Style>
<Style x:Key="botonSalirInicio" TargetType="Button" BasedOn="{StaticResource botonSalir}">
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="50"/>
<Setter Property="Margin" Value="0 0 10 0"/>
</Style>
<ResourceDictionary>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Idiomas/IdiomasLogin.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ResourceDictionary>
</Window.Resources>
The resource file
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ElEscribaDelDJ"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<system:String x:Key="Titulo">Bienvenido a "el escriba del DJ"</system:String>
<system:String x:Key="Narración">a</system:String>
<system:String x:Key="User">Usuario:</system:String>
<system:String x:Key="UserText">Introduzca el nombre de usuario:</system:String>
<system:String x:Key="CheckUser">Recordar Usuario:</system:String>
<system:String x:Key="CheckLogin">Recordar Login:</system:String>
<system:String x:Key="License">Este producto esta bajo la licencia</system:String>
<system:String x:Key="Credits">Creditos</system:String>
</ResourceDictionary>
Other weird thing is that i cant use clr-namespace:System;assembly=mscorlib, when i try to use it, always get an error
Properties of the ResourceDictionary
Embebed Resource
Custom tool: XamlIntelliSenseFileGenerator
and lastly the textblock
<TextBlock Style="{DynamicResource titulo}" x:Name="titulo" Text="{DynamicResource Titulo}"/>
you should put all styles into one ResourceDictionary. at the moment you have too many <ResourceDictionary> declarations, and the outer-most is treated as Resource but doesn't have x:Key, hense the error.
rewrite it like this (<ResourceDictionary.MergedDictionaries> can be written first)
<Window.Resources>
<ResourceDictionary>
<!--<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Idiomas/IdiomasLogin.xaml"/>
</ResourceDictionary.MergedDictionaries>-->
<Style x:Key="titulo" TargetType="TextBlock" BasedOn="{StaticResource Encabezado}">
<Setter Property="Margin" Value="20"/>
<Setter Property="Foreground" Value="White"/>
</Style>
<Style x:Key="tipoBase" TargetType="TextBlock">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Margin" Value="10"/>
</Style>
<Style x:Key="imagenTitulo" TargetType="Image">
<Setter Property="Height" Value="200"/>
<Setter Property="Source" Value="/images/asistente.jpg" />
</Style>
<Style x:Key="narracion" TargetType="TextBlock" BasedOn="{StaticResource tipoBase}">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="FontSize" Value="18"/>
<Setter Property="TextAlignment" Value="Center"/>
</Style>
<Style x:Key="botonSalirInicio" TargetType="Button" BasedOn="{StaticResource botonSalir}">
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="50"/>
<Setter Property="Margin" Value="0 0 10 0"/>
</Style>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Idiomas/IdiomasLogin.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
In App.xaml I have styled all my Buttons.
<Style TargetType="Button">
<Setter Property="Margin" Value="3"/>
</Style>
I realized if the Button is in a DataGrid then I not need a margin. I have a lot of DataGrid and I put this code into all of them one by one.
<DataGrid.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0"/>
</Style>
</DataGrid.Resources>
Is there a more clever way to do this?
You can define Style for DataGrid and within that, add child control style to particular modification.
If you want to add this Style to all the DataGrids, no need to defineKey.
<Style x:Key="dataGrid" TargetType="DataGrid">
<Style.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="0" />
</Style>
</Style.Resources>
</Style>
Declare a style with the key in the Window.Resources or App.Resources as shown below.
<Window.Resources>
<Style TargetType="Button" x:Key="dataGridButtonStyle">
<Setter Property="Margin" Value="3"/>
<Setter Property="Background" Value="Wheat"/>
</Style>
</Window.Resources>
Then apply the style to the control using the StaticResource with the key(in this example key name is dataGridButtonStyle)
<Button Style="{StaticResource ResourceKey= dataGridButtonStyle}" Content="Hello"/>
Please add resource file at Windows or User control level so that it will apply all child controls as below,
<Window.Resources>
<Style TargetType="DataGrid">
<Style.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Red" />
<Setter Property="Margin" Value="0" />
</Style>
</Style.Resources>
</Style>
<Window.Resources>
or
<UserControl.Resources>
<Style TargetType="DataGrid">
<Style.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Red" />
<Setter Property="Margin" Value="0" />
</Style>
</Style.Resources>
</Style>
</UserControl.Resources>
Inside of app.xaml I have datagrid style
and for each window that contains a datagrid, I added an event setter for the datagridrow.
however, when I run it, I can see the style, but when I try to trigger the event nothing happens. if I remove the style from app.xaml. the event works well.
why app.xaml style disable my eventsetter?
here's the datagrid inside the app.xaml:
<Style TargetType="DataGrid">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="GridLinesVisibility" Value="None"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="RowStyle">
<Setter.Value>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ColumnHeaderStyle">
<Setter.Value>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Margin" Value="5,0,0,0"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{ x:Type DataGridColumnHeader}">
<Border BorderThickness="2,2,2,0" CornerRadius="5,5,0,0">
<Border.Style>
<Style TargetType="Border">
<Setter Property="BorderBrush" Value="White"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}" Value="{x:Null}">
<Setter Property="BorderBrush" Value="Transparent"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<ContentPresenter HorizontalAlignment="Center" Margin="5"></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
here's the eventsetter inside each window that contains datagrid:
<Style TargetType="DataGridRow">
<EventSetter Event="MouseDoubleClick" Handler="EditItem"/>
</Style>
It seems, that the grid uses the RowStyle from it's property later, than it has been set in window's resources. It will work, if you clear your RowStyle property in grid. Put this to the window's resources:
<Style TargetType="DataGrid" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="RowStyle" Value="{x:Null}"/>
</Style>
For you can use the style set in app.xaml, you have to move the RowStyle from property to global(in app.xaml):
<Style TargetType="DataGrid">
....
<Setter Property="RowStyle" Value="{x:Null}"/>
...
</Style>
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="Background" Value="Green"/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="Foreground" Value="White"/>
</Style>
and in window's resources:
<Style TargetType="DataGridRow" BasedOn="{StaticResource {x:Type DataGridRow}}">
<EventSetter Event="MouseDoubleClick" Handler="PreviewMouseDown"/>
</Style>
I have a DatePicker in my application, that consists of DockPanels in a StackPanel in a ScrollViewer in a UserControl.
The (simplified) xaml of the UserControl looks like this:
<UserControl x:Class="project.UI1"
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"
xml:lang="de-DE"
mc:Ignorable="d" >
<UserControl.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Width" Value="250"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="3"/>
</Style>
<Style TargetType="{x:Type TextBox}">
<Setter Property="DockPanel.Dock" Value="Top"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="MaxWidth" Value="{Binding RelativeSource={RelativeSource AncestorType=Border},Path=ActualWidth}"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
</Style>
<Style TargetType="{x:Type DatePicker}">
<Setter Property="Margin" Value="2"/>
<Setter Property="Width" Value="105"/>
<Setter Property="MaxWidth" Value="105"/>
<Setter Property="Height" Value="24"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
<Style TargetType="{x:Type DockPanel}">
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="2"/>
</Style>
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="HorizontalScrollBarVisibility" Value="Disabled"/>
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
</Style>
</UserControl.Resources>
<ScrollViewer>
<StackPanel>
<DockPanel>
<TextBlock Text="IK-Nummer des Absenders (z.B. D-Arzt)"/>
<Border>
<TextBox Text="{Binding p_1}"/>
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="IK-Nummer der UNI-DAV aus UV-Träger-Verzeichnis"/>
<Border>
<TextBox Text="{Binding p_2}" />
</Border>
</DockPanel>
<DockPanel>
<TextBlock Text="Datum der Erstellung durch den Leistungserbringer"/>
<DatePicker SelectedDate="{Binding p_3}"/>
</DockPanel>
</StackPanel>
</ScrollViewer>
</UserControl>
The TextBlocks have a 250px fixed width and hold the description of the fields while the TextBoxes for the user input are supposed to grow & shrink dynamically as the user resizes the window, to fit the rest of the window width.This is why I came up with packing every TextBlock/TextBox-combo in a DockPanel, and stack those in a StackPanel.
However, when I click on the DatePicker, the calendar beyond spans across the whole screen:
Even though this looks quite funky, I would prefer to get the common square calendar "popup" back.
Unfortunately, I don't know what causes the calendar to go wild like this at the moment...
The problem is your TextBlock style. It is being applied to the DateTimePicker because it has no key and so applies to all TextBlocks down the visual tree.
Can you give the style a key and only apply it to certain TextBlocks?
<Style TargetType="{x:Type TextBlock}" x:Key="TextBlockStyle">
<Setter Property="Width" Value="250"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="TextAlignment" Value="Right"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="3"/>
</Style>
....
<TextBlock Text="IK-Nummer der UNI-DAV aus UV-Träger-Verzeichnis"
Style="{StaticResource TextBlockStyle}"/>
UPDATE:
OR, you could just add a new TextBlock style to the DatePicker's resource to override the style higher up in the visual tree:
<DatePicker SelectedDate="{Binding p_3}">
<DatePicker.Resources>
<Style TargetType="{x:Type TextBlock}"/>
</DatePicker.Resources>
</DatePicker>
I would like to nest Styles in WPF.
I have a resource dictionary:
<Style x:Key="BottomButtonBar" TargetType="{x:Type Grid}">
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,2" />
<Setter Property="Width" Value="90" />
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="2,0"/>
</Style>
</Style>
What I want is: If I apply the style "BottomButtonBar" on a grid, Buttons which are inside this grid have Margin and Width I've defined and the same for the TextBlock inside this grid.
How to do that?
I finally found the following solution:
<Style x:Key="BottomButtonBar" TargetType="{x:Type Grid}">
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,2" />
<Setter Property="Width" Value="90" />
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="2,0"/>
</Style>
</Style.Resources>
</Style>
And in the XAML:
<Grid DockPanel.Dock="Bottom" Style="{DynamicResource BottomButtonBar}">
If you want to add styles to elements in the Grid, you can make the styles implicit and add them to the resources of the Grid to limit their scope:
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10,2" />
<Setter Property="Width" Value="90" />
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="2,0"/>
</Style>
<Grid.Resources />
<Button ... />
</Grid>