Laden...

Loaded-Event löst im mehrfach vererbten User-Control nicht mehr aus.

Erstellt von Hunv vor 9 Jahren Letzter Beitrag vor 9 Jahren 2.702 Views
Hunv Themenstarter:in
193 Beiträge seit 2005
vor 9 Jahren
Loaded-Event löst im mehrfach vererbten User-Control nicht mehr aus.

Hi,

ich habe ein Problem.
Ich habe ein UserControl, dass von einem UserControl erbt, das von einem UserControl erbt, das von UserControl und INotifiyPropertyChanged erbt.

Der Reihe nach: Hier einmal die Basis, die von UserControl und INotifyPropertyChanged erbt:

namespace myapp
{
public class TabBase : UserControl, INotifyPropertyChanged
    {
        public TabBase()
        {
        }

        private string _Title = "Unnamed";
        public string Title
        {
            get { return _Title; }
            set
            {
                if (_Title == value) return;

                _Title = value;
                RaisePropertyChanged("Title");
            }
        }

        #region PropertyChanged
        protected virtual void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
}

Hier die Klasse, die davon erbt:

namespace myapp
{
    public class TabHuman : TabBase
    {
        public string Firstname { get; set; }
        public string Lastname { get; set; }
    }
}

Und hier das UserControl, welches das "Endprodukt" darstellt:

namespace myapp.Tabs.TabHumanEuropean
{
    public partial class TabHumanEuropean
    {
        public TabHumanEuropean()
        {
            InitializeComponent();
        }

        private void TabHuman_Loaded(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Loaded");
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(this.IsLoaded.ToString());
        }

    }
}

und der XAML-Code:

<tabs:TabHuman x:Class="myapp..Tabs.TabHumanEuropean.TabHumanEuropean"
             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:="clr-namespace:myapp.tabs"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             Loaded="TabHuman_Loaded"
             DataContext="{Binding RelativeSource={RelativeSource Self}}"
            >
    <Grid Background="Red">
        <Button Content="Show" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click"/>
    </Grid>
</tabs:TabHuman>

Im letzten der drei Klassen wird nun aber das "Loaded"-Event nicht ausgelöst und die Eigenschaft "IsLoaded" bleibt auf false. Im Gegensatz dazu wird das Initialized-Event (nicht im Code) aber aufgerufen.
Wenn ich jetzt nicht von TabHuman erben lasse, sondern von TabBase funktioniert das Loaded-Event.
Ich kann mir das nicht so richtig erklären. Kann mir hier jemand weiterhelfen? Was habe ich für Möglichkeiten das Problem zu lösen?

Hinweis von Coffeebean vor 9 Jahren

Mit Hinweis auf

[Hinweis] Wie poste ich richtig? Punkt 4.1 habe ich die angehängte Solution entfernt.

Visit me @ www.beremote.net

5.658 Beiträge seit 2006
vor 9 Jahren

Hi Hunv,

ein paar Sachen, die mir so aufgefallen sind:

  • Ein ViewModel ist kein Steuerelement, und erbt daher auch nicht von UserControl.
  • Du hast keine Ereignisse definiert
  • Deine Methoden sind als private deklariert und daher von außen nicht zugreifbar

Schau dir mal ein paar Beispiele zu MVVM und WPF an und beachte bitte unseren [Hinweis] Wie poste ich richtig?. Den Umgang mit Ereignissen und den Unterschied zwischen öffentlichen und privaten Methoden setzen wir als bekannt voraus.

Christian

Weeks of programming can save you hours of planning

Hunv Themenstarter:in
193 Beiträge seit 2005
vor 9 Jahren

Ein ViewModel ist kein Steuerelement, und erbt daher auch nicht von UserControl.

Ja, da hast du Recht. Das ganze ist etwas gewachsen und nicht final. Primär geht es mir um das Problem und nicht um die Struktur.

Du hast keine Ereignisse definiert

Das Loaded-Event wird doch von UserControl mitvererbt? Beim "ViewModelTabHuman" klappt das ganze ja auch.

Deine Methoden sind als private deklariert und daher von außen nicht zugreifbar

Ja, das ist richtig. Da das Control es selbst aufruft, funktioniert es aber. Bitte die Namensgebung ignorieren. Es ist definitiv kein ViewModel - anders als es benannt ist. Das muss ich noch korrigieren, wenn ich das grundsätzliche Problem gelöst habe.

Edit:
Ich habe im Ursprungs-Post einmal den Begriff "ViewModel" entfernt um Verwirrungen zu vermeiden und den Fokus auf das Problem und nicht das drum herum zu lenken.

Visit me @ www.beremote.net

Hunv Themenstarter:in
193 Beiträge seit 2005
vor 9 Jahren

Nachdem ich nun einen ziemlich frustrierenden Tag damit verbracht habe dem Problem auf die Spur zu kommen habe ich folgendes festgestellt:

Wenn ich in dem Constructor von TabBase und/oder TabHuman das Event abonniere und in der Methode dazu eine Exception schmeiße, wird diese Exception im Designer angezeigt.


        public TabBase()
        {
            this.Loaded += TabBase_Loaded;
        }

        void TabBase_Loaded(object sender, System.Windows.RoutedEventArgs e)
        {
            throw new NotImplementedException("TabBase_Loaded");
        }

Wenn ich das Projekt compiliere und starte gibt es aber keine Exception. Auch im Output-Fenster steht nichts. Wende ich das gleiche ausschließlich auf das TabHumanEuropean an, passiert nichts. Der Designer zeigt keinen Fehler mehr an, compilieren und starten geht.
Mir will einfach nicht in den Kopf, warum das Loaded-Event nicht aufgerufen wird.

Des Weiteren Funktionieren im TabHumanEuropean scheinbar alle anderen Events, nur das Loaded-Event nicht.
Wie bereits geschrieben, funktioniert das Initialized Event. Auch das Loaded-Event des eingebetteten Grids wird aufgerufen. Nur eben nicht das Loaded-Event des Controls selbst.
Auch ein "manuell" definiertes Event im Constructor wird nicht aufgerufen:

        public TabHumanEuropean()
        {
            InitializeComponent();
            
            this.Loaded += TabHumanEuropean_Loaded;
        }

        void TabHumanEuropean_Loaded(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Loaded by Constructor-Loaded");
        }

Visit me @ www.beremote.net

U
1.688 Beiträge seit 2007
vor 9 Jahren

Hallo,

das ist sicherlich falsch:

xmlns:="clr-namespace:myapp.tabs"

Vermutlich soll es

xmlns:tabs="clr-namespace:myapp.tabs"

heißen - was aber ist myapp.tabs?

Bau Dir doch mal eine minimale, lauffähige Anwendung mit den von Dir geposteten Code-Snippets. Die sollte dann funktionieren. Falls nicht, kann man aber am konkreten, vollständigen Code den Fehler suchen.

Hunv Themenstarter:in
193 Beiträge seit 2005
vor 9 Jahren

Hi ujr,

ich habe einmal eine minimale Anwendung erstellt.
Dabei ist mir zu meiner Verwunderung aufgefallen, dass das Problem nur aufzutreten scheint, wenn ich das Control via Binding in das Dock einbette.
Wenn ich das Control direct in die Form oder direkt in das Dock via XAML lege, wird Loaded aufgerufen.

Ich habe das Minimalprojekt einmal angehängt.

Visit me @ www.beremote.net