【问题标题】:Replicating standard system menu with WindowChrome?使用 WindowChrome 复制标准系统菜单?
【发布时间】:2011-05-25 22:24:02
【问题描述】:

当使用WindowChrome (downloadable 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));
}

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

【问题讨论】:

    标签: c# .net wpf window-chrome


    【解决方案1】:

    要禁用标准 Chrome 按钮,只需在 shell:WindowsChrome 的 XAML 代码中添加一个额外的属性 CaptionHeight="0"

    原来是这样的

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

    同时为 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);
    }
    

    【讨论】:

    【解决方案2】:

    我的 xaml 并不完全相同(我不使用 WindowChrome,而是我自己的,并且我有一个标题栏模板),但我遇到了完全相同的问题,该解决方案也应该对您有用。

    首先是简单的:要使双击起作用,只需使用 ClickCount。

    然后,让菜单消失需要保持一些状态来判断它当前是否处于活动状态:诀窍是在第二次单击时会触发不同的事件(如使用 Snoop 计算出来的那样。第一次单击只是MousweDown,第二个是 MouseDown 后跟 MouseUp(我猜是第一次点击的 up 是由 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多