When using WindowChrome (downloadable here) to customize the nonclient area of a window, a natural starting point is to make a title bar that looks and acts identical to a standard title bar. This requires adding a "fake" application icon and title bar, because apparently WindowChrome disables those features (the minimize, maximize and close buttons still work.)
Here's what I have so far:
<Window x:Class="MyApp.MainWindow"
x:Name="MainWindowItself"
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"
xmlns:local="clr-namespace:MyApp"
Title="My Application" Icon="App.ico" Height="350" Width="525">
<Window.Resources>
<Style 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}">
<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="32,8,0,0"/>
<Image x:Name="SystemMenuIcon" 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" MouseDown="SystemMenuIcon_MouseDown">
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TextBlock Text="Client area content goes here"/>
</Grid>
</Window>
Code behind:
private void SystemMenuIcon_MouseDown(object sender, MouseButtonEventArgs e)
{
var offs = SystemParameters2.Current.WindowNonClientFrameThickness;
SystemCommands.ShowSystemMenu(this, new Point(Left + offs.Left, Top + offs.Top));
}
This comes very close to working. The first problem is that after you click the application icon and the system 开发者_运维百科menu appears, the menu should disappear if you click a second time--instead, the menu just redraws. Also, if you double-click then the window should close, but Image doesn't have a double-click event. How would you suggest adding these features?
To disable working of standard Chrome Buttons Just add an extra attribute CaptionHeight="0"
in your XAML code of shell:WindowsChrome
So it will be like that
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome CaptionHeight="0" />
</Setter.Value>
</Setter>
To make a fake Chrome. Modify the Template to like this:
<ControlTemplate TargetType="Window">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="PART_Chrome" shell:WindowChrome.IsHitTestVisibleInChrome="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="105" />
</Grid.ColumnDefinitions>
<Image Source="Application Favicon Path" />
<TextBlock Grid.Column="1" Text="{TemplateBinding Title}" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" Grid.Column="3" >
<Button Command="{Binding Source={x:Static shell:SystemCommands.MinimizeWindowCommand}}" >
<Path Data="M0,6 L8,6 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2" />
</Button>
<Button x:Name="MaximizeButton" Command="{Binding Source={x:Static shell:SystemCommands.MaximizeWindowCommand}}" >
<Path Data="M0,1 L9,1 L9,8 L0,8 Z" Width="9" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2" />
</Button>
<Button x:Name="RestoreButton" Command="{Binding Source={x:Static shell:SystemCommands.RestoreWindowCommand}}" >
<Path Data="M2,0 L8,0 L8,6 M0,3 L6,3 M0,2 L6,2 L6,8 L0,8 Z" Width="8" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="1" />
</Button>
<Button Command="{Binding Source={x:Static shell:SystemCommands.CloseWindowCommand}}" >
<Path Data="M0,0 L8,7 M8,0 L0,7 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="1.5" />
</Button>
</StackPanel>
</Grid>
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</AdornerDecorator>
<ControlTemplate.Triggers>
<Trigger Property="WindowState" Value="Normal">
<Setter Property="Visibility" Value="Collapsed" TargetName="RestoreButton" />
<Setter Property="Visibility" Value="Visible" TargetName="MaximizeButton" />
</Trigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="Visibility" Value="Visible" TargetName="RestoreButton" />
<Setter Property="Visibility" Value="Collapsed" TargetName="MaximizeButton" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Also do command binding for proper working of Fake Chrome bar
public MainWindow()
{
this.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, OnCloseWindow));
this.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, OnMaximizeWindow, OnCanResizeWindow));
this.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, OnMinimizeWindow, OnCanMinimizeWindow));
this.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, OnRestoreWindow, OnCanResizeWindow));
}
private void OnCanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.ResizeMode != ResizeMode.NoResize;
}
private void OnCanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.ResizeMode == ResizeMode.CanResize || this.ResizeMode == ResizeMode.CanResizeWithGrip;
}
private void OnCloseWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.CloseWindow(this);
}
private void OnMaximizeWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.MaximizeWindow(this);
}
private void OnMinimizeWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.MinimizeWindow(this);
}
private void OnRestoreWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.RestoreWindow(this);
}
My xaml is not exactly alike (I do not use WindowChrome but my own and I have a titlebar template), but I had the exact same problem and the solution should be usable for you as well.
First the easy one: for the doubleclick to work just use the ClickCount.
Then, geting the menu disappear requires keeping some state telling whether it is currently active or not: the trick is that different events are fired on the second click (as figured out by using Snoop. The first click is only a MousweDown, the second is MouseDown followed by MouseUp (my guess is that the up from the first click is handled by the sysmenu).
private bool inSysMenu = false;
void SystemMenuIcon_MouseDown( object sender, MouseButtonEventArgs e )
{
if( e.ClickCount == 1 && !inSysMenu )
{
inSysMenu = true;
ShowSystemMenu(); //replace with your code
}
else if( e.ClickCount == 2 && e.ChangedButton == MouseButton.Left )
{
window.Close();
}
}
void SystemMenuIcon_MouseLeave( object sender, MouseButtonEventArgs e )
{
inSysMenu = false;
}
void SystemMenuIcon_MouseUp( object sender, MouseButtonEventArgs e )
{
inSysMenu = false;
}
精彩评论