【问题标题】:Customize nested control inside a BasedOn style在 BasedOn 样式中自定义嵌套控件
【发布时间】:2023-03-30 15:38:01
【问题描述】:

我正在接近 WPF(特别是我正在研究 MVVM)并且我正在尝试创建一个将在整个应用程序中使用的自定义窗口样式。我想要做的是创建一个基本样式,它将定义窗口颜色、边框、图标、标题等。窗口可以调整大小或类似对话框,所以我使用 WindowChrome 设置具有最小化的“可调整大小的窗口” ,默认情况下最大化和关闭按钮,实际上是可调整大小的。对于登录窗口,我希望有一个使用基本样式的窗口,但用户无法调整大小或最大化它,因此根本不应该看到最大化按钮。我一直在研究 BasedOn 样式,我可以成功地覆盖属性,但我无法定义哪些按钮可以或不可以在窗口内可见。所以我要做的是更改嵌套的 UI 控件(在本例中为 StackPanel)。

这是我创建的基本样式,目前它包含所有窗口属性和窗口按钮(我尽量评论它):

<ControlTemplate TargetType="{x:Type Window}" x:Key="DefaultWindowsTemplate">

    <!-- The outer border of the Window -->
    <Border Padding="{Binding OuterMarginSizeThickness, FallbackValue=10}">

        <!-- The inner border of the Window and the Window itself, from the contour line to the shadow -->
        <Grid>
            <Border CornerRadius="{Binding WindowCornerRadius}" 
                    BorderBrush="{StaticResource AlizarinBrush}" 
                    BorderThickness="{Binding OutlineBorderThickness, FallbackValue=1}"
                    Background="{StaticResource VoidnessBrush}">
                <Border.Effect>
                    <DropShadowEffect Color="{StaticResource Voidness}" ShadowDepth="0" Opacity="1"/>
                </Border.Effect>
            </Border>

            <!-- The Container grid, composed by the title bar and the content area -->
            <Grid>

                <!-- Rows definition -->
                <Grid.RowDefinitions>
                    <RowDefinition Height="{Binding TitleHeight, FallbackValue=30}"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>

                <!-- Title bar row that contains icon, title and window buttons -->
                <Grid Margin="{Binding TitleHeightMargin}" 
                      Background="{StaticResource VoidnessBrush}" 
                      Grid.Row="0" 
                      Panel.ZIndex="1"
                      >

                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>

                    </Grid.ColumnDefinitions>

                    <!-- Window icon -->
                    <Button Margin="5 5 0 0" 
                            Style="{StaticResource WindowIconButtonStyle}" 
                            Command="{Binding MenuCommand}">

                        <Image Source="/Images/Logos/khm_logo_titlebar.png"/>

                    </Button>

                    <!-- Window title -->
                    <TextBlock Grid.Column="1"
                               Foreground="{StaticResource ConcreteBrush}"
                               Margin="15 5 0 0"
                               TextAlignment="Center"
                               VerticalAlignment="Center"
                               HorizontalAlignment="Left"
                               Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title, FallbackValue='Window Title'}"/>

                    <!-- Window buttons - THIS IS THE CONTROL I WANT TO DEFINE INSIDE 'BASED ON' STYLES, WHERE I WILL NOT HAVE THE MAXIMIZE BUTTON -->
                    <StackPanel Grid.Column="2"
                                Orientation="Horizontal">

                        <Button Style="{StaticResource WindowButtonsStyle}"
                                Content="0"
                                Command="{Binding MinimizeCommand}"/>

                        <ToggleButton Style="{StaticResource MaximizeWindowButtonStyle}"
                                      Command="{Binding MaximizeCommand}"/>

                        <Button Style="{StaticResource WindowCloseButtonStyle}"
                                Content="r"
                                Command="{Binding CloseCommand}"/>

                    </StackPanel>

                </Grid>

                <!-- The Window content -->
                <Grid Margin="1 5 0 0" Grid.Row="1">
                    <ContentPresenter/>
                </Grid>

            </Grid>
        </Grid>
    </Border>

</ControlTemplate>

<Style TargetType="Window" x:Key="DefaultWindowsStyle">
    <Setter Property="Template" Value="{StaticResource DefaultWindowsTemplate}"/>
    <Setter Property="MinWidth" Value="{Binding WindowMinWidth}"/>
    <Setter Property="MinHeight" Value="{Binding WindowMinHeight}"/>
    <Setter Property="WindowStyle" Value="None"/>
    <Setter Property="AllowsTransparency" Value="True"/>
</Style>

然后我开始编辑基本样式如下(当然是在另一个 XAML 文件中):

<Style TargetType="Window" x:Key="DialogWindowsStyle" BasedOn="{StaticResource DefaultWindowsStyle}">
    <!-- REMOVE THE MAXIMIZE BUTTON INSIDE THE NESTED STACK PANEL -->
</Style>

那么在使用相同样式的同时编辑部分 UI 的正确方法是什么?

提前感谢您的帮助。

【问题讨论】:

  • 我将定义布尔附加属性local:MyWindow.ShowMaximizeButton 等,默认值为true,然后在模板中使用TemplateBinding,并根据需要将它们应用到样式中。如果需要可以提供示例,它只是一堆样板代码。
  • @EdPlunkett 不幸的是,附加属性的概念对我来说是相当新的,所以非常感谢一个例子。无论如何,这是我可以开始四处寻找的东西,所以谢谢!

标签: c# wpf xaml user-interface styles


【解决方案1】:

我将定义布尔附加属性local:WindowExt.ShowMaximizeButton 等,默认值为true。在 ControlTemplate 中,我会将它们应用到带有 TemplateBindings 的按钮,并通过样式设置器(或直接在 XAML 中的 Window 元素上)设置它们。

这是ShowMaximizeButton 的示例;其他的只是相同的东西,但名称不同。复制和粘贴依赖项属性定义时,请注意在属性名称出现的每个位置都更新它。我使用 sn-ps 来创建这些,以尽量减少粗心的错误。

public static class WindowExt
{
    public static bool GetShowMaximizeButton(Window obj)
    {
        return (bool)obj.GetValue(ShowMaximizeButtonProperty);
    }

    public static void SetShowMaximizeButton(Window obj, bool value)
    {
        obj.SetValue(ShowMaximizeButtonProperty, value);
    }

    public static readonly DependencyProperty ShowMaximizeButtonProperty =
        DependencyProperty.RegisterAttached("ShowMaximizeButton", typeof(bool), typeof(WindowExt),
            new PropertyMetadata(true));
}

确保这是在同一个资源字典中定义窗口控件模板之前的某个位置:

    <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />

窗口控件模板中的最大化按钮。除了将这些适当绑定的可见性属性添加到按钮之外,控件模板不会有任何更改。注意绑定路径中的括号;这些很关键,因为它是单个属性的多部分标识符。

<ToggleButton 
    Style="{StaticResource MaximizeWindowButtonStyle}"
    Command="{Binding MaximizeCommand}"
    Visibility="{TemplateBinding local:WindowExt.ShowMaximizeButton, Converter={StaticResource BooleanToVisibilityConverter}}"
    />

以及在窗口样式中的用法:

<Style TargetType="Window">
    <Setter Property="local:WindowExt.ShowMaximizeButton" Value="True" />
</Style>

请注意,附加属性是控件本身的依赖属性,与任何 DataContexts 或视图模型无关。

【讨论】:

  • 我对那些 AttachedProperties 做了一个快速的研究,老实说我不知道​​我怎么会错过它们......这就像一个魅力,这正是我正在寻找的解决方案的提示(实际上我在样式继承上浪费时间)。非常感谢!
  • @Belfed 很高兴为您提供帮助
猜你喜欢
  • 2011-09-09
  • 2019-04-11
  • 2011-03-17
  • 2010-09-09
  • 2023-04-04
  • 1970-01-01
  • 1970-01-01
  • 2015-06-12
  • 1970-01-01
相关资源
最近更新 更多