WPF Style All Controls in One Instance UserControl - wpf

Is it possible to have say one UserControl with couple of Controls on it, but when reusing the UserControl to only apply on some of the a style or template for all those controls?
For example local:UserControl1 Grid.Column="1"'s Controls have a style/template applied?
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<local:UserControl1 />
<local:UserControl1 Grid.Column="1" />
</Grid>

You can not selectively apply a style based on some condition of the styled control without implementing an according behavior. Still, you can achieve the same thing with style triggers. You can set the template of the control in a style trigger, so applying a certain template based on some condition of the templated control is possible with out of the box features. In the example the background is set selectively:
<Grid>
<Grid.Resources>
<!-- Defining the TargetType but no x:Key will apply the style
to ALL children of this grid of the specified type -->
<Style TargetType="{x:Type local:UserControl1}">
<Style.Triggers>
<Trigger Property="Grid.Column" Value="1">
<!-- All setters here get applied only when
trigger condition is met -->
<Setter Property="Background" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<local:UserControl1 />
<local:UserControl1 Grid.Column="1" />
</Grid>

Related

Rational creation of XAML markup for readibility and sanity

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}" />

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.

Grid ColumnDefinition.Width in DataTemplate

In my project, I've made a control that inherits from Control. It is called DialogHeader and, as its name stands, is for displaying a header on modal, non-resizable dialogs. In truth, it binds by default to its parent Window. The control has a property called IconLocation, i.e. whether the image should be displayed on the left or right side of the control's label:
[Image] [Label] -- or -- [Label] [Image]
The template used with DialogHeader is basically the following:
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="COLN_Left" Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition x:Name="COLN_Right" Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Name="PART_Image" />
<Separator Grid.Column="1"
Visibility="Collapsed" Width="{TemplateBinding SpacerWidth}" />
<TextBlock Grid.Column="2" Name="PART_Text" />
</Grid>
<ControlTemplare.Triggers>
<Trigger Property="ImageLocation" Value="Right">
<Setter TargetName="PART_Image" Property="Grid.Column" Value="2" />
<Setter TargetName="PART_Text" Property="Grid.Column" Value="0" />
<!-- The following doesn't work! Help! -->
<Setter TargetName="COLN_Left" Property="Width" Value="*" />
<Setter TargetName="COLN_Right" Property="Width" Value="auto" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Simply put, when the ImageLocation property is set to Location.Right, the widths of COLN_Left and COLN_Right should be exchanged. So instead of [auto][auto][*], I should have [*][auto][auto].
How can I make this work from the ControlTemplate? If not, is there are way that doesn't involve using C# code?
Thank you in advance.
That part does work, it possibly is just not what you want i presume. Try to remove it and the result should be different.

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>

Why does the style change when I add an an accelerator to my button?

When I have my button's content as a normal string e.g. <Button Content="Ok" /> then button behaves as normal. But if I change the content to have a keyboard accelerator e.g. <Button Content="_Ok" /> the button's style changes to have different margins and sizes.
I have a TextBlock style that doesn't have a key so is being applied to all TextBlocks, my question is why is it applied when it the content has an accelerator and not when it doesn't?
Edit: For extra info: The default style is inside the resources of a StackPanel that is the button is inside. I guess the question is really, why doesn't the default TextBlock style get applied when the button has an accelerator?
WPF adds a TextBlock to each Button (and Menu) with an accelerator.
You can see this effect by running the following XAML (remember to hook up the Command if needed).
The key to fixing the problem, given the scope of your question, is to set the TextAlignment to a value of Center for the TextBlock. If you set the Width for the TextBlock style (my line is commented out below) the text will start to shift. Adding HorizontalAlignment = Center also helps center text in the TextBlock/Button, but this also impacts the other TextBlock controls.
<Window x:Class="ButtonAccelerator.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Main Window" Height="400" Width="800">
<Window.Resources>
<Style TargetType="TextBlock">
<!--<Setter Property="Width" Value="70"/>-->
<Setter Property="Height" Value="23"/>
<Setter Property="Background" Value="Pink"/>
<Setter Property="TextAlignment" Value="Center"/>
</Style>
<Style TargetType="Button">
<Setter Property="Width" Value="70"/>
<Setter Property="Height" Value="23"/>
</Style>
</Window.Resources>
<DockPanel>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="1" Grid.Column="1"
HorizontalAlignment="Right"
Orientation="Horizontal">
<TextBlock Text="OK" />
<Button
Content="OK"/>
<Button
Content="_OK"/>
</StackPanel>
</Grid>
</DockPanel>
</Window>
You should verify this with Snoop but <Button Content="Ok" /> produces a TextBlock to handle the text within a button. Since TextBlock doesn't support accelerator keys I would bet that <Button Content="_Ok" /> causes it to produce a Label instead since a Label will take care of the accelerator key.

Resources