了解WPF派生的WIndow类

时间:2011-08-24 10:56:23

标签: c# wpf inheritance

我确信这很容易,但对于使用C#的WPF来说我是新手。我知道从类中继承并且已经完成了很多次,例如在C#WinForms项目中......

public class MyClass : DerivedFromClass
{}

然而,难倒在WPF中,这就是问题所在。我想构建我自己的一组控件,用作新学习项目的基线...预设我自己的样式,颜色,背景和其他功能。没问题。首先使用WPF窗口启动并创建“MyWindow”。

现在,我想将此基线“MyWindow”和子类THAT用于另一类MySubClassedWindow。因此,我创建了一个新的Window类,默认情况下,VS2010构建表单的设计器和代码部分。我在MySubClassedWindow上查看代码并找到

partial class MySubclassedWindow : Window
{}

在使用WinForms的C#中,我只想改为(并且我已经包含了包含“MyWindow”声明的类库引用。

partial class MySubclassedWindow : MyWindow
{}

当我这样做时,我收到编译错误

Partial declarations of 'MyNameSpace.MySubclassedWindow' must not specify different base classes

2 个答案:

答案 0 :(得分:34)

您的基类应该只是一个类文件(不是Window)。

所以创建WindowBase.cs

public class WindowBase : Window
{
    // ...
}

在MainWindow中(例如)将xaml.cs文件更改为从WindowBase继承

public partial class MainWindow : WindowBase
{
    public MainWindow()
    {
        InitializeComponent();
    }
    // ...
}

在MainWindow.xaml中,包含WindowBase的命名空间,并将Window更改为base:WindowBase,如下所示

<base:WindowBase x:Class="SubclassWindow.MainWindow"
                  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                  xmlns:base="clr-namespace:NamespaceForWindowBase"
                  Title="MainWindow" Height="350" Width="525">
    <!--...-->
</base:WindowBase>

答案 1 :(得分:3)

拥有一个基类Window类会带来一个严重的缺点,即绑定到基类中的属性要难得多(并且当前接受的答案无法解决此问题)。如果你不能引用基本属性,那么继承的重点是什么?我已经想出了如何在经过一段长时间的努力之后解决这个问题,并希望分享其他人将免于这种痛苦的希望。

您可能需要使用值转换器之类的东西,这些东西只能通过静态绑定来引用,在我看来,它在WindowBase类中是有意义的。我已经包含了一个例子,因为我发现很难在设计和运行模式下一致地使用这些转换器。

您不能通过XAML设置此继承Window的x:Name属性,但如果使用以下方法,则可能不需要这样做。我已经包含了一个如何设置名称的示例,因为继承自Window将不允许您在设计时在子类中设置名称。我不建议在设计时依赖窗口名称,但设置d:DataContext应该为您处理任何绑定需求。

请注意,在设计模式而非运行模式下,WindowBase(或d:DataContext中指定的类)的副本将在设计模式下实例化,并用作绑定上下文。因此,在非常具体的情况下,您可能会看到数据差异,但在绝大多数用例中,这种方法应该足够了。

WindowBase.cs

````

public class WindowBase : Window
{
    //User-Defined UI Configuration class containing System.Drawing.Color 
    //and Brush properties (platform-agnostic styling in your Project.Core.dll assembly)
    public UIStyle UIStyle => Core.UIStyle.Current;

    //IValueConverter that converts System.Drawing.Color properties 
    //into WPF-equivalent Colors and Brushes 
    //You can skip this if you do not need or did not implement your own ValueConverter
    public static IValueConverter UniversalValueConverter { get; } = new UniversalValueConverter();

    public WindowBase()
    {
        //Add window name to scope so that runtime properties can be referenced from XAML
        //(Name setting must be done here and not in xaml because this is a base class)
        //You probably won't need to, but working example is here in case you do.
        var ns = new NameScope();
        NameScope.SetNameScope(this, ns);
        ns["window"] = this;

        //Call Initialize Component via Reflection, so you do not need 
        //to call InitializeComponent() every time in your base class
        this.GetType()
            .GetMethod("InitializeComponent", 
                System.Reflection.BindingFlags.Public | 
                System.Reflection.BindingFlags.NonPublic | 
                System.Reflection.BindingFlags.Instance)
            .Invoke(this, null);

        //Set runtime DataContext - Designer mode will not run this code
        this.DataContext = this;
    }

    //Stub method here so that the above code can find it via reflection
    void InitializeComponent() { }
}  

SubClassWindow.xaml

<local:WindowBase
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:YourProjectNamespace"
        x:Class="YourProjectNamespace.SubClassWindow"
        mc:Ignorable="d"
        d:DataContext="{d:DesignInstance Type= {x:Type local:WindowBase}, IsDesignTimeCreatable=True}"
        Title="SubClassWindow" Height="100" Width="300">
    <!--Design-time DataContext is set in d:DataContext. That option does not affect runtime data binding
        Replace local:WindowBase with local:SubClassWindow if you need to access properties in SubClassWindow-->
    <Grid Background="{Binding UIStyle.BackgroundColor, Converter={x:Static local:WindowBase.UniversalValueConverter}}"></Grid>
</local:WindowBase>

后面的SubClassWindow代码(甚至不是构造函数)都不需要。