myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Code-Reviews » WPF MVVM - Richtig umgesetzt?
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

WPF MVVM - Richtig umgesetzt?

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16


Denis1501 ist offline

WPF MVVM - Richtig umgesetzt?

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

Nach stundenlanger suche im Internet, bin ich immer noch nicht schlauer..
Habe ich mein MVVM - Pattern richtig umgesetzt? Kann ich irgendetwas verbessern oder vielleicht alles neu machen?

Es würde mich sehr freuen, wenn ihr mir kurz zu meinem Quellcode ein Review geben könntet.

MfG Denis1501

C#-Code:
Mein View:

<Window x:Class="DataBinding.View"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="View" Height="350" Width="525">
    <StackPanel>
        <TextBlock Name="lblDate" HorizontalAlignment="Center" Text="{Binding Path=Date}" />
        <TextBlock Name="lblTime" HorizontalAlignment="Center" Text="{Binding Path=Time}" />
    </StackPanel>
</Window>

C#-Code:
Mein ViewModel:

    public class ViewModel : BindingBase
    {
        Model myModel;

        public ViewModel()
        {
            myModel = new Model();
            myModel.PropertyChanged += myModel_PropertyChanged;
        }

        void myModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "Time": Time = myModel.Time;
                    break;
                case "Date": Date = myModel.Date;
                    break;
                default:
                    break;
            }
        }

        #region Properties

        private string _Date;
        public string Date
        {
            get { return _Date; }
            set { SetProperty(ref _Date, value); }
        }

        private string _Time;
        public string Time
        {
            get { return _Time; }
            set { SetProperty(ref _Time, value); }
        }

        #endregion
    }

C#-Code:
Meine BindingBase:

    public class BindingBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (!Equals(storage, value))
            {
                storage = value;
                OnPropertyChanged(propertyName);
            }
        }
    }

C#-Code:
Mein Model:

    public class Model : BindingBase
    {
        public Model()
        {
            DispatcherTimer dpt = new DispatcherTimer();

            dpt.Interval = new TimeSpan(0, 0, 1);
            dpt.Tick += new EventHandler(dpt_Tick);

            dpt.Start();
        }

        private void dpt_Tick(object sender, EventArgs e)
        {
            new Thread(UpdateData).Start();
        }

        private void UpdateData()
        {
            Date = DateTime.Now.ToString("d");
            Time = DateTime.Now.ToString("T");
        }

        #region Properties

        private string _Date;
        public string Date
        {
            get { return _Date; }
            set { SetProperty(ref _Date, value); }
        }

        private string _Time;
        public string Time
        {
            get { return _Time; }
            set { SetProperty(ref _Time, value); }
        }

        #endregion
    }
Neuer Beitrag 06.02.2015 15:35 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Denis1501,

du hast das mit dem INotifyPropertyChanged noch nicht (komplett) verstanden ;).

Du brauchst das Event nicht abonnieren. Durch dein Setter im Property läuft ja das NotifyPropertyChanged ja schon.

Du setzt von aussen einfach dein Property am ViewModel neu, dann läuft dein INotifPropChanged und dein UI wird informiert.

Kannst auch mal  Wpf Basics I – How to make first steps of Databinding und  Wpf Basics II – INotifyPropertyChanged schauen.

Die Forensuche bringt auch:
 Wieso Model, wenn man das ViewModel nutzen kann?
 [erledigt] XAML Update bei Property Änderung
 MVVM in Verbindung mit der 3 Schichten-Architektur

Gruss

Coffeebean
Neuer Beitrag 06.02.2015 16:44 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Palin Palin ist männlich
myCSharp.de-Mitglied

Dabei seit: 22.08.2011
Beiträge: 1.090
Entwicklungsumgebung: VB.net


Palin ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@Coffeebean,

wenn ich das richtig sehe Aboniert er im VM das PropertyChange des Models.
Wenn das VM auf Änderungen des Models Reagieren soll, ist da auch korrekt.
Neuer Beitrag 06.02.2015 17:18 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Palin,

ja, habs auch gesehen. Trotzdem: Das Update zu abonnieren ist erstmal nicht verkehrt, wenn es darum geht das VM irgendwie in Kenntnis darüber zu setzen, dass woanders was passiert ist. Aber dann mit einem Switch/Case da durch zu laufen ist, bei mehreren Properties einfach nicht sauber. Dein Switch/Case wächst und wächst...

Gruss

Coffeebean
Neuer Beitrag 06.02.2015 17:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Sollte ich dann mein Model von INotifyPropertyChanged erben lassen
und für jede Eigenschaft, die ich im ViewModel brauch, ein Event erstellen?

@Coffeebean:
Deine Links hab ich mir angeschaut, aber wirklich weitergeholfen haben sie mir nicht, trotzdem Danke!


Gruß,
Denis1501

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Denis1501 am 09.02.2015 08:02.

Neuer Beitrag 09.02.2015 07:59 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Denis1501,

du lässt im Moment dein Model und dein VM erben von BindingBase. Also schmeisst du das Event, dass sich etwas geändert hat, zwei mal. Du bindest aber nur auf das Property im ViewModel. Du musst das Notify nur an der Stelle schmeissen, auf das das UI auch "hört". Das Switch/Case ist hässlich, das wollen wir weghaben.

Möglichkeit 1) Schmeiss das NotifPropChanged im ViewModel. Somit: Das Model informiert das VM, dass sich etwas geändert hat, nachdem es fertig ist. Model schmeisst das Event. Im VM machst du dann auf die Properties ein NotifyPropertyChanged. Das Model kann das VM schon kennen, setzt die Properties direkt darauf (Durch dein "SetProperty" schmeisst du dann das Update automatisch.)
(Die Möglichkeit und die Tatsache, dass du das schmeisst, finde ich nicht so schön)

Möglichkeit 2) Dein Model hat ja schon die Properties und dort sind sie auch richtig. Damit arbeitest du ja. Somit: Binde vom UI über das Model auf deine Properties im Model. Biete das Model als Property an. Ich würde es auch nicht "Model" nennen, sondern in dem Fall "TimerService" oder sowas. (Über die Namensgebung will ich nicht diskutieren hier ;) ). Im XAML bindest du dann auf das Property vom Service. Und das setzt du ja im Service. Dann brauchst du im ViewModel dein BindingBase nicht mehr. Die Properties können dann aus dem ViewModel raus. Auch dein Eventhandler und dein Switch/Case.

Mach für den Service und für das ViewModel noch Interfaces.

Gruss

Coffeebean
Neuer Beitrag 09.02.2015 08:21 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Coffeebean,

Wenn ich dich richtig verstehe ist dann ja mein ViewModel nutzlos?
Ich bräuchte dann nur mein View und mein Model?

Gruß,
Denis1501
Neuer Beitrag 09.02.2015 08:30 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von Denis1501:
Wenn ich dich richtig verstehe ist dann ja mein ViewModel nutzlos?

Um himmels Willen, diese Annahme ist fundamental falsch.

Dein ViewModel representiert deine View. Wenn ich als Entwickler wissen will, welche Infos eine View hat, nehme ich das Interface vom ViewModel und schaue, was da so drauf ist. Du benutzt es nur als Fassade und bietest die Infos nicht flach, sondern einfach eine Stufe tiefer an.

Stell dir vor es kommt noch ein Service dazu, der komplett andere Properties hat. Irgendwann würde dein VM so "voll" werden, dass man es nicht mehr versteht. Daher kapselst du das in den Service, der wiederum die Props hat.

Zweitens kann es dazu noch Properties geben, die keinen Service (oder eine Kapselung) verdienen. Beispielsweise ein "IsXYZVisible" meinetwegen. (Auch hier kommt es wieder drauf an, aber gehen wir mal von einem einfachen Fall aus). Das würde man auf das VM nehmen. Flach, weil es sehr nah an der View hängt.

Wie gesagt: Es kommt immer drauf an. ;) Nutzlos ist dein VM nie! Es ist ja dein DataContext für deine View!

Gruss

Coffeebean
Neuer Beitrag 09.02.2015 08:37 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 5.287


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

ich bin malwieder böse und widerspreche:
Die Trennung von Viewmodel und Model ist in einfachen Fällen eben doch nutzlos.
Tatsächlich müssen die Fälle sogar ziemlich kompliziert werden, damit diese Trennung architektonisch plausibel wird.
Und ich bin immer für KISS, also nicht komplizierter coden als nötig.
Denn bei vorauseilender Verkomplizierung (könnte ja mal ein Service hinzukommen, könnte ja mal in Teamwork Absprachen mit Designer-Kollegen zu berücksichtigen sein) macht mans zu 90% falsch, was sich aber erst dann erweist, wenn die vorausgeeilte Verkomplizierung tatsächlich in Anspruch genommen werden soll.
Und dann hat man nicht nur das einfache Modell zu überarbeiten, sondern auch noch seine fehlerhafte Verkomplizierung erstmal rückzubauen.
Daher trenne ich Viewmodel und Model erst, wenns sich tatsächlich als nötig erweist, nicht vorher.

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von ErfinderDesRades am 09.02.2015 14:28.

Neuer Beitrag 09.02.2015 08:58 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von ErfinderDesRades:
Daher trenne ich Viewmodel und Model erst, wenns sich tatsächlich als nötig erweist, nicht vorher.

Nichts anderes versuchte ich zu sagen. Ich mache es gern von Anfang an so, aber das ist wieder so ein "Kommt-drauf-an"-Ding

Danach müsste der Service oben die Properties einfach auf dem ViewModel setzen. Auch die Möglichkeit habe ich in einem Vorschlag gezeigt.

Gruss

Coffeebean
Neuer Beitrag 09.02.2015 09:00 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo ErfinderDesRades und Coffeebean,

@ErfinderDesRades:

Ich weiß, für mein Beispiel ist das MVVM vielleicht übertrieben, aber es geht mir
vielmehr darum, dass ich dieses Pattern verstehe und deswegen habe ich mir ein relativ simples Beispiel ausgesucht :)

@Coffeebean:

Dieser Satz hat mich verwirrt :)

Zitat von Coffeebean:
Möglichkeit 2) ... Binde vom UI über das Model auf deine Properties im Model. ...

Du hast warscheinlich gemeint, dass ich über das ViewModel auf mein Model binden soll!

Mein Code sieht jetzt so aus:

C#-Code:
View:

<Window x:Class="DataBinding.View"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="clr-namespace:DataBinding"
        Title="View" MinHeight="170" Height="170" MinWidth="300" Width="300" Background="LightBlue">
    <Window.DataContext>
        <vm:ViewModel />
    </Window.DataContext>
    <Grid>
        <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
            <TextBlock Name="lblDate" HorizontalAlignment="Center" Text="{Binding Path=myModel.Date}" FontSize="50" FontWeight="ExtraBold"/>
            <TextBlock Name="lblTime" HorizontalAlignment="Center" Text="{Binding Path=myModel.Time}" FontSize="50" FontWeight="ExtraBold"/>
        </StackPanel>
    </Grid>
</Window>

C#-Code:
ViewModel:

namespace DataBinding
{
    public class ViewModel
    {
        public Model myModel { get; set; }

        public ViewModel()
        {
            myModel = new Model();
        }
    }
}

C#-Code:
Model:

using System;
using System.Threading;
using System.Windows.Threading;

namespace DataBinding
{
    public class Model : BindingBase
    {
        public Model()
        {
            DispatcherTimer dpt = new DispatcherTimer();

            dpt.Interval = new TimeSpan(0, 0, 1);
            dpt.Tick += new EventHandler(dpt_Tick);

            dpt.Start();
        }

        private void dpt_Tick(object sender, EventArgs e)
        {
            new Thread(UpdateData).Start();
        }

        private void UpdateData()
        {
            Date = DateTime.Now.ToString("d");
            Time = DateTime.Now.ToString("T");
        }

        #region Properties

        private string _Date;
        public string Date
        {
            get { return _Date; }
            set { SetProperty(ref _Date, value); }
        }

        private string _Time;
        public string Time
        {
            get { return _Time; }
            set { SetProperty(ref _Time, value); }
        }

        #endregion
    }
}

C#-Code:
BindingBase:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace DataBinding
{
    public class BindingBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
        {
            if (!Equals(storage, value))
            {
                storage = value;
                OnPropertyChanged(propertyName);
            }
        }
    }
}

So hast du es gemeint, oder?

Gruß,
Denis1501

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Denis1501 am 09.02.2015 09:26.

Neuer Beitrag 09.02.2015 09:25 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Möglichkeit 2) ... Binde vom UI über das Model auf deine Properties im Model. ...

Natürlich. Entschuldige: Ich meinte "über das ViewModel...".

ErfinderDesRades meinte nicht, dass MVVM übertrieben wäre! Hier geht es darum, was man abstrahiert und was nicht. Ich bin eher Fan von dieser Variante (ViewModel als Fassade). Aber es geht natürlich auch "einfacher" die Props im VM anzubieten, das Model dann arbeiten zu lassen und dem VM bescheid zu sagen.

Aber ja, das wäre eine Möglichkeit. Wie gesagt, du kannst auch dein Model die Properties auf deinem VM setzen lassen. Dann kannst du direkt auf die Props im VM binden. So, wie jetzt, hast dus halt eine Ebene tiefer. KISS beachten. Das meinte ErfinderDesRades in seinem Beitrag. Was eben für dich in Frage kommt.

Aber so sollte es funktionieren. Achte noch auf die  C#-Coding-Conventions.

Gruss

Coffeebean
Neuer Beitrag 09.02.2015 09:30 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Dann bedanke ich mich herzlich :)

Welchen Punkt von der Coding Conventions meinst du im speziellen?

Gruß,
Denis1501
Neuer Beitrag 09.02.2015 10:52 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo Denis1501,

eigentlich wollte ich auf die  Naming-Guidelines hinaus. Felder sollten nach camelCase benannt werden. Properties werden jedoch gross geschrieben.

Gruss

Coffeebean
Neuer Beitrag 09.02.2015 11:03 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ok, vielen Dank!
Werde ich mir zu herzen nehmen.

Der Beitrag kann geschlossen werden :)

Gruß,
Denis1501
Neuer Beitrag 09.02.2015 11:05 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
FZelle
myCSharp.de-Poweruser/ Experte

Dabei seit: 23.04.2004
Beiträge: 9.843


FZelle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Nein, bitte nicht.

Denn das was du da als letztes gepostest hast ist nicht MVVM, sondern eine Mischung aus MVVM und MVC.

Der View sollte bei echtem MVVM nicht aufs Model zugreifen, sondern immer übers VM gehen.
Wie willst du z.b. bei einem reinen (nichtEdit)View, in deinem Fall das Beschreiben eines Properties unterbinden?
Dadurch das Xaml nur auf das VM zugreift, kannst du eben z.b. bei einem (nichtedit)ViewModel nur Getter machen.
In deinem Fall geht es nicht.
Und MVVM ist gerade dazu gedacht, dem Designer ( derjenige der XAML schreibt) nur genau das zu ermöglichen was der aktuelle BusinessCase verlangt.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von FZelle am 09.02.2015 14:17.

Neuer Beitrag 09.02.2015 14:17 Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo FZelle,

Was müsste ich denn deiner Meinung nach ändern?

Gruß,
Denis1501
Neuer Beitrag 09.02.2015 14:42 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat von FZelle:
Wie willst du z.b. bei einem reinen (nichtEdit)View, in deinem Fall das Beschreiben eines Properties unterbinden?
Dadurch das Xaml nur auf das VM zugreift, kannst du eben z.b. bei einem (nichtedit)ViewModel nur Getter machen.

Nun, man kann in einem Interface vom Service auch nur Getter machen und mit Feldern arbeiten.

Zitat von FZelle:
Und MVVM ist gerade dazu gedacht, dem Designer ( derjenige der XAML schreibt) nur genau das zu ermöglichen was der aktuelle BusinessCase verlangt.

Richtig. Daher muss man eben schauen, was geeigneter ist. Wenn man (zu) viele Properties für die View anbietet, verwirrt ein (Interface von einem) VM mehr, als dass es hilft. Daher halte ich eine Kapselung schon für sinnvoll.

Gruss

Coffeebean
Neuer Beitrag 09.02.2015 14:50 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
FZelle
myCSharp.de-Poweruser/ Experte

Dabei seit: 23.04.2004
Beiträge: 9.843


FZelle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

@Coffeebean:

Zitat:
Nun, man kann in einem Interface vom Service auch nur Getter machen und mit Feldern arbeiten.

Dann hast du sogar 2 Models zu dem selben Model. Das halte ich persönlich dann doch eher für zu overengineered.

@Denis1501:
Alles auf das von XAML aus zugegriffen wird ist im ViewModel.
Neuer Beitrag 09.02.2015 20:05 Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

Wie aktualisiere ich dann ordnungsgemäß die Daten im ViewModel?
Das Beispiel das ich hier hab ist nämlich ein stark vereinfachtes Problem von mir..

Ich bekomme in meinem Model Daten von einer SPS, und diese würde ich gerne dem View im ViewModel zur verfügung stellen.

Gruß,

Denis1501
Neuer Beitrag 10.02.2015 08:54 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Coffeebean Coffeebean ist männlich
myCSharp.de-Team

avatar-3295.gif


Dabei seit: 25.08.2011
Beiträge: 2.201
Entwicklungsumgebung: VS 2005-2017, VS Code
Herkunft: Deutschland/Schweiz


Coffeebean ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Was FZelle meint, haben wir vorher in den zwei Möglichkeiten schon besprochen. Es war die erste ;)

BindingBase ins ViewModel, aus dem Model raus
Lass das Model arbeiten, schmeiss ein Event, wenn es fertig ist. Du brauchst ja irgendeine Benachrichtigung, wenn dein Service fertig ist.
Fang das Event im ViewModel und aktualisiere im Eventhandler deine Properties
(Ich würde das SetProperty rausnehmen und RaisePropertyChanged explizit aufrufen, wenn sich das Prop ändert)

Wie gesagt: Beides geht. Je nach UseCase ist die Möglichkeit, die du jetzt hast besser oder eben alles "flach" auf dem ViewModel zu haben. Dann representiert dein VM auch deine View.

Gruss

Coffeebean
Neuer Beitrag 10.02.2015 09:09 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
FZelle
myCSharp.de-Poweruser/ Experte

Dabei seit: 23.04.2004
Beiträge: 9.843


FZelle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Jede Klasse die die Änderung von Properties Signalisieren muss, sollte INotifyPropertyChanged implementieren.
Wenn du das dann im Model und im VM machen musst, ist das eben so.

Ich würde das SetProperty lassen, denn man sollte RaisePropertyChanged nur machen wenn das Property sich auch ändert.

Ich würde aber anstatt Equals den EqualityComparer benutzen.
Auch würde ich da ein CheckPropertyChanged draus machen, denn es gibt Situationen im Setter wo du bei Änderungen des Properties was machen musst, und dann hast du das gleich als Antwort.

C#-Code:
        public bool CheckPropertyChanged<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
        {
            if (EqualityComparer<T>.Default.Equals(field, newValue))
            {
                return false;
            }
            field = newValue;
            RaisePropertyChanged(propertyName);
            return true;
        }
Neuer Beitrag 10.02.2015 09:32 Beiträge des Benutzers | zu Buddylist hinzufügen
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 5.287


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

jo, ich hab auch sone Methode, nenne sie aber ChangePropIfDifferent(), denn die macht ja wesentlich was anderes als nur PropertyChanged zu checken.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von ErfinderDesRades am 10.02.2015 09:38.

Neuer Beitrag 10.02.2015 09:37 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.961
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Davon abgesehen, dass sich der Threadersteller ohnehin sehr schwer tut macht es diese Diskussion nicht leichter.

Wenn es sich bei dem Model um zB eine (Datenbank-)Entität handelt, dann bin ich bei euch beim Thema, dass dieses ebenfalls INotifyPropertyChanged implementiert; also Model und ViewModel.
Wenn es sich beim Model allerdings um einen Service handelt, der gemäß allgemeiner Programmiercharaktere nichts von seiner Bindung wissen sollte, dann würde ich Kopf-schüttelnd Abwinken und dem Threadersteller nur empfehlen: lass den Scheiss mit dem INotifyPropertyChanged im Service.

Klar, man kann das mit dem INotifyPropertyChanged im Service schon machen; aber dann isses halt - auf Deutsch - k*cke.

Ich halt mich aber jetzt aus dem Thema wieder raus, denn je mehr Köche hier am Werk sind, desto mehr verwirrt das die Suppe des Threaderstellers.
Neuer Beitrag 10.02.2015 09:45 Beiträge des Benutzers | zu Buddylist hinzufügen
FZelle
myCSharp.de-Poweruser/ Experte

Dabei seit: 23.04.2004
Beiträge: 9.843


FZelle ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ein Service ist kein Model, also warum sollte er INPC senden?

Manchmal muss man einfach nicht so komplex denken, dann wird vieles einfacher.
Neuer Beitrag 10.02.2015 17:18 Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo zusammen,

Wie würdet ihr dann dieses Programm programmieren?

Ihr bekommt Daten, in dem fall die Zeit von der Uhr, und die muss auf der Oberfläche angezeigt werden. Komplett in MVVM versteht sich, und keine Mischung aus MVC oder sonst was, nur reines MVVM!

Ein bisschen Code wäre an dieser stelle vielleicht nützlich :)

Gruß,
Denis1501
Neuer Beitrag 12.02.2015 11:05 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 5.287


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ich hab keine Sps, von der ich Daten bekomme - da bräuchte ich einen Mock.

Also code du den DatenGenerator, dann code ich die Anzeige

Und dass die Daten nur aus "Zeit von der Uhr" bestehen kaufe ich dir nicht ab.

Und von manchem Standpunkt aus kann man meiner Lösung widersprechen, denn ich trenne Daten nur dann auf in Model und ViewModel, wenn das auch nötig ist, und sonst nicht.

Also "reines MVVM" ist ein Begriff über den keine Einigkeit besteht.

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von ErfinderDesRades am 12.02.2015 11:15.

Neuer Beitrag 12.02.2015 11:09 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Das mit der Zeit von der Uhr ist nur eine Vereinfachung, damit man sich mehr auf das MVVM selber konzentrieren kann.. Und ob es nötig ist oder nicht, darüber kann man streiten. Allerdings ist dies ja so extrem simpel gehalten, damit man das MVVM leichter versteht.

Gruß,
Denis1501
Neuer Beitrag 12.02.2015 11:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 5.287


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Ja, aber das ist grad der Punkt.
Wenn da nur "Zeit von der Uhr" kommt, werde ichn Deibel tun, mich mit Model-Viewmodel-Brimborium verrückt zu machen.
Wenn aber das Programm auf bestimmte Daten-Zustände komplex reagieren muss, evtl. Steuersignale zurücksenden, Benachrichtigungen ausgeben, Funktionalität (de-)aktivieren oder whatever: dann würde das in einem Viewmodel-Klumpen sicher überaus hässlich und gehört aufgedröselt.
Neuer Beitrag 12.02.2015 11:27 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Denis1501
myCSharp.de-Mitglied

Dabei seit: 04.06.2014
Beiträge: 16

Themenstarter Thema begonnen von Denis1501

Denis1501 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Sollte ich mein ViewModel so aufbauen?

C#-Code:
using System.ComponentModel;
namespace DataBinding
{
    public class ViewModel : BindingBase
    {
        Model myModel;

        public ViewModel()
        {
            myModel = new Model();
            myModel.PropertyChanged += myModel_PropertyChanged;
        }

        void myModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            OnPropertyChanged(e.PropertyName);
        }

        #region Properties

        public string Date
        {
            get { return myModel.Date; }
            set { myModel.Date = value; }
        }

        public string Time
        {
            get { return myModel.Time; }
            set { myModel.Time = value; }
        }

        #endregion
    }
}
Neuer Beitrag 12.02.2015 13:55 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 5.287


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

ich hab nichts dagegen.
Nur ein Datum und eine Zeit sollten niemals den Datentyp string haben - dafür gibts den Datentyp DateTime.
Und es wäre nur eine Property, ein DateTime beinhaltet nämlich beides.

Und - das ist immer mein Problem: Bislang ist nichts erkennbar, was das Viewmodel kann, was das Model nicht kann. Also ein Sinn, bzw. eine Aufgabe, ein Concern.

Aber vlt müsste man das Model angucken.

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von ErfinderDesRades am 12.02.2015 14:14.

Neuer Beitrag 12.02.2015 14:13 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Khalid Khalid ist männlich
myCSharp.de-Poweruser/ Experte

avatar-2534.gif


Dabei seit: 19.07.2005
Beiträge: 3.507
Entwicklungsumgebung: Visual Studio 15/17
Herkunft: Hannover


Khalid ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

warum das Model im ViewModel? Ist unnötig. Das Model zum ViewModel mappen und später wieder zurück. Geht es dir nur um die Anzeige der Zeit aus der Uhr? Dann brauchst du noch nicht mal ein Model. Da reicht ein ViewModel vollkommen aus.

Kleines Beispiel:
Irgendwo hast du ein Service, der dir die Uhrzeit sagt:

C#-Code:
public interface ITimer
{
  public event EventHandler<TimerEventArgs> EventName;
}

public class TimerEventArgs: EventArgs
{
  public DateTime Value { get; set; }
}

ViewModel:

C#-Code:
public class ViewModel: ViewModelBase
{
  private readonly ITimer _timer;
  private string _date;
  private string _time;

  private void OnEventName(object sender, TimerEventArgs e)
  {
    Time = e.Value.ToString("HH:mm");
    Date = e.Value.ToString("dd.mm.yyyy");
  }

  public ViewModel(ITimer timer)
  {
    _timer = timer;
    _timer.EventName += OnEventName;
  }

  public string Time
  {
    get { return _time; }
    set
    {
      _time = value;
      NotifyPropertyChanged(nameof(Time));
    }
  }

  // Date sieht genau so aus
}

Zum Schluss das Ganze einfach im Xaml irgendwo mit den TwoWay Binding binden und fertig.

Stimme übrigens ErfinderDesRades zu: Date und Time nicht als String. Habe es jetzt aber absichtlich im Beispiel so gelassen.
Neuer Beitrag 12.02.2015 14:21 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
ErfinderDesRades
myCSharp.de-Poweruser/ Experte

avatar-3151.jpg


Dabei seit: 31.01.2008
Beiträge: 5.287


ErfinderDesRades ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
Das Model zum ViewModel mappen und später wieder zurück.

Das ist jetzt noch ne Variante.
Imo ist sone hin-her-mapperei sicherlich aufwändiger, als wenn mans Model as it is mit rein-nimmt - aber egal.
Ich bin jetzt auch draussen - der TE kriegt hier so viele verschiedene Meinungen, glaub nicht, dass das ihm Orientierung gibt.

Also meine 2 Ratschlag, ganz allgemein:
  1. code nichts, was du nicht begründen kannst.
    Also die Frage "Wieso machste das so?" musst du für jedes einzelne Wort im Code stichhaltig beantworten können.
    Kannstes nicht, muss der Code weg.
    Und dass irgendetwas irgendeinen Pattern befriedigt gehört nicht zu den stichhaltigen Begründungen.

  2. Mach immer konsequent so einfach wie möglich (aber nicht einfacher). Mach dir keine Sorgen, wenn dir was **zu** einfach erscheint - normalerweise wirds eher früher als später wie von selbst kompliziert genug.

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von ErfinderDesRades am 12.02.2015 14:39.

Neuer Beitrag 12.02.2015 14:36 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 5 Jahre.
Der letzte Beitrag ist älter als 5 Jahre.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 11.07.2020 19:04