Laden...

Problem mit dem MVVM Pattern

Erstellt von crank vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.158 Views
C
crank Themenstarter:in
8 Beiträge seit 2012
vor 11 Jahren
Problem mit dem MVVM Pattern

Hallo Zusammen,

ich habe ein kleines Demo Projekt erstellt, basierend auf meinem Problem.
Ich möchte mittels Views und Viewsmodels eine schönere Architektur schaffen.

Also habe ich eine Basisklasse für die ViewModels mit INotifyPropertyChanged Implementierung:

class BasicViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

Dann habe ich eine Demo View mit einem Textblock und Property Binding:

<UserControl x:Class="DemoMVVM.Views.DemoView"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <TextBlock HorizontalAlignment="Left" Margin="81,73,0,0" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top"/>

    </Grid>
</UserControl>

Die Codebehind der DemoView:

    /// <summary>
    /// Interaction logic for DemoView.xaml
    /// </summary>
    public partial class DemoView : UserControl
    {
        public DemoView()
        {
            InitializeComponent();
            this.DataContext = new DemoViewModel();
        }
    }

Und natürlich die DemoViewModel Klasse mit dem entsprechenden property:

    class DemoViewModel : BasicViewModel
    {
        private string _name;

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
                _name = value;
                if (_name != null)
                    OnPropertyChanged("Name");
                return;
            }
        }
    }

Das habe ich nun nach etlichem Lesen und Googlen in allen möglichen Farben und Formen sehen dürfen. Also habe ich mich wirklich nur auf die Basis Funktionalitäten oben beschränkt. Und trotzdem funktioniert das ganze nicht.

Im Konstruktor meiner DemoMainView rufe ich folgendes auf:

DemoViewModel dm = new DemoViewModel();
dm.Name = "Max Mustermann!";

Beim Debuggen sieht man das auch die richtigen Properties angesprochen werden (Wie gesagt, ist ein Demo Projekt basierend auf meinem Problem) Zuerst wird der setter aufgerufen:
http://www.imagebanana.com/view/x29be2lt/debug1.png

Im setter werden also die richtigen werde zugewiesen, aber wie sieht es denn im getter aus? (Der im anschluss aufgerufen wird)
http://www.imagebanana.com/view/8u3wflhr/debug2.png

Wieso ist der "_name" string wieder auf null?
Was mache ich denn falsch?

Ich würde mich wirklich über den erleuchteten Schlag auf den Hinterkopf freuen, weil ich einfach nicht dahinter komme. Und ich glaube, dass es wirklich banal ist.

Vielen Dank.

Gruß
crank

D
615 Beiträge seit 2009
vor 11 Jahren

Hallo Crank

Das sollte etwa so aussehen:


class DemoViewModel : BasicViewModel
    {
        private string _name;

        public string Name
        {
            get
            {
                return _name;
            }
            set
            {
               if(_name!=value)
               {
                _name = value;
                 OnPropertyChanged("Name");
                }
            }
        }
    }

Es heisst ja "OnPropertyChanged", somit solltest du dies auch nur feuern, wenn dem so ist...

Wenn du nun dem Property Name ein Wert zuweist und die selbe Instanz weiterhin nutzt, ist der Wert sicherlich vorhanden.

Beste Grüsse

Diräkt

C
crank Themenstarter:in
8 Beiträge seit 2012
vor 11 Jahren

Hallo Diräkt

Ich bedanke mich für die Schnelle Antwort.
Ich habe das berücksichtigt, ist natürlich vollkommen Logisch.

Allerdings - bleibt das Problem (Ich hätte auch nicht vermutet, dass es sich dadurch ändert).
Obwohl der Wert _name gesetzt wird, ist er beim nächsten Aufruf: null.
Die Instanz sollte die gleiche sein, den Code von MainWindow habe ich ja gepostet.
Eine weitere Zuweisung gibt es nicht.

Ich hoffe jemand hat eine Erklärung dafür.

EDIT: Beim debuggen ist mir gerade auch aufgefallen, dass das "PropertyChanged" event immer null ist.

Gruß
crank

1.552 Beiträge seit 2010
vor 11 Jahren

Hallo crank,

  1. Bitte ich dich um die Einhaltung von 6.1 [Hinweis] Wie poste ich richtig?.

Es heisst ja "OnPropertyChanged", somit solltest du dies auch nur feuern, wenn dem so ist... Naja, das ist Geschmacksache, man kann es auch feuern wenn es sich nicht ändert, denn im Prinzip ist jede String Zuweisung auch eine Änderung, auch wenn man denselben String zuweist: [FAQ] Besonderheiten der String-Klasse (immutabler Referenztyp mit Wertsemantik)

  1. Zu deinem Fehler: Du erstellst 2x ein DemoViewModel, einmal im Constructor der DemoView, und einmal irgendwo anders. Da du zwei Instanzen hast und der Falschen den Wert zuweist, wirst du in der GUI auch keine Änderung erkennen. D.h. die Lösung ist, dass du jenem DemoViewModel den Namen zuweist welcher du als DataContext definierst.

Gruß,
Michael

Mein Blog
Meine WPF-Druckbibliothek: auf Wordpress, myCSharp

C
crank Themenstarter:in
8 Beiträge seit 2012
vor 11 Jahren

Hallo Michael

  1. Ist natürlich richtig, nur so wird eine dauerhafte Verfügbarkeit gewährleistet. Das habe ich ehrlich gesagt vergessen.
  2. Geschmackssache - Ja. Ich würde das Event trotzdem nicht immer feuern. Es macht einfach keinen Sinn. (Wobei einige Beispiele so aufgebaut sind).
  3. Das war der Punkt den ich gesucht habe. Ich stand auf dem Schlauch, danke.

Gruß
crank

C
112 Beiträge seit 2009
vor 11 Jahren

Moinsen,

für Deinen geschilderten Anwundungsfall brauchst Du noch kein propertyChanged. Die Zuweisung erfolgt während des Erstellens, die Daten sind schon vorhanden, an die gebunden wird. Wenn Du aber auf Änderungen reagieren willst, musst Du PropertyChanged auch in der GUI abonnieren, also in der Textbox muss so etwas stehen:
TextBox Text={Binding Name, UpdateSourceTrigger=PropertyChanged}...

Grüße

Christian

5.299 Beiträge seit 2008
vor 11 Jahren

1.) ich bin da ziemlich rigoros: Jede Property im Viewmodel testet im Setter, ob wirklich eine Änderung stattfindet.
Ein unnütz ausgelöstes PropertyChanged-Event bewirkt ja unnütze Update-Vorgänge in der Oberfläche, also Zeichenvorgänge, aber auch Suchvorgänge in sortierten CollectionViews, und wasweißichnichtalles.
Und jedes ViewModel erbt auch von meine ViewmodelBase-Klasse, sodass OnPropertyChanged immer verfügbar ist.

2.) Soweit ich weiß, wenn man eine Textbox bindet, braucht man keinen UpdateSourceTrigger anzugeben, Textbox setzt den defaultmäßig auf "OnValidation".
kannsteja leicht probieren, indemde eine 2. Textbox an dieselbe Property desselben Viewmodels bindest - dann synchronisieren die sich.

Der frühe Apfel fängt den Wurm.