Created
June 14, 2016 21:24
-
-
Save willryan/52cd64b2038928957088315b5e41d1e8 to your computer and use it in GitHub Desktop.
how to bind a Property on an MVVM Light ViewModelBase to an IObservable<>
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace App | |
{ | |
class AppPropertyInfo | |
{ | |
public IDisposable Subscription; | |
public string Name; | |
public Func<IDisposable> Subscribe; | |
} | |
class AppCommandInfo | |
{ | |
public IDisposable ActionSubscription; | |
public IDisposable CanExecSubscription; | |
public object CanExecHolder; | |
public object ActionHolder; | |
} | |
public abstract class AppViewModel : ViewModelBase | |
{ | |
private static readonly List<Action> RaisePropChanged = new List<Action>(); | |
public static int RenderLoadSize | |
{ | |
get | |
{ | |
try | |
{ | |
return RaisePropChanged.Count; | |
} | |
catch | |
{ | |
return 0; | |
} | |
} | |
} | |
private static bool _stopMonitoringFlag; | |
public static void StartMonitoring() | |
{ | |
_stopMonitoringFlag = false; | |
var frameMax = 1000;// engine.Config.Ui.MaxUpdatesPerFrame; | |
var sleepTime = 20; //engine.Config.Ui.UpdateFrameTime; | |
new Thread(() => | |
{ | |
// keep it going until updates are done | |
while (!_stopMonitoringFlag || RaisePropChanged.Count > 0) | |
{ | |
Thread.Sleep(sleepTime); | |
List<Action> props; | |
lock (RaisePropChanged) | |
{ | |
props = RaisePropChanged.GetRange(0, Math.Min(frameMax, RaisePropChanged.Count)); | |
RaisePropChanged.RemoveRange(0, props.Count); | |
} | |
props.ForEach(a => a()); | |
} | |
}).Start(); | |
} | |
public static void StopMonitoring() | |
{ | |
_stopMonitoringFlag = true; | |
} | |
private readonly List<AppPropertyInfo> _propertyInfos; | |
private readonly List<AppCommandInfo> _commandInfos; | |
protected AppViewModel() | |
{ | |
_propertyInfos = new List<AppPropertyInfo>(); | |
_commandInfos = new List<AppCommandInfo>(); | |
ViewModelLocator.RegisterViewModel(this); | |
} | |
public override void Cleanup() | |
{ | |
base.Cleanup(); | |
_propertyInfos.ForEach(p => p.Subscription.Dispose()); | |
_commandInfos.ForEach(i => | |
{ | |
i.ActionHolder = null; | |
i.CanExecHolder = null; | |
i.ActionSubscription.Dispose(); | |
if (i.CanExecSubscription != null) | |
{ | |
i.CanExecSubscription.Dispose(); | |
} | |
}); | |
_propertyInfos.Clear(); | |
_commandInfos.Clear(); | |
} | |
protected void BindPropertyToObservable<T>(Expression<Func<T>> property, | |
IObservable<T> obs) | |
{ | |
if (obs == null) | |
{ | |
obs = Observable.Never<T>(); | |
} | |
var body = property.Body as MemberExpression; | |
if (body == null) | |
{ | |
var ubody = (UnaryExpression)property.Body; | |
body = ubody.Operand as MemberExpression; | |
} | |
var name = body.Member.Name; | |
_propertyInfos.Where(p => p.Name == name).ForEach(p => p.Subscription.Dispose()); | |
_propertyInfos.RemoveAll(p => p.Name == name); // remove old bindings | |
var propInfo = new AppPropertyInfo(); | |
propInfo.Name = name; | |
var type = GetType(); | |
MethodInfo setMethod = null; | |
//var propVal = property.Compile(); | |
while (setMethod == null) | |
{ | |
var prop = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); | |
setMethod = prop.GetSetMethod(true); | |
type = type.BaseType; | |
} | |
var setAct = (Action<T>)Delegate.CreateDelegate(typeof(Action<T>), | |
this, setMethod); | |
propInfo.Subscribe = () => obs.Subscribe(v => | |
{ | |
setAct(v); | |
lock (RaisePropChanged) | |
{ | |
RaisePropChanged.Add(() => RaisePropertyChanged(name)); | |
} | |
}); | |
propInfo.Subscription = propInfo.Subscribe(); | |
_propertyInfos.Add(propInfo); | |
} | |
protected RelayCommand BuildCommand(string name, ObservableCommandInfo info) | |
{ | |
return BuildCommand(() => name, info); | |
} | |
protected RelayCommand BuildCommand(Func<string> name, ObservableCommandInfo info) | |
{ | |
if (info == null) | |
{ | |
return new RelayCommand(() => { }); | |
} | |
return BuildCommand(name, info.Execute, info.CanExecute); | |
} | |
protected RelayCommand BuildCommand(string name, IObservable<Action> command, | |
IObservable<bool> canExecute = null) | |
{ | |
return BuildCommand(() => name, command, canExecute); | |
} | |
protected RelayCommand<T> BuildCommand<T>(IObservable<Action<T>> command, IObservable<bool> canExecute) | |
{ | |
RelayCommand<T> ret; | |
// for testing, mostly | |
if (command == null) | |
{ | |
return new RelayCommand<T>(_ => { }); | |
} | |
var info = new AppCommandInfo(); | |
_commandInfos.Add(info); | |
Action<T> comm = _ => { }; | |
Action<T> wrapper = t => comm(t); | |
info.ActionHolder = wrapper; | |
info.ActionSubscription = command.Subscribe(a => | |
{ | |
comm = a; | |
}); | |
if (canExecute == null) | |
{ | |
ret = new RelayCommand<T>(wrapper); | |
} | |
else | |
{ | |
var canExec = false; | |
Func<T, bool> canExecWrapper = _ => canExec; | |
info.CanExecHolder = canExecWrapper; | |
ret = new RelayCommand<T>(wrapper, canExecWrapper); | |
info.CanExecSubscription = canExecute.Subscribe(b => | |
{ | |
canExec = b; | |
Util.RunOnUI(ret.RaiseCanExecuteChanged); | |
}); | |
} | |
return ret; | |
} | |
protected RelayCommand BuildCommand(Func<string> name, IObservable<Action> command, | |
IObservable<bool> canExecute = null) | |
{ | |
RelayCommand ret; | |
// for testing, mostly | |
if (command == null) | |
{ | |
return new RelayCommand(() => { }); | |
} | |
var info = new AppCommandInfo(); | |
_commandInfos.Add(info); | |
Action comm = () => { }; | |
Action wrapper = () => comm(); | |
info.ActionHolder = wrapper; | |
info.ActionSubscription = command.Subscribe(a => | |
{ | |
comm = a; | |
}); | |
if (canExecute == null) | |
{ | |
ret = new RelayCommand(wrapper); | |
} | |
else | |
{ | |
var canExec = false; | |
Func<bool> canExecWrapper = () => canExec; | |
info.CanExecHolder = canExecWrapper; | |
ret = new RelayCommand(wrapper, canExecWrapper); | |
info.CanExecSubscription = canExecute.Subscribe(b => | |
{ | |
canExec = b; | |
Util.RunOnUI(ret.RaiseCanExecuteChanged); | |
}); | |
} | |
return ret; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment