Skip to content

Instantly share code, notes, and snippets.

@willryan
Created June 14, 2016 21:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save willryan/52cd64b2038928957088315b5e41d1e8 to your computer and use it in GitHub Desktop.
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<>
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