Laden...

Profil von StayCalm

myCSharp.de - Member Mitglied seit
S
StayCalm
myCSharp.de - Member
4
Themen
12
Beiträge
Dabei seit
10.12.2023
Letzte Aktivität
vor 5 Monaten
Erstellt vor 5 Monaten

Hallo Forumgemeinde,

ich habe eine Frage wie ich etwas einfacher implementieren kann.

Ich habe eine Oberfläche mit WPF/MVVM erstellt. Ich nutze eine serielle Schnittstelle, um Teile eines Microcontroller zu bedienen. Meine Oberfläche hat unter anderem 8 Bereiche, die optisch komplett gleich aussehen. Ich habe alle diese Bereiche aufwendig und separat erstellt. Wenn mir ein Fehler in einem Bereich aufgefallen ist, musste ich das Ganze natürlich in allen anderen Bereichen nach-korrigieren.

Ich würde jetzt gerne eine Art Vorlage erstellen, und diese dann in meinem Hauptfenster instanziieren. Ich weiß jetzt aber nicht, wie ich das am besten machen kann.

Ich habe im Internet schon über „user-controls“ gelesen, aber das sieht für mich immer danach aus, als sind das eher popups die mal für eine Eingabe genutzt werden können. Aber ich möchte die Vorlage permanent sehen.

Ich habe mal in Excel zwei Ansichten erstellt, um exemplarisch darzustellen, was ich meine. Das Bild zeigt oben links, wie ein Bereich generell aussehen soll. Es sollte ein Textfeld haben, um dem Bereich einen Namen zu geben. Es soll einen Slider habe, um Werte einzustellen und ein Feld um den eingestellten Wert als Status darzustellen. Zwei Buttons um Ein- und Aus- zu schalten. Und zwei Checkboxen.

Die andere Ansicht auf dem Bild zeigt dann, wie das Hauptfenster aussehen soll. Auf der Mainview soll die Vorlage dann mehrfach instanziiert werden. Wenn ich dann einen Fehler in der instanziierten Ansicht finde, muss ich das nur in der Vorlage anpassen, und nicht in allen Ansichten einzeln.

Ich weiß nicht, wie man so etwas realisiert. Geht das überhaupt?

Kann mir jemand hier einen Hinweis geben in welche Richtung ich suchen soll?

Habt ihr so etwas schonmal gemacht?

Viele Dank schonmal für eure Hilfe und viele Grüße

Erstellt vor 9 Monaten

Hallo zusammen,

der Fehlerteufel hat mein Problem gelöst.

Es ist doch möglich, dass man manuell das "Bind_bChBoxArea1ActiveIsChecked" zurück nimmt. Meine Gedanke war doch richtig.

Sorry, dass ich nicht richtig geschaut habe. Durch das Schreiben hier, ist mir der Fehler dann doch selber aufgefallen.

Schönen Sonntag noch und viele Grüße 😉

Erstellt vor 9 Monaten

Hallo Th69,

danke für Deinen Hinweis. Ich werde das mal ausprobieren.

Übrigens, ich sehe gerade, dass sich ein Fehlerteufel eingeschlichen hat.

In "set" stimmt die If-Abfrage nicht. Natürlich sollte die Abfrage auf "Bind_bChBoxArea1ActiveIsChecked" erfolgen, und nicht "Bind_bChBoxArea1EnableIsChecked"

Also:

// Checkbox button [Active] => "IsChecked" parameter
private bool bChBoxArea1ActivateIsChecked;
public bool Bind_bChBoxArea1ActiveIsChecked {
get { return bChBoxArea1ActivateIsChecked; }
set {
	SetProperty(ref bChBoxArea1ActivateIsChecked, value);
    // when [Active] on UI has been clicked, check the user dot settings
    if(Bind_bChBoxArea1ActiveIsChecked) {
    	validateArea1DotSettings();
        } else {
        // otherwise dehighlight everything
        SetStateArea1(AreaStatus.AREA_STATE_WAIT_TO_ACTIVATE);
        }
	}
}

Mit der Enable Checkbox soll generell alles aktiviert werden. Ist ein Schritt vor der Möglichkeit, alles zu aktivieren. Daher der Validierungs-Schritt dazwischen.
Sorry.
ich probiere das mal mit dem Flag.

Viele Grüße.

Erstellt vor 9 Monaten

Hallo Forum-Gemeinde. Ich bin noch recht neu in der C# Welt unterwegs und habe daher vermutlich eher eine Anfängerfrage.

Ich habe auf dem UI einen Bereich „Area1“ welche aktiviert und deaktiviert werden kann. Ich realisiere das Ganze über eine einfache Checkbox.

Allerdings gibt es zwei Textboxen zu dieser Area, die beim Aktivieren nicht leer bzw. auch nicht Null sein dürfen.

Meine Idee war jetzt, wenn ich von der Checkbox das „IsChecked“ auswerte, wollte ich noch eine Validierung ausführen um sicher zu stellen, dass die TextBoxen gültig sind.

Allerdings habe ich ein Problem, dass ich nicht weiß, was ich tun soll, wenn die Validierung fehl schlägt. Denn in diesem Fall wurde die Checkbox bereits im UI „gehighlighted“ und ich kann es nicht mehr zurück nehmen so dass die Eingabe der Textboxen erneut vorgenommen werden muss. Ich habe versucht, das manuell wieder zurück zu nehmen aber dann renne ich „natürlich“ in einen Loop. Der Code ruft sich quasi wieder selbständig auf.

Was da passiert, kann ich irgendwie nachvollziehen.

Meine Frage ist jetzt, kann ich meine Idee überhaupt mit einer Checkbox lösen? Muss ich es vielleicht über einen anderen Weg lösen? Vielleicht über einen Button? Oder gibt es doch eine Möglichkeit über die Checkbox, ich weiß nur nicht wie man das macht?

Die CheckBox in der MainWindow.xaml:

<CheckBox x:Name="chBoxArea1Active" Content="Active" Margin="20,10,0,0" Height="17" IsChecked="{Binding Control.Bind_bChBoxArea1ActiveIsChecked}" IsEnabled="{Binding Control.Bind_bChBoxArea1ActiveIsEnabled}" />		

In der ViewModel Klasse Control:

// Checkbox button [Active] => "IsChecked" parameter
private bool bChBoxArea1ActivateIsChecked;
public bool Bind_bChBoxArea1ActiveIsChecked {
get { return bChBoxArea1ActivateIsChecked; }
set {
	SetProperty(ref bChBoxArea1ActivateIsChecked, value);
    // when [Active] on UI has been clicked, check the user dot settings
    if(Bind_bChBoxArea1EnableIsChecked) {
    	validateArea1DotSettings();
        } else {
        // otherwise dehighlight everything
        SetStateArea1(AreaStatus.AREA_STATE_WAIT_TO_ACTIVATE);
        }        
	}
}

private void validateArea1DotSettings() {
	if (!(validateUserDots(Bind_txBoxArea1FirstDotValue, Bind_txBoxArea1MaxDotsValue, "Area 1 settings"))) {
   	// uncheck again the checkBox because it hast just been checked on the UI
	Bind_bChBoxArea1ActiveIsChecked = false; // => DURCH DAS SETZEN AUF FALSE WIRD HIER ALLES WIEDER ERNEUT AUFGERUFEN
	// AN DIESER STELLE WÜRDE ICH GERNE DIE CHECKBOX WIEDER ZURÜCK NEHMEN,
	// SO DASS AM UI DIE TEXTBOXEN WIEDER NEU AUSGEFÜLLT WERDEN MÜSSEN
	return;
	}
	SetStateArea1(AreaStatus.AREA_STATE_ACTIVE);
}

Ich denke es ist nicht nötig, die Methode „SetStateArea1“ hier zu posten.

Also die Idee war, wenn die Validierung fehlschlägt, dann wollte ich das IsChecked wieder zurück nehmen, so dass die Checkbox auf dem UI wieder zurück genommen wird. Das manuelle zurücknehmen fürhrt dann aber dazu, dass das "set" für das Binding "Bind_bChBoxArea1ActiveIsChecked" wieder ausgelöst wird.
Ich hoffe ich habe es einigermaßen gut erklärt, was ich vor habe.

Vielleicht hat jemand eine Idee, wie man eine Validierung in einem CheckBox einbauen kann, so dass die Checkbox nur dann aktiv auf dem UI erscheint, wenn die Validierung auch OK war.

Danke schonmal für eure Hilfe.

Viele Grüße und einen schönes Wochenende (",)

Erstellt vor 2 Jahren

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.

Erstellt vor 2 Jahren

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...

Erstellt vor 2 Jahren

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 😊

Erstellt vor 2 Jahren

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 😊

Erstellt vor 2 Jahren

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?

Erstellt vor 2 Jahren

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 😃