使用WindowChrome复制标准系统菜单?

时间:2011-05-25 22:24:02

标签: c# .net wpf window-chrome

使用WindowChromedownloadable here)来自定义窗口的非客户区域时,一个自然的起点是使标题栏的外观和行为与标准标题栏相同。这需要添加“假”应用程序图标和标题栏,因为显然WindowChrome禁用了这些功能(最小化,最大化和关闭按钮仍然有用。)

这是我到目前为止所拥有的:

<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>

代码背后:

private void SystemMenuIcon_MouseDown(object sender, MouseButtonEventArgs e)
{
    var offs = SystemParameters2.Current.WindowNonClientFrameThickness;
    SystemCommands.ShowSystemMenu(this, new Point(Left + offs.Left, Top + offs.Top));
}

这非常接近于工作。第一个问题是,单击应用程序图标并出现系统菜单后,如果再次单击,菜单应该会消失 - 而菜单只会重绘。此外,如果您双击,则窗口应该关闭,但图像没有双击事件。您如何建议添加这些功能?

2 个答案:

答案 0 :(得分:3)

禁用标准Chrome按钮的工作只需在您的XAML代码CaptionHeight="0"

中添加额外的属性shell:WindowsChrome

所以它就像那样

<Setter Property="shell:WindowChrome.WindowChrome">
    <Setter.Value>
        <shell:WindowChrome CaptionHeight="0" />
    </Setter.Value>
</Setter>

制作假冒Chrome。将模板修改为:

<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>

同时执行命令绑定以正确使用假冒Chrome吧

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

答案 1 :(得分:1)

我的xaml不完全相同(我不使用WindowChrome但我自己也有标题栏模板),但我遇到了完全相同的问题,解决方案对你也有用。

首先是简单的方法:双击工作只需使用ClickCount。

然后,确保菜单消失需要保持一些状态,告诉它当前是否处于活动状态:诀窍是第二次点击时会触发不同的事件(如使用Snoop所示。)第一次点击是只有一个MousweDown,第二个是MouseDown,然后是MouseUp(我的猜测是第一次点击的上升是由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;
}