我确信这很容易,但对于使用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
答案 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代码(甚至不是构造函数)都不需要。