如何为控件创建基础 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:使用 ContentControlControl 创建基础 class 作为超级class(推荐)

如果您想将视图明确定义为基础 class 的一部分,则让您的基础 class 扩展 ContentControl(或 Control)并定义Generic.xaml 资源中的默认 StyleGeneric.xaml 文件位于 Themes 文件夹中控件库)。
这样,所有派生的 classes 将自动继承默认视图(除非它们显式覆盖默认 Style)。
扩展 ContentControlControl 通常是推荐的方法,而不是使用 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()
  {}
}