【问题标题】:Understanding WPF deriving WIndow class了解 WPF 派生窗口类
【发布时间】:2016-04-04 08:54:16
【问题描述】:

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

public class MyClass : DerivedFromClass
{}

但是,在 WPF 中遇到了困难,这就是问题所在。我想构建自己的一组控件,用作新学习项目的基线……预设我自己的样式、颜色、背景和其他功能。没问题。首先从 WPF 窗口开始并创建“MyWindow”。

现在,我想采用这个基线“MyWindow”并将其子类化为另一类 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

【问题讨论】:

  • 您的 XAML(不是 xaml.cs)解密看起来如何?

标签: c# wpf inheritance


【解决方案1】:

您的基类应该只是一个类文件(而不是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>

【讨论】:

  • 是的......那就是......多么明显(不)......此外,我的 BASE 类声明必须是所有代码而不是通过 xaml 的澄清是我在做WinForms开发的时候就做过了,所以这部分很容易预料。谢谢
  • 不要忘记在 AppStartup 事件中添加FrameworkElement.StyleProperty.OverrideMetadata(typeof(Window), new FrameworkPropertyMetadata(TryFindResource(typeof(Window))));。否则你的派生类将不会使用任何默认的窗口样式
  • 来自另一个类似问题的不错的博客文章链接 - Creating a Base Window Class in WPF
【解决方案2】:

拥有一个基类 Window 会带来一个严重的缺点,即绑定到基类中的属性要困难得多(并且当前接受的答案不能解决这个问题)。如果不能引用基本属性,继承有什么意义?经过几个小时后,我想出了如何设置它,并希望与大家分享,希望其他人能够免于这种痛苦。

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

您无法通过 XAML 设置此继承窗口的 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 代码中不需要任何东西(甚至不需要构造函数)。

【讨论】:

  • 根据本主题,Visual Studio 正在创建 WPF 应用程序的错误结构。如果有,为什么?
猜你喜欢
  • 2016-10-24
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 2021-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-18
相关资源
最近更新 更多