WPF SimpleCommand possible with generics? - c#

I am using this code to make a Simple Command:
public class SimpleCommand : ICommand
{
public Predicate<object> CanExecuteDelegate { get; set; }
public Action<object> ExecuteDelegate { get; set; }
#region ICommand Members
public bool CanExecute(object parameter)
{
if (CanExecuteDelegate != null)
return CanExecuteDelegate(parameter);
return true;// if there is no can execute default to true
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
if (ExecuteDelegate != null)
ExecuteDelegate(parameter);
}
#endregion
}
I did not write this. But I enjoy using it. When I use it it ends up being like this:
// This is the value that gets set to the command in the UI
public SimpleCommand DoSomethingCommand { get; set; }
public DoSomethingCommandConstructor()
{
DoSomethingCommand = new SimpleCommand
{
ExecuteDelegate = x => RunCommand(x)
};
}
private void RunCommand(object o)
{
// Run the command.
}
The only problem with this is that the parameter of RunCommand is an object. I think I have been spoiled by generics. I always want the IDE/compiler to just know what the type I am working with is with out casting.
Is it possible to change this SimpleCommand class to be implemented using generics?

Sure. Was gonna point you to Prism's implementation, but CodePlex source tab seems to not be working. It would look something like:
public class SimpleCommand<T> : ICommand
{
public Predicate<T> CanExecuteDelegate { get; set; }
public Action<T> ExecuteDelegate { get; set; }
#region ICommand Members
public bool CanExecute(object parameter)
{
if (CanExecuteDelegate != null)
return CanExecuteDelegate((T)parameter);
return true;// if there is no can execute default to true
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
if (ExecuteDelegate != null)
ExecuteDelegate((T)parameter);
}
#endregion
}
Incidentally, your usage of SimpleCommand in your question is a little roundabout. Instead of this:
DoSomethingCommand = new SimpleCommand
{
ExecuteDelegate = x => RunCommand(x)
};
You could just have:
DoSomethingCommand = new SimpleCommand
{
ExecuteDelegate = this.RunCommand
};
Specifying a lambda is really only useful if you're doing the work inline like this:
DoSomethingCommand = new SimpleCommand
{
ExecuteDelegate = o => this.SelectedItem = o,
CanExecuteDelegate = o => o != null
};

Related

using ICommand interface for CRUD

I am working on WPF (MVVM) ..
using this tutorial :
https://www.tutorialspoint.com/mvvm/mvvm_validations.htm
when trying to implement "AddEditCustomerViewModel"
I received two errors for this class:
namespace MVVMHierarchiesDemo.ViewModel
{
class AddEditCustomerViewModel : BindableBase
{
public AddEditCustomerViewModel()
{
CancelCommand = new MyICommand(OnCancel);
SaveCommand = new MyICommand(OnSave, CanSave);
}
private bool _EditMode;
public bool EditMode
{
get { return _EditMode; }
set { SetProperty(ref _EditMode, value); }
}
private SimpleEditableCustomer _Customer;
public SimpleEditableCustomer Customer
{
get { return _Customer; }
set { SetProperty(ref _Customer, value); }
}
private Customer _editingCustomer = null;
public void SetCustomer(Customer cust)
{
_editingCustomer = cust;
if (Customer != null) Customer.ErrorsChanged -= RaiseCanExecuteChanged;
Customer = new SimpleEditableCustomer();
Customer.ErrorsChanged += RaiseCanExecuteChanged;
CopyCustomer(cust, Customer);
}
private void RaiseCanExecuteChanged(object sender, EventArgs e)
{
SaveCommand.RaiseCanExecuteChanged();
}
public MyICommand CancelCommand { get; private set; }
public MyICommand SaveCommand { get; private set; }
public event Action Done = delegate { };
private void OnCancel()
{
Done();
}
private async void OnSave()
{
Done();
}
private bool CanSave()
{
return !Customer.HasErrors;
}
}
}
First one;on this lines :
CancelCommand = new MyICommand(OnCancel);
SaveCommand = new MyICommand(OnSave, CanSave);
an error appear on MyICommand constructor as in :
Error CS0305 Using the generic type 'MyICommand' requires 1 type
arguments
Second one , on this line :
CopyCustomer(cust, Customer);
an error appear for CopyCustomer function as in :
Error CS0103 The name 'CopyCustomer' does not exist in the current
context
the implementation of MyICommand as in :
public class MyICommand<T> : ICommand
{
Action<T> _TargetExecuteMethod;
Func<T, bool> _TargetCanExecuteMethod;
public MyICommand(Action<T> executeMethod)
{
_TargetExecuteMethod = executeMethod;
}
public MyICommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_TargetExecuteMethod = executeMethod;
_TargetCanExecuteMethod = canExecuteMethod;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
if (_TargetCanExecuteMethod != null)
{
T tparm = (T)parameter;
return _TargetCanExecuteMethod(tparm);
}
if (_TargetExecuteMethod != null)
{
return true;
}
return false;
}
// Beware - should use weak references if command instance lifetime is
//longer than lifetime of UI objects that get hooked up to command
// Prism commands solve this in their implementation
public event EventHandler CanExecuteChanged = delegate { };
void ICommand.Execute(object parameter)
{
if (_TargetExecuteMethod != null)
{
_TargetExecuteMethod((T)parameter);
}
}
#endregion
}
}
to see the whole Project folder ; please see this link :
https://drive.google.com/drive/folders/1jFDU6vDMW_TO0J88NT53oLVWVYbAYyTv?usp=sharing

Message box disappears when using a relay command

I have a RelayCommand class:
public class RelayCommand : ICommand
{
private readonly Func<bool> canExecuteEvaluator;
private readonly Action methodToExecute;
public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
{
this.methodToExecute = methodToExecute;
this.canExecuteEvaluator = canExecuteEvaluator;
}
public RelayCommand(Action methodToExecute)
: this(methodToExecute, null)
{
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
if (this.canExecuteEvaluator == null)
{
return true;
}
else
{
bool result = this.canExecuteEvaluator.Invoke();
return result;
}
}
public void Execute(object parameter)
{
this.methodToExecute.Invoke();
}
In my view model I create my commands like this:
public RelayCommand CmdUpdate { get; private set; }
CmdUpdate = new RelayCommand(() => Updater.CheckForUpdate());
Updater.CheckForUpdate() simply checks if there is an update available and shows an message box.
I then bind the command to a menu item in my view.
When I press the item, the message box pops up for a split second and disappears again. Where is the propblem. An explanation is highly appreciated, so I can understand it.

How to observe multiple properties with Delegatecommand.ObservesProperty

I would like to observe multiple properties in my viewmodel to check the CanExecute Method.
Question:
How to register more properties?
Example:
class MyViewModel
{
public int myproperty1 { get; set; }
public int myproperty2 { get; set; }
public DelegateCommand MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new DelegateCommand(MyCommandMethod,CanExecuteMyCommandMethod);
MyCommand.ObservesProperty((() => myproperty1));
// line below doesnt work Exeception "Value is already observed". How to register more properties to observe?
MyCommand.ObservesProperty((() => myproperty2));
}
private bool CanExecuteMyCommandMethod()
{
throw new NotImplementedException();
}
private void MyCommandMethod()
{
throw new NotImplementedException();
}
}
Update:
PropertChanged event is done by Propertchanged.Fody INPC.
ObservesProperty is to RaiseCanExecuteChanged.
The second property observe registration raise a exception "Value is already observed).
Additional Question:
ObservesProperty((() => myproperty;
Allows multiple or single property?
If somebody else confirm multiple registration shall be possible?
Calling the ObservesProperty method for another property like you are doing should work. But you need to raise the PropertyChanged event in the setters of the source properties for the CanExecuteChanged event to be raised.
Please refer to the following sample code.
public class MyViewModel : BindableBase
{
private int _myproperty1;
public int myproperty1
{
get { return _myproperty1; }
set { _myproperty1 = value; RaisePropertyChanged(); }
}
private int _myproperty2;
public int myproperty2
{
get { return _myproperty2; }
set { _myproperty2 = value; RaisePropertyChanged(); }
}
public DelegateCommand MyCommand { get; set; }
public MyViewModel()
{
MyCommand = new DelegateCommand(MyCommandMethod, CanExecuteMyCommandMethod);
MyCommand.ObservesProperty((() => myproperty1));
MyCommand.ObservesProperty((() => myproperty2));
}
private bool CanExecuteMyCommandMethod()
{
return true;
}
private void MyCommandMethod()
{
}
}

How refresh textbox binding in a thread

The following code is an example to explain my issue.
I have a textbox in binding. When I click on a button, executes a function. (In this case it's a for loop).
Now I want, the text box updated with the content of the i variable. (public void MyAction())
So, I made a thread, but this doesn't work.
Why ?
Thanks in advance
XAML code
<TextBox Text ="{Binding MyValue}" HorizontalAlignment="Left" Height="47" Margin="4,4,4,4" VerticalAlignment="Top" Width="342"/>
<Button Command="{Binding ClickCommand}" Content="Run" Margin="114,69,283,216"/>
C# code
class Vm_Main : ViewModelBase
{
public string _MyValue { get; set; } //String in my XAML
public string MyValue
{
get { return _MyValue; }
set
{
_MyValue = value;
base.OnPropertyChanged("MyValue");
}
}
private bool _canExecute=true;
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
}
}
public Vm_Main()
{
MyValue = "Hallo"; //Default value
}
public void MyAction() // This is the function where I want update the TextBox in Binding
{
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
workerThread.Start();
for (int i = 1; i <= 10; i++)
{
MyValue = i.ToString();
workerObject.Value = MyValue;
Thread.Sleep(500);
}
workerObject.RequestStop();
workerThread.Join();
MessageBox.Show("End");
}
}
// The THREAD
public class Worker : ViewModelBase
{
// This method will be called when the thread is started.
public string _Value { get; set; }
public string Value
{
get { return _Value; }
set
{
_Value = value;
base.OnPropertyChanged("Value");
}
}
public void DoWork()
{
while (!_shouldStop)
{
Console.WriteLine("Value is..." + _Value);
}
Console.WriteLine("End.");
}
public void RequestStop()
{
_shouldStop = true;
}
// Volatile is used as hint to the compiler that this data
// member will be accessed by multiple threads.
private volatile bool _shouldStop;
}
And Class ViewModelBase and Class Command
public abstract class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string strPropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
}
#endregion
#region Messages and Progressbar
private string _errorMessage;
public string ErrorMessage
{
get { return _errorMessage; }
set
{
if (_errorMessage == value) return;
_errorMessage = value;
OnPropertyChanged("ErrorMessage");
}
}
private string _errorTooltip;
public string ErrorTooltip
{
get { return _errorTooltip; }
set
{
if (_errorTooltip == value) return;
_errorTooltip = value;
this.OnPropertyChanged("ErrorTooltip");
}
}
private string _statusMessage;
public string StatusMessage
{
get { return _statusMessage; }
set
{
if (_statusMessage == value) return;
_statusMessage = value;
//pulizia dei messaggi di errore
ErrorMessage = string.Empty;
ErrorTooltip = string.Empty;
OnPropertyChanged("StatusMessage");
}
}
protected void ClearMessage()
{
ErrorMessage = string.Empty;
StatusMessage = string.Empty;
}
private int _currentProgress;
public int CurrentProgress
{
get { return _currentProgress; }
set
{
_currentProgress = value;
OnPropertyChanged("CurrentProgress");
}
}
#endregion
protected ViewModelBase()
{
}
}
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
So I think you have to issues.
First use async await pattern.
It goes like this - to not block ur UI thread.
public async Task<object> MyAsyncMethod()
{
return await Task.Run<object>(() =>
{
return null;
});
}
or for ICommand just:
public async void MyAsyncMethod()
{
await Task.Run(() =>
{
});
}
Second is that you may want to update your UI - while processing async. This is a common problem for updating progress for example. You can solve this with SynchronizationContext.
public interface IUpdateProgess
{
void SendMessage(string val);
}
public async Task<object> MyAsyncMethod(IUpdateProgess progress)
{
//UI thread
SynchronizationContext context = SynchronizationContext.Current;
return await Task.Run<object>(() =>
{
//other thread
if (context != null && progress != null)
{
context.Post(new SendOrPostCallback((o) =>
{
progress.SendMessage("Progress");
}), null);
}
return null;
});
}
You can use this obviously to not just update progress - i think you get the idea.

c# object with a ValueChanged event that fires when each property is changed

What is the best way to create a class with an event that fires when one of its Properties is changed? Specifically, how do you convey to any subscribers which Property was changed?
Ex:
public class ValueChangedPublisher
{
private int _prop1;
private string _prop2;
public static event ValueChangedHandler(/*some parameters?*/);
public int Prop1
{
get { return _prop1; }
set
{
if (_prop1 != value)
{
_prop1 = value;
ValueChangedHandler(/*parameters?*/);
}
}
}
public string Prop2
{
get { return _prop2; }
set
{
if (_prop2 != value)
{
_prop2 = value;
ValueChangedHandler(/*parameters?*/);
}
}
}
}
public class ValueChangedSubscriber
{
private int _prop1;
private string _prop2;
public ValueChangedSubscriber()
{
ValueChangedPublisher.ValueChanged += ValueChanged;
}
private void ValueChanged(/*parameters?*/)
{
/*how does the subscriber know which property was changed?*/
}
}
My goal is to make this as extensible as possible (e.g. I don't want a bunch of huge if/else if/switch statements lumbering around). Does anybody know of a technique to achieve what I'm looking for?
EDIT:
What I'm really looking for is how to utilize the INotifyPropertyChanged pattern on the subscriber side. I don't want to do this:
private void ValueChanged(string propertyName)
{
switch(propertyName)
{
case "Prop1":
_prop1 = _valueChangedPublisher.Prop1;
break;
case "Prop2":
_prop2 = _valueChangedPublisher.Prop2;
break;
// the more properties that are added to the publisher, the more cases I
// have to handle here :/ I don't want to have to do it this way
}
}
.NET provides the INotifyPropertyChanged interface. Inherit and implement it:
public class ValueChangedPublisher : INotifyPropertyChanged
{
private int _prop1;
private string _prop2;
public event PropertyChangedEventHandler ValueChangedHandler;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public int Prop1
{
get { return _prop1; }
set
{
if (_prop1 != value)
{
_prop1 = value;
NotifyPropertyChanged();
}
}
}
public string Prop2
{
get { return _prop2; }
set
{
if (_prop2 != value)
{
_prop2 = value;
NotifyPropertyChanged();
}
}
}
}
Here is what I did to solve my problem. It's a bit large, so maybe not performant, but works for me.
Edit
See this question for performance details: C# using properties with value types with Delegate.CreateDelegate
Base Class for publishing property change stuff:
public abstract class PropertyChangePublisherBase : INotifyPropertyChanged
{
private Dictionary<string, PropertyInfo> _properties;
private bool _cacheProperties;
public bool CacheProperties
{
get { return _cacheProperties; }
set
{
_cacheProperties = value;
if (_cacheProperties && _properties == null)
_properties = new Dictionary<string, PropertyInfo>();
}
}
protected PropertyChangePublisherBase(bool cacheProperties)
{
CacheProperties = cacheProperties;
}
public bool ContainsBinding(PropertyChangedEventHandler handler)
{
if (PropertyChanged == null)
return false;
return PropertyChanged.GetInvocationList().Contains(handler);
}
public object GetPropertValue(string propertyName)
{
if (String.IsNullOrEmpty(propertyName) || String.IsNullOrWhiteSpace(propertyName))
throw new ArgumentException("Argument must be the name of a property of the current instance.", "propertyName");
return ProcessGetPropertyValue(propertyName);
}
protected virtual object ProcessGetPropertyValue(string propertyName)
{
if (_cacheProperties)
{
if (_properties.ContainsKey(propertyName))
{
return _properties[propertyName].GetValue(this, null);
}
else
{
var property = GetType().GetProperty(propertyName);
_properties.Add(propertyName, property);
return property.GetValue(this, null);
}
}
else
{
var property = GetType().GetProperty(propertyName);
return property.GetValue(this, null);
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Base Class for receiving property change stuff:
public abstract class PropertyChangeSubscriberBase
{
protected readonly string _propertyName;
protected virtual object Value { get; set; }
protected PropertyChangeSubscriberBase(string propertyName, PropertyChangePublisherBase bindingPublisher)
{
_propertyName = propertyName;
AddBinding(propertyName, this, bindingPublisher);
}
~PropertyChangeSubscriberBase()
{
RemoveBinding(_propertyName);
}
public void Unbind()
{
RemoveBinding(_propertyName);
}
#region Static Fields
private static List<string> _bindingNames = new List<string>();
private static List<PropertyChangeSubscriberBase> _subscribers = new List<PropertyChangeSubscriberBase>();
private static List<PropertyChangePublisherBase> _publishers = new List<PropertyChangePublisherBase>();
#endregion
#region Static Methods
private static void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
string propertyName = args.PropertyName;
if (_bindingNames.Contains(propertyName))
{
int i = _bindingNames.IndexOf(propertyName);
var publisher = _publishers[i];
var subscriber = _subscribers[i];
subscriber.Value = publisher.GetPropertValue(propertyName);
}
}
public static void AddBinding(string propertyName, PropertyChangeSubscriberBase subscriber, PropertyChangePublisherBase publisher)
{
if (!_bindingNames.Contains(propertyName))
{
_bindingNames.Add(propertyName);
_publishers.Add(publisher);
_subscribers.Add(subscriber);
if (!publisher.ContainsBinding(PropertyChanged))
publisher.PropertyChanged += PropertyChanged;
}
}
public static void RemoveBinding(string propertyName)
{
if (_bindingNames.Contains(propertyName))
{
int i = _bindingNames.IndexOf(propertyName);
var publisher = _publishers[i];
_bindingNames.RemoveAt(i);
_publishers.RemoveAt(i);
_subscribers.RemoveAt(i);
if (!_publishers.Contains(publisher))
publisher.PropertyChanged -= PropertyChanged;
}
}
#endregion
}
Actual class to use for subscribing to property change stuff:
public sealed class PropertyChangeSubscriber<T> : PropertyChangeSubscriberBase
{
private PropertyChangePublisherBase _publisher;
public new T Value
{
get
{
if (base.Value == null)
return default(T);
if (base.Value.GetType() != typeof(T))
throw new InvalidOperationException(String.Format("Property {0} on object of type {1} does not match the type Generic type specified {2}.", _propertyName, _publisher.GetType(), typeof(T)));
return (T)base.Value;
}
set { base.Value = value; }
}
public PropertyChangeSubscriber(string propertyName, PropertyChangePublisherBase bindingPublisher)
: base(propertyName, bindingPublisher)
{
_publisher = bindingPublisher;
}
}
Here is an example of the Class with properties that you wish to be notified about:
public class ExamplePublisher: PropertyChangedPublisherBase
{
private string _id;
private bool _testBool;
public string Id
{
get { return _id; }
set
{
if (value == _id) return;
_id = value;
RaisePropertyChanged("Id");
}
}
public bool TestBool
{
get { return _testBool; }
set
{
if (value.Equals(_testBool)) return;
_testBool = value;
RaisePropertyChanged("TestBool");
}
}
}
Here is an example of the Class that will be notified when the properties in the class above change:
public class ExampleReceiver
{
public PropertyChangeSubscriber<string> Id { get; set; }
public PropertyChangeSubscriber<bool> TestBool { get; set; }
public MyExampleClass(PropertyChangePublisherBase publisher)
{
Id = new PropertyChangeSubscriber<string>("Id", publisher);
TestBool = new PropertyChangeSubscriber<bool>("TestBool", publisher);
}
}

Categories

Resources