Vorsetzung letzter Post…
@BlonderHans:
Nach dem ersten Versuch wollte ich es mit der ReactiveUI weiter versuchen. Ich habe die beiden NuGet Pakete „ReactiveUI.Fody“ und „ReactiveUI.WPF“ installiert und meine Klassen erneut angepasst.
MainWindow.xaml
<Window x:Class="Fictiv.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Fictiv.View"
xmlns:localVM="clr-namespace:Fictiv.ViewModel" d:DataContext="{d:DesignInstance Type=localVM:MainViewModel}"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<Canvas Name="FictivExample" IsEnabled="True" >
<Label x:Name="lblHedline" Width="160" Height="40" HorizontalAlignment="Left" VerticalAlignment="Center"
FontSize="20" FontWeight="Bold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Canvas.Left="80" Canvas.Top="5" Content="Fictiv-Example" />
<GroupBox x:Name="grBoxPerson" BorderThickness="2" BorderBrush="Green" Width="160" Height="100" Header="Person" VerticalAlignment="Center" Canvas.Left="10" Canvas.Top="40" HorizontalAlignment="Left">
<Grid>
<StackPanel Orientation="Horizontal" Height="70" Width="135" Margin="0,0,0,0">
<UniformGrid Columns="2" Width="135" Margin="5,0,0,0" >
<!--First row-->
<Label x:Name="lblPersonName" Content="Name:" Height="25" Width="55" Margin="5,0,0,0"/>
<TextBox x:Name="txBoxPersonName" Height="25" Width="50" Margin="0,0,0,0" Text="{Binding Combine.Person.Name, UpdateSourceTrigger=PropertyChanged}"/>
<!--Second row-->
<Label x:Name="lblPersonOutput" Content="{Binding Combine.Person.Name}" Height="25" Margin="70,0,-59,0" Background="#FFC0F3E9" />
</UniformGrid>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox x:Name="grBoxAnimal" BorderThickness="2" BorderBrush="Blue" Width="160" Height="100" Header="Animal" VerticalAlignment="Center" Canvas.Left="180" Canvas.Top="40" HorizontalAlignment="Left">
<Grid>
<StackPanel Orientation="Horizontal" Height="70" Width="135" Margin="0,0,0,0" >
<UniformGrid Columns="2" Width="135" Margin="5,0,0,0" >
<!--First row-->
<Label x:Name="lblAnimalSpecies" Content="Species:" Height="25" Width="55" Margin="5,0,0,0"/>
<TextBox x:Name="txBoxAnimalSpecies" Height="25" Width="50" Margin="0,0,0,0" Text="{Binding Combine.Animal.Species, UpdateSourceTrigger=PropertyChanged}"/>
<!--Second row-->
<Label x:Name="lblAnimalOutput" Content="{Binding Combine.Animal.Species}" Height="25" Margin="70,0,-59,0" Background="#FFC0F3E9" />
</UniformGrid>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox x:Name="grBoxCombine" BorderThickness="2" BorderBrush="Orange" Width="330" Height="65" Header="Combine Person and Animal" VerticalAlignment="Top" Canvas.Left="10" Canvas.Top="150" HorizontalAlignment="Center">
<Grid>
<StackPanel Orientation="Horizontal" Margin="10,5,10,5">
<Button x:Name="btnCombine" Width="100" Height="25" Content="Combine" Command="{Binding Combine.Bind_BtnCombineCommand, Mode=OneWay}" />
<Label x:Name="lblCombineOutput" Content="{Binding Combine.Bind_combinedText}" Width="150" Height="25" Margin="30,0,-80,0" Background="#FFC0F3E9" />
</StackPanel>
</Grid>
</GroupBox>
</Canvas>
</Window>
MainViewModel.cs
public abstract class ViewModelBase : ReactiveObject
{
[Reactive] public bool IsInitialized { get; private set; }
public async Task InitializeAsync(CancellationToken cancellationToken = default)
{
IsInitialized = false;
await OnInitializeAsync(cancellationToken);
IsInitialized = true;
}
protected virtual Task OnInitializeAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
public class MainViewModel : ViewModelBase
{
public ViewModelCombine Combine { get; }
public MainViewModel(ViewModelCombine combine)
{
Combine = combine;
}
protected override async Task OnInitializeAsync(CancellationToken cancellationToken)
{
await base.OnInitializeAsync(cancellationToken);
await Combine.InitializeAsync(cancellationToken);
}
}
ViewModelAnimal.cs
public class ViewModelAnimal : ViewModelBase
{
[Reactive] public string? Species { get; set; }
// Constructor
public ViewModelAnimal() { }
}
ViewModelPerson.cs
public class ViewModelPerson : ViewModelBase
{
[Reactive] public string? Name { get; set; }
// Constructor
public ViewModelPerson() { }
}
ViewModelCombine.cs
public class ViewModelCombine : ViewModelBase
{
public ViewModelAnimal Animal { get; }
public ViewModelPerson Person { get; }
public ReactiveCommand<Unit, Unit> Bind_BtnCombineCommand { get; }
[Reactive] public string? Bind_combinedText { get; private set; }
// Constructor
public ViewModelCombine(ViewModelPerson person, ViewModelAnimal animal)
{
Person = person;
Animal = animal;
var canCombine = this.WhenAnyValue(e => e.Person.Name, e => e.Animal.Species, (name, species) => !string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(species));
Bind_BtnCombineCommand = ReactiveCommand.CreateFromTask(OnCombine, canCombine);
}
private async Task OnCombine(CancellationToken cancellationToken)
{
await Task.Delay(250);
Bind_combinedText = $"{Person.Name} - {Animal.Species}";
}
protected override async Task OnInitializeAsync(CancellationToken cancellationToken)
{
await base.OnInitializeAsync(cancellationToken);
await Animal.InitializeAsync(cancellationToken);
await Person.InitializeAsync(cancellationToken);
}
}
Ich habe mich hier noch zusätzlich etwas an Deinem Beispielprojekt „WpfApp“ orientiert. Allerdings klappt das in meinem Beispiel auch noch nicht. Ich denke ich habe das gleiche Problem wie schon in meinem ersten Versuch. Auch hier wird der Konstruktor nicht aufgerufen.
Wenn ich in Deinem Beispielprojekt einen Breakpoint im Konstruktor „MainViewModel“ platziere, dann scheint mit er so zu sein, als würde diese über die Program.cs, bzw. vermutlich über die App.xaml.cs aufgerufen zu werden? Da bin ich mir aber nicht sicher. Ich habe hier die Vermutung, dass es noch weitere Wege in C# gibt, wie man Konstruktoren, außer über die MainWindow.xaml aufrufen kann. 😊
Dann habe ich noch eine Frage zu Deinem Beispielprojekt. In der „CombineControl.xaml, werden in den „ContentControl“, lediglich „Person“ und „Animal“ gebunden. Woher ist an dieser Stelle klar, dass die Strings „Name“ und „Species“ verwendet werden soll?
Viele Grüße.
Hallo zusammen.
@Alf:
OK, dann habe ich das wirklich falsch verstanden. Danke nochmal für Deine Rückmeldung.
@BlonderHans:
Danke für Deine Vorschläge. Mich hat Dein Ansatz interessiert und daher wollte ich es zumindest mal nachimplementieren, um zu sehen, wie das Ganze funktioniert.
Der erste Versucht war, die Instanzen automatisch zu übergeben, wie Du es in den ersten Code Schnipseln dargestellt hast. Allerdings klappt das bei mir noch nicht. Zuvor wurde das „MainViewModel“ in der MainWindow.xaml durch den „<Window.DataContext>“ aufgerufen. Jetzt habe ich aber einen Konstruktor, welcher einen Parameter erwartet. Ich weiß jetzt nicht, wie ich diesen neun Konstruktor mit einem Parameter aufrufen kann. Daher bin ich der Meinung, dass ich da noch etwas nicht verstanden habe.
MainWindow.xaml
<Window x:Class="Fictiv.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Fictiv.View"
xmlns:localVM="clr-namespace:Fictiv.ViewModel" d:DataContext="{d:DesignInstance Type=localVM:MainViewModel}"
mc:Ignorable="d"
Title="MainWindow" Height="300" Width="400">
<Canvas Name="FictivExample" IsEnabled="True" >
<Label x:Name="lblHedline" Width="160" Height="40" HorizontalAlignment="Left" VerticalAlignment="Center"
FontSize="20" FontWeight="Bold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Canvas.Left="80" Canvas.Top="5" Content="Fictiv-Example" />
<GroupBox x:Name="grBoxPerson" BorderThickness="2" BorderBrush="Green" Width="160" Height="100" Header="Person" VerticalAlignment="Center" Canvas.Left="10" Canvas.Top="40" HorizontalAlignment="Left">
<Grid>
<StackPanel Orientation="Horizontal" Height="70" Width="135" Margin="0,0,0,0">
<UniformGrid Columns="2" Width="135" Margin="5,0,0,0" >
<!--First row-->
<Label x:Name="lblPersonName" Content="Name:" Height="25" Width="55" Margin="5,0,0,0"/>
<TextBox x:Name="txBoxPersonName" Height="25" Width="50" Margin="0,0,0,0" Text="{Binding Person.Bind_personNameText, UpdateSourceTrigger=PropertyChanged}"/>
<!--Second row-->
<Label x:Name="lblPersonOutput" Content="{Binding Person.Bind_personNameText}" Height="25" Margin="70,0,-59,0" Background="#FFC0F3E9" />
</UniformGrid>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox x:Name="grBoxAnimal" BorderThickness="2" BorderBrush="Blue" Width="160" Height="100" Header="Animal" VerticalAlignment="Center" Canvas.Left="180" Canvas.Top="40" HorizontalAlignment="Left">
<Grid>
<StackPanel Orientation="Horizontal" Height="70" Width="135" Margin="0,0,0,0" >
<UniformGrid Columns="2" Width="135" Margin="5,0,0,0" >
<!--First row-->
<Label x:Name="lblAnimalSpecies" Content="Species:" Height="25" Width="55" Margin="5,0,0,0"/>
<TextBox x:Name="txBoxAnimalSpecies" Height="25" Width="50" Margin="0,0,0,0" Text="{Binding Animal.Bind_animalSpeciesText, UpdateSourceTrigger=PropertyChanged}"/>
<!--Second row-->
<Label x:Name="lblAnimalOutput" Content="{Binding Animal.Bind_animalSpeciesText}" Height="25" Margin="70,0,-59,0" Background="#FFC0F3E9" />
</UniformGrid>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox x:Name="grBoxCombine" BorderThickness="2" BorderBrush="Orange" Width="330" Height="65" Header="Combine Person and Animal" VerticalAlignment="Top" Canvas.Left="10" Canvas.Top="150" HorizontalAlignment="Center">
<Grid>
<StackPanel Orientation="Horizontal" Margin="10,5,10,5">
<Button x:Name="btnCombine" Width="100" Height="25" Content="Combine" Command="{Binding Bind_btnCombineCommand, Mode=OneWay}" />
<Label x:Name="lblCombineOutput" Content="{Binding Bind_combineCombinedText}" Width="150" Height="25" Margin="30,0,-80,0" Background="#FFC0F3E9" />
</StackPanel>
</Grid>
</GroupBox>
</Canvas>
</Window>
MainViewModel.cs
public class MainViewModel
{
public ViewModelCombine Combine { get; }
public MainViewModel(ViewModelCombine combine)
{
Combine = combine;
}
}
ViewModelAnimal.cs
public class ViewModelAnimal : ObservableObject
{
// the viewmodel have to know the model.
private readonly Model.Animal Animal;
private string animalSpeciesText;
public string Bind_animalSpeciesText
{
get { return animalSpeciesText; }
set { SetProperty(ref animalSpeciesText, value); }
}
// Constructor
public ViewModelAnimal() { }
}
ViewModelPerson.cs
public class ViewModelPerson : ObservableObject
{
// the viewmodel have to know the model.
private readonly Model.Person Person;
private string personNameText;
public string Bind_personNameText
{
get { return personNameText; }
set { SetProperty(ref personNameText, value); }
}
// Constructor
public ViewModelPerson() { }
}
ViewModelCombine.cs
public class ViewModelCombine : ObservableObject
{
// the viewmodel have to know the model.
//private readonly Model.Combine CombineModel;
//private readonly ViewModel.ViewModelPerson Person;
//private readonly ViewModel.ViewModelAnimal Animal;
// neu in Fictiv_2
public ViewModelPerson Person { get; }
public ViewModelAnimal Animal { get; }
private string combineCombinedText;
public string Bind_combineCombinedText
{
get { return combineCombinedText; }
set { SetProperty(ref combineCombinedText, value); }
}
// button to combine person with animal text
public ICommand Bind_btnCombineCommand { get; }
// Constructor
public ViewModelCombine(ViewModelPerson person, ViewModelAnimal animal)
{
Bind_combineCombinedText = "EMPTY";
// handle binding methods
Bind_btnCombineCommand = new RelayCommand(BtnCombineCommand);
Person = person;
Animal = animal;
}
private void BtnCombineCommand()
{
//Bind_combineCombinedText = "Button has been pressed";
Bind_combineCombinedText = Person.Bind_personNameText;
Bind_combineCombinedText += " has a ";
Bind_combineCombinedText += Animal.Bind_animalSpeciesText;
}
}
to be continued...
Vorsetzung, Sorry...
@Th69:
Ich habe es verstanden, dass Du das gleiche meinst, wie „BlonderHans“ in seinem zweiten Punkt.
Ich habe das auch mal so implementiert wie Du es vorgeschlagen hast:
Ich poste hier den Code, so dass andere auch etwas davon haben:
MainViewModel.cs ⇒ NEU/ANGEPASST
namespace Fictiv.ViewModel
{
public class MainViewModel
{
public ViewModelAnimal Animal { get; private set; } = new ViewModelAnimal();
public ViewModelPerson Person { get; private set; } = new ViewModelPerson();
//public ViewModelCombine Combine { get; private set; } = new ViewModelCombine();
public ViewModelCombine Combine { get; init; }
public MainViewModel()
{
Combine = new ViewModelCombine(Person, Animal);
}
}
}
ViewModelCombine.cs ⇒ NEU/ANGEPASST
namespace Fictiv.ViewModel
{
public class ViewModelCombine : ObservableObject
{
// the viewmodel have to know the model.
private readonly Model.Combine CombineModel;
private readonly ViewModel.ViewModelPerson Person;
private readonly ViewModel.ViewModelAnimal Animal;
private string combineCombinedText;
public string Bind_combineCombinedText
{
get { return combineCombinedText; }
set { SetProperty(ref combineCombinedText, value); }
}
// button to combine person with animal text
public ICommand Bind_btnCombineCommand { get; }
// Constructor
public ViewModelCombine(ViewModelPerson person, ViewModelAnimal animal)
{
Bind_combineCombinedText = "EMPTY";
// handle binding methods
Bind_btnCombineCommand = new RelayCommand(BtnCombineCommand);
Person = person;
Animal = animal;
}
private void BtnCombineCommand()
{
//Bind_combineCombinedText = "Button has been pressed";
Bind_combineCombinedText = Person.Bind_personNameText;
Bind_combineCombinedText += " has a ";
Bind_combineCombinedText += Animal.Bind_animalSpeciesText;
}
}
}
Also zusammengefasst, der MessageBus scheint mir, zumindest im Moment als Anfänger, noch etwas aufwendig zu sein.
Bei der Dependecy Injection wird über den Konstruktor das entsprechende ViewModel übergeben. Das finde ich eine charmante Lösung. (Ich denke so ist der Ansatz von Dependency Injection gemeint)
Auf jeden Fall möchte ich mich bei allen für die vielen Vorschläge bedanken. Das hat mir wirklich sehr geholfen, weil das alles noch neu für mich war.
Viele Grüße und noch einen schönen Abend 😊
Hallo, alles zusammen.
Erst mal vielen Dank für die Vorschläge. Ich musste mich erst mal in die einzelnen Themen grob einlesen.
@BlonderHans:
Das mit dem „MessageBus“ kannte ich noch gar nicht. Was ich auf der Seite von Microsoft lesen kann, ist es schon dafür gedacht, dass ein Publish Event an mehrere Subscriber verteilt werden kann. Nutzt Du solch einen MassageBus in Deinen Projekten? Sind das dann große/Aufwendige Projekte?
DI? Das musste ich auch erst Googlen 😊 „Dependency Injection“. Ich denke in meinem Fall ist gemeint, dass ich dem „ViewModelCombine“ bereits bei der Instanziierung (über den Konstruktor) die beiden Strings mitgeben muss?
@Alf:
Du hast ja einen Link bezüglich eines Schichtmodels gepostet. Aber ist das nicht bereits das, was MVVM macht? Auf mich macht es den Eindruck, als wäre das so. Ich kann mich da aber auch irren.
So wie Du es mit dem ICommand beschrieben hast, nutze ich es bereits. (Ich denke so hast Du es gemeint)
Meine Klassen sehen so aus: (auf die Model-Klassen habe ich verzichten)
ViewModelAnimal.cs:
namespace Fictiv.ViewModel
{
public class ViewModelAnimal : ObservableObject
{
// the viewmodel have to know the model.
private readonly Model.Animal Animal;
private string animalSpeciesText;
public string Bind_animalSpeciesText
{
get { return animalSpeciesText; }
set { SetProperty(ref animalSpeciesText, value); }
}
// Constructor
public ViewModelAnimal() { }
}
}
ViewModelCombine.cs:
namespace Fictiv.ViewModel
{
public class ViewModelCombine : ObservableObject
{
// the viewmodel have to know the model.
private readonly Model.Combine Combine;
private string combineCombinedText;
public string Bind_combineCombinedText
{
get { return combineCombinedText; }
set { SetProperty(ref combineCombinedText, value); }
}
// button to combine person with animal text
public ICommand Bind_btnCombineCommand { get; }
// Constructor
public ViewModelCombine()
{
Bind_combineCombinedText = "EMPTY";
// handle binding methods
Bind_btnCombineCommand = new RelayCommand(BtnCombineCommand);
}
private void BtnCombineCommand()
{
Bind_combineCombinedText = "Button has been pressed";
/*
here the two strings "Person.Bind_personNameText"
and "Animal.Bind_animalSpeciesText" should be
added to eachother.
*/
}
}
}
ViewModelPerson.cs:
namespace Fictiv.ViewModel
{
public class ViewModelPerson : ObservableObject
{
// the viewmodel have to know the model.
private readonly Model.Person Person;
private string personNameText;
public string Bind_personNameText
{
get { return personNameText; }
set { SetProperty(ref personNameText, value); }
}
// Constructor
public ViewModelPerson() { }
}
}
MainViewModel.cs:
namespace Fictiv.ViewModel
{
public class MainViewModel
{
public ViewModelAnimal Animal { get; private set; } = new ViewModelAnimal();
public ViewModelPerson Person { get; private set; } = new ViewModelPerson();
public ViewModelCombine Combine { get; private set; } = new ViewModelCombine();
}
}
MainWindow.xaml
…
<Window.DataContext >
<localVM:MainViewModel/>
</Window.DataContext>
<Canvas Name="FictivExample" IsEnabled="True" >
<Label x:Name="lblHedline" Width="160" Height="40" HorizontalAlignment="Left" VerticalAlignment="Center"
FontSize="20" FontWeight="Bold" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Canvas.Left="80" Canvas.Top="5" Content="Fictiv-Example" />
<GroupBox x:Name="grBoxPerson" BorderThickness="2" BorderBrush="Green" Width="160" Height="100" Header="Person" VerticalAlignment="Center" Canvas.Left="10" Canvas.Top="40" HorizontalAlignment="Left">
<Grid>
<StackPanel Orientation="Horizontal" Height="70" Width="135" Margin="0,0,0,0">
<UniformGrid Columns="2" Width="135" Margin="5,0,0,0" >
<!--First row-->
<Label x:Name="lblPersonName" Content="Name:" Height="25" Width="55" Margin="5,0,0,0"/>
<TextBox x:Name="txBoxPersonName" Height="25" Width="50" Margin="0,0,0,0" Text="{Binding Person.Bind_personNameText, UpdateSourceTrigger=PropertyChanged}"/>
<!--Second row-->
<Label x:Name="lblPersonOutput" Content="{Binding Person.Bind_personNameText}" Height="25" Margin="70,0,-59,0" Background="#FFC0F3E9" />
</UniformGrid>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox x:Name="grBoxAnimal" BorderThickness="2" BorderBrush="Blue" Width="160" Height="100" Header="Animal" VerticalAlignment="Center" Canvas.Left="180" Canvas.Top="40" HorizontalAlignment="Left">
<Grid>
<StackPanel Orientation="Horizontal" Height="70" Width="135" Margin="0,0,0,0" >
<UniformGrid Columns="2" Width="135" Margin="5,0,0,0" >
<!--First row-->
<Label x:Name="lblAnimalSpecies" Content="Species:" Height="25" Width="55" Margin="5,0,0,0"/>
<TextBox x:Name="txBoxAnimalSpecies" Height="25" Width="50" Margin="0,0,0,0" Text="{Binding Animal.Bind_animalSpeciesText, UpdateSourceTrigger=PropertyChanged}"/>
<!--Second row-->
<Label x:Name="lblAnimalOutput" Content="{Binding Animal.Bind_animalSpeciesText}" Height="25" Margin="70,0,-59,0" Background="#FFC0F3E9" />
</UniformGrid>
</StackPanel>
</Grid>
</GroupBox>
<GroupBox x:Name="grBoxCombine" BorderThickness="2" BorderBrush="Orange" Width="330" Height="65" Header="Combine Person and Animal" VerticalAlignment="Top" Canvas.Left="10" Canvas.Top="150" HorizontalAlignment="Center">
<Grid>
<StackPanel Orientation="Horizontal" Margin="10,5,10,5">
<Button x:Name="btnCombine" Width="100" Height="25" Content="Combine" Command="{Binding Combine.Bind_btnCombineCommand}" />
<Label x:Name="lblCombineOutput" Content="{Binding Combine.Bind_combineCombinedText}" Width="150" Height="25" Margin="30,0,-80,0" Background="#FFC0F3E9" />
</StackPanel>
</Grid>
</GroupBox>
</Canvas>
Ich weiß, man sollte nicht so viel Code posten. Ich hoffe das war in diesem Fall OK. So haben andere Interessierte auch etwas davon 😊
Hi T-Virus,
danke für Deine Antwort. Ja, generell hast Du natürlich vollkommen Recht. Gerade was mein minimalistisches Beispiel betrifft, wäre es absolut einfacher, wenn man alles zusammen in einem Model und auch einem ViewModel hat.
Allerdings bin ich an einem anderen Projekt dran, bei dem ich deutlich mehr auf dem MainWindow anzeige. Da kommen schon einige Sachen zusammen und daher habe ich mich dazu entschlossen, nicht alles zusammen zu packen, da ich es übersichtlicher finde. Generell lässt sich das auch gut implementieren da die Teile eigentlich nichts miteinander zu tun haben. Es gibt nur einen Teil, der von einigen anderen Teilen doch Informationen benötigt, damit ich diese gesammelt irgendwie anzeigen kann. Deshalb habe ich jetzt diese Situation.
Ich habe daher die Frage, wenn man in MVVM mehrere ViewModelle hat, wie können diese Informationen austauschen?
Hallo Forumgemeinde,
ich habe erneut eine Anfängerfrage und ich interessiere mich dafür, wie man meine Situation am besten implementiert. Ich habe für mich mein Problem in ein Beispielprojekt heruntergebrochen. Ich könnte zwar dieses minimalistische Projekt hier posten, aber darauf soll laut Forenregel lieber verzichtet werden. Ich bin aber der Meinung, dass ich es auch so erklären kann, ohne dass man mein Projekt überhaupt anschauen muss.
Mein Projekt sieht so aus:
[Model]
- Animal.cs
- Combine.cs
- Person.cs
[View]
- MainWindow.xaml
[ViewModel]
- MainViewModel.cs
- ViewModelAnimal.cs
- ViewModelCombine.cs
- ViewModelPerson.cs
Ich habe im MainWindow drei GroupBoxes angelegt:
Die erste GroupBox "Person" hat eine TextBox, in der man einen Namen eintragen kann.
Die zweite GroupBox "Animal" hat eine TextBox, in der man eine Tier-Spezies eintragen kann.
Die dritter GroupBox "Combine" hat einen Button und ein Label. Wenn man in der dritten GroupBox den Button „Combine“ klickt, soll im Label der String aus der Person-TextBox mit dem String des Animal-TextBox zusammengeführt werden.
Was mir nicht klar ist, Woher bekommt das „ViewModelCombine“ die Daten, um den kombinierten String erstellen zu können? Muss in „ViewModelCombine“ zusätzlich die beiden Modelle „Person“ und „Animal“ eingebunden werden, oder werden die beiden ViewModelle „ViewModelPerson“ und „ViewModelAnimal“ eingebunden. Hierarchisch gesehen liegen die ViewModelle nebeneinander. Darf es hier „Verbindungen“ untereinander geben?
Wie schon gesagt, ich habe es auf ein Beispielprojekt herunter gebrochen. Ich habe ein eigenes ViewModel für „Combine“ angelegt, allerdings nur, um es exemplarisch für mich darzustellen. Kann man solche Dinge, die Informationen aus mehreren ViewModels benötigt, in einer Art „globales ViewModel“, oder sogar im MainViewModel abhandeln? Das MainViewModel existiert bei mir auch nur, um darin die beiden anderen zwei ViewModelle einzubinden, so dass diese dann im MainWindow genutzt werden können. In meinem Beispielprojekt finde ich ein eigenes ViewModel für den „Combine“-Button eigentlich übertrieben.
Ich denke eine solche Situation wie ich sie habe, ist bestimmt keine Seltenheit in Projekten und daher interessiert es mich, wie ihr es Implementieren würdet.
Nur noch zur Info: Diese Frage hier basiert auf eine ältere Frage, die ich bereits hier https://mycsharp.de/forum/threads/125590/wpf-mvvm-einbindung-mehrere-viewmodelle-in-einer-view gestellt habe. Die erste Frage, habe ich soweit sehr gut beantwortet bekommen und war Basis für diese zweite Frage.
Viele Grüße 😃
Hi Th69,
erst mal recht herzlichen Dank für Deine sehr schnelle und für mich gut erklärte Rückantwort. Ich habe es nachimplementiert und es hat auf Anhieb funktioniert. Alle Binding funktioniert wieder. Vielen Dank! Es ist tatsächlich ganz einfach und ich finde es so auch immer noch sehr logisch anzuwenden 😊
Danke auch für die zusätzliche Erklärungen und Links. Ich werde mir diese nochmal im Detail anschauen. „lostindetails“ kannte ich noch nicht.
Ich komme aus der C- Welt (µC) und C# erschlägt mich schon ein wenig. Trotz guter Lektüre und unzähligen Stunden im Netz. Das MVVM-Prinzip gefällt mir sehr gut. Das ist ähnlich wie in C, mit dem OSI-Schichtmodel. 😉
Genug abgeschweift... Danke nochmal für Deine Hilfe und einen schönen Sonntag noch 😊
Hallo Forumgemeinde,
ich bin neu hier und beschäftige mich seit einiger Zeit mit WPF und MVVM. Meine ersten Demo-Projekte haben auch gut funktioniert. Wenn man das mit der Bindungen mal verstanden hat, finde ich das einer sehr saubere Lösung.
Jetzt habe ich mich an ein größeres Projekt getraut. Ich bin zu dem Entschluss gekommen, dass ich neben mehreren „Modellen“, auch immer eine dazugehöriges „ViewModel“ erstellen möchte. Ich habe mich dazu entschieden, da eine einzelne „ViewModel- Datei“ sehr groß werden könnte und da leidet für mich immer die Übersichtlichkeit darunter.
In manchen Forenbeiträge liest man, es sollte bei einem „View“, auch nur eine „ViewModel“ geben. In anderen Forenbeiträge liest man wiederum, das MVVM nur ein Vorschlag ist und man es eigentlich halten kann, wie man es selbst bevorzugt.
In dem neuen Projekt bin ich jetzt auf das Problem gestoßen, dass ich nicht weiß, wie man mehrere „ViewModele“ in einer „View“ verwendet.
Mein Projekt sieht exemplarisch so aus:
[Model]
- Person.cs
- Tier.cs
[ViewModel]
- ViewModelPerson.cs
- ViewModelTier.cs
[View]
- MainWindow.xaml
Weil ich zuerst mit der Implementierung der Person angefangen habe, sah meine .xaml Datei erst so aus:
xmlns:localVM="clr-namespace:MyProject_1.ViewModel"
und dann noch:
<Window.DataContext >
<localVM:ViewModelPerson/>
</Window.DataContext>
So haben die Bindings auch wunderbar geklappt. So wie es bei meinen ersten Demo-Projekten, mit nur einem „ViewModel“, auch immer war. Jetzt wollte ich mit der Implementierung von „Tier“ weiter machen. Aber jetzt stehe ich vor dem Problem, wie bekomme ich das „ViewModelTier“ in die .xaml Datei? Man kann ja nur einen DataContext vergeben?
Klar, die eine Lösung wäre, ich nutze nur ein „ViewModel“. Arrangieren könnte ich mich schon damit, aber dann würde zum einen bei noch größeren Projekten das „ViewModel“ sehr groß werden und zum anderen Löst es nicht mein fehlendes Verständnis dafür, wie man es mit mehreren „ViewModels“ lösen kann. Da fehlt mir einfach noch die Erfahrung wie man das zusammenbringt.
Kann mir hier im Forum jemand weiterhelfen?
Wie macht ihr es denn?
Ein „ViewModel“ für eine „View“, auch bei mehreren „Modellen“? Oder seid Ihr auch der Meinung, dass das Aufteilen auf mehrere „ViewModelle“ die Übersichtlichkeit besser wird?
Schönen Sonntag nach und viel Grüße.