Hallo Forum,
mit folgendem Code versuche ich Daten in ein View zu bekommen, die sich in einer Methode einer Klasse (MyClass) außerhalb der aufrufenden Klasse (ViewModel) befinden. Bei Aufruf der Methode innerhalb ViewModel funktioniert der Code, außerhalb nicht. Auch wenn ich ein Object im Konstruktor erstelle geht es nicht. Wie ist die richtige Vorgehensweise?
namespace WpfApp8
{
class ViewModel : INotifyPropertyChanged
{
public MyCommand ActionCommand
{
get;
private set;
}
public ViewModel()
{
ActionCommand = new MyCommand()
{
CanExecuteFunc = obj => true,
//ExecuteFunc = MyActionFunc,
ExecuteFunc = MyClass.MyActionFunc
};
}
public event PropertyChangedEventHandler PropertyChanged;
private string myname;
public string MyName
{
get => myname;
set {
myname = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyName)));
}
}
public void MyActionFunc(object param)
{
MyName = "Fred";
}
}
public class MyClass
{
public static void MyActionFunc(object param)
{
string MyName = "Peter";
}
}
}
Und das Binding im View,
<TextBox Name="textBox1" Grid.Column="2" Grid.Row="2" Text="{Binding MyName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
VG Matthias
Hallo und willkommen,
deine Code innerhalb von MyClass.MyActionFunc
string MyName = "Peter";
bewirkt gar nichts, da du damit eine lokale Variable erstellst (die nichts mit der MyName
-Eigenschaft im ViewModel zu tun hat).
Warum willst du das denn auslagern?
Du könntest aber einfach den Text zurückgeben lassen:
MyName = MyClass.GetName();
public class MyClass
{
public static string GetName()
{
return "Peter";
}
}
Außerdem erstelle am besten für die INotifyPropertyChanged
-Funktionalität eine Basisklasse (häufig BaseViewModel
oder ViewModelBase
genannt).
Es gibt das CallerMemberNameAttribute
, um den Aufruf zu vereinfachen: How To Use CallerMemberName Attribute In WPF With MVVM Pattern.
PS: Falsches Subforum -> WPF
Hallo Th69,
vielen Dank für Deine Überlegung. Der Code macht natürlich nichts, weil ich noch keine Funktionalität zwischen den Klassen habe. Ich habe einiges rumprobiert mit Objekten und Settern etc.
Im konkreten Fall befindet sich in der Klasse ein Thread, daher ist eine Rückgabe mit
return
unpraktisch, weil der Task beendet würde. Außerdem steht die Variable
Name
nur beispielhaft für die übertragenden kontinuierlich erzeugten Daten aus dem Thread.
Ich denke in Richtung Interface, weiss aber nicht genau wie ich die Verbindung dann schaffe.
public class MyClass
{
public string MyName;
public static async Task MyActionFunc(object param)
{
string MyName = "Peter";
...
var task = Task.Run(() => { ... });
...
await task;
}
}
VG Matthias
PS: Soweit ich we iss gibt es in Net Core 5 kein Member ViewModelBase. Benutze daher ObservableObject.
Das ist ein Task, kein Thread. Tasks sind aber der richtige Weg.
Hast Du kontinuierlich Aktualisierungen, dann macht oft ein Message Pattern sinn, zB via Reactive Extensions.
https://github.com/dotnet/reactive
Daten werden in eine Subscription gepumpt, und das ViewModel reagiert auf Subscription Events, um Daten anzuzeigen (binden).
Erleichtert auch das asynchrone Handling enorm.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Wenn du aus einem Thread (bzw. Task) Daten an den UI-Thread senden willst, dann s. [FAQ] Controls von Thread aktualisieren lassen (Control.Invoke/Dispatcher.Invoke).
Und am besten dann mittels eines eigenen Ereignisses (mit entsprechend eigener von EventArgs
abgeleiteter Klasse): [FAQ] Eigenen Event definieren / Information zu Events (Ereignis/Ereignisse)
Es ginge zwar auch mit einem Interface (auch hier mittels Dispatcher.Invoke
), aber die Kopplung ist dann enger als bei einem Ereignis.
PS: Ich halte await Task.Run()
eher für ein Anti-Pattern (s. z.B. Avoiding simple mistakes in async await).