Reputation: 199
I have a base library that defines a user control
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();
}
Then, different libraries can inherit from the control and overload the methods. They only implement abstract methods, and do nothing to the underlying XAML
Lib2.dll
public class NewControl : UC
{
protected override Foo(){ /* do stuff*/}
}
The base lib compiles just fine, and so do the derived classes. But, when I try to create an instance of the derived controls, I get this error on InitializeComponent() in the UC constructor:
System.Exception: 'The component 'NewControl' does not have a resource identified by the URI '/BaseLib;component/UC.xaml'.'
Is this something I could fix with pack URI somewhere to point to the baselib xaml? I don't know where I would put it. I've looked at similar questions, but they don't exactly fit my intention. I'm not trying to inherit WPF elements and change their appearance or layout
Upvotes: 0
Views: 989
Reputation: 28968
To create a base class that extends UserControl
, you must follow a few rules.
partial class
(have a XAML file).InitializeComponent()
.partial class
that has a XAML class definition.If you want to reuse the actual view (the XAML definition) you must define it as a resource e.g. as a ControlTemplate
.
The recommended approach is to extend ContentControl
instead of UserControl
(see second example below). This approach has no limitations compared to extending UserControl
. It is very straight forward especially if you want to extend features without overriding the default appearance.
UserControl
as superclass (not recommended)BaseUserControl.cs
The abstract base class to be used in libraries.
public abstract class BaseUserControl : UserControl, IFoo
{
protected abstract void Foo();
}
App.xaml
The BaseUserControlTemplate
definition.
<Application.Resources>
<ControlTemplate x:Key="BaseUserControlTemplate"
TargetType="BaseUserControl">
<TextBlock Text="Reusable view from template" />
</ControlTemplate>
</Application.Resources>
Library1UserControl.xaml.cs
A custom library implementation of BaseUserControl
.
public partial class Library1UserControl : BaseUserControl
{
public Library1UserControl()
{
InitializeComponent();
}
protected override void Foo()
{}
}
Library1UserControl.xaml.cs
The view definition of the Library1UserControl
which reuses the predefined (e.g., in App.xaml) BaseControlTemplate
.
<BaseControl x:Class="Library1UserControl"
Template="{StaticResource BaseUserControlTemplate}" />
ContentControl
or Control
as superclass (recommended)In case you want to explicitly define the view as part of the base class, then let your base class extent ContentControl
(or Control
) and define the default Style
in the Generic.xaml resources (the Generic.xaml file is located in the Themes folder of the control library).
This way, all derived classes will automatically inherit the default view (unless they explicitly override the default Style
).
Extending ContentControl
or Control
is generally the recommended approach over using UserControl
.
You should consider to extend ContentControl
instead of 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
The Generic.xaml file is located in the Themes folder of the control library.
<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
The derived type automatically inherits the default Style
(and ControlTemplate
) that was defined in the base type.
public class Library1Control : BaseControl
{
public Library1Control()
{}
protected override void Foo()
{}
}
Upvotes: 2
Reputation: 1675
So I did a similar project structure to test. This works fine. The line xmlns:local="clr-namespace:PoopToTest"
would reference your BaseLib.dll namespace.
<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>
The base 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();
}
}
Implementation.
namespace PoopToTest
{
public class ActualControl : BaseControl
{
/// <inheritdoc />
protected override void Foo()
{
//Do Stuff.
}
}
}
Upvotes: 0