开发者

Replicating standard system menu with WindowChrome?

开发者 https://www.devze.com 2023-03-08 05:18 出处:网络
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 req

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;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消