Tabcontrol und MVVM



  • Ne, page ist ein ViewModel welches die Basis der ViewModel darstellt (wegen den Titel)(Hätte es PageViewModel benennen müssen).
    Ich habe das gemacht damit nicht jedes ViewModel das property Titel implementieren muss.
    Das kannst du alles handhaben wie du möchtest.

    FirstViewModel sowie SecondViewModel sind lediglich Wrapperklassen mit den Daten die du in der Oberfläche Anzeigen willst.



  • Achso. Aber das ganze ist doch komplizierter wie gedacht. Wieso sind hier nun Daten in den ViewModels hinterlegt. Sollte es nicht noch ein PageModel geben in denen der Titel hinterlegt ist?



  • Wenn du es in den Modeldaten brauchst, why not, wenn du es nicht brauchst ist es durchaus legitim das es "Title" nur in den ViewModels gibt.
    ViewModels geben nicht nur die Models wieder (denken viele MVVM Einsteiger), sondern sie bereiten die Daten auf sodass sie von einer Ausgabe (z.B.: Xaml oder Console) konsumiert werden können (in irgend einer Form, ist für die ViewModels irrelevant).
    Wenn dazu gehört das es nur in den ViewModels ein Titel gibt, dann ist das eben so.

    In Prinzip ist es Simpel.
    Nehmen wir an du hast eine Person und du willst ein Tab mit einer "Basic" und "Advanced" Ansicht machen.
    Dann erstellst du dir ein BasicPersonViewModel sowie ein AdvancedPersonViewModel und gibst beiden die selbe Person.
    Beide ViewModels packst du in eine Liste (Pages z.B.)(Daher wäre eine gemeinsame Basisklasse gut, z.B. eine PersonViewModel [vor allem für den Titel]) und bindest es aus der Xaml heraus wie oben zu sehen.
    Die Xaml entscheidet dann selber wie es die ViewModels anzeigt, durch den unterschiedlichen Typ hat sie dann die Möglichkeit sie zu unterscheiden (DataType).



  • Danke für deine Antwort. Da stellt sich für mich nun aber noch eine Frage. Wo lege ich dann meine Person ab.

    Wenn ich ja zwei ViewModels für ein Model habe muss ich das Model ja woanders ablegen und in den Viewmodels nur noch verweise auf die Models ablegen. Oder?



  • Rüchtüg. Da C# eh nur mit den Referenzen arbeitet, hast du auch immer die Selbe, ist also kein Problem.
    Es gibt verschiedene Ansätze das Problem grundsätzlich zu Lösen, am Ende ist es dir überlassen.
    Man könnte es z.B. so machen:

    public class Person
    {
    	public string Name { get; set; }
    }
    
    public class ComponentObject<T>
    {
    	public T Component { get; private set; }
    
    	public ComponentObject(T component)
    	{
    		Component = component;
    	}
    }
    
    public class PageViewModel<T> : ComponentObject<T>, INotifyPropertyChanged
    {
    	public PageViewModel(T component)
    		: base(component)
    	{
    	}
    
    	public string Title
    	{
    		get { return _title; }
    		set
    		{
    			_title = value;
    			NotifyPropertyChanged(() => Title);
    		}
    	}
    	private string _title;
    
    	#region NotifyPropertyChanged
    	public event PropertyChangedEventHandler PropertyChanged;
    	protected void NotifyPropertyChanged<T>(Expression<Func<T>> property)
    	{
    		var handler = PropertyChanged;
    		if (handler != null)
    		{
    			var memberExpression = (MemberExpression)property.Body;
    			handler(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
    		}
    	}
    	#endregion NotifyPropertyChanged
    }
    
    public class PersonViewModel : PageViewModel<Person>
    {
    	public PersonViewModel(Person person)
    		: base(person)
    	{
    	}
    
    	public string Name
    	{
    		get { return Component.Name; }
    		set
    		{
    			Component.Name = value;
    			NotifyPropertyChanged(() => Name);
    		}
    	}
    }
    
    public class BasicPersonViewModel : PersonViewModel
    {
    	public BasicPersonViewModel(Person person)
    	: base(person)
    	{
    		Title = "Basic";
    	}
    
    	// Properties for the basic view
    }
    
    public class AdvancePersonViewModel : PersonViewModel
    {
    	public AdvancePersonViewModel(Person person)
    		: base(person)
    	{
    		Title = "Advance";
    	}
    
    	// Properties for the advance view
    }
    
    public class MainViewModel
    {
    	public MainViewModel()
    	{
    		var person = new Person {Name = "sausebrause"};
    		Pages = new ObservableCollection<PersonViewModel>
    		        	{new BasicPersonViewModel(person), new AdvancePersonViewModel(person)};
    	}
    
    	public ObservableCollection<PersonViewModel> Pages { get; set; }
    }
    

    Properties die in beiden Views benutzt werden, z.B. der Name, wird in PersonViewModel definiert, dadurch aktualisieren sich beide Views sobald sich das Property ändert.



  • Dank dir. Ich komm der Sache schon näher.

    Etwas erschreckt mich nun aber:

    Seit ich nun diesen Codeabschnitt in meinem Programm umgesetzt habe dauert es eine gefühlte Ewigkeit ( > 1 Sekunde) bis das Tabcontrol umschaltet

    <TabControl ItemsSource="{Binding Pages}">
        <TabControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Title}" />
            </DataTemplate>
        </TabControl.ItemTemplate>
        <TabControl.Resources>
            <DataTemplate DataType="{x:Type Local:FirstViewModel}">
                <Controls:FirstView />
            </DataTemplate>
            <DataTemplate DataType="{x:Type Local:SecondViewModel}">
                <Controls:SecondView />
            </DataTemplate>
        </TabControl.Resources>
    </TabControl>
    


  • Mach mal die UserControls leer bis auf eine TextBlock oder so.
    Ist es dann immer noch langsam?

    Ich vermute das liegt an den Bindings innerhalb der UserControls selber, diese werden erst ausgewertet sobald du das entsprechende Tab ausgewählt hast. (Beim ersten der direkt sichtbar ist (sofern so eingestellt) lädt es sobald das Fenster öffnet, dadurch merkst du sowas vermutlich nicht.)



  • Ja wenn ich nur nen Textblock rein mache geht es schnell.
    Was kann ich dagegen unternehmen. Und wieso geht es nur so langsam wenn ich es über DataTemplate mache?



  • Zeitpuntk des Ladens.
    Wenn du es direkt rein packs (Statische Elemente statt Liste) dann wird alles beim Start des Fensters geladen (dadurch merkst du es nicht).
    Ansonsten wird ein TabItem erst vollständig geladen wenn du es auswählst (Fang dir mal das Loaded Event des UserControls in dessen Codebehind rein, dann merkst du wann das rein kommt).

    Dagegen machen? Standard Performance beachten.

    Erster Anfang wäre die Xaml selber zu überarbeiten,
    - unnötige Controls raus schmeißen (Grid als einzelnes Element im Grid usw.)
    - Teure Controls sofern möglich gegen günstige austauschen (Das Grid ist sehr teuer, ein StackPanel oder DockPanel ist günstiger)
    - Farbverläufe, Animationen usw in die Resourcen auslagern (dann kann es gefreezed werden)
    - Wenn Daten im Model lazy geladen werden, dann eventuell über ein PriorityBinding nachdenken

    Jemand der sich auskennt müsste sich mal die echten UserControls anschauen und ggf optimieren.

    Kommentier doch mal Sachen ein und aus, eventuell findest du einen Übeltäter.



  • Dagegen machen? Standard Performance beachten.

    Versteh ich nicht was du mir damit sagen willst.



  • Na das man die Standardmittel ausnutzt um die Performance zu verbessern. http://msdn.microsoft.com/en-us/library/aa970683.aspx

    Ein paar Punke habe ich ja aufgeführt die mir als erstes einfallen.

    => Da gibt aber kein Allheilmittel. Was am besten funktioniert weiß man nur wenn man den echten Code kennt.

    //Edit
    Ich hatte auch schon mehrere TabControls derart in Benutzung, aber noch nie Performanceprobleme. Man muss nun forschen woher die kommen bzw, wie ich oben schon sagte, wer der Übeltäter ist.



  • Es liegt denke ich nicht am Tabcontrol sondern am Inhalt des ersten Tabs

    Das erste Tab beinhaltet ein UserControl.

    Dieses UserControl hat ein ItemsControl die eine Liste aus weiteren Usercontrols ist. Dieses UserControl beinhaltet wieder ein Itemscontrol mit Usercontrols. Und das ganze dann noch einmal. Das letzte ist dann nur noch eine Combobox.

    Ich habe sozusagen eine 3 Fache Rekursion.

    Vieleicht ein Beispiel: Oberste Ebene ist ein Haus (UserControl_1). Dies beinhaltet eine Liste von Zimmern (UserControl_2). Jedes Zimmer beinhaltet eine Liste von Bereichen (Usercontrol_3). Jeder Bereich beinhaltet eine Liste von Stühlen (UserControl_4).

    Besonderheit alles ist symetrisch. Also jedes Zimmer besteht aus der selben Anzahl von Bereichen, jeder Bereich hat die selbe Anzahl von Stühlen. Letzendlich kann man dann für jeden Stuhl über die Combobox eine individuele Farbe geben.

    Die Usercontrols bestehen fast ausschliesslich aus Itemcontrols. Maximal noch ein Label oder Button dazu. Panel ist dann immer ein Stackpanel.

    Die Rekursion macht hier das ganze langsam. Habe ich z.B. 12 Zimmer. Jedes Zimmer hat 2 Bereiche und jeder Bereich 4 Stühle. Sind dann insgesammt 96 Stühle dementsprechend 96 Comboboxen. Stellt die Combobox nur 3 Farben zur Auswahl wird die Applikation so langsam, dass wenn das Programm aus VS gestartet wird, die UserControls teilewiese gar nicht vollständig angezeigt werden. Erst nach einem Tabwechsel.

    Aussehen ist komplett in Resourcen ausgelagert. Die Geschwindigkeit erhöht sich in der Relation wie ich die untergeordneten Usercontrols wegnehme. Also es gibt nicht den "einen" Übeltäter.



  • (Von den Inhalt der Tabs habe ich doch geredet)

    Die Massen von UserControls sind die Übeltäter.

    Das Control vom Typen "UserControl" ist recht teuer.
    Warum sind es überhaupt UserControls wenn es so wenig beinhaltet?
    Geht doch alles über DataTemplates. Und so wie du es schilderst klingt das gar nach einer TreeView 😃

    Ein UserControl nur für eine ComboBox ist schlichtweg gesagt Blödsinn.
    Genauso ein UserControl das nur eine Liste von weiteren Controls hält, auch quatsch, pack doch die Liste direkt rein.

    Du solltest deine Exzessive Benutzung der UserControls überdenken.



  • Nun gut dachte dadurch wird das ganze überischtlicher. Ist ja auch so. Aber wenn das Control halt sehr teuer ist. Dann mach ichs über Datatemplates.

    Treeview ist denke ich nicht das was ich suche. Möchte das ganze grafisch darstellen und nicht einfach nur als Liste.

    Also Haus als Border. Raum wieder als Border der im Border des Hauses gezeichnet wird usw.



  • Eine TreeView kann das auch, musst nur das Style umbauen und das auf und zu klappen unterbinden.
    Schon hast du die Möglichkeit des HiraricalDataTemplates und musst dich nicht mit UserControls rum prügeln.



  • @Offtopic.

    Ich find ja der David sollte ruhig mal ein Artikel über MVVM schreiben oder auf seiner Seite nen Tutorial machen. Finde das das nen sehr wichtiges Thema ist, und es gibt wirklich kaum gute Tutorials im Netz. Da ich ja weiß das er gut erklären kann, fänd ich das echt mal Cool 😃



  • Wie man MVVM "richtig" macht ist im Web recht kontrovers, und ich will meine herangehensweise (auch wenn sie sich immer mit den anderen deckt) nicht als die absolut richtige verkaufen.
    Tutorials wirds schon genug geben ^^

    Firefighter schrieb:

    Da ich ja weiß das er gut erklären kann

    Kommt auf meine Stimmung an, manchmal bin ich einfach nur ein Kotzbrocken ^^



  • David W schrieb:

    Wie man MVVM "richtig" macht ist im Web recht kontrovers, und ich will meine herangehensweise (auch wenn sie sich immer mit den anderen deckt) nicht als die absolut richtige verkaufen.
    Tutorials wirds schon genug geben ^^

    Jedoch ist deine herangehensweise hier im Forum die einzig bekannte 🙂 Von der man auf jeden Fall immer was lernt wenn es Probleme gibt 🙂

    David W schrieb:

    Kommt auf meine Stimmung an, manchmal bin ich einfach nur ein Kotzbrocken ^^

    Joa jeder hat mal seine schlechten Tage.


  • Administrator

    David W schrieb:

    Tutorials wirds schon genug geben ^^

    Viele allerdings auf Englisch. Aber was man allenfalls mal tun könnte, sind entsprechende Links zu sammeln und in die FAQ zu verschieben. MVVM war bei mir eines der grössten Probleme bei WPF, allerdings vor allem deswegen weil mein WPF Buch kein einziges Wörtchen darüber verloren hat. Nicht mal MVVM wurde erwähnt 🙂

    Grüssli



  • Isses bei MVC nicht auch so?
    Kann mich nicht erinnern das dies mal in ein Buch erwähnt wurde ^^

    Anyway, ich denk nicht das meine Website dafür zu gebrauchen ist, sie ist zum einen auch nur auf Englisch 😉 und zudem geht es da nur um meine libs, nichts generelles.

    Firefighter, du hast doch ne website, stell doch selber eins Online ^^ Auf deutsch.
    Ich könnte eins schreiben wenn ich eventuell mal ein real live szenario hätte wo man es deutlicher sieht, die meisten Tutorials die ich kenne behandeln nur die Grundzüge, für Detailfragen muss man wieder irgendwo anklopfen. Daher wäre ein Tutorial gut wo alles mögliche mal aufgezeigt wird.

    Spontan würde mir einfallen.
    - Ne TreeView.
    - Ein Settings Fenster mit diversen Unterseiten.
    - Ein TabControl mit diversen Seiten.
    - Unterfenster, Systemdialoge oder MessageBoxen, how to use.
    - Elegantes Testen von ViewModels mit Mocks und/oder Extract and Override.


Anmelden zum Antworten