【问题标题】:Border Margin in Window template doesn't have any effect when used with WindowChrome与 WindowChrome 一起使用时,Window 模板中的 Border Margin 没有任何效果
【发布时间】:2013-05-12 15:50:57
【问题描述】:

我正在查看 System.Windows.Shell 库 (v 3.5.41019.1) 中的 WindowChrome 类。当我尝试创建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 时还是一样。

【问题讨论】:

    标签: c# wpf xaml windows-shell window-chrome


    【解决方案1】:

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

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

    解决方法可以是:

    • 将您的模板嵌入到一个新的、无外观的根元素中,例如网格,因此您的原始根的边距不会被覆盖
    • 调整根的填充

    【讨论】:

      【解决方案2】:

      根据 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 示例代码中复制的,但错过了真正的根 Grid。 边界上的边距是为了给 NC 一些空间。 在 MSDN 示例代码中,ContenPreseter 仅包含客户端区域,而 NC 包含BorderTextBlock 用于窗口标题,Image 用于窗口图标。

      回顾一下,设置WindowChrome 可以让您在Window.Template 中自定义窗口的NC 区域。

      注意: 示例 MSDN 示例代码在 .Net 4.5 中似乎有点过时,System.Windows.Shell.WindowChrome 现在在 PresentationFramework.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>
      

      【讨论】:

      • 我相信你是对的。他们从 MSDN 复制了代码,但不知怎的就一直这样。
      【解决方案3】:

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

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

      因此,如果您尝试自定义窗口的非客户端区域,则它不是您应该设置 Border 的 Content Presenter。那是客户区。相反,您可以在模板中添加除 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 没有任何意义。尝试将其设置为普通窗口,我认为它不会尊重它。

      【讨论】:

      • 首先,对迟到的评论表示歉意。最近几天我真的很忙。我知道如何使用WindowChrome。问题是,当您使用WindowChrome 并定义模板时,ControlTemplate 中第一个Layout 元素的Margin 属性没有任何效果。您的代码具有完全相同的内容。尝试将Margin 放入您的GridControlTemplate 中的第一个),它没有任何效果。如果你去掉WindowChrome,你可以看到Margin在普通Window上用黑色填充。
      • 问题是,我正在查看ModernUI,看到他们在WindowControlTemplate 的第一个Layout 元素周围放置了一个Margin(然后我在其他一些地方也看到了同样的事情,我猜他们都是从同一个地方复制粘贴的。)但这对我来说没有任何改变。我想知道,因为也许有一些我不知道的东西,也许它适用于 Windows XP 或 Windows 8。我只是想澄清一下。
      • 我不太理解您的评论,对此我深表歉意。您不想在控件模板中为网格设置边距。该边距将相对于哪个元素?我的意思是,例如,哪个元素的边距为 100?
      • 保证金到Window 本身。只需尝试在没有WindowChrome 的情况下在Grid 中添加一个边距,您就会发现它的行为有所不同。但问题不在于我想给它留出余地。我刚刚看到,在一些项目中,他们为第一个布局元素设置了边距,我想也许有一个问题。这就是我想知道的。
      猜你喜欢
      • 2021-03-29
      • 2011-12-29
      • 2019-03-02
      • 2011-07-22
      • 1970-01-01
      • 1970-01-01
      • 2020-11-20
      • 2017-06-01
      • 2021-01-30
      相关资源
      最近更新 更多