Laden...

Vererbung im Zusammenhang mit WPF

Erstellt von FrankenDerStein vor 2 Jahren Letzter Beitrag vor 2 Jahren 774 Views
FrankenDerStein Themenstarter:in
72 Beiträge seit 2015
vor 2 Jahren
Vererbung im Zusammenhang mit WPF

Grüße,

Ich habe eine Frage in Bezug auf Vererbung in Verbindung WPF Elemente.

Ich versuche einen Rahmen zu definieren, der derzeit so aufgebaut ist:


    public abstract class ClosableUserControl : UserControl
    {
        public abstract event EventHandler Closing;
    }


    [ContentProperty("Children")]
    public abstract partial class ListItem : ClosableUserControl
    {
        public ListItem()
        {
            InitializeComponent();
            StyleHelper.SetButtonColor(closeButton);
        }
        public string Title
        {
            set
            {
                tilte.Text = value;
            }
            get
            {
                return tilte.Text;
            }
        }
        public UIElementCollection Children
        {
            get
            {
               return contentGrid.Children;
            }
        }
        protected void Close(object sender, RoutedEventArgs e)
        {
            Closing(this, new EventArgs());
        }
        public abstract void OpenDialog();

        public override event EventHandler Closing;

        protected void Open(object sender, RoutedEventArgs e)
        {
            OpenDialog();
        }
    }


<helper:ClosableUserControl  xmlns:helper="clr-namespace:AnimatedModules.Helper;assembly=AnimatedModules" x:Class="xxx.Items.ListItem"
             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:ShareAndReceive.Items"
             mc:Ignorable="d" d:DesignWidth="246.876" Height="107.813">
    <Grid Margin="5">
        <Grid Background="#FF404040">
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <TextBlock Height="25" Margin="0" VerticalAlignment="Top" x:Name="tilte" FontStyle="Normal" FontSize="15" FontFamily="Calibri" Grid.Row="0" Foreground="DarkGray"/>
            <Grid x:Name="contentGrid" VerticalAlignment="Stretch" Background="DarkGray"  Grid.Row="1" /> <!--------- Der Inhalt --------------->
            <Button x:Name="actionTitle" Grid.Row="2" Height="20" MinWidth="50" HorizontalAlignment="Right" Margin="0,0,8,0" FontFamily="Calibri" FontSize="11" TextOptions.TextFormattingMode="Display" TextOptions.TextHintingMode="Fixed" Click="Open">
                >>
            </Button>
            <TextBlock Grid.Row="2" Height="20" HorizontalAlignment="Left" Margin="8,8,0,0" FontFamily="Webdings" FontSize="11" MouseUp="Close" Background="Transparent" Text="r" Foreground="Gray" FontStyle="Normal" FontWeight="Bold" x:Name="closeButton"/>
        </Grid>
    </Grid>
</helper:ClosableUserControl>

Der erbe des Rahmens:


    public partial class TextItem : xxx.Items.ListItem
    {
        string value;
        public TextItem(string v)
        {
            InitializeComponent();
            value = v;
            Title = "Text";
        }
        public override void OpenDialog()
        {
            Text textWindow = new Text();
            textWindow.Value = value;
            textWindow.ShowDialog();
        }
        private void UserControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (value.Length > 25)
                msg.Text = value.Substring(0, 25) + "...";
            else
                msg.Text = value;
        }
    }


<local2:ListItem
             xmlns:local2="clr-namespace:xxx.Items" x:Class="Communication.TextItem"
             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:Communication"
             mc:Ignorable="d" d:DesignWidth="280"
             Height="95" VerticalAlignment="Top"  Loaded="UserControl_Loaded">
    <TextBlock Height="25" Margin="8" VerticalAlignment="Center" Background="DarkGray"  Grid.Row="1" Name="msg" Padding="3">
        <TextBlock.Resources>
            <Style TargetType="Border">
                <Setter Property="BorderBrush" Value="Black"/>
                <Setter Property="BorderThickness" Value="2,0"/>
            </Style>
        </TextBlock.Resources>
    </TextBlock>
</local2:ListItem>

So weit so gut, wenn nicht dieser Fehler kommen würde, denn ich nicht verstehe:
Fehler Bei "xxx.Items.ListItem" darf es sich nicht um den Stamm einer XAML-Datei handeln, da beim Definieren XAML verwendet wurde. Zeile 2 Position 14. ...\xxx\Items\TextItem.xaml 2

Meine Frage ist was ist der Fehler, oder kann ich auf diese Art und Weise keine Vererbung in Bezug mit WPF gestallten?

Ich hoffe Ihr könnt mir helfen.

MfG.

2.078 Beiträge seit 2012
vor 2 Jahren

Vererbung als Faulheit (sorry 😉) würde ich bei WPF nicht machen - also nur um Schreibarbeit zu sparen. (Vererbung, die nur das zum Ziel hat, würde ich generell in Frage stellen)
Lagere das lieber in ein eigenes Control aus und nutze das dann überall dort, wo Du es brauchst.

Zu deinem Code:
Was meinst Du passiert, wenn in deinem "Basis-XAML" der Content setzt und im "abgeleiteten" XAML nochmal? 😉
Du musst WPF schon mitteilen, wie es die beiden Inhalte zusammen bringen soll.
Außerdem erzwingt XAML, dass man das Control instanziieren kann.
Ob das dein Fehler ist, weiß ich nicht, könnte aber auch ein Folgefehler sein.

Tatsächliche Ableitung macht man normalerweise ganz anders.
Erstell mal ein CustomControl (gibt's ein Template für) und Du siehst, wie man es macht.
Die Arbeitsweise ist da ein bisschen anders:* Du hast ein Control (ausschließlich C# und kein UserControl) und kannst dabei normal ableiten.

  • Du hast ein Style für dieses Control mit einigen Settern und - ganz wichtig - einem Template
  • Du überschreibst im Control den DefaultStyleKey auf den Key des Styles

Immer wenn Du dieses Control nutzt, wird der Style angewendet, das gilt auch für Ableitungen - solange, bis der DefaultStyleKey wieder überschrieben wird.
Wenn Du nun in einer Ableitung Elemente hinzufügen willst, musst Du entweder in dem Basis-Control einen Platz samt passender Property für das Element definieren, oder Du ersetzt das Template, musst den Inhalt dann aber komplett neu definieren.

Beides ist in Ordnung, für Letzteres kannst Du dir aber mit "Teil-Templates", passenden Controls oder Styles behelfen, sodass der tatsächliche Template-Code eher klein ausfällt.
Außerdem gibt es TemplateParts, das sind passend benannte Elemente, auf die Du im C#-Code zugreifen kannst, während das Template angewendet wird.

Aber wie gesagt:
Am einfachsten ist, Du arbeitest nicht mit UI-Vererbung, sondern lagerst die sich wiederholenden Elemente aus und nutzt sie als eigenständige Controls.
Es gibt ja auch die Möglichkeit, von ContentControl (Docs) oder Decorator (Docs) abzuleiten und dann eine Art Wrapper zu entwerfen, in den Du den spezifischen Code "hinein" schreibst.

FrankenDerStein Themenstarter:in
72 Beiträge seit 2015
vor 2 Jahren

Vererbung als Faulheit (sorry ) würde ich bei WPF nicht machen - also nur um Schreibarbeit zu sparen. (Vererbung, die nur das zum Ziel hat, würde ich generell in Frage stellen)

Ist nicht die ganze Objektorientierung drauf aufgebaut um Schreibarbeit zu sparen? 😁

Was meinst Du passiert, wenn in deinem "Basis-XAML" der Content setzt und im "abgeleiteten" XAML nochmal?
Du musst WPF schon mitteilen, wie es die beiden Inhalte zusammen bringen soll.
Außerdem erzwingt XAML, dass man das Control instanziieren kann.
Ob das dein Fehler ist, weiß ich nicht, könnte aber auch ein Folgefehler sein.

Zu diesem Zweck habe ich folgendes in der Klasse angegeben :


[ContentProperty("Children")] <-------------
public abstract partial class ListItem : ClosableUserControl
{
...
public UIElementCollection Children
    {
        get
        {
           return contentGrid.Children;
        }
    }...

Zum Rest muss ich mir noch Gedanken machen, mmm ....

J
641 Beiträge seit 2007
vor 2 Jahren

Dein Basis Usercontrol darf kein XAML definieren.

how-can-a-wpf-usercontrol-inherit-a-wpf-usercontrol

Sicher das du überhaupt Usercontrol und nicht lieber CustomControls erstellen willst?

Siehe: https://www.wpftutorial.net/customvsusercontrol.html

cSharp Projekte : https://github.com/jogibear9988

2.078 Beiträge seit 2012
vor 2 Jahren

Ist nicht die ganze Objektorientierung drauf aufgebaut um Schreibarbeit zu sparen?

Wenn man die Vererbung auf das Schreiben reduziert - ja.
Aber wenn es nur darum geht, Code in eine Basisklasse auszulagern, damit man sie in den Ableitungen nutzen kann, dann kann die Vererbung zu einem massiven Problem werden.
Du darfst ja nicht vergessen: Das ist die schlimmste Abhängigkeit, die Du dir einbauen kannst.

Zu diesem Zweck habe ich folgendes in der Klasse angegeben : [ContentProperty("Children")]

Ob man die ContentProperty überschreiben kann, weiß ich nicht.
Aber auch wenn, dann setzt Du einmal die Children mit einem Element und danach noch mal mit einem Element.
Auch nicht das, was Du haben willst, oder? 😉

2.078 Beiträge seit 2012
vor 2 Jahren

Dein Basis Usercontrol darf kein XAML definieren.

Dann wäre es ein CustomControl mit dem Overhead der UserControl-Klasse.

Sicher das du überhaupt Usercontrol und nicht lieber CustomControls erstellen willst?

Exakt das braucht er 😉

FrankenDerStein Themenstarter:in
72 Beiträge seit 2015
vor 2 Jahren

Danke, auch wenn ich etwas spät antworte.

Ich habe deinem Rat gefolgt und habe alles noch mal umgestellt.

Ich finde es schade, dass man in WPF keine komplexeren Vererbungsketten aufbauen kann.
Ich arbeite gerne mit Vererbung, da zum einen es (wie schon erwähnt) Schreibarbeit verringert aber auch die Wartungsarbeiten.
In dem Fall wollte ich einen Rahmen bauen der gewisse Grundfunktionen und Optische Eigenschaften festlegt. Der Erbe sollte somit den Rahmen besitzen und die eigenen Funktionen, z.b.:
In meinem Fall ist der Rahmen eine Einheit welche die Grundbedürfnisse befriedigt um in eine Liste eingegliedert zu werden. Der Rahmen sollte dann geerbt werden von den eigentliche Elementen die aufgelistet werden sollten. (Neben bei geschieht das durch ein Workaround im Hintergrund und sind nicht starr immer die selben Elemente.) Die Elemente haben dann die Fähigkeiten, z.b Video Player, Musik Player, Bild Darstellung, Text und Link. Durch den Rahmen sollte auch verhindert werden das es doof aussieht und einheitlicher ist.

Das ist der Hintergrund was der Sinn dahinter war. Jetzt ist es halt anders und Funktioniert auch.

Ich Danke für deinen Hilfe, Anregungen sind weithin gern gesehen.

Mit freundlichen Grüßen.

2.078 Beiträge seit 2012
vor 2 Jahren

Ich finde es schade, dass man in WPF keine komplexeren Vererbungsketten aufbauen kann.

Kann man - nur eben nicht so 😉
Du musst schon dem folgen, wie WPF geplant war, wenn Du mit aller Kraft dagegen rennst, wirst Du nicht damit aufhören, gegen Wände zu rennen.

Ich arbeite gerne mit Vererbung, da zum einen es (wie schon erwähnt) Schreibarbeit verringert aber auch die Wartungsarbeiten.

Es kann die Wartungsarbeiten aber auch deutlich erschweren, weshalb ich Vererbung immer mit Vorsicht genießen würde.
Und es passiert leicht, dass Du plötzlich Mehrfachvererbung brauchst, was aus gutem Grund aus C# und Java verbannt wurde.

In dem Fall wollte ich einen Rahmen bauen der gewisse Grundfunktionen und Optische Eigenschaften festlegt. Der Erbe sollte somit den Rahmen besitzen und die eigenen Funktionen

Und wie willst Du einem bereits vorhandenen Control diese Grundfunktionalität bieten? Ein Control kann keine zwei Basisklassen haben.

Ich habe etwas ähnliches, aber als eine MasterDetailsView, auf der einen Seite Details zur anderen Seite und die Maße verschiebbar.
Aufgebaut ist es wie ein ContentControl, bloß mit einem weiteren Content + Template. Dazu ein Style, der dem Ganzen die Optik gibt.
So funktioniert es ohne riskante Vererbung und ich kann jedes beliebige Control mit den Funktionen von meinem Control ergänzen, ganz egal, wie komplex es wird.

So arbeitet WPF und wenn Du dir die anderen Controls von Microsoft anschaust, bieten viele davon genau diese Funktionalität.
Ein paar Beispiele: Border, Button, ScrollViewer, DataGridCell, Window, ... such dir was aus.