繁体   English   中英

WPF:设置通用基本控件的样式

[英]WPF: Styling a Generic Base Control

是否可以在WPF中为通用基本控件提供默认样式?

假设我有以下基类:

public abstract class View<T> : ContentControl
    where T : ViewModel
{
    static View()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(View<T>), 
            new FrameworkPropertyMetadata(typeof(View<T>)));
    }

    // Other properties, methods, etc in here
}

public abstract class ViewModel
{
    // Other properties, methods, etc in here
}

假设我有两个继承自这些基类的类:

public partial class TestView : View<TestViewModel>
{
    public TestView()
    {
        InitializeComponent();
    }

    // TestView specific methods, properties, etc
}

public class TestViewModel : ViewModel
{ /* TestViewModel specific methods, properties, etc */ }

现在我想为我的派生控件使用的基本控件提供一个默认样式:

<Style TargetType="{x:Type local:View`1}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:View`1}">
                <Border Background="Magenta"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <StackPanel>
                        <Button>Test</Button>
                        <ContentPresenter ContentSource="Content" />
                    </StackPanel>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

但是,当我使用我的TestView控件时,我没有应用模板标记(因此我可能在我的TestView控件的XAML中定义的任何内容都不在视觉/逻辑树中)。

我基本上试图采用我的基本视图/ viewmodel类并应用一致的外观。 这当然适用于非通用基本视图案例。 但是,我需要在视图和视图模型之间进行类型安全连接,这样我就可以从视图中调用视图模型上的方法(我知道这些方法可能与某些人已经实现MVVM的方式“坐得不好”)。

我找到了一个涉及自定义TypeExtension相当简单的解决方案。

1 - 将DefaultStyleKey设置为CodeNaked的答案中提到的默认泛型类型:

    DefaultStyleKeyProperty.OverrideMetadata(typeof(View<T>), 
        new FrameworkPropertyMetadata(typeof(View<>)));

2 - 创建以下类,而不是继承自System.Windows.Markup.TypeExtension


    [System.Windows.Markup.ContentProperty("Type")]
    public class TypeExtension : System.Windows.Markup.TypeExtension
    {
        public TypeExtension()
            : base()
        { }

        public TypeExtension(Type type)
        : base(type)
        { }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            if (Type == null)
                throw new InvalidOperationException("Must specify the Type");

            return Type;
        }
    }

3 - 更新样式的TargetType以指向新的local:Type扩展名而不是通常的x:Type扩展名


    <Style>
        <Style.TargetType>
            <local:Type Type="{x:Type local:View`1}" />
        </Style.TargetType>
        <Setter Property="Control.Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Control}">

        . . .

多数民众赞成。但有一点需要注意,当您尝试绑定/设置View <T>类中定义的任何依赖项属性时,VS会抛出编译错误。 所以你不能使用{TemplateBinding ViewTProperty}类的简单语法......

简答:不

答案很长:

在您的代码中,您指定了typeof(View<T>)的DefaultStyleKey,其中T被解析为实际类型。 在XAML中,您实际上正在执行typeof(Value<>) ,其中T仍然是“未定义”。

您可以将DefaultStyleKey设置为:

DefaultStyleKeyProperty.OverrideMetadata(typeof(View<T>), 
        new FrameworkPropertyMetadata(typeof(View<>)));

这将正确地找到Style,但会导致异常(因为TestView不能用于View<> )。

你最好的选择是像你一样定义你的基本风格,但给它一个x:Key,如“ViewBaseStyle”。 然后为每个派生类型创建一个Style,它是BasedOn ViewBaseStyle。

我这样做的方式是我创建了一个没有泛型的基类,并模仿它。 然后我继承了一个泛型类(不是模板化)的基类,它可以用来制作你的类变体(也没有模板化)。 实际上,继承基类(没有泛型)的所有内容都将具有相同的模板。

例如,

//You can define a template for this!
public class ViewBase : UserControl
{
    public ViewBase() : base()
    {
        DefaultStyleKey = typeof(ViewBase);
    }
}

//Use for class variations
public class View<T> : ViewBase
{
    public View() : base()
    {
        //Do whatever
    }
}

//Example class variation
public class DecimalView : View<decimal>
{
    public DecimalView() : base()
    {
        //Do whatever
    }
}

ViewBaseView<T>DecimalView现在都共享相同的默认样式。 此外,您还可以根据原始样式( ViewBase )为每个类变体指定单独的样式,而不是针对泛型类。

值得注意的是,绑定到顶级类属性的最佳方法是使用语法{Binding Path, RelativeSource={RelativeSource AncestorType={x:Type ViewBase}}}{TemplateBinding Path} 后者以及{Binding Path, RelativeSource={RelativeSource TemplatedParent}}仅适用于ViewBase拥有的ViewBase

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM