Tipps und Tricks

Frage (549) zu C#.NET 4.5 (VS 2012), WPF:

Wie kann man in WPF ein UserControl von einem Basis-UserControl erben?

Antwort:
Beim Vererben eines WPF-Steuerelementes mit XAML und CodeBehind gibt es Probleme, da sowohl vom XAML als auch vom CodeBehind zu erben ist. Das ist in VB.NET und C#.NET nicht möglich.

Im ersten Beispiel wird ein Lösungsweg in C#.NET gezeigt, wie ein Basis-Steuerelement mit Design (XAML) und CodeBehind zu gestalten ist, von dem dann geerbet werden kann. Im Basis-Steuerelement sind 2 Befehlsschaltflächen mit Klickereignissen im CodeBehind des Basis-Steuerelementes. Zuerst ist der CodeBehind des Basis-Steuerelementes als Klasse zu erstellen, die von UserControl erbt:

Damit die Schaltflächen auch in der Oberfläche sichtbar sind, wird anstelle des XAML zum Basis-Steuerelementes ein Style erstellt und in der Anwendung bereitgestellt. Dieser Style wird im Generic.xaml bereitgestellt.

[c#]

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace UserControlErbenCS
{
  /// <summary>
  /// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
  ///
  /// Step 1a) Using this custom control in a XAML file that exists in the current project.
  /// Add this XmlNamespace attribute to the root element of the markup file where it is 
  /// to be used:
  ///
  ///     xmlns:MyNamespace="clr-namespace:UserControlErbenCS"
  ///
  ///
  /// Step 1b) Using this custom control in a XAML file that exists in a different project.
  /// Add this XmlNamespace attribute to the root element of the markup file where it is 
  /// to be used:
  ///
  ///     xmlns:MyNamespace="clr-namespace:UserControlErbenCS;assembly=UserControlErbenCS"
  ///
  /// You will also need to add a project reference from the project where the XAML file lives
  /// to this project and Rebuild to avoid compilation errors:
  ///
  ///     Right click on the target project in the Solution Explorer and
  ///     "Add Reference"->"Projects"->[Browse to and select this project]
  ///
  ///
  /// Step 2)
  /// Go ahead and use your control in the XAML file.
  ///
  ///     <MyNamespace:BasisControl/>
  ///
  /// </summary>
  public class BasisUserControl : UserControl
  {
    static BasisUserControl()
    {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(BasisUserControl), new FrameworkPropertyMetadata(typeof(BasisUserControl)));
    }

    public static RoutedCommand TestButton1 = new RoutedCommand();
    public static RoutedCommand TestButton2 = new RoutedCommand();

    public BasisUserControl()
    {
      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("Schaltfläche 1 vom Basis-Steuerelement");
    }

    private void TestButton2_Executed(object sender, ExecutedRoutedEventArgs e)
    {
      MessageBox.Show("Schaltfläche 2 vom Basis-Steuerelemen1");
    }
  }
}
[/c#]


[xml]
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:UserControlErbenCS">

  <Style TargetType="{x:Type local:BasisUserControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:BasisUserControl}">
          <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
            <Grid x:Name="rootGrid">
              <Grid.ColumnDefinitions>
                <ColumnDefinition Width="45*" />
                <ColumnDefinition Width="55*"/>
              </Grid.ColumnDefinitions>

              <StackPanel>
                <Button Height="23" Margin="4" Content="Test 1" x:Name="btnTest1" 
                                        Command="{x:Static local:BasisUserControl.TestButton1}"/>
                <Button Height="23" Margin="4" Content="Test 2" x:Name="btnTest2" 
                                        Command="{x:Static local:BasisUserControl.TestButton2}"/>
              </StackPanel>

              <Grid Grid.Column="1">
                <ContentPresenter />
              </Grid>
            </Grid>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

</ResourceDictionary>
[/xml]

Im erbenden Steuerelement wird sowohl im XAML als auch im CodeBehind vom Basissteuerelement geerbt.

[xml]
<local:BasisUserControl x:Class="UserControlErbenCS.UserControlErbe"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:UserControlErbenCS"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <Button Width="120" Height="25" Content="Test 3"  Click="Button_Click"/>
  </Grid>
</local:BasisUserControl>
[/xml]

[c#]
using System.Windows;

namespace UserControlErbenCS
{
  /// <summary>
  /// Interaction logic for UserControlErbe.xaml
  /// </summary>
  public partial class UserControlErbe : BasisUserControl 
  {
    public UserControlErbe()
    {
      InitializeComponent();
    }
    private void Button_Click(object sender, RoutedEventArgs e)
    {
      MessageBox.Show("Schaltfläche im geerbten Steuerelement");
    }
  }
}
[/c#]

In der Oberfläche der Anwendung kann dann das erbende Steuerelement genutzt werden.

[xml]
<Window x:Class="UserControlErbenCS.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UserControlErbenCS"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <local:UserControlErbe x:Name="test" Width="250" Height="150" />
  </Grid>
</Window>
[/xml]

Im zweiten Beispiel wird gezeigt, wie in VB.NET ein Basis-Steuerelement mit lediglich CodeBehind erstellt wird. Das erbende Steuerelement ruft Methoden aus dem Basis-Steuerelement auf. Zuerst ist der CodeBehind des Basis-Steuerelementes als Klasse zu erstellen, die von UserControl erbt:

[vb]
Public Class BasisUserControl
  Inherits System.Windows.Controls.UserControl

  Shared Sub New()
    'This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
    'This style is defined in themes\generic.xaml
    DefaultStyleKeyProperty.OverrideMetadata(GetType(BasisUserControl), New FrameworkPropertyMetadata(GetType(BasisUserControl)))
  End Sub

  Protected Friend Sub Methode1()
    MsgBox("Methode1 aus Basis-Steuerelement aufgerufen")
  End Sub

End Class
[/vb]

Dazu der Style, der im Generic.xaml bereitgestellt wird. Im Style wird über den ContentPresenter der Bereich definiert, in welchem dann das erbende Steuerelement seine Darstellung platzieren kann.

[xml]
<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:UserControlErbenVB">
  <Style TargetType="{x:Type local:BasisUserControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type local:BasisUserControl}">
          <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
            <Grid>
              <ContentPresenter/>
            </Grid>
          </Border>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>
</ResourceDictionary>
[/xml]

Im erbenden Steuerelement wird im XAML vom Basissteuerelement geerbt.

[xml]
<local:BasisUserControl x:Class="UserControlErbe"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:UserControlErbenVB"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <Button Width="200" Height="25" Content="Methode in Basis-Steuerelement" Click="Button_Click"/>
  </Grid>
</local:BasisUserControl>
[/xml]

[vb]
Public Class UserControlErbe
  Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
    Me.Methode1()
  End Sub
End Class
[/vb]

In der Oberfläche der Anwendung kann dann das erbende Steuerelement genutzt werden.

[xml]
<Window x:Class="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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:UserControlErbenVB"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <local:UserControlErbe x:Name="test" Width="250" Height="150" />
  </Grid>
</Window>
[/xml]



Stand des Beitrages: 17.11.15 08:43, zuletzt geändert: 17.11.15 10:59



Bitte wählen sie den Themenbereich aus
Bitte geben sie einen Suchbegriff ein

Die hier dargestellten Tipps und Tricks sind das Ergebnis selbst ersteller Lösungsvarianten, die für Projekte und Schulungen erarbeitet wurden.