如何为控件创建基础 class
How to create a base class for controls
我有一个定义用户控件的基础库
BaseLib.dll:
UC.xaml.cs
<UserControl x:Class="BaseLib.UC" ...
UC.xaml.cs
public abstract partial class UC : UserControl, IFoo
{
public UC()
{
InitializeComponent();
}
protected abstract Foo();
}
然后,不同的库可以继承控件并重载方法。它们只实现了抽象方法,对底层什么都不做XAML
Lib2.dll
public class NewControl : UC
{
protected override Foo(){ /* do stuff*/}
}
基础库编译得很好,派生的 类 也是如此。但是,当我尝试创建派生控件的实例时,我在 UC 构造函数中的 InitializeComponent() 上收到此错误:
System.Exception: 'The component 'NewControl' does not have a resource identified by the URI '/BaseLib;component/UC.xaml'.'
我可以在某处使用 pack URI 指向 baselib xaml 来解决这个问题吗?我不知道我会把它放在哪里。我看过类似的问题,但它们并不完全符合我的意图。我没有尝试继承 WPF 元素并更改它们的外观或布局
所以我做了一个类似的项目结构来测试。这很好用。 xmlns:local="clr-namespace:PoopToTest"
行将引用您的 BaseLib.dll 命名空间。
<Window
x:Class="PoopToTest.MainWindow"
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:local="clr-namespace:PoopToTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<local:ActualControl>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="Hello" />
</local:ActualControl>
</Grid>
</Window>
基地class.
namespace PoopToTest
{
public abstract class BaseControl : UserControl
{
public BaseControl()
{
//InitializeComponent() is not in UserControl. Needed a place to break.
int x = int.MaxValue;
}
protected abstract void Foo();
}
}
实施。
namespace PoopToTest
{
public class ActualControl : BaseControl
{
/// <inheritdoc />
protected override void Foo()
{
//Do Stuff.
}
}
}
要创建扩展 UserControl
的基础 class,您必须遵循一些规则。
- 基本类型不能是
partial class
(有 XAML 文件)。
- 由于基 class 不允许有部分 XAML class 定义,因此基 class 不能调用
InitializeComponent()
.
- 扩展基 class 的派生非抽象类型必须是具有 XAML class 定义的
partial class
。
- 此派生控件的 XAML 根元素必须是基础 class。
如果您想重用实际视图(XAML 定义),您必须将其定义为资源,例如作为 ControlTemplate
.
推荐的方法是扩展 ContentControl
而不是 UserControl
(参见下面的第二个示例)。与扩展 UserControl
相比,这种方法没有任何限制。它非常简单,特别是如果您想扩展功能而不覆盖默认外观。
解决方案 1:使用 UserControl
作为超级 class 创建基础 class(不推荐)
BaseUserControl.cs
将在库中使用的抽象基础 class。
public abstract class BaseUserControl : UserControl, IFoo
{
protected abstract void Foo();
}
App.xaml
BaseUserControlTemplate
定义。
<Application.Resources>
<ControlTemplate x:Key="BaseUserControlTemplate"
TargetType="BaseUserControl">
<TextBlock Text="Reusable view from template" />
</ControlTemplate>
</Application.Resources>
Library1UserControl.xaml.cs
BaseUserControl
.
的自定义库实现
public partial class Library1UserControl : BaseUserControl
{
public Library1UserControl()
{
InitializeComponent();
}
protected override void Foo()
{}
}
Library1UserControl.xaml.cs
重用预定义的 Library1UserControl
的视图定义(例如,在 App.xaml 中)BaseControlTemplate
.
<BaseControl x:Class="Library1UserControl"
Template="{StaticResource BaseUserControlTemplate}" />
解决方案 2:使用 ContentControl
或 Control
创建基础 class 作为超级class(推荐)
如果您想将视图明确定义为基础 class 的一部分,则让您的基础 class 扩展 ContentControl
(或 Control
)并定义Generic.xaml 资源中的默认 Style
(Generic.xaml 文件位于 Themes 文件夹中控件库)。
这样,所有派生的 classes 将自动继承默认视图(除非它们显式覆盖默认 Style
)。
扩展 ContentControl
或 Control
通常是推荐的方法,而不是使用 UserControl
.
您应该考虑扩展 ContentControl
而不是 UserControl
.
BaseControl.cs
public abstract class BaseControl : Control, IFoo
{
static BaseControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BaseControl), new FrameworkPropertyMetadata(typeof(BaseControl)));
}
protected abstract void Foo();
}
Generic.xaml
Generic.xaml 文件位于控件库的 Themes 文件夹中。
<ResourceDictionary>
<Style TargetType="BaseControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="BaseControl">
<TextBlock Text="Reusable view from template" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Library1Control.cs
派生类型自动继承基类型中定义的默认 Style
(和 ControlTemplate
)。
public class Library1Control : BaseControl
{
public Library1Control()
{}
protected override void Foo()
{}
}
我有一个定义用户控件的基础库
BaseLib.dll:
UC.xaml.cs
<UserControl x:Class="BaseLib.UC" ...
UC.xaml.cs
public abstract partial class UC : UserControl, IFoo
{
public UC()
{
InitializeComponent();
}
protected abstract Foo();
}
然后,不同的库可以继承控件并重载方法。它们只实现了抽象方法,对底层什么都不做XAML
Lib2.dll
public class NewControl : UC
{
protected override Foo(){ /* do stuff*/}
}
基础库编译得很好,派生的 类 也是如此。但是,当我尝试创建派生控件的实例时,我在 UC 构造函数中的 InitializeComponent() 上收到此错误:
System.Exception: 'The component 'NewControl' does not have a resource identified by the URI '/BaseLib;component/UC.xaml'.'
我可以在某处使用 pack URI 指向 baselib xaml 来解决这个问题吗?我不知道我会把它放在哪里。我看过类似的问题,但它们并不完全符合我的意图。我没有尝试继承 WPF 元素并更改它们的外观或布局
所以我做了一个类似的项目结构来测试。这很好用。 xmlns:local="clr-namespace:PoopToTest"
行将引用您的 BaseLib.dll 命名空间。
<Window
x:Class="PoopToTest.MainWindow"
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:local="clr-namespace:PoopToTest"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<local:ActualControl>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="50"
Text="Hello" />
</local:ActualControl>
</Grid>
</Window>
基地class.
namespace PoopToTest
{
public abstract class BaseControl : UserControl
{
public BaseControl()
{
//InitializeComponent() is not in UserControl. Needed a place to break.
int x = int.MaxValue;
}
protected abstract void Foo();
}
}
实施。
namespace PoopToTest
{
public class ActualControl : BaseControl
{
/// <inheritdoc />
protected override void Foo()
{
//Do Stuff.
}
}
}
要创建扩展 UserControl
的基础 class,您必须遵循一些规则。
- 基本类型不能是
partial class
(有 XAML 文件)。 - 由于基 class 不允许有部分 XAML class 定义,因此基 class 不能调用
InitializeComponent()
. - 扩展基 class 的派生非抽象类型必须是具有 XAML class 定义的
partial class
。 - 此派生控件的 XAML 根元素必须是基础 class。
如果您想重用实际视图(XAML 定义),您必须将其定义为资源,例如作为 ControlTemplate
.
推荐的方法是扩展 ContentControl
而不是 UserControl
(参见下面的第二个示例)。与扩展 UserControl
相比,这种方法没有任何限制。它非常简单,特别是如果您想扩展功能而不覆盖默认外观。
解决方案 1:使用 UserControl
作为超级 class 创建基础 class(不推荐)
BaseUserControl.cs
将在库中使用的抽象基础 class。
public abstract class BaseUserControl : UserControl, IFoo
{
protected abstract void Foo();
}
App.xaml
BaseUserControlTemplate
定义。
<Application.Resources>
<ControlTemplate x:Key="BaseUserControlTemplate"
TargetType="BaseUserControl">
<TextBlock Text="Reusable view from template" />
</ControlTemplate>
</Application.Resources>
Library1UserControl.xaml.cs
BaseUserControl
.
public partial class Library1UserControl : BaseUserControl
{
public Library1UserControl()
{
InitializeComponent();
}
protected override void Foo()
{}
}
Library1UserControl.xaml.cs
重用预定义的 Library1UserControl
的视图定义(例如,在 App.xaml 中)BaseControlTemplate
.
<BaseControl x:Class="Library1UserControl"
Template="{StaticResource BaseUserControlTemplate}" />
解决方案 2:使用 ContentControl
或 Control
创建基础 class 作为超级class(推荐)
如果您想将视图明确定义为基础 class 的一部分,则让您的基础 class 扩展 ContentControl
(或 Control
)并定义Generic.xaml 资源中的默认 Style
(Generic.xaml 文件位于 Themes 文件夹中控件库)。
这样,所有派生的 classes 将自动继承默认视图(除非它们显式覆盖默认 Style
)。
扩展 ContentControl
或 Control
通常是推荐的方法,而不是使用 UserControl
.
您应该考虑扩展 ContentControl
而不是 UserControl
.
BaseControl.cs
public abstract class BaseControl : Control, IFoo
{
static BaseControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(BaseControl), new FrameworkPropertyMetadata(typeof(BaseControl)));
}
protected abstract void Foo();
}
Generic.xaml
Generic.xaml 文件位于控件库的 Themes 文件夹中。
<ResourceDictionary>
<Style TargetType="BaseControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="BaseControl">
<TextBlock Text="Reusable view from template" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Library1Control.cs
派生类型自动继承基类型中定义的默认 Style
(和 ControlTemplate
)。
public class Library1Control : BaseControl
{
public Library1Control()
{}
protected override void Foo()
{}
}