XAML UserControl inheritance

33,549

Ok, let me split this into parts:

Coming from Java

Forget java. It's a really antiquated language which has not evolved since the 90's. C# is a million times better and WPF is the best UI framework up to date.

from what I've seen, java UI frameworks such as swing are conceptually similar to .Net's winforms, which has also been replaced by WPF.

WPF (and it's XAML-based brethren) are fundamentally different from any other frameworks around because of their enhanced capability for customization via Styles and Templates and support for DataBinding.

Because of this, a Significant Mindshift is required when starting on WPF.


I usually do some sort of base class which contains all the common objects for my GUI components and then I extend it.

In WPF, there's the Content Model, which removes the need for inheritance and other bloated unnecesary practices, by introducing the capability to put "anything inside anything".

For example, a Button can be defined like this:

<Button>
    <Button.Content>
        <StackPanel Orientation="Horizontal">
            <Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
            <TextBlock Text="Click Me"/>
        </StackPanel>
    <Button.Content>
 </Button>

which results in

A button with a red dot

There's no need to inherit from Button just to define it's content.

There is an additional advantage WPF provides and is really handy, the ContentProperty Attribute which defines what the content of the XAML tags <Button> </Button> represents. Button is derived from ContentControl, which is declared like this:

//Declaration of the System.Windows.Control.ContentControl class,
//inside the PresentationFramework.dll assembly
//...  
[ContentProperty("Content")]
public class ContentControl: Control //...
{
   //...
}

This means that the following XAML is functionally identical to the above:

<Button>
   <StackPanel Orientation="Horizontal">
       <Ellipse Fill="Red" Height="10" Width="10" Margin="2"/>
       <TextBlock Text="Click Me"/>
    </StackPanel>
</Button>
  • Notice we have removed the <Button.Content> tag, because the ContentProperty attribute takes care of that.

All this is made possible thanks to a feature called ControlTemplates, which define the Visual appearance of a Control, independently of it's behavior.


what I'd like to do is to have the DerivedClass into the BaseClass container.

There are several ways to achieve that, one of them is to leverage ControlTemplates and define a specific container inside the XAML that will host the content:

<UserControl x:Class="BaseClass">
    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">
            <DockPanel>
                <TextBlock DockPanel.Dock="Top" Text="I'm the Container"/>

                <!-- This is where the Hosted Content will be placed -->
                <ContentPresenter ContentSource="Content"/>
            </DockPanel>
        </ControlTemplate>
     </UserControl.Template>
</UserControl>

Then you could reuse this template like this:

<Window>
   <my:BaseClass>
       <Border Background="Gray" BorderBrush="Blue" BorderThickness="2"
               VerticalAlignment="Center" HorizontalAlignment="Center">
           <TextBlock Text="Im the Hosted Content" Foreground="AliceBlue"/>
       </Border>
   </my:BaseClass>
</Window>

which results in:

An application window

No need for inheritance or any procedural code stuff.


Another very important aspect when starting in WPF, which is extensively explained in the "Significant Mindshift" link above, is what I tell everyone here:

Learn MVVM before you ever write a single line of code in WPF

  • Most of the time you don't put any code in WPF UI elements, because most things can be achieved by DataBinding (covered in the "DataBinding" link above), or by implementing Reusable Attached Behaviors or Attached Properties. Only VIEW-Specific code should be placed in code behind, which does not deal with Data or Business Logic

  • The boilerplate you might be used to in other frameworks, such as:

    txtLastName.Text = person.LastName;
    txtFirstName.Text = person.FirstName;
    btnSubmit.IsEnabled = person.IsActive;
    

    and stuff like that, is completely unneeded in WPF, again, because of DataBinding.


Another concept which enables high flexibility when it comes to showing data in the UI is WPF's DataTemplates, which allow you to define a specific UI to be used when some Data Type is "rendered" on screen.


Because of all of the above, WPF is fundamentally different from most UI frameworks out there, and thus removes the need for all the horrible boilerplate and hacks which are common in other frameworks,

I suggest you read up on all the links provided and keep in mind all these concepts and practices when defining an application's structure and UI in general.

Let me know if you need further assistance.

Share:
33,549

Related videos on Youtube

StepTNT
Author by

StepTNT

(your about me is currently blank)

Updated on February 21, 2020

Comments

  • StepTNT
    StepTNT over 4 years

    Coming from Java, I'm really used to a common practice when it comes to make GUI components: I usually do some sort of base class which contains all the common objects for my GUI components and then I extend it.

    So, basically, this is what I'd like to achieve with C# and XAML.

    To make the question clear, here's an example (that is not working!) of what I'm doing:

    We've got a base class with its own XAML

    <UserControl x:Class="BaseClass"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        d:DesignHeight="480" d:DesignWidth="480">
    
        <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
            <Border BorderBrush="Aqua" BorderThickness="10" CornerRadius="10" x:Name="Border" HorizontalAlignment="Left" Height="480" VerticalAlignment="Top" Width="480"/>
    
        </Grid>
    </UserControl>
    

    and then we've got a class which extends the first one

    <base:BaseClass x:Class="DerivedClass"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:base="clr-namespace:BaseClass"
        mc:Ignorable="d"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        d:DesignHeight="60" d:DesignWidth="200">
    
        <Grid x:Name="LayoutRoot" Margin="0" Width="200" Height="60" MaxWidth="200" MaxHeight="60" Background="{StaticResource PhoneAccentBrush}">        
            <TextBlock x:Name="dummyText" HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Dummy Plugin" VerticalAlignment="Top" Height="40" Width="180" Foreground="White" TextAlignment="Center"/>
        </Grid>
    </base:BaseClass>
    

    Starting from the 2 XAML codes, what I'd like to do is to have the DerivedClass into the BaseClass container. This will allow me to share components between the various derived classes without having to write the code everytime that I need it.

    For example, if I want all my components to have that rounded border, I'd like to just put it in the bass class and then have it in all the derived ones without having to rewrite it.

    Of course, each c# class has its own InitializeComponent() method and this probably means that the derived component will build its own content by removing the base class' one.

    Removing the method from the DerivedClass constructor gives me the base content even in the derived class, but, of course, I lose everything I made in the XAML design window of the DerivedClass.

    Calling the base constructor from the DerivedClass has no effect, as it's called before the derived InitializeComponent().

    So the question is: how can I use the XAML design from a base class into the derived one without breaking the XAML design of the derived class? Is there any way to simply add content to the base class while still working with the designer itself?

    (I know that I can remove the XAML for the derived class and do what I want to do by code, but I want to know if I can do this just with the designer as I don't want to write my GUI when I have a designer available)

    EDIT:

    Following HighCore's reply, I did something that works on Windows Phone but I'm not sure that I'm doing the right thing (yeah, it works, but maybe is just wrong!).

    Here's what I did:

    BaseControl.xaml

    <UserControl x:Class="TestInheritance.BaseControl"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        d:DesignHeight="480" d:DesignWidth="480">
    
    
         <Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">        
            <TextBlock HorizontalAlignment="Center">BASE</TextBlock>        
            <ContentPresenter Name="Presenter" Content="{Binding PresenterContent}"/>
        </Grid>
    </UserControl>
    

    BaseControl.xaml.cs

    namespace TestInheritance
    {
        public partial class BaseControl : UserControl
        {
    
            public Grid PresenterContent { get; set; }        
    
            public BaseControl()
            {
                DataContext = this;
                InitializeComponent();            
            }
        }
    }
    

    DerivedControl.xaml

    <local:BaseControl x:Class="TestInheritance.DerivedControl"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestInheritance"
        mc:Ignorable="d"
        FontFamily="{StaticResource PhoneFontFamilyNormal}"
        FontSize="{StaticResource PhoneFontSizeNormal}"
        Foreground="{StaticResource PhoneForegroundBrush}"
        d:DesignHeight="480" d:DesignWidth="480">
    
        <local:BaseControl.PresenterContent>
            <Grid>
                <TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Center">DERIVED</TextBlock>
            </Grid>
        </local:BaseControl.PresenterContent>
    </local:BaseControl>
    

    Please note that the DerivedClass is an instance of BaseClass as I need them to have some common properties/methods for other reasons.

    What do you think about my solution? Does it make sense?

    • Federico Berasategui
      Federico Berasategui almost 11 years
      Yeah, basically this is a more "home-made" version of what I've described. I guess it's acceptable due to the differences in the implementation of XAML between WPF and Windows Phone.
    • GreenEyedAndy
      GreenEyedAndy over 10 years
      Hi @StepTNT If I type in your solution and try it for myself, I get an Error: 'WpfApplication18.BaseControl' cannot be the root of a XAML file because it was defined using XAML. Line 1 Position 20. What I'm doing wrong?
    • StepTNT
      StepTNT over 10 years
      You should open a new question and add some code, copy&paste doesn't always work :)
    • George Birbilis
      George Birbilis over 9 years
      on Silverlight (and Windows Phone that is) and probably on WinRT too (and thus Universal apps too) you can extend UserControls and they should work in the Visual Studio designer. From what I've seen this is not the case with WPF. The XAML designer for WPF in Visual Studio doesn't support this (yet).
    • George Birbilis
      George Birbilis over 9 years
      for WPF workaround with Visual inheritance see: svetoslavsavov.blogspot.gr/2009/09/… or for explicitly defining the GUI in the ancestor see support.microsoft.com/kb/957231
  • StepTNT
    StepTNT almost 11 years
    First of all thank you for the long reply! While I take my time to read and understand what you wrote, I've got a simple question: does everything work with Windows Phone too or there's something that's "desktop-only"?
  • Federico Berasategui
    Federico Berasategui almost 11 years
    @StepTNT there are slight differences in the implementation of XAML between WPF, Silverlight, WinRT and Windows Phone, but the higher level concepts (Styles, ControlTemplates, DataTemplates, Content Model, MVVM, DataBinding) all the same.
  • StepTNT
    StepTNT almost 11 years
    Finally I've had some time to check out your reply and see how things work. Working on WP, I've found some problems when working with your last example: Setting the Template property on a UserControl is not supported and there's no ContentSource property in the ContentPresenter class.
  • StepTNT
    StepTNT almost 11 years
    I've edited the question with my solution, please tell me what do you think about it!
  • makim
    makim almost 11 years
    +1 for this Sentence "Forget java. It's a really antiquated language which has not evolved since the 90's"
  • Matt
    Matt over 9 years
    @HighCore, "because of all of the above, WPF is fundamentally different from most UI frameworks out there, and thus removes the need for all the horrible boilerplate and hacks which are common in other frameworks". => That may apply in theory but even a cursory intro to WPF reveals the real world looks very different. Customer/Third party user controls with tons of events? Welcome to the event->command or event->behavior mess. Segregation of concerns? Nowhere to be seen. You still need to match string command parameters between view and view model. So far I see WPF appeals to certain people...
  • Matt
    Matt over 9 years
    ...in that it commands higher salaries/project costs because of the complex way a lot of things are achieved in WPF. And MVVM is more often than not making people go completely overboard in that it becomes a god rather than remain a loose set of guidelines. UI specific elements in the view code behind? Perfectly fine but often badly criticized by MVVM apostles. In the end you have tons of direct relationships between view and viewmodel and vm and model. Testability remains the only excuse to setup a convoluted code mess with MVVM even though there are many other ways to effective test.
  • Matt
    Matt over 9 years
    ...one of the most horrifying things imho is to have to setup a separate commanding infrastructure (aka maze) for each simple binding between a textblock/label and property in the viewmodel. Instead it would make tons more sense to offer binding technology that provides a loosely coupled class object instance to the view. Similarly to Winforms you can hook up multiple UI elements to properties of such class. The example you showed above about txtLastName, txtFirstName makes perfect sense vs binding each property in separate ways which is entire nonsense and introduces nothing but bloat.
  • Matt
    Matt over 9 years
    @HighCore, I never mentioned Java (it was you who ranted). And DataTemplates plus ViewModel Locators (or whatever you call it) just to have a usercontrol switched out for another one? Wow, I think anyone who is not on the WPF payroll would call that laughably complex. You end up having to add code at 4-5 different places in entirely different files just to add a simple usercontrol ((1) view file, (2) viewmodel file, (3) app.xaml to add to your data template, (4) adding in vm locator, (5) references to the viewmodellocator , (6) and a reference to the matching viewmodel in the view xaml code.).
  • Federico Berasategui
    Federico Berasategui over 9 years
    @MattWolf LOL I never talked about ViewModel locators. You're an ignorant (no offense). I'm talking about ItemsControls, which you seem to not know anything about, which are the most useful feature in WPF. Also, I have no idea what you're talking about "commading infrastructure" when it comes to labels or stuff. It seems like you're the one who produces convoluted over engineered designs, and then blame the platform. I've yet to see anything that comes even close to WPF (and DataBinding) in terms of developer productivity and customizability.
  • Matt
    Matt over 9 years
    Really? That is why 20+ different MVVM third-party (most open source) libraries exist that assist in implementing this mess because it is simple to implement and so much better than anything else in existence? I would suggest you present your expert knowledge in a more honest fashion. You just brushed aside some valid concerns and questions at stackoverflow.com/questions/27625181/… with criticm that was entriely unrelated to the topic under discussion. Your last statement must be a joke (I hope). Enough said.
  • Matt
    Matt over 9 years
    Nobody mentioned Winforms is superior to WPF. You for the second time imply things nobody claimed (Java, now Winforms). MS has pretty much abandoned Silverlight and they only cling on to WPF because they have nothing better to come up with. Alone the decision to create and require xaml was stupid by the admission of some of the core developers at MS. Hopefully some team will come up with a better idea to replace WPF soon. Btw, the net is littered with former UI gurus' stories who left in disgust for better pastures at iOS/HTML5 & Co. You can ignore those facts as much as you want.
  • Federico Berasategui
    Federico Berasategui over 9 years
  • Matt
    Matt over 9 years
    @HighCore, for your reference, another issue/bug not sure what it is nor does anyone else who attempted to help: stackoverflow.com/questions/27659698/… (mind taking a look?) Thanks
  • George Birbilis
    George Birbilis over 9 years
    Swing had pluggable Look&Feel system (L&F), it was not like WinForms. Btw, even WinForms could get skinning with third party libraries, as classic native Windows can anyway, see fine apps by StarDock and others that can even skin on the fly precompiled apps and Windows itself
  • Federico Berasategui
    Federico Berasategui over 9 years
    @GeorgeBirbilis Yes, I'm pretty sure that given enough time, anyone can build a pretty decent UI using butterflies too. However my point still stands that if you have superior tools at your disposal you should use them, and ditch the inferior ones.
  • George Birbilis
    George Birbilis over 9 years
    There are dozens of languages and tools out there and some are picked depending on the job, others are picked because of deployment requirements and others because of constraints imposed by clients or the company one works in. Sometimes it's just a matter of personal taste. I don't consider Java inferior, it just hasn't caught up with all the goodies in .NET, given its troubled "ownership" in the past.
  • AMissico
    AMissico over 8 years
    You guys talk too much. A tool is a tool. It doesn't make a difference how sharp or dull your scissors are. You still have to cut the paper. The professional will make a straight cut regardless. The amateur will complain their scissors are dull.
  • Federico Berasategui
    Federico Berasategui over 8 years
    @AMissico yes, that's an excuse I often hear from java developers, that languages are "just a tool", however that's a fallacy. A language is not "just a tool", a language conditions your thinking, and an inferior, inexpressive language like java leads to very convoluted workarounds and patterns whose sole purpose is to palliate the language's deficiencies. This is the reason why the java world is so utterly full of "patterns" everywhere, while modern languages don't need any of that. None of those patterns change the fact that java is an inferior language.
  • Federico Berasategui
    Federico Berasategui over 8 years
    @AMissico BTW, I have no idea who you refer to when you say "you guys".
  • Federico Berasategui
    Federico Berasategui over 8 years
    @AMissico "cheap talk" - yes, that's your opinion. Notice how your opinion doesn't change, nor disprove my statement that java is a tremendously inferior language. If you want to avoid "cheap talk", please show me with technical arguments how java is superior, or even comparable to modern languages like C#, let alone F#. Thanks.
  • AMissico
    AMissico over 8 years
    You need to relax. That is my point. Check out metrolyrics.com/elephant-talk-lyrics-king-crimson.html.
  • Federico Berasategui
    Federico Berasategui over 8 years
    @AMissico please notice that I'm pretty relaxed atm, your comment is off-topic, and none of that changes the fact that java is an inferior language. Thanks. Goodbye.
  • AMissico
    AMissico over 8 years
    @HighCore; It is not off-topic when you make subjective assertions, such as "Forget java. It's a really antiquated language which has not evolved since the 90's". Then to prove a point, you tell someone "You're an ignorant (no offense)." How do you think you made that person feel?
  • Federico Berasategui
    Federico Berasategui over 8 years
    @AMissico my statement still stands until you disprove my claim by showing with technical arguments how java is comparable to modern languages. Thanks. Goodbye.
  • Kyle Strand
    Kyle Strand over 5 years
    Your sentence about Java vs WPF is mostly just opinion, and the fact-based claim (that Java hasn't evolved since the 90s) is flatly untrue. I'm not a fan of Java myself, but your opening is off-topic, and removing it would improve the answer.