Laden...

Command enabled Button nicht

Erstellt von Quaneu vor 11 Jahren Letzter Beitrag vor 11 Jahren 2.526 Views
Quaneu Themenstarter:in
692 Beiträge seit 2008
vor 11 Jahren
Command enabled Button nicht

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

5.658 Beiträge seit 2006
vor 11 Jahren

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

Quaneu Themenstarter:in
692 Beiträge seit 2008
vor 11 Jahren

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

M
402 Beiträge seit 2005
vor 11 Jahren

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

Quaneu Themenstarter:in
692 Beiträge seit 2008
vor 11 Jahren

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

5.658 Beiträge seit 2006
vor 11 Jahren

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

Quaneu Themenstarter:in
692 Beiträge seit 2008
vor 11 Jahren

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);
    }
}