仅在一个自定义窗口中自动继承控件样式

时间:2016-02-05 18:55:31

标签: wpf xaml mvvm wpf-controls

我想知道是否可以将某些控件的样式与WPF中的自定义窗口相关联。

这是场景 - 我创建了一个自定义窗口,并为我将在此窗口中使用的许多控件定义了样式。它们包含在便携式类库中。

问题是我希望控件在自定义窗口中使用时使用我的库中的样式(应用程序中有几个不同的窗口)。

我知道我可以为这些样式分配一个键,然后使用包语法在我的应用程序的app.xaml中从我的可移植库中加载它们,例如:

 <Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/Custom.Application.Library.Controls;component/Styles/CheckBox.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

然后在我的自定义窗口中添加和设置控件样式:

<CheckBox x:Name="checkBox" Style="{StaticResource SpecialCheckBox}" 

但我真正想做的是在没有键的情况下在我的类库中定义它们的样式,如下所示:

<Style TargetType="{x:Type CheckBox}">

而不是:

<Style x:Key="SpecialCheckBox" TargetType="{x:Type CheckBox}">

因此,当我的自定义窗口中使用此复选框时,它会自动继承该样式。如果我定义这样的样式,并将其加载到我的app.xaml中,问题显然是所有复选框都将继承此样式,而不仅仅是我的自定义窗口中使用的复选框。

所以,我想要找出的是,是否有任何方法可以明确地将样式资源与自定义窗口相关联,这样我就可以在没有键的情况下定义样式,并且默认情况下让它们继承“特殊” “在我的自定义窗口中使用时的样式,但在应用程序的任何其他窗口中使用WPF默认值。有没有人有这方面的经验?

为清楚起见,这是我的自定义窗口的代码:

XAML:     

<!-- Window style -->
<Style TargetType="{x:Type Controls:CCTApplicationWindow}">
    <Setter Property="WindowStyle" Value="None"/>
    <Setter Property="AllowsTransparency" Value="True"/>
    <Setter Property="ResizeMode" Value="CanResizeWithGrip"/>
    <Setter Property="MinWidth" Value="500"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Controls:CCTApplicationWindow}">
                <Border BorderBrush="#FF999999">
                    <Border.Style>
                        <Style TargetType="{x:Type Border}">
                            <Setter Property="BorderThickness" Value="1"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=WindowState}" Value="Maximized">
                                    <Setter Property="BorderThickness" Value="7"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Border.Style>
                    <Grid>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="29"/>
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <Controls:CCTApplicationHeader Grid.Row="0" 
                                                           Margin="0" 
                                                           Title="{TemplateBinding Title}" 
                                                           DragMoveCommand="{TemplateBinding DragMoveCommand}" 
                                                           MaximizeCommand="{TemplateBinding MaximizeCommand}" 
                                                           MinimizeCommand="{TemplateBinding MinimizeCommand}" 
                                                           CloseCommand="{TemplateBinding CloseCommand}"/>
                            <Grid Background="White" Grid.Row="1" Margin="0">
                                <AdornerDecorator>
                                    <ContentPresenter/>
                                </AdornerDecorator>
                            </Grid>
                        </Grid>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

CS:

public partial class CCTApplicationWindow : Window
{
    public static readonly DependencyProperty MaximizeCommandProperty = DependencyProperty.Register("MaximizeCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
    public static readonly DependencyProperty MinimizeCommandProperty = DependencyProperty.Register("MinimizeCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
    public static readonly DependencyProperty CloseCommandProperty = DependencyProperty.Register("CloseCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));
    public static readonly DependencyProperty DragMoveCommandProperty = DependencyProperty.Register("DragMoveCommand", typeof(DelegateCommand), typeof(CCTApplicationWindow));

    public CCTApplicationWindow()
    {
        MaximizeCommand = new DelegateCommand(MaximizeExecute);
        MinimizeCommand = new DelegateCommand(MinimizeExecute);
        CloseCommand = new DelegateCommand(CloseExecute);
        DragMoveCommand = new DelegateCommand(DragMoveExecute);
    }

    static CCTApplicationWindow()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(CCTApplicationWindow), new FrameworkPropertyMetadata(typeof(CCTApplicationWindow)));
    }

    public DelegateCommand MaximizeCommand
    {
        get
        {
            return (DelegateCommand)GetValue(MaximizeCommandProperty);
        }
        set
        {
            SetValue(MaximizeCommandProperty, value);
        }
    }

    public DelegateCommand MinimizeCommand
    {
        get
        {
            return (DelegateCommand)GetValue(MinimizeCommandProperty);
        }
        set
        {
            SetValue(MinimizeCommandProperty, value);
        }
    }

    public DelegateCommand CloseCommand
    {
        get
        {
            return (DelegateCommand)GetValue(CloseCommandProperty);
        }
        set
        {
            SetValue(CloseCommandProperty, value);
        }
    }

    public DelegateCommand DragMoveCommand
    {
        get
        {
            return (DelegateCommand)GetValue(DragMoveCommandProperty);
        }
        set
        {
            SetValue(DragMoveCommandProperty, value);
        }
    }

    private void MaximizeExecute(object obj)
    {
        if (this.WindowState != WindowState.Maximized)
        {
            this.WindowState = WindowState.Maximized;
        }
        else
        {
            SystemCommands.RestoreWindow(this);
        }
    }

    private void MinimizeExecute(object obj)
    {
        SystemCommands.MinimizeWindow(this);
    }

    private void CloseExecute(object obj)
    {
        SystemCommands.CloseWindow(this);
    }

    private void DragMoveExecute(object obj)
    {
        DragMove();
    }
}

2 个答案:

答案 0 :(得分:0)

是的,你可以这样做,但你不应该这样做!您已将此问题标记为MVVM,但您的架构设计完全打破了MVVM。 MVVM的重点在于视图逻辑包含在视图模型层中;您的视图模型应该跟踪逻辑层次结构,它们应该是向视图公开属性以控制其外观。换句话说,仅仅因为XAML足够灵活并且足够强大以实现这样的逻辑并不意味着它是实际执行它的最佳位置!

要回答您的问题,是的,可以使用DataTrigger通过ObjectToTypeConverter绑定到父级来完成。这是将TextBlock背景设置为CornflowerBlue的示例,除非它的直接父级是Grid,在这种情况下它应该设置为PaleGoldenrod:

<StackPanel Orientation="Vertical">

    <StackPanel.Resources>
        <converters:ObjectToTypeConverter x:Key="ObjectToTypeConverter" />
        <Style TargetType="{x:Type TextBlock}">
            <Setter Property="Background" Value="CornflowerBlue" />
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=Parent, RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource ObjectToTypeConverter}}" Value="{x:Type Grid}">
                    <Setter Property="Background" Value="PaleGoldenrod" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </StackPanel.Resources>

    <Grid Width="100" Height="32" HorizontalAlignment="Left">
        <TextBlock Text="TextBox A" /> <!-- Gets a PaleGoldenrod background -->
    </Grid>
    <Canvas Width="100" Height="32" HorizontalAlignment="Left">
        <TextBlock Text="TextBox B" /> <!-- Gets a CornflowerBlue background -->
    </Canvas>

</StackPanel>

这是转换器代码。值得指出的是,如果您很乐意只是检查某个类型的父级是否存在于层次结构中的某个位置(而不是直接的父级),那么您甚至不需要这样做,您可以尝试绑定到RelativeSource,并将AncestorType设置为相关的父类型。

// based on http://stackoverflow.com/questions/8244658/binding-to-the-object-gettype
[ValueConversion(typeof(object), typeof(Type))]
public class ObjectToTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value == null ? null : value.GetType();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new InvalidOperationException();
    }
}

但是再一次,我恳求你,如果你真的想坚持MVVM,那么这样做吧!这正是&#34;适当的&#34; MVVM旨在解决。

答案 1 :(得分:0)

最简单的方法是为ResourceDictionary窗口创建单独的Custom。并使用XAML或使用Code加载它。