WPF window with custom WindowChrome maximizes differently - c#

I have just started to use WindowChrome Class on a WPF window. I have noticed that when maximizing the window, it displays differently depending on whether I use the standard Chrome or the custom Chrome. Why?
I am double clicking the top of the window to maximize in both cases there is no code behind.
Here is the XAML - to add or remove the WindowChrome I am just deleting the Style property.
<Window
x:Class="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:local="clr-namespace:IPSurfer"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Title="MainWindow"
Width="800"
Height="450"
Style="{DynamicResource ResourceKey=StandardStyle}"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d">
<!-- Style="{DynamicResource ResourceKey=StandardStyle}" -->
<Window.Resources>
<Style x:Key="StandardStyle" TargetType="{x:Type local:MainWindow}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome
CaptionHeight="50"
GlassFrameThickness="0"
ResizeBorderThickness="5" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainWindow}">
<Grid>
<Border BorderBrush="PeachPuff" BorderThickness="5">
<Grid>
<ContentPresenter Content="{TemplateBinding Content}" />
<TextBlock
Margin="10,8,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Text="This is the new title" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Border BorderBrush="Teal" BorderThickness="5">
<Grid>
</Grid>
</Border>
</Grid>
Not Maximized, Standard Chrome - notice the Teal border
Maximized,Standard Chrome - this is showing the full Teal border fitting into the screen.
With the Window styled with the WindowChrome this is what it looks like not maximized. You see a border from my WindowChrome and the Teal border.
This is what it looks like maximized. My border in the Window Chrome is gone and only about half of the Teal border is there.
I also note that when I look at the Actual Width and Height of the Window when maximized the value is the same. It is just what is displayed is different.

Related

Disable WPF Button Hover Effect

The standard WPF Button control has a mouseover highlight that changes the colour of the control. I'm trying to disable this effect. Whilst I see lots of questions and answers to this on SO that involve changes to XAML, I'm trying to do this 100% programmatically, and I haven't been able to find any solution to this problem.
I don't have any XAML, as my WPF app dynamically adds a list of (derived) button controls to the form. The derived button class has no XAML whatsoever.
var button = new CustomButton(); // Inherits System.Windows.Controls.Button
button.Content = textBlock; // System.Windows.Controls.TextBlock
// other various property changes to the custom button removed
button.Focusable = false;
grid.Children.Add(button);
The button works perfectly with my custom code, but I just can't remove the mouseover effect. This is particularly bad, as it's being used on a touch screen, so the last touched button retains the mouseover effect until another button is pressed or the user clicks an unused area of the window.
Thanks to Kostas in the comments, I now know I need to create a global style and apply this to my custom control, however as per my original question, all the examples of doing this involve XAML, so my question really ought to be more specifically, how can I create a global style without a mouse over trigger, entirely programmatically?
The piece of the puzzle I was missing, was that the XAML for the style doesn't need to go in the button class, it instead goes into the parent window XAML. The Style is then added to the button class as it's added to the Window:
<Window x:Class="SomeOrg.SomeApp.WpfInterface.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:WpfInterface"
mc:Ignorable="d"
Title="SomeApp" Height="450" Width="800"
Background="#ffffffe1">
<Window.Resources>
<Style x:Key="MyButton" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border" BorderThickness="0" BorderBrush="Black" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid x:Name="grid" HorizontalAlignment="Stretch" Height="Auto" Margin="10,10,10,10" VerticalAlignment="Stretch"
Width="Auto" Background="#ffffffe1"/>
</Grid>
</Window>
In the parent window's code, where the custom button is added to the window, the above style (MyButton), is applied as follows:
var button = new CustomButton();
customButton.Style = (Style) this.Resources["MyButton"];
grid.Children.Add(button);

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.

How can I set the background of a StackPanel to an ImageBrush defined in a style resource?

I can apply a background image to a StackPanel with this XAML code:
<StackPanel>
<StackPanel.Background>
<ImageBrush ImageSource="bg.png" Stretch="Uniform" AlignmentY="Bottom" />
</StackPanel.Background>
...
</StackPanel>
But that's kind of a lot to type when applied to multiple StackPanels. So I'd rather define a Style (aka Static Resource?) that I can quickly apply to the individual StackPanels.
So I started writing this:
<Window.Resources>
<ImageBrush x:Key="SpBg" ImageSource="bg.png" Stretch="Uniform" AlignmentY="Bottom" />
<Style x:Key="StackPanelBg" TargetType="StackPanel">
<!-- begin invalid xaml -->
<Setter Property="Background" Value="SpBg" />
<!-- end invalid xaml -->
</Style>
</Window.Resources>
So that I'd be able to do this:
<StackPanel Style="{StaticResource StackPanelBg}">
...
</StackPanel>
Except that doesn't work; the Setter line isn't correct. How can I make this work?
Like that:
<Window x:Class="WpfApplication12.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>
<ImageBrush x:Key="SpBg" ImageSource="bg.png" Stretch="Uniform" AlignmentY="Bottom" />
<Style x:Key="StackPanelBg" TargetType="StackPanel">
<Setter Property="Background" Value="{StaticResource SpBg}" />
</Style>
</Window.Resources>
<StackPanel Style="{StaticResource StackPanelBg}">
</StackPanel>
</Window>

Border Margin in Window template doesn't have any effect when used with WindowChrome

I was checking out the WindowChrome class in System.Windows.Shell library (v 3.5.41019.1). When I try to create a Window template, the margin of the Border element in the template seems to have no effect:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
Title="MainWindow" Height="350" Width="525" Style="{DynamicResource WindowStyle1}">
<Window.Resources>
<Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
<!-- Here is the WindowChrome.-->
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<!-- And here is the Border. Its margin has no effect as far as I can tell.-->
<Border Margin="25" Background="Red">
<AdornerDecorator>
<ContentPresenter/>
</AdornerDecorator>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
</Grid>
</Window>
What do you think is the reason for that? I am wondering that, because I saw that some people use something like*:
<Border x:Name="WindowBorder" Margin="{Binding Source={x:Static shell:SystemParameters2.Current}, Path=WindowNonClientFrameThickness}" Background="Red">
But as it doesn't have any effect in my tests, what could be the point of doing this?
(*) One of the places it is used is the ModernUI project on CodePlex.
Edit: I have tested this on Windows 7 with Aero on.
Edit 2: It's still the same with Aero off.
According to MSDN, WindowChrome is
Represents an object that describes the customizations to the non-client area of a window.
After reading MSDN sample and playing your code a while, I noticed your code should be like following from MSDN sample code:
<Style x:Key="StandardStyle" TargetType="{x:Type local:MainWindow}">
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainWindow}">
<!--Note there is a Grid as the root-->
<Grid>
<Border Background="White"
Margin="{Binding Source={x:Static shell:SystemParameters2.Current}, Path=WindowNonClientFrameThickness}">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="36,8,0,0"/>
<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Icon}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(shell:WindowChrome.WindowChrome).ResizeBorderThickness}"
Width="{Binding Source={x:Static shell:SystemParameters2.Current}, Path=SmallIconSize.Width}"
shell:WindowChrome.IsHitTestVisibleInChrome="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note, there's a Grid as the root element which contains a few elements for customizing the NC of the window.
UPDATE:
You may notice in the remark of the MSDN page, it contains sections:
WindowStyle.None
WindowChrome
These are the two ways of customizing the appearance of a WPF application window.
However, setting the Window.WindowStyle property to WindowStyle.None:
This removes the non-client frame from the window and leaves only the
client area, to which you can apply a custom style. However, when the
non-client frame is removed, you also lose the system features and
behaviors that it provides, such as caption buttons and window
resizing. Another side effect is that the window will cover the
Windows taskbar when it is maximized.
Then WindowChrome is introduced to enable NC customization using WPF:
To customize a window while retaining its standard functionality, you
can use the WindowChrome class. The WindowChrome class separates the
functionality of the window frame from the visuals, and lets you
control the boundary between the client and non-client areas of your
application window. The WindowChrome class lets you put WPF content in
the window frame by extending the client area to cover the non-client
area. At the same time, it retains system behaviors through two
invisible areas; the resize border and caption areas.
So back to your question, the template you found, should be copied from the MSDN sample code, but missed the true root Grid.
The Margin on the Border is for giving some space to the NC.
In the MSDN sample code, the ContenPreseter only contains the Client area, while the NC contains the Border, a TextBlock for window title, and an Image for window icon.
To recap, setting WindowChrome enables you to customize the NC area of the window in the Window.Template.
NOTE:
The sample MSDN sample code seems a little out of date in .Net 4.5, the System.Windows.Shell.WindowChrome is now in the PresentationFramework.dll, so the code may look like:
<Window x:Class="WpfApplication1.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" Style="{DynamicResource WindowStyle1}" Icon="Icon1.ico">
<Window.Resources>
<Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border Background="Red"
Margin="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}}">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="36,8,0,0"/>
<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Icon}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowChrome.WindowChrome.ResizeBorderThickness}"
Width="{Binding Source={x:Static SystemParameters.SmallIconWidth}}"
WindowChrome.IsHitTestVisibleInChrome="True"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button />
</Grid>
I think there is some misunderstanding in the way you are trying to set the border. Here is the explanation of WindowChrome Class as provided in msdn
The WindowChrome class separates the functionality of the window frame from the visuals, and lets you control the boundary between the client and non-client areas of your application window. The WindowChrome class lets you put WPF content in the window frame by extending the client area to cover the non-client area. At the same time, it retains system behaviors through two invisible areas; the resize border and caption areas.
So if you are trying to customize the NonClient Area of the Window, its not the Content Presenter that you should set Border onto. That is the client area. Instead in the Template you can add your XAML other than Content Presenter to define your NonClient Area. I just tried a simple code based on your code and it shifts the Title Property of the Window to the right by a value of 100. Here is the code.
<Window x:Class="WPF_ToggleButton.ShellWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
Title="MainWindow" Height="350" Width="525" Style="{DynamicResource WindowStyle1}"
>
<Window.Resources>
<Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Grid>
<Border Background="Yellow">
<AdornerDecorator>
<ContentPresenter Content="{TemplateBinding Content}"/>
</AdornerDecorator>
</Border>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="100,0,0,0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Border Margin="50" Background="AliceBlue"/>
</Grid>
Thus you can have any elements in the NonClient Area like image representing your Window Close Button, etc using XAML code. The last element in the Window defines the Client Area which is passed to the Content Presenter in the Template
In short if you wan't to customize Client Area use the Content Presenter, whereas if you are interested in changing the NonClient Area like Title Bar display, close image icon then you define it in the Template.
One short observation. I think Margin doesn't make any sense for a Window. Try setting it for a normal window and I think it won't respect it.
Run into the same problem. No matter how I tried to set the Margin of the root element (its type does not matter) in MainWindow's ControlTemplate, it was always 0 when starting the app. The answer (thx to ILSpy) is in WindowChrome implementation.
When WindowChrome is attached to a Window, it is applying the Chrome feature, and among others, it executes a method WindowChromWorker._FixupTemplateIssues, which simple resets the window first visual element's Margin to a new Thickness instance, thus, erasing your custom settings by styles, templates, etc.
Workarounds can be:
Embedd your template into a new, look-less root element e.g. a Grid, so your original root's Margin won't be overridden
Adjust Padding of your root

ContentControl not showing Content

I have a custom ContentControl which has a fixed XAML layout like a UserControl (Rather than the usual applied generic template).
Previously this layout had no extra markup, so it was literally:
<ContentControl x:Class="MyControls.CustomViewControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
</ContentControl>
This worked fine.
I now want to put a border around the content, so I have changed the XAML to:
<ContentControl x:Class="MyControls.CustomViewControl"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<ContentControl.Template>
<ControlTemplate>
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter />
</Border>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
This shows the border, but no content.
I tried supplying an explicit binding for ContentPresenter:
<ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
But this made no difference.
Setting an explicit Content does work:
<ContentPresenter Content="TEST" />
Anyone know why the Content binding doesn't work? I guess I could fall back to the usual generic template but it would be easier if I could just do it directly like a UserControl.
Add TargetType for Control Template
<ContentControl.Template>
<ControlTemplate TargetType="Button">
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter />
</Border>
</ControlTemplate>
</ContentControl.Template>
Use TemplateBinding instead of Binding inside a ControlTemplate :
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</ContentControl.Template>
EDIT:
The important part of the shown code snippet is the TargetType in the definition of the ControlTemplate. With the target type
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl">
<Border BorderThickness="5" BorderBrush="LightGreen">
<ContentPresenter/>
</Border>
</ControlTemplate>
</ContentControl.Template>
already works without any TemplateBinding

Categories

Resources