У меня есть базовая библиотека, которая определяет пользовательский элемент управления
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*/}
}
Базовая библиотека прекрасно компилируется, как и производные классы. Но когда я пытаюсь создать экземпляр производных элементов управления, я получаю эту ошибку в InitializeComponent() в конструкторе UC:
System.Exception: 'The component 'NewControl' does not have a resource identified by the URI '/BaseLib;component/UC.xaml'.'
Могу ли я что-то исправить с URI пакета где-нибудь, чтобы указать на baselib xaml? Я не знаю, куда бы я его положил. Я просмотрел похожие вопросы, но они не совсем соответствуют моим намерениям. Я не пытаюсь наследовать элементы WPF и изменять их внешний вид или макет.
Что в файле UC.xaml
? Абстрактный базовый класс не должен иметь частичный класс XAML.
Поэтому я сделал аналогичную структуру проекта для тестирования. Это прекрасно работает. Строка 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>
Базовый класс.
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
, вы должны следовать нескольким правилам.
partial class
(иметь файл XAML).InitializeComponent()
.partial class
, имеющим определение класса XAML.Если вы хотите повторно использовать фактическое представление (определение XAML), вы должны определить его как ресурс, например. как ControlTemplate
.
Рекомендуемый подход — расширить ContentControl
вместо UserControl
(см. второй пример ниже). Этот подход не имеет ограничений по сравнению с расширением UserControl
. Это очень просто, особенно если вы хотите расширить функции, не переопределяя внешний вид по умолчанию.
UserControl
в качестве суперкласса (не рекомендуется)Базеусерконтрол.cs
Абстрактный базовый класс для использования в библиотеках.
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}" />
ContentControl
или Control
в качестве суперкласса (рекомендуется)Если вы хотите явно определить представление как часть базового класса, дайте экстенту базового класса ContentControl
(или Control
) и определите значение по умолчанию Style
в ресурсах Универсальный.xaml (файл Универсальный.xaml находится в папке Themes библиотеки управления ).
Таким образом, все производные классы будут автоматически наследовать представление по умолчанию (если только они явно не переопределяют представление по умолчанию 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();
}
Универсальный.xaml
Файл Универсальный.xaml находится в папке Темы библиотеки управления.
<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()
{}
}
Мне это не нравится, но я полагаю, что нарушал правила :) Я приму этот ответ. Мои изменения пользовательского интерфейса настолько нулевые, что я, вероятно, просто передам реализацию, которую хочу, в качестве аргумента конструктора класса.
Есть много способов расширить функциональность класса. Состав один. Просто обратите внимание, что механизму XAML требуется конструктор по умолчанию на случай, если элемент управления будет добавлен в XAML (например, через DataTemplate). Использование внедрения конструктора для элементов управления пользовательского интерфейса может быть проблематичным, особенно если элементы управления являются частью библиотеки. Создание прикрепленного поведения также может предоставить мощную альтернативу наследованию или композиции.
Это то, что вы имеете в виду? wpf-controls.com/behaviors-and-attached-properties
Да. Это прикрепленное свойство, которое обычно реализует функцию (поведение) на основе элемента, к которому оно прикреплено. Вы можете наблюдать за элементом, обрабатывая его события. Если это полезное решение, конечно, зависит от того, какую функцию вы хотите расширить.
Хорошо, я просмотрел немного документации, и это определенно что-то новое и интересное для изучения. Спасибо.
Я думаю, вы хотите вызвать класс напрямую, используя его тег
<namespace:NewControl></namespace:NewControl>
, который является пользовательским элементом управления.