Rational creation of XAML markup for readibility and sanity - wpf

Disclaimer: Not sure what to put in the title to make it clear as the words to be used are the ones that I don't know (yet) and am asking about. Feel free to correct.
Imagine a scenario with GUI consisting of 4x3 inputs, where every input consists of a label and a textbox. At the moment, it's done by explicitly declaring all the components and each component has the for as follows.
<Label x:Name="Label1"
Content="Text1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="10,210,0,0" />
<TextBox x:Name="TextBox1"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="120"
Height="23" Margin="10,241,0,0"
TextWrapping="Wrap" Text="TextBox" />
Is there a recommended way to generate those from "something else", like a template or such, that's governing all the common attributes in it, eliminating the need for me to type them in over and over again (well, those were autogenerated but still...)? The alignments and sizes are tedious...
As for the margins, perhaps there's a layouting functionality? I've googled it but the hits related to XAML I've got were either suspiciously weird or relying on code behind. Is that the way to go or is it doable from XAML straight off?

To properly configure your layout, you should use WPF Layout Controls. In order to make the grid layout, you can use Grid, UniformGrid, etc., depending on your needs.
In order to apply several properties to the all controls inside the layout control, you can define the Style in the Resources of that control, as was mentioned already:
<Grid>
<Grid.Resources>
<Style TargetType="TextBox">
<Setter Property="Width" Value="120" />
<Setter Property="Height" Value="25" />
<!-- etc... -->
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<!-- Row definitions here. -->
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<!-- Column definitions here. -->
</Grid.ColumnDefinitions>
<!-- controls ... -->
<TextBox Text="{Binding YourProperty}"
Grid.Row="1"
Grid.Column="2"
/>
<!-- controls ... -->
</Grid>
Here the style will be applied to all TextBox's controls.

You're referring to a WPF "Style." With styles, you define a set of properties that will be the same between all instances of a control which use that style.
<Style x:Key="MyTextBoxStyle" TargetType="TextBox">
<Setter Property="Width" Value="120" />
<Setter Property="Height" Value="23" />
<Setter Property="TextWrapping" Value="Wrap" />
<!-- etc... -->
</Style>
<!-- This textbox will default its property values to those defined above -->
<TextBox Style="{StaticResource MyTextBoxStyle}" />

Related

When does using styles that are based on other styles become excessive?

As a parallel to DRY for code I don't like having the same property assignments in XAML. I've seen code examples where quite a bit of code can be consolidated as a style. Once that's done and cleaned up more code can be represented by another style that's based on the initial style that has some more specific edits, and so on. There comes a point where this leads to styles that are way to "clever". A change in one of the styles leads to a cascading affect to all those that depend on it. Is there a rule of thumb or general guideline to remember when using Style ... BasedOn={...}?
An example from Head First C# and using the WPF version. On page 754 they have the example shown below. As this is an introduction there are several property assignments that can be consolidated using styles.
<StackPanel Margin="20">
<TextBlock Foreground="White" FontFamily="Segoe" FontSize="20px"
FontWeight="Bold" Text="{Binding TeamName}" />
<TextBlock Foreground="White" FontFamily="Segoe" FontSize="16px"
Text="Starting Players" Margin="0,5,0,0"/>
<ListView Background="Black" Foreground="White" Margin="0,5,0,0"
ItemTemplate="{StaticResource PlayerItemTemplate}"
ItemsSource="{Binding Starters}" />
<TextBlock Foreground="White" FontFamily="Segoe" FontSize="16px"
Text="Bench Players" Margin="0,5,0,0"/>
<ListView Background="Black" Foreground="White" ItemsSource="{Binding Bench}"
ItemTemplate="{StaticResource PlayerItemTemplate}" Margin="0,5,0,0"/>
</StackPanel>
Refactoring using styles to remove duplicated property assignments. This shows an intermediary step where the Margin property could further be consolidated.
<StackPanel Margin="20">
<StackPanel.Resources>
<Style x:Key="whiteForground" TargetType="TextBlock">
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe" />
</Style>
<Style TargetType="ListView">
<Setter Property="Margin" Value="0,5,0,0" />
<Setter Property="Background" Value="Black" />
<Setter Property="Foreground" Value="White" />
</Style>
<Style x:Key="appliedMargin" TargetType="TextBlock" BasedOn="{StaticResource whiteForground}">
<Setter Property="Margin" Value="0,5,0,0" />
</Style>
</StackPanel.Resources>
<TextBlock Style="{StaticResource whiteForground}" FontSize="20px"
FontWeight="Bold" Text="{Binding TeamName}" />
<TextBlock Style="{StaticResource appliedMargin}" FontSize="16px"
Text="Starting Players" />
<ListView ItemTemplate="{StaticResource PlayerItemTemplate}"
ItemsSource="{Binding Starters}" />
<TextBlock Style="{StaticResource appliedMargin}" FontSize="16px"
Text="Bench Players" />
<ListView ItemsSource="{Binding Bench}"
ItemTemplate="{StaticResource PlayerItemTemplate}" />
</StackPanel>
I try to stick with only one level of basedon.
So base style, one inheritance (leaf) style only.
But, obviously, with set values overriding any in the control the leaf is used on.
At most another layer.
There are several reasons for this.
1)
It's a nightmare tracking complex inheritance - as you have probably realised.
2)
There used to be ( and probably still is ) a potential problem with resource dictionary chaining (rd).
What you'll want to do is have your styles in rd.
These often get big pretty quick.
So you split them up into a number of rd.
You want to base one on another so that needs to know about the base style.
You therefore merge the base one in within the leaf rd.
One layer of merge dictionaries like:
<ResourceDictionary
...
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Blaa.Validation.UILib;component/Resources/UIlibResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type ComboBox}" BasedOn="{StaticResource ErrorStyle}"/>
<Style TargetType="{x:Type DatePicker}" BasedOn="{StaticResource ErrorStyle}"/>
Here the combobxox style is basedon the errorstyle in UILibResources.
That works OK.
If a rd merges another rd that in turn merges another rd... and so on.
You can have mysterious problems which seem to come from a delay in merging.
I've seen intermittent errors and styling problems.
I think there is therefore best to keep that chaining shallow and the base resource dictionaries small.
This could have been fixed in recent versions of the framework but I've seen nothing mention it and there's very little changed in WPF for quite some time now.
3)
It's all too easy to find you need this level before that but it depends on something else if you try and get too "clever".
It'd be great if styles cascaded or there were mixins but they don't and we haven't.
So this basedon stuff is all we have and it's kind of clunky compared to the web.
Take a look at Dependency Property Value Precedence

Using a generic style as base vs custom user control

I'm creating a resource dictionary to my application, where I'll have some "icon+text" buttons. Since they will all look the same (except for the icon and the text), I've created a generic style to serve as base to the others:
<!-- Generic ActionButtonStyle -->
<Style x:Key="ActionButtonStyle" TargetType="{x:Type Button}">
<!-- some setter properties -->
<Setter Property="ContentTemplate" Value="{DynamicResource ButtonDataTemplate}"/>
</Style>
<DataTemplate x:Key="ButtonDataTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding Source}"
Stretch="Uniform"
Grid.Column="0"
Margin="2"/>
<TextBlock Text="{Binding text}"
TextWrapping="Wrap"
Grid.Column="1"
Margin="2"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
And I have some images for the icons:
<!-- Icons -->
<ImageSource x:Key="textToSpeech">Images/GreyIcons/TextToSpeech.png</ImageSource>
<ImageSource x:Key="play">Images/GreyIcons/Play.png</ImageSource>
<ImageSource x:Key="playSound">Images/GreyIcons/PaySound.png</ImageSource>
.
.
.
.
<ImageSource x:Key="group">Images/GreyIcons/Goup1.png</ImageSource>
And I'd like to create individual styles for each button (corresponding to each icon). Something like this:
<!-- Specific ActionButtonStyles -->
<Style x:Key="TextToSpeechButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource ActionButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<Image Source="{StaticResource textToSpeech}"
</Setter.Value>
</Setter>
</Style>
I know that this doesn't work.. How should I do it? Should I create a custom user control for the generic button? The text will be binding to an object in my Model, and so will the command (to an action).
The example of what you are looking for seems to be missing, but it seems that you may be looking for "BasedOn" - which allows you to inherit, but still override a previously defined style. You can implement it like this:
<Style x:Key="MyButtonStyle" BasedOn="{StaticResource ActionButtonStyle}">
<Setter.../>
</Style>
You need to create a derived class from Button that adds two new DependancyProperties. They would be called something like Text and ImageSource. Your derived class would also set the ContentTemplate as you have indicated. This ContentTemplate would bind against the Text and ImageSource dependancy properties.
You can then create your custom control in XAML like this...
<app:CustomButton Text="Play" Source="{Binding play}"/>
...but if you want the same button over and over again you could create a style that is applied to the CustomButton and sets those two properties as required.

How to set a default Margin for all the controls on all my WPF windows?

I want to set a default Margin of 3 on all the controls I put on all my windows and be able to override this value just on a really few number of items.
I've seen some approaches like doing styles but then I need to style everything, I would prefer something than can be done for all the controls together. I've seen other things like the MarginSetter but looks like it does not traverse subpanels. I want the Margin only on the controls I put at the window, nothing to do with the borders or other things of the visual tree.
Looks something pretty basic to me. Any ideas?
Thanks in advance.
The only solution I can find is to apply the style to each of the controls you are using on the window (I know that's not quite what you want). If you're only using a few different control types it's not too onerous to do something like this:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<!-- One style for each *type* of control on the window -->
<Style TargetType="TextBox">
<Setter Property="Margin" Value="10"/>
</Style>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="10"/>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Text="TextBox"/>
<TextBlock Text="TextBlock"/>
</StackPanel>
</Window>
Good luck...
You can link all of your Margin properties by referring to a "Thickness" defined in your resources. I just did this in a project...
<!-- somwhere in a resource-->
<Thickness x:Key="CommonMargin" Left="0" Right="14" Top="6" Bottom="0" />
<!-- Inside of a Style -->
<Style TargetType="{x:Type Control}" x:Key="MyStyle">
<Setter Property="Margin" Value="{StaticResource CommonMargin}" />
</Style>
<!-- Then call the style in a control -->
<Button Style="{StaticResource MyStyle}" />
<!-- Or directly on a Control -->
<Button Margin="{StaticResource CommonMargin}" />
The key for me was figuring out that Margin was defined by "Thickness". Let me know if that's clear enough or if you need me to put it in a fully working XAML example.
You can apply margin in your buttons style. And when you use buttons with this style in StackPanel wpf will apply need spacing.
for example
define in resourcedictionary or whatever:
<Style x:Key="myButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Margin" Value="10"/>
....
</Style>
then in yor StackPanel xaml definition:
<StackPanel>
<Border BorderThickness="0"/>
<Button x:Name="VertBut1" Style="{StaticResource myButtonStyle}" Content="Button1"/>
<Button x:Name="VertBut2" Style="{StaticResource myButtonStyle}" Content="Button2"/>
<Button x:Name="VertBut3" Style="{StaticResource myButtonStyle}" Content="Button3"/>
</StackPanel>
regards
Georgi

WPF, Font style for multiple controls

ok, I might be missing something really easy, But I want to use the same Font family, Font Size, and color for multiple controls.
Is there a way to create one style for this and apply it different controls?
Sorry if this has been asked before.
Thanks
Are the controls all in the same container? For example, in the same Window or StackPanel? If so, you can make set those properties on the parent container and they'll apply to any children. For example:
<StackPanel TextBlock.FontFamily="Comic Sans"
TextBlock.FontSize="14"
TextBlock.Foreground="Purple">
<TextBlock Text="Yeah, baby! I love me some Comic Sans!" />
<Button Content="Me too!" />
</StackPanel>
If you want to standardize the font across your entire app, you can use an implict style in your App.xaml file, like this:
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="Comic Sans" />
<Setter Property="FontSize" Value="14" />
<Setter Property="Foreground" Value="Purple" />
</Style>
I wanted to add this for the newbies (like myself).
If you want to set a property for multiple items within a container:
You can set a "style" within the "resources" for a control like so:
<Grid>
<Grid.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="22"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="hello text" />
<TextBlock Grid.Row="1" Text="hello text1" />
<TextBlock Grid.Row="2" Text="hello text2" />
</Grid>

Setting VerticalAlignment property to all controls

My WPF UserControl contains two stack panels and each of them contains labels, text boxes and radio buttons.
I would like to set VerticalAlignment property to Center to all controls in my UserControl with as little code as possible.
Now I have following solutions:
brute force - put VerticalAlignment="Center" in each control
define one style for FrameworkElement and apply it directly
define styles for each type of the controls on user control (this needs 3 style definitions, but automatically applies style to the control)
These three solutions need too much code.
Is there any other way to write this?
I hoped that defining style for FrameworkElement would automatically set property to all controls, but it does not work.
Here is snippet of my current XAML (I omitted second, very similar stack panel):
<UserControl.Resources>
<Style x:Key="BaseStyle" TargetType="FrameworkElement">
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Style="{StaticResource BaseStyle}" Text="Value:" />
<RadioButton Style="{StaticResource BaseStyle}">Standard</RadioButton>
<RadioButton Style="{StaticResource BaseStyle}">Other</RadioButton>
<TextBox Style="{StaticResource BaseStyle}" Width="40"/>
</StackPanel>
</Grid>
Edit:
Re Will's comment: I really hate idea of writing control formatting code in codebehind. XAML should be sufficient for this really simple user control.
Re Muad'Dib's comment: Controls I use in my user control are derived from FrameworkElement, so this is not an issue here.
I had come across the same conundrum awhile ago as well. Not sure if this is the "best" way, but it was easy enough to manage by defining your base style and then creating separate styles for each control on the page that inherited from the base style:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="300" Background="OrangeRed">
<Page.Resources>
<Style TargetType="FrameworkElement" x:Key="BaseStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Margin" Value="0,0,5,0" />
</Style>
<Style TargetType="TextBlock" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="RadioButton" BasedOn="{StaticResource BaseStyle}" />
<Style TargetType="TextBox" BasedOn="{StaticResource BaseStyle}" />
</Page.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Value:" />
<RadioButton>Standard</RadioButton>
<RadioButton>Other</RadioButton>
<TextBox Width="75"/>
</StackPanel>
</Grid>
</Page>

Resources