12

我正在查看图书馆中的WindowChrome课程System.Windows.Shell(v 3.5.41019.1)。当我尝试创建Window模板时,模板中元素的边距Border似乎没有效果:

<Window x:Class="WpfApplication7.MainWindow"
        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"
        Title="MainWindow" Height="350" Width="525" Style="{DynamicResource WindowStyle1}">
    <Window.Resources>
        <Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
<!-- Here is the WindowChrome.-->
            <Setter Property="shell:WindowChrome.WindowChrome">
                <Setter.Value>
                    <shell:WindowChrome />
                </Setter.Value>
            </Setter>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type Window}">
<!-- And here is the Border. Its margin has no effect as far as I can tell.-->
                        <Border Margin="25" Background="Red">
                            <AdornerDecorator>
                                <ContentPresenter/>
                            </AdornerDecorator>
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>
    <Grid>

    </Grid>
</Window>

你认为这是什么原因?我想知道,因为我看到有些人使用*:

<Border x:Name="WindowBorder" Margin="{Binding Source={x:Static shell:SystemParameters2.Current}, Path=WindowNonClientFrameThickness}" Background="Red">

但由于它对我的测试没有任何影响,这样做有什么意义呢?

(*) 它使用的地方之一是CodePlex上的ModernUI项目。

编辑:我已经在带有 Aero 的 Windows 7 上对此进行了测试。

编辑 2: Aero 关闭时仍然如此。

4

3 回答 3

9

根据 MSDN,WindowChrome

表示一个对象,该对象描述对窗口非客户区的自定义。

在阅读 MSDN 示例并播放您的代码一段时间后,我注意到您的代码应该类似于 MSDN 示例代码中的以下代码:

<Style x:Key="StandardStyle" 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}">
            <!--Note there is a Grid as the root-->
            <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="36,8,0,0"/>
                <Image 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"/>
            </Grid>
        </ControlTemplate>
    </Setter.Value>
</Setter>

请注意,有一个 Grid 作为根元素,其中包含一些用于自定义窗口 NC 的元素。

更新:

您可能会在 MSDN 页面的备注中注意到,它包含以下部分:

WindowStyle.None

WindowChrome

这是自定义 WPF 应用程序窗口外观的两种方法。

但是,将Window.WindowStyle属性设置为WindowStyle.None

这会从窗口中删除非客户框架,只留下客户区域,您可以对其应用自定义样式。但是,当非客户端框架被移除时,您也会失去它提供的系统功能和行为,例如标题按钮和窗口大小调整。另一个副作用是窗口最大化时会覆盖 Windows 任务栏。

然后WindowChrome介绍使用WPF启用NC自定义:

要在保留其标准功能的同时自定义窗口,您可以使用 WindowChrome 类。WindowChrome 类将窗口框架的功能与视觉效果分开,并允许您控制应用程序窗口的客户端和非客户端区域之间的边界。WindowChrome 类允许您通过扩展客户区以覆盖非客户区来将 WPF 内容放入窗口框架中。同时通过两个隐形区域保留系统行为;调整边框和标题区域。

所以回到你的问题,你找到的模板应该是从 MSDN 示例代码中复制的,但错过了真正的 root Grid。边界上的边距是为了给 NC 一些空间。在 MSDN 示例代码中,ContenPreseter仅包含客户端区域,而 NC 包含Border、一个TextBlock用于窗口标题和一个Image用于窗口图标。

回顾一下,设置WindowChrome使您能够在Window.Template.

注意: 示例 MSDN 示例代码在 .Net 4.5 中似乎有点过时,现在位于 .NetSystem.Windows.Shell.WindowChromePresentationFramework.dll,因此代码可能如下所示:

<Window x:Class="WpfApplication1.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" Style="{DynamicResource WindowStyle1}" Icon="Icon1.ico">
<Window.Resources>
    <Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
        <Setter Property="WindowChrome.WindowChrome">
            <Setter.Value>
                <WindowChrome />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Grid>
                        <Border Background="Red"
                        Margin="{Binding Source={x:Static SystemParameters.WindowNonClientFrameThickness}}">
                            <ContentPresenter Content="{TemplateBinding Content}" />
                        </Border>
                        <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}" 
                           VerticalAlignment="Top" HorizontalAlignment="Left" 
                           Margin="36,8,0,0"/>
                        <Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Icon}"
                       VerticalAlignment="Top" HorizontalAlignment="Left"
                       Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowChrome.WindowChrome.ResizeBorderThickness}" 
                       Width="{Binding Source={x:Static SystemParameters.SmallIconWidth}}"
                       WindowChrome.IsHitTestVisibleInChrome="True"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <Button />
</Grid>

于 2013-05-26T05:24:42.157 回答
1

我认为您尝试设置边界的方式存在一些误解。这是msdn中提供的 WindowChrome 类的解释

WindowChrome 类将窗口框架的功能与视觉效果分开,并允许您控制应用程序窗口的客户端和非客户端区域之间的边界。WindowChrome 类允许您通过扩展客户区以覆盖非客户区来将 WPF 内容放入窗口框架中。同时通过两个隐形区域保留系统行为;调整边框和标题区域。

因此,如果您尝试自定义窗口的非客户端区域,则它不是您应该设置边框的内容演示器。那是客户区。相反,您可以在模板中添加除 Content Presenter 之外的 XAML 来定义您的非客户区。我刚刚根据您的代码尝试了一个简单的代码,它将窗口的Title 属性向右移动了 100 的值。这是代码。

<Window x:Class="WPF_ToggleButton.ShellWindow"
    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"
    Title="MainWindow" Height="350" Width="525" Style="{DynamicResource WindowStyle1}" 
    >

<Window.Resources>
    <Style x:Key="WindowStyle1" TargetType="{x:Type Window}">
        <Setter Property="shell:WindowChrome.WindowChrome">
            <Setter.Value>
                <shell:WindowChrome />
            </Setter.Value>
        </Setter>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Window}">
                    <Grid>
                        <Border Background="Yellow">
                            <AdornerDecorator>
                                <ContentPresenter Content="{TemplateBinding Content}"/>
                            </AdornerDecorator>
                        </Border>
                        <TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}" 
                           VerticalAlignment="Top" HorizontalAlignment="Left" 
                           Margin="100,0,0,0"/>
                    </Grid>                        
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid>
    <Border Margin="50" Background="AliceBlue"/>
</Grid>

因此,您可以使用 XAML 代码在非客户端区域中拥有任何元素,例如表示您的窗口关闭按钮的图像等。Window 中的最后一个元素定义了客户区,该客户区被传递给模板中的 Content Presenter

简而言之,如果您不想自定义客户区,请使用 Content Presenter,而如果您有兴趣更改非客户区,如标题栏显示,请关闭图像图标,然后在模板中定义它。

一个简短的观察。我认为 Margin 对于 Window 没有任何意义。尝试将其设置为普通窗口,我认为它不会尊重它。

于 2013-05-19T15:20:06.477 回答
0

遇到同样的问题。无论我如何尝试在 MainWindow 的 ControlTemplate 中设置根元素的 Margin(其类型无关紧要),启动应用程序时它始终为 0。答案(感谢 ILSpy)在 WindowChrome 实现中。

当 WindowChrome 附加到 Window 时,它会应用 Chrome 功能,除此之外,它会执行 WindowChromWorker._FixupTemplateIssues 方法,该方法简单地将窗口第一个视觉元素的 Margin 重置为新的 Thickness 实例,从而通过样式擦除您的自定义设置, 模板等

解决方法可以是:

  • 将您的模板嵌入到一个新的、无外观的根元素中,例如 Grid,这样您的原始根的 Margin 就不会被覆盖
  • 调整根的填充
于 2021-07-15T10:52:34.737 回答