Hallo zusammen,
ich habe folgendes Command in einem UserControl und dessen ViewModel:
<Button Content="Open" Command="{Binding OpenCommand}" />
this.OpenCommand = new RelayCommand(Open, CanOpen); // Im Konstruktor
...
private void Open(Object parameter)
{
}
private Boolean CanOpen(Object parameter)
{
return Controller.Instance.IsConnected;
}
In dem MainWindow gibt es einen Button, bei dessen Click eine Connection zur Datenbank aufgebaut wird. Daher gibt es in dem ViewModel (= Controller ist eine Singleton Klasse) für das MainWindow folgenden Code.
this.OpenConnectionCommand = new RelayCommand(OpenConnection); // Im Konstruktor
...
private void OpenConnection(Object parameter)
{
BackgroundWorker connectionBackgroundWorker = new BackgroundWorker();
connectionBackgroundWorker.WorkerSupportsCancellation = false;
connectionBackgroundWorker.WorkerReportsProgress = false;
connectionBackgroundWorker.RunWorkerCompleted += _connectionBackgroundWorker_RunWorkerCompleted;
connectionBackgroundWorker.DoWork += _connectionBackgroundWorker_DoWork;
connectionBackgroundWorker.RunWorkerAsync();
}
private void _connectionBackgroundWorker_DoWork(Object sender, DoWorkEventArgs e)
{
Connector.OpenConnection();
}
private void _connectionBackgroundWorker_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
this.IsConnected = true;
}
else
{
HandleException(e.Error);
}
}
public Boolean IsConnected
{
get { return this._isConnected; }
set
{
this._isConnected = value;
OnPropertyChanged("IsConnected");
}
}
Wenn ich nun den Button klicke wird die Verbindung aufgebaut und das Property IsConnected auf true gesetzt. Doch leider bleibt der Button Open in dem UserControl disabled. Sobald ich aber in das UserControl klicke "aktualisiert" sich der Button.
Es funktioniert auch, wenn ich keinen BackgroundWorker verwende.
private void OpenConnection(Object parameter)
{
Connector.OpenConnection();
this.IsConnected = true;
}
Doch leider verstehe ich nicht warum sich die GUI nicht updatet.
Ich hoffe mir kann jemand weiterhelfen.
Schöne Grüße
Quaneu
Hi Quaneu,
du müßtest das ICommand.CanExecuteChanged-Ereignis auslösen, damit sich deine GUI aktualisieren kann. Das RelayCommand bietet dafür die RaiseCanExecuteChanged-Methode an.
Christian
Weeks of programming can save you hours of planning
Hallo Christian,
nur um sicher zu gehen, das RelayCommand sieht wie folgt aus:
public sealed class RelayCommand : ICommand
{
#region Fields
private readonly Action<Object> _execute;
private readonly Predicate<Object> _canExecute;
#endregion
#region Constructors
public RelayCommand(Action<Object> execute, Predicate<Object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this._execute = execute;
this._canExecute = canExecute;
}
public RelayCommand(Action<Object> execute)
: this(execute, null)
{ }
#endregion
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(Object parameter)
{
return this._canExecute == null ? true : this._canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(Object parameter)
{
this._execute(parameter);
}
#endregion
}
Und es funktionert ja "normal" auch. Doch wenn ich über den BackgroundWorker gehe, klappt es nicht. Z.B. führe ich den disconnect nciht über einen BackgroundWorker und das UserControl setzt den Button sofort auf disabled.
Schöne Grüße
Quaneu
Hi...
ich vermute dass du aus dem Background-Worker keinen Zugriff auf das UI hast.
Versuch mal die "IsConnected" - Property per Dispatcher zu setzen.
Dispatcher.BeginInvoke((Action)(() => { this.IsConnected = true; }));
Bin mir aber nicht sicher ob das eine "saubere" Lösung ist...
lg
Hallo M@TUK,
ich vermute dass du aus dem Background-Worker keinen Zugriff auf das UI hast.
Daran dürfte es nicht liegen, da ich das Property in
private void _connectionBackgroundWorker_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
{
...
}
setzte und hier ist er wieder im "Main-Thread".
Schöne Grüße
Quaneu
Hi Quaneu,
Und es funktionert ja "normal" auch. Doch wenn ich über den BackgroundWorker gehe, klappt es nicht. Z.B. führe ich den disconnect nciht über einen BackgroundWorker und das UserControl setzt den Button sofort auf disabled.
Ich weiß jetzt nicht, was genau du connectest und disconnectest, und ob es da evtl. zu Threadproblemen kommen könnte. Auf jeden Fall solltest du sicherstellen, daß die GUI darüber informiert wird, daß sich der Zustand deines Commands geändert hat!
Du machst das derzeit über diese Konstruktion:
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Dazu gibt es bereits einen Beitrag: Frage zum RelayCommand
Dort schreibt xxxprod:
Ich finde die Implementierung mittels CommandManager.RequerySuggested auch eher unschön:
Es wird dann ja quasi für jeden Command der gerade in einer View steckt bei jeder Änderung die die View bemerkt ein CanExecuteChanged erzeugt oder? Zusätzlich bekommt der Command nichts davon mit, wenn sich dessen Zustand im Hintergrund geändert haben sollte.
Hier kann man nachlesen, wie das ganze eigentlich funktioniert:
What is the actual task of CanExecuteChanged and CommandManager.RequerySuggested?.
Kurz gesagt, das Event wird relativ oft aufgerufen, aber eben manchmal nicht, wenn es eigentlich gebraucht wird.
Daß es auch Probleme bei asynchronen Methoden bereiten kann, kann man hier nachlesen: How does CommandManager.RequerySuggested work?.
However, I can tell you that you should be careful when using the CommandManager in connection with asynchronous operations.
Die Lösung ist eigentlich nur, dein Command an der richtigen Stelle selbst das Event werfen zu lassen.
Christian
Weeks of programming can save you hours of planning
Hallo MrSparkle,
nochmals vielen Dank für Deine Hilfe.
Ich weiß jetzt nicht, was genau du connectest und disconnectest
Also ich stelle hier eine Verbindung mit einer Datenbank her. Da das verbinden manchmal länger dauert geschieht dies in einem BackgroundWorker. Wenn dieser fertig ist und zurück zum Main-Thread kommt setzte ich das Property IsConnected. Daher sollten auch keine Threadproblemen auftreten.
Ich habe jetzt einen interessanten Artikel hierzu gefunden
Stackoverflow - CommandManager
Und habe das Problem "gelöst" indem ich ein OnPropertyChanged ausführe.
D.h. in meiner Controller Klasse gibt es ein Property ActivController an das sich ein ContentPresenter bindet, dieser stellt das UserControl dar, indem die Buttons nicht aktualisiert werden.
private void _connectionBackgroundWorker_RunWorkerCompleted(Object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
this.IsConnected = true;
OnPropertyChanged("ActivController");
}
else
{
HandleException(e.Error);
}
}