Implementing style for Button in WPF - c#

I have a quick question. I have implemented style for button. And it basically works. Here is the code (complete example: you can copy and paste, it will work):
<Window x:Class="TestWPFApplication.Window5"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestWPFApplication"
mc:Ignorable="d"
Title="Window5" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="AButton">
<Setter Property="Width" Value="120"></Setter>
<Setter Property="Height" Value="50"></Setter>
<Setter Property="Background" Value="DarkGreen" />
<Setter Property="Foreground" Value="LightGreen" />
<Setter Property="FontSize" Value="22" />
<Setter Property="FontWeight" Value="Light" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="15" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}"
HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,0,0,0" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button x:Name="QuitButton" Style="{StaticResource AButton}" Content="Quit" />
</Grid>
</Window>
It's all fine. Button looks like this:
BUT
If I move slightly the button, and the Margin property will be auto-generated, to this...
<Button x:Name="QuitButton" Style="{StaticResource AButton}" Content="Quit" Margin="88,114,87,108.5" />
.. button will look like this:
Right and bottom side of the button has been cut off. Don't know why:/
The question is: Can anyone explain me this? Thanks in advance!

You've set the top margin to 114 and the bottom margin to 108.5 for a total of 225.5, but you've also set the total Window height to 300. That leaves just 77.5 pixels for the caption bar, the top and bottom window borders and the button (which you've set to 120 pixels high). The only way for WPF to make everything fit is to crop the button. (The same thing is happening in the X axis).
Set WindowStyle="None" and ResizeMode="NoResize" on your main window and you'll see the button now has enough room to draw fine. Better yet, set the right and bottom margin values to 0 and you can now set left and top to whatever you want.

Related

Why WPF cut components when I run the program

I'm doing a WPF program, with some textbox, labels and buttons.
Using the XAML designer, it works fine, the components are shown as they should. But when I run the program, it seems that the window shortens and cut some components in the sides.
Why are the components (button and labbel) being cut only when I run the program?
Here's the Xaml code of the 2 affected components:
<Window x:Class="XML_Edit.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XML_Edit"
mc:Ignorable="d"
Title="XML_Edit" Height="380" Width="470" ResizeMode="NoResize" Icon="Imagenes/xml.png">
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="4" />
</Style>
</Style.Resources>
</Style>
<Style TargetType="{x:Type TextBox}">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="4" />
</Style>
</Style.Resources>
</Style>
</Window.Resources>
<Grid Background="#FF363944">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="49*"/>
<ColumnDefinition Width="183*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="15"/>
<RowDefinition Height="80"/>
<RowDefinition Height="80"/>
<RowDefinition Height="45"/>
<RowDefinition Height="50*"/>
</Grid.RowDefinitions>
<!-- Components -->
<Button Name="btCambiarContenido" Grid.Row="4" Content="Cambiar Contenido" VerticalAlignment="Center" HorizontalAlignment="Left" VerticalContentAlignment="Center" Margin="26,80,0,16" Height="36" Width="135" Click="BtCambiarContenido_Click" TabIndex="5" Background="{x:Null}" BorderBrush="#FF4EB8CE" FontSize="14" Grid.ColumnSpan="2" Foreground="#FF4EB8CE"/>
<Label Name="lbSeleccionarRuta" Grid.Row="1" ToolTip="Seleccionar archivo" Height="32" Width="32" Margin="293.333,32,41,16" MouseDown="LbSeleccionarRuta_MouseDown" Grid.Column="1">
<Label.Style>
<Style TargetType="Label">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="Imagenes/folder_azul.png"/>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background">
<Setter.Value>
<ImageBrush ImageSource="Imagenes/folder_gris.png"/>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
</Grid>
They are shown fine in the WPF editor:
But they fet cut when I run the program:
You're setting your Button height to 36 pixels and giving it a margin of 26,80,0,16. This is effectively telling the layout manager that you want 36+80+16=132 pixels reserved to accommodate that button in row 4.
Meanwhile, in your grid layout you're specifying the rows 0-3 to have 15+80+80+45 pixels reserved. Combined with the 132 you're reserving for your button that's 352. But you're also explicitly setting your window height to 380, and that has to accommodate not just your 352 client area pixels but also the border and caption (as an experiment set your WindowStyle to None to remove the border and caption bar and you'll see your full control appear again). The layout manager has to cut pixels somewhere, and since row 4 is the only one you've specified with a "*" that's where they get cut, so the top and bottom of your button get cut as well. The reason you're not seeing this in designer is because it's using different theming to your OS, which is taking into account things like screen DPI, Windows theme settings, accessibility and several other things; the caption bar in designer is simply a bit smaller.
Issues like this are one of the many reasons you have to be careful doing explicit pixel layouts in WPF, and why you have to be especially careful with margins.

Removing full screen in xaml dynamically

This will probably be very simple for most of you, I am new to XAML and WPF.
I have an app that startes att full screen, I did this by adding
WindowState="Maximized"
WindowStyle="None"
I want to have a button that simply eliminates this part. I have a "Full screen" button in the xaml and by click he is calling a "FullScreen_Click" function in my code.
I just need to know what to write in the code that will eliminate the full screen if it is on full screen mode and restore it to full screen when it is not.
Try this:
private void FullScreen_Click(object sender, RoutedEventArgs e)
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
}
This will toggle between WindowState.Maximized and WindowState.Normal each time the Button is clicked.
For a xaml only technique just for reference to see a xaml example in comparison (but I would do #mm8's route, it's simpler);
1. Bind your property to that of another like:
<Window WindowState="{Binding Tag, ElementName=toggleState}" .... />
2. Use a `ToggleButton` or similar control and `Triggers`
.
<!-- like this PoC -->
<Grid>
<Grid.Resources>
<Style x:Key="cwWindowState_PoC" TargetType="{x:Type ToggleButton}">
<Setter Property="Tag" Value="Maximized"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Border Background="{TemplateBinding Background}"/>
<ContentPresenter x:Name="MyContentPresenter"
Content="{TemplateBinding Tag}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Tag" Value="Normal" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Tag" Value="Maximized" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ToggleButton x:Name="toggleState" Content="Click Me"
Background="Green"
Style="{StaticResource cwWindowState_PoC}"/>
</Grid>
Could also use DataTrigger but that requires interaction triggers instead of just a property setter from a template.

WPF: PART_ContentHost not scrolling

I am trying to make a Log area within my application and the customer has requested the ability to cut/paste the log messages from this area.
I originally was using the following to setup the log area with scrolling, but this does not allow the user to select & copy text:
<ScrollViewer DataContext="{StaticResource Log}"
Content="{Binding Appender.Notification}"
Height="150">
<ScrollViewer.Resources>
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
<Setter Property="VerticalScrollBarVisibility" Value="Auto" />
</Style>
</ScrollViewer.Resources>
</ScrollViewer>
I found this solution to create a read only TextBox with select-able text:
<TextBox Name="LoggingTextBox"
Height="250"
Width="950"
DataContext="{StaticResource Log}"
Text="{Binding Appender.Notification}"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border x:Name="PART_ContentHost" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Style>
</TextBox>
This works to allow the selection of text within the log area but the scrolling does not work. I added the properties for *ScrollBarVisibility (not in the original solution).
How can I get the scrolling to work using this TextBox styling?
The fix is pretty simple: just change your Border to a ScrollViewer, and you will get the standard scrolling behavior for a TextBox.

ContentControl Rotate decorator rendering

I have recently stumbled upon following issue: In my WPF application I've implemented a little designer, where you can put elements on canvas, move, scale and rotate them.
While searching the web I found following solution to this problem . This solution implements moving, scaling and rotating by System.Windows.Controls.Primitives.Thumb class so I thought I would just adjust this solution to my app and move on. The problem is, while on my machine everything is fine, on the others there are some rendering problems. I've made a screen shot of what I'm saying:
I'm using Windows 7 even though I run my app on other Windows 7 and it is also rendered wrong. I run my app with Windows XP and other compatibility settings on my machine but I wasn't able to reproduce this bug. What is this about and what am I possibly doing wrong?
This is my xaml file I'm using for content control styling:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:COMPANY.WPUI.LayoutDesignModel.Thumbs">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MoveThumb.xaml"/>
<ResourceDictionary Source="ResizeDecorator.xaml"/>
<ResourceDictionary Source="RotateDecorator.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="DesignerItemStyle" TargetType="ContentControl">
<Setter Property="MinHeight" Value="50"/>
<Setter Property="MinWidth" Value="50"/>
<Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Control Name="RotateDecorator" Template="{StaticResource RotateDecoratorTemplate}" Visibility="Collapsed"/>
<s:MoveThumb Template="{StaticResource MoveThumbTemplate}" Cursor="SizeAll"/>
<Control x:Name="ResizeDecorator" Template="{StaticResource ResizeDecoratorTemplate}" Visibility="Collapsed"/>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Selector.IsSelected" Value="True">
<Setter TargetName="ResizeDecorator" Property="Visibility" Value="Visible"/>
<Setter TargetName="RotateDecorator" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
A this is RotateDecorator.xaml file that happens to cause problems:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:COMPANY.WPUI.LayoutDesignModel.Thumbs">
<Style TargetType="{x:Type s:RotateThumb}">
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type s:RotateThumb}">
<Grid Width="30" Height="30">
<Ellipse Width="30" Height="30" Fill="#B0B0BB" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="RotateDecoratorTemplate" TargetType="{x:Type Control}">
<Grid>
<s:RotateThumb Margin="-18,-18,0,0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
<s:RotateThumb Margin="0,-18,-18,0" VerticalAlignment="Top" HorizontalAlignment="Right" />
<s:RotateThumb Margin="0,0,-18,-18" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
<s:RotateThumb Margin="-18,0,0,-18" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
The first thing I think of whenever seeing something like this is the graphics cards. You can get some strange behaviors with certain graphics cards, especially if their drivers are not installed properly/up to date.
This is caused by MergedDictionaries. The Diagram Designer Project splits the Move, Resize, and Rotate actions into 3 separate dictionaries. From the screenshot you can see the resize thumb is loaded. In my case the move action also worked, but like the question the rotate thumbs weren't displayed. No errors were thrown, but examination with Snoop shows that it wasn't able to find the rotate dictionary.
This solution expands on what I've covered above: https://stackoverflow.com/a/17083360/978622
To solve: Combine the resource dictionaries into a single resource dictionary.

WPF: how to style a class like in css?

Let's say I have a UserControl with 4 Borders:
<Border />
<Border />
<Border />
<Border />
Now in my Resources I can go:
<Style TargetType="{x:Type Border}">
... change some properties here
</Style>
Now this is all good, but it will target all borders in my UserControl.
But what if I just want to target a subset of them?
I'd like to go:
<Border Class="Type1" />
<Border Class="Type1" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" TargetClass="Type1">
... change some properties here
</Style>
But this obviously doesn't exist, is there some other way I can achieve what I'm after?
Thanks
Though the syntax isn't quite as clean as in CSS, it is a lot more specific.
To build on your example, what you're looking for is:
<Border Style="{StaticResource Type1}" />
<Border Style="{StaticResource Type1}" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" x:Key="Type1">
... change some properties here
</Style>
Remember that WPF styles don't actually cascade like CSS does.
A more detailed styling reference:
https://web.archive.org/web/20141210000517/http://dotnetslackers.com/articles/wpf/StylesResourcesAndControlTemplatesInWPF.aspx
Something that I find most people are not aware of is WPF's ability to nest Styles within Style.Resources. For example:
<!-- Define a new style for Borders called InfoBox, that will have a red background,
and further override all buttons within it to have Yellow Text. An extra style,
"Strawberry" is also defined, that lets specific buttons be selected to be styled
as Green FG on DarkRed BG -->
<Style TargetType="{x:Type Border}" x:Key="InfoBox">
<Setter Property="Background" Value="Red"/>
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="DarkYellow"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="Strawberry">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Background" Value="DarkRed"/>
</Style>
</Style.Resources>
</Style>
...
<Border Style="{DynamicResource InfoBox}">
<StackPanel>
<Button Content="I am a banana!"/>
<Button Style="{DynamicResource Strawberry}" Content="I am red!"/>
</StackPanel>
</Border>
While not exactly the same as CSS (There isn't much support for standard pseudo-selectors), this gives you a huge amount of power and flexibility. Couple this with skillful use of ItemsControls and you can do some great things.
you can set the style directly on the <Border> using an x:key and the StaticResource (or DynamicResource) property of the Border. if you would like to change the style at runtime, then you should lean towards using the DynamicResource over the StaticResource.
<Style x:Key="something" TargetType="{x:Type Border}">
</Style>
<Border style="{StaticResource something}"/>
<Style x:Key="styleKey" TargetType="{x:Type Border}">
... change some properties here
</Style>
and
<Border Style="{StaticResource styleKey}"

Categories

Resources