How do i get a Menu in the Window bar? - c#

I would like to know how to get a menu in the window bar, the same as Visual studio does.
It would be good to be able to have File, Edit, etc on the left and the standard Minimize, Maximize and Close buttons on the right. Is this at all possible?
I have tried setting Window WindowStyle="None" and adding my own icons in the bar but it doesnt seem right but is this the only way?
This is what i have at the moment.
<Window
Title="MainWindow"
Height="{x:Static SystemParameters.PrimaryScreenHeight}"
Width="{x:Static SystemParameters.PrimaryScreenWidth}"
Closing="Window_Closing"
WindowState="Maximized">

You must create your custom window chrome using the WindowChrome class:
The Window element in WPF is actually hosted in a non-WPF (non-client) host. This host includes the title bar (caption) and the standard buttons, an icon and the actual frame. This is known as window chrome.
Usually you can only modify the client area of a Window. But whith the help of the WindowChrome class, WPF allows the client area to extend into the non-client area.
The disadvantage is that you would have to basically replicate the original non-client area in order to preserve the original look and feel. But after all you still get some basic behavior like maximizing the window on double click ou of the box.
The following example is very basic, but should give you an idea how to achieve your task:
I highly recommend to follow the provided link to the WindowChrome class and read the remarks section, which contains very valuable information.
You can use the actual system values provided by the static SystemParameters class to get the current dimension values e.g. SystemParameters.WindowResizeBorderThickness which you should use in your custom chrome style.
Also note that to allow WPF to capture mouse events on you custom chrome's input elements like buttons or menus you must set the attached property WindowChrome.IsHitTestVisibleInChrome on each relevant element to true:
WindowChrome.IsHitTestVisibleInChrome="True"
The very basic style that creates the above visual is as followed:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:System.Windows.Shell;assembly=PresentationFramework">
<Style x:Key="WindowStyle" TargetType="{x:Type Window}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="WindowChrome.WindowChrome">
<Setter.Value>
<WindowChrome NonClientFrameEdges="Right"
ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Background="Transparent">
<AdornerDecorator>
<Border Background="Transparent" Margin="{x:Static SystemParameters.WindowNonClientFrameThickness}">
<ContentPresenter />
</Border>
</AdornerDecorator>
<ResizeGrip x:Name="WindowResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Visibility="Collapsed"
IsTabStop="false" />
<Grid Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
Background="#FF3F3F3F"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<!-- Custom window chrome -->
<StackPanel Grid.Column="0" Orientation="Horizontal"
Margin="{x:Static SystemParameters.WindowResizeBorderThickness}">
<Image Source="{TemplateBinding Icon}" />
<TextBlock Text="{TemplateBinding Title}"
TextTrimming="CharacterEllipsis"
VerticalAlignment="Center"
Margin="16,0" />
<Menu shell:WindowChrome.IsHitTestVisibleInChrome="True">
<MenuItem Header="CustomChromeMenu">
<MenuItem Header="Action" />
</MenuItem>
</Menu>
</StackPanel>
<StackPanel Grid.Column="1"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button Width="45"
Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
ToolTip="Minimize window"
ToolTipService.ShowDuration="5000"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<TextBlock Foreground="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=Foreground}"
FontFamily="Segoe MDL2 Assets"
FontSize="11"
Text="" />
</Button>
<Button ToolTip="Maximize window"
Width="45"
Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
ToolTipService.ShowDuration="5000"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<TextBlock Foreground="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=Foreground}"
FontFamily="Segoe MDL2 Assets"
FontSize="11"
Text="" />
</Button>
<Button ToolTip="Close application"
ToolTipService.ShowDuration="5000"
Width="50"
Height="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}, Path=Top}"
shell:WindowChrome.IsHitTestVisibleInChrome="True">
<TextBlock Foreground="{Binding RelativeSource={RelativeSource AncestorType=Control}, Path=Foreground}"
FontFamily="Segoe MDL2 Assets"
FontSize="11"
Text="" />
</Button>
</StackPanel>
</Grid>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ResizeMode"
Value="CanResizeWithGrip">
<Setter TargetName="WindowResizeGrip"
Property="Visibility"
Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

The title bar contains
An icon
A title
A minimize button
A maximize button
A close button
And that's all. You can't add anything.
In VS, the title bar is masked. The "title bar" you see is in fact the content of the window. That is, as you seemed to suspect, the only way.
That's also why tooltips on the three right buttons are in the OS language for most apps (because the title bar is managed by the system), but are in the app language for Visual Studio.
You have to set WindowStyle to None to mask the real title bar.
Then, inside your window, you should add a DockPanel and dock to the top an image and a menu on the left, and 3 buttons on the right.
The minimize button should change the WindowState to Minimized.
The maximize button should change the WindowState to either Normal or Maximized and its icon should be based on the WindowState.
The close button should call the Close() method and/or the Application.Current.Shutdown(0) method.
You should also subscribe to events like MouseLeftButtonDown, MouseLeftButtonUp and MouseMove to move the window.

Related

Customize nested control inside a BasedOn style

I'm approaching to WPF (in particular I'm working on MVVM) and I'm trying to creating a custom Window style that will be used throughout the application. What I want to do is create a base style that will define the window color, the border, icon, title, etc. The Window can be resizable or dialog like, so I used WindowChrome to set up the "resizable window" that has minimize, maximize and close buttons by default and that's, in fact, resizable. For the Login window I would like to have a window that uses the base style, but the user can't resize or maximize it, so the maximize button should not be visible at all. I've been working on BasedOn styles and I can override properties succesfully, but I can't manage to define which buttons can or can't be visible inside the window. So what I'm trying to do is changing a nested UI control (a StackPanel in this case).
Here is the base style I created, that contains, for now, all the window properties and the window buttons as well (I tried to comment it the best I could):
<ControlTemplate TargetType="{x:Type Window}" x:Key="DefaultWindowsTemplate">
<!-- The outer border of the Window -->
<Border Padding="{Binding OuterMarginSizeThickness, FallbackValue=10}">
<!-- The inner border of the Window and the Window itself, from the contour line to the shadow -->
<Grid>
<Border CornerRadius="{Binding WindowCornerRadius}"
BorderBrush="{StaticResource AlizarinBrush}"
BorderThickness="{Binding OutlineBorderThickness, FallbackValue=1}"
Background="{StaticResource VoidnessBrush}">
<Border.Effect>
<DropShadowEffect Color="{StaticResource Voidness}" ShadowDepth="0" Opacity="1"/>
</Border.Effect>
</Border>
<!-- The Container grid, composed by the title bar and the content area -->
<Grid>
<!-- Rows definition -->
<Grid.RowDefinitions>
<RowDefinition Height="{Binding TitleHeight, FallbackValue=30}"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- Title bar row that contains icon, title and window buttons -->
<Grid Margin="{Binding TitleHeightMargin}"
Background="{StaticResource VoidnessBrush}"
Grid.Row="0"
Panel.ZIndex="1"
>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- Window icon -->
<Button Margin="5 5 0 0"
Style="{StaticResource WindowIconButtonStyle}"
Command="{Binding MenuCommand}">
<Image Source="/Images/Logos/khm_logo_titlebar.png"/>
</Button>
<!-- Window title -->
<TextBlock Grid.Column="1"
Foreground="{StaticResource ConcreteBrush}"
Margin="15 5 0 0"
TextAlignment="Center"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title, FallbackValue='Window Title'}"/>
<!-- Window buttons - THIS IS THE CONTROL I WANT TO DEFINE INSIDE 'BASED ON' STYLES, WHERE I WILL NOT HAVE THE MAXIMIZE BUTTON -->
<StackPanel Grid.Column="2"
Orientation="Horizontal">
<Button Style="{StaticResource WindowButtonsStyle}"
Content="0"
Command="{Binding MinimizeCommand}"/>
<ToggleButton Style="{StaticResource MaximizeWindowButtonStyle}"
Command="{Binding MaximizeCommand}"/>
<Button Style="{StaticResource WindowCloseButtonStyle}"
Content="r"
Command="{Binding CloseCommand}"/>
</StackPanel>
</Grid>
<!-- The Window content -->
<Grid Margin="1 5 0 0" Grid.Row="1">
<ContentPresenter/>
</Grid>
</Grid>
</Grid>
</Border>
</ControlTemplate>
<Style TargetType="Window" x:Key="DefaultWindowsStyle">
<Setter Property="Template" Value="{StaticResource DefaultWindowsTemplate}"/>
<Setter Property="MinWidth" Value="{Binding WindowMinWidth}"/>
<Setter Property="MinHeight" Value="{Binding WindowMinHeight}"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="AllowsTransparency" Value="True"/>
</Style>
Then I start editing the base style as following (in another XAML file of course):
<Style TargetType="Window" x:Key="DialogWindowsStyle" BasedOn="{StaticResource DefaultWindowsStyle}">
<!-- REMOVE THE MAXIMIZE BUTTON INSIDE THE NESTED STACK PANEL -->
</Style>
So what is the right way to edit part of the UI while using the same style?
Thank you in advance for the help.
I would define boolean attached properties local:WindowExt.ShowMaximizeButton etc. with default values of true. In the ControlTemplate I'd apply those to the buttons with TemplateBindings, and set them via style setters (or directly on Window elements in XAML).
Here's an example of ShowMaximizeButton; the others are just the same thing with different names. When you copy and paste the dependency property definition, be careful you update the property name every place it appears. I use snippets to create those, to minimize careless errors.
public static class WindowExt
{
public static bool GetShowMaximizeButton(Window obj)
{
return (bool)obj.GetValue(ShowMaximizeButtonProperty);
}
public static void SetShowMaximizeButton(Window obj, bool value)
{
obj.SetValue(ShowMaximizeButtonProperty, value);
}
public static readonly DependencyProperty ShowMaximizeButtonProperty =
DependencyProperty.RegisterAttached("ShowMaximizeButton", typeof(bool), typeof(WindowExt),
new PropertyMetadata(true));
}
Make sure this is somewhere prior to where the window control template is defined, in the same resource dictionary:
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
The maximize button in the window control template. There won't be any change to the control template other than adding these appropriately bound visibility attributes to the buttons. Note the parens in the Binding's Path; those are critical because it's a multi-part identifier for a single property.
<ToggleButton
Style="{StaticResource MaximizeWindowButtonStyle}"
Command="{Binding MaximizeCommand}"
Visibility="{TemplateBinding local:WindowExt.ShowMaximizeButton, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
And usage in the window style:
<Style TargetType="Window">
<Setter Property="local:WindowExt.ShowMaximizeButton" Value="True" />
</Style>
Note that attached properties are dependency properties of the control itself, nothing to do with any DataContexts or viewmodels.

ToolBar ControlTemplates, Missing Overflow Items, and ToolBarTray Orientation

I'm writing a ControlTemplate for toolbars that utilizes a UniformGrid in order to provide multiple columns for toolbar items. So far, it seems that even with mostly default ControlTemplate markup (a la MSDN Documentation), Overflow into the ToolBarOverflowPanel no longer works. I have tried using both the default ToolBarOverflowPanel and a StackPanel, to no effect; the relevant template snippit can be found here.
How do I get Overflow to work properly?
Additionally, when placing a ToolBar control inside a ToolBarTray, how can I detect and set the "Thumb", "Content", and "Overflow Toggle Button" to their correct positions? The documentation for ToolBar ControlTemplates does not seem to cover this.
edit: Also something I'm curious about, how can I tie into the UniformGrid's Column property in my Main window xaml?
edit 2: Got the basic for dealing with ToolBarTray Orientation resolved, just needed to add ControlTemplate triggers for the related attached properties (Trigger Property="ToolBarTray.Orientation" Value="Vertical"). Also, who downvotes a decently written question, seriously?
<ToggleButton DockPanel.Dock="Right"
ClickMode="Press"
IsChecked="{Binding
IsOverflowOpen,
Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}
}"
IsEnabled="{TemplateBinding HasOverflowItems}"
>
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="Border"
Background="{StaticResource
{x:Static SystemColors.MenuBarBrushKey}
}"
CornerRadius="0,3,3,0"
SnapsToDevicePixels="true"
>
<Grid>
<Path x:Name="Arrow"
Data="M -0.5 3 L 5.5 3 L 2.5 6 Z"
Fill="{StaticResource {x:Static
SystemColors.ControlTextBrushKey
}}"
Margin="2,3"
VerticalAlignment="Bottom"
/>
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
</ToggleButton.Template>
<Popup x:Name="OverflowPopup"
AllowsTransparency="true"
Focusable="false"
IsOpen="{Binding
IsOverflowOpen,
RelativeSource={RelativeSource TemplatedParent}
}"
Placement="Bottom"
PopupAnimation="Slide"
StaysOpen="false"
>
<Border x:Name="DropDownBorder"
Background="{StaticResource
{x:Static SystemColors.MenuBarBrushKey}
}"
BorderBrush="{StaticResource
{x:Static SystemColors.ControlDarkBrushKey}
}"
BorderThickness="1"
>
<StackPanel x:Name="PART_ToolBarOverflowPanel"
Focusable="true"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
Margin="2"
/>
<!--<ToolBarOverflowPanel
x:Name="PART_ToolBarOverflowPanel"
Focusable="true"
FocusVisualStyle="{x:Null}"
KeyboardNavigation.TabNavigation="Cycle"
KeyboardNavigation.DirectionalNavigation="Cycle"
Margin="2"
WrapWidth="200"
/>-->
</Border>
</Popup>
</ToggleButton>

Bind to child element of content template by name from outside

How can I get the binding from the outer ConnectingLine (a custom control that binds to FrameworkElements and connects them with a line) to the inner TextBlocks named "Top" and "Bottom" to work? Note that I want the whole FrameworkElements for position information.
<Grid>
<ConnectingLine From="{Binding ElementName=Button1.Top}" To="{Binding ElementName=Button2.Top}" />
<ConnectingLine From="{Binding ElementName=Button1.Bottom}" To="{Binding ElementName=Button2.Bottom}" />
<ToggleButton x:Name="Button1">
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<Rectangle x:Name="Top" />
<Rectangle x:Name="Bottom" />
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
<ToggleButton x:Name="Button2">
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<Rectangle x:Name="Top" />
<Rectangle x:Name="Bottom" />
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</Grid>
My goal is to be able to bind from within XAML. Ideally with no extra fluff, but a solution involving a custom binding operator or attached properties might be acceptable.
Edit:
How I'd like to have the output:
Each distinct colored column is one of the templated ToggleButtons, already with one dashed ConnectingLine between Top and Bottom elements. The horizontal filled lines are what I'm interested in. Currently I'm achieving what I want from code-behind.
<ToggleButton x:Name="Button">
<ToggleButton.Template>
<ControlTemplate>
<Grid>
<CheckBox x:Name="FindMe" IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsChecked}"/>
</Grid>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
Let me know if it works.

Adding different elements based on button clicked

I have two different panels like this
.
When i click on the first button, it should display some text, second button should display a datagrid and so on.
How do i change the elements of the right hand panel to achieve this. Initially i just used different windows. Is there a way to call them into the panel ? Then I though of hiding elements based on the button clicked, but that would look like a mess. Should i code the elements when a button is clicked otherwise ? How is this functionality usually added ? What concepts do i need to learn to implement this ?
After coming across this question and seeing the absolutely poor answers that you had been provided with, I felt obliged to offer you a decent answer. There are many different ways of achieving your requirements. Here is probably, the simplest method:
Declare one Grid in MainWindow that displays your three Buttons on the left hand side and the three possible controls on the right hand side:
<Grid>
<Grid.Resources>
<Style x:Key="ButtonStyle" TargetType="{x:Type ToggleButton}">
<Setter Property="Width" Value="150" />
<Setter Property="Height" Value="40" />
<Setter Property="Content" Value="Button" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<ToggleButton Grid.Row="0" Name="Button1" Style="{StaticResource ButtonStyle}" />
<ToggleButton Grid.Row="1" Name="Button2" Style="{StaticResource ButtonStyle}" />
<ToggleButton Grid.Row="2" Name="Button3" Style="{StaticResource ButtonStyle}" />
<TextBlock Grid.Column="1" Grid.Row="0" Name="TextBlock" Text="Here is some text"
HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding
IsChecked, ElementName=Button1, Converter={StaticResource
BooleanToVisibilityConverter}}" />
<Rectangle Grid.Column="1" Grid.Row="1" Name="Rectangle" Width="150" Height="40"
Fill="LightGreen" Stroke="Black" Visibility="{Binding IsChecked, ElementName=
Button2, Converter={StaticResource BooleanToVisibilityConverter}}" />
<RadioButton Grid.Column="1" Grid.Row="2" Name="RadioButton" Content="Here is an
option" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{
Binding IsChecked, ElementName=Button3, Converter={StaticResource
BooleanToVisibilityConverter}}" />
</Grid>
Note that I used ToggleButtons here simply because they have a bool IsChecked property that is set when they are clicked on and can be used to show and hide the controls on the right hand side. To do this, we use a BooleanToVisibilityConverter (which was introduced into the .NET Framework in .NET 4.5) to convert our bool ToggleButton.IsChecked property values into Visibility values.
Now this example probably wasn't exactly what you were thinking of, but I'm sure that it will give you enough of an example for you to experiment with and come up with your desired UI... remember that you are going to have to do some work too.
Create your Xaml page with all the views Textboxt, DataGrid and TextBox2. then you just need to play with the Visibility of controls

Trying to WPF Tab header, need to make 3 adjustments

I want to alter the styling of some WPF tab headers. I would like to keep all the original styling of the tab headers, except for these three things -
Increase the height of the headers
Make the heights of each header the same. Normally the selected tab has a bigger height, I need the heights of both selected and unselected tabs to be the same.
Add a picture above the text on each header
Here is a before and after image of what I am looking to do -
Anyone know how to do this?
There you go, you can replace Stack panel with your nice images.
Update 1- in order to remove sizing effect when seelcting a tab you'll need to alter the TabItem style (header template is too light for it). Just get a StyleSnooper (http://blog.wpfwonderland.com/2007/01/02/wpf-tools-stylesnooper/) open it with VS2010 recompile it for .NET4, launch, navigate to TabItem and search for:
<Setter Property="FrameworkElement.Margin">
<Setter.Value>
<Thickness>
2,2,2,2</Thickness>
</Setter.Value>
</Setter>
<Setter Property="FrameworkElement.Margin" TargetName="Content">
<Setter.Value>
<Thickness>
2,2,2,2</Thickness>
</Setter.Value>
margins are the values you want to change to fix your 2. Then just put the modified version into the resources, so the app can pick it up. The style contains a lot of handy stuff you can tweak.
<Window x:Class="Immutables.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">
<Grid>
<TabControl TabStripPlacement="Left" x:Name="AreasTabControl" Margin="1">
<TabItem x:Name="AttributesTab">
<TabItem.HeaderTemplate>
<DataTemplate>
<Grid Width="100" Height="40">
<Border BorderThickness="1" BorderBrush="Gray" HorizontalAlignment="Left" VerticalAlignment="Top">
<StackPanel Orientation="Horizontal">
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="White" />
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="Blue" />
<Rectangle VerticalAlignment="Top"
Width="5" Height="5" Fill="Red" />
</StackPanel>
</Border>
<TextBlock Margin="0,20,0,0">Go Russia!</TextBlock>
</Grid>
</DataTemplate>
</TabItem.HeaderTemplate>
</TabItem>
</TabControl>
</Grid>
</Window>

Categories