'SomeBaseControl' cannot be the root of a XAML file because it was defined using XAML
Now, what can we do to avoid this unpleasant message?
Variant 1,
Use a custom control for the base class (defined only with code, not XAML)
This is handy sometimes. What you do is create the base control as a custom control, using just the code
public class TestBaseCtrl : Control
{
// Add some base logic here...
}
You can add your controls from the code and create the base logic. Then you can inherit that control even in XAML, like this.
This is a working solution, but it's not very convenient. You don't have the flexibility of XAML in the base control. So let's take a look at the other solution.
Variant 2,
Create the base control using XAML
OK, but how on earth do we do that? We all saw the error message - we cannot define a base control in XAML and then inherit it again in XAML, according to the compiler. However, there is an easy way to trick the compiler to allow us to do that.
The main idea is to create a base control as a custom control again, and then define a control template in XAML. It's basically the same as defining the base control with XAML. Here is what I mean. This is the code-behind for the control:
public class TestBaseCtrl : UserControl
{
public static RoutedCommand TestButton1 = new RoutedCommand();
public static RoutedCommand TestButton2 = new RoutedCommand();
static TestBaseCtrl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TestBaseCtrl), new FrameworkPropertyMetadata(typeof(TestBaseCtrl)));
}
public TestBaseCtrl()
{
this.CommandBindings.Add(new CommandBinding(TestButton1, TestButton1_Executed));
this.CommandBindings.Add(new CommandBinding(TestButton2, TestButton2_Executed));
}
private void TestButton1_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("This is button 1 from the base control");
}
private void TestButton2_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("This is button 2 from the base control");
}
}
And this is the control template, defined in XAML:
You can see that we have specified an area of the base control, in which the content of the child controls would be displayed, using ContentPresenter.
Now, let's inherit that control:
Now, the WPF compiler is totally OK with it, and even displays the inherited control in the designer:
And there you go - now you have a base control and an inherited control which both use the power of XAML. Everything goes well in the compilation and the compiler is no longer mad at us:
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
Download sample application
The sample can be downloaded here: Download sample
This works great in C#...but the implementation doesn't work in VB.Net. :( Seems in VB when you specify to inherit from the base control, it doesn't update it in the generated file. So you end up with two partial classes inheriting from two different objects. :(
ReplyDeleteAfter looking at this again...it does work in VB. I forgot to change the base control to inherit from UserControl instead of Control. After doing that, the derived control was able to inherit from the base. Sorry for the confusion.
ReplyDeleteHow on Earth did you get this to work with VB? It seems to be easy using C#, but when I try doing this with VB, in my xaml (InheritedUserControl) the compiler does not find the "TestBaseCtrl" in the namespace "UserControlInheritance". :-(
ReplyDeleteHi Svetoslav,
ReplyDeleteThe control template, if its defined inside Generic.xaml then its working fine. But when I want to define the control template in another class library its not working. Can you please look into that..
If you define the control template in another XAML file, you'll have to include it explicitly somewhere, for example add a Merged Dictionary in generic.xaml.
ReplyDeleteHi Svetoslav,
ReplyDeleteThanks for adding nice article about control inheritance in WPF. Can you explain how to access base control (if protected) in designer?
Hello,
ReplyDeleteYou should have access to all properties of the base control in the designer of the inherited control, simply by using something like
...
You cannot directly access elements defined in the generic template though. If you want to do that, you should create some kind of wrapper for them in the code-behind of the base control and use it instead.
Can't seem to make this work in VB.Net. This doesn't work in VB: "<local:TestBaseCtrl"
ReplyDeleteThe design time error is: "Error 39 The type 'Local:TestBaseCtrl' was not found. Verify that you are not missing an assembly reference and that all referenced assemblies have been built."
How do I download this example.
ReplyDeleteThe link does not seem to be working.
The link is working, you'll just have to wait a few seconds (a limitation of the free access to the file hosting provider). But if you still can't download the example, contact me by e-mail.
ReplyDelete1. The link is working, but the content of the downloaded zip seems to be wrong somehow...
ReplyDelete2. Svetoslav, your work is excellent as far it is shown here. But I have the promblem, that I can't use the "InheritedUserControl". I mean I can drop it on a window (included in a grid), compile it without any error but no control is shown on my window. Why is that?
Hello Michael,
ReplyDeleteCan you contact me by e-mail? I will send you the zip file so you can use it as a starting point or as an example.
I understand the trick and example you provided but what is real purpose of doing that...
ReplyDeleteIf needed i can also put the controls itself that i will be showing in contentpresenter directly in template itself so what will be real use.
I want to develop wpf application and i want to display module's icon on main window and i want to move that icon set with left mouse click, do u have any idea how to develop this application.
ReplyDeleteI tried it for myself, but I can't get it to work.
ReplyDeleteCan you look at http://stackoverflow.com/questions/21728012/customcontrol-with-templateparts and help?
Thanks Its Nice .
ReplyDeleteThanks a million!
ReplyDeleteSaved my posterior in a project of mine :)
How can you bind properties to controls in your base class?
ReplyDelete