Generic ReactiveCommand

268 views
Skip to first unread message

Xcalllibur

unread,
Apr 25, 2011, 10:51:48 AM4/25/11
to ReactiveUI mailing list
Can you add a generic version of ReactiveCommand to the framework.
Coming from nRoute background i miss this because it makes it easier
to process the parameters passed to the command. The one i am
currently using looks like the following. Note that it does not
implement IReactiveCommand because having to implement
IObservable<object> causes ambiguity when i also have to implement
IObservable<T>. If IReactiveCommand was changed to be generic this
wouldnt be an issue. Since the interface is not explicitly used
anywhere in the framework it doesnt currently cause an issue:

public class ReactiveCommand<T> : ICommand, IEnableLogger,
IObservable<T>
{
private readonly Func<T, bool> canExecuteExplicitFunc;
private ObservableAsPropertyHelper<bool> canExecuteLatest;
private IScheduler scheduler;
private Subject<T> executeSubject;

public ReactiveCommand(IObservable<bool> canExecute = null,
IScheduler scheduler = null)
{
canExecute = canExecute ??
Observable.Return(true).Concat(Observable.Never<bool>());
this.Initialise(scheduler);
canExecute.Subscribe(this.CanExecuteSubject.OnNext);
}

protected ReactiveCommand(Func<T, bool> canExecute, IScheduler
scheduler = null)
{
this.canExecuteExplicitFunc = canExecute;
this.Initialise(scheduler);
}

public event EventHandler CanExecuteChanged;

protected Subject<bool> CanExecuteSubject
{
get;
private set;
}

public IObservable<bool> CanExecuteObservable
{
get
{
return this.CanExecuteSubject.DistinctUntilChanged();
}
}

public virtual bool CanExecute(T parameter)
{
if (this.canExecuteExplicitFunc != null)
{
bool ret = this.canExecuteExplicitFunc(parameter);
this.CanExecuteSubject.OnNext(ret);

return ret;
}

return this.canExecuteLatest.Value;
}

public void Execute(T parameter)
{
this.Log().InfoFormat("{0:X}: Executed",
this.GetHashCode());
this.executeSubject.OnNext(parameter);
}

public IDisposable Subscribe(IObserver<T> observer)
{
return
this.executeSubject.ObserveOn(this.scheduler).Subscribe(observer);
}

public static ReactiveCommand<T> Create(IObservable<bool>
canExecute = null, Action<T> onNext = null, IScheduler scheduler =
null)
{
var command = new ReactiveCommand<T>(canExecute,
scheduler);

if (onNext != null)
{
command.Subscribe(onNext);
}

return command;
}

protected virtual T ParseParameter(object parameter, Type
type)
{
if (parameter == null)
{
return default(T);
}

if (type.IsEnum)
{
return (T)Enum.Parse(type,
Convert.ToString(parameter), true);
}

if (type.IsValueType)
{
return (T)Convert.ChangeType(parameter, type, null);
}

return (T)parameter;
}

protected void CheckParameterType(object parameter)
{
if (parameter == null)
{
return;
}

if (typeof(T).IsValueType)
{
return;
}

if (!typeof(T).IsAssignableFrom(parameter.GetType()))
{
var message =
String.Format(CultureInfo.CurrentCulture,
Messages.CommandTypeUnexpected, this.GetType().FullName,
typeof(T).FullName);
throw new ArgumentException(message, "parameter");
}
}

private void Initialise(IScheduler scheduler)
{
this.scheduler = scheduler ?? RxApp.DeferredScheduler;

this.CanExecuteSubject = new Subject<bool>();
this.canExecuteLatest = new
ObservableAsPropertyHelper<bool>
(
this.CanExecuteSubject,
b =>
{
if (this.CanExecuteChanged != null)
{
this.CanExecuteChanged(this, EventArgs.Empty);
}
},
true,
this.scheduler
);

this.executeSubject = new Subject<T>();
}

bool ICommand.CanExecute(object parameter)
{
this.CheckParameterType(parameter);

return this.CanExecute(this.ParseParameter(parameter,
typeof(T)));
}

void ICommand.Execute(object parameter)
{
this.CheckParameterType(parameter);
this.Execute(this.ParseParameter(parameter, typeof(T)));
}
}

Paul Betts

unread,
Apr 25, 2011, 5:47:55 PM4/25/11
to reacti...@googlegroups.com
I like this idea, I'll see if I can add it to the next release.
Reply all
Reply to author
Forward
0 new messages