Skip to content

Commit

Permalink
- Consolidated TargetActionComandBinder functionality into UIKitComma…
Browse files Browse the repository at this point in the history
…ndBinders

- FlexibleCommandBinder and UIKitObservableForPropertyBase now support derived types as well (Bug)
  • Loading branch information
Oliver Weichhold committed Jun 5, 2013
1 parent 17ba9d7 commit f33907c
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 124 deletions.
99 changes: 0 additions & 99 deletions ReactiveUI.Platforms/Cocoa/EventBasedCommandBinder.cs

This file was deleted.

182 changes: 182 additions & 0 deletions ReactiveUI.Platforms/Cocoa/FlexibleCommandBinder.cs
@@ -0,0 +1,182 @@
using System;
using ReactiveUI;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
using System.Reactive.Disposables;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using MonoTouch.ObjCRuntime;

namespace ReactiveUI.Cocoa
{
public abstract class FlexibleCommandBinder :
ICreatesCommandBinding
{
#region ICreatesCommandBinding implementation

public int GetAffinityForObject(Type type, bool hasEventTarget)
{
var match = config.Keys.FirstOrDefault(x=> x.IsAssignableFrom(type));
if(match == null)
return 0;

var typeProperties = config[match];
return typeProperties.Affinity;
}

public IDisposable BindCommandToObject(System.Windows.Input.ICommand command, object target, IObservable<object> commandParameter)
{
var type = target.GetType();

var match = config.Keys.FirstOrDefault(x=> x.IsAssignableFrom(type));
if(match == null)
throw new NotSupportedException(string.Format("CommandBinding for {0} is not supported", type.Name));

var typeProperties = config[match];

return typeProperties.CreateBinding(command, target, commandParameter);
}

public IDisposable BindCommandToObject<TEventArgs>(System.Windows.Input.ICommand command, object target, IObservable<object> commandParameter, string eventName)
{
throw new NotImplementedException();
}

#endregion

internal class CommandBindingInfo
{
public int Affinity;
public Func<System.Windows.Input.ICommand, object, IObservable<object>, IDisposable> CreateBinding;
}

/// <summary>
/// Configuration map
/// </summary>
readonly Dictionary<Type, CommandBindingInfo> config =
new Dictionary<Type, CommandBindingInfo>();

/// <summary>
/// Registers an observable factory for the specified type and property.
/// </summary>
/// <param name="type">Type.</param>
/// <param name="property">Property.</param>
/// <param name="createObservable">Create observable.</param>
protected void Register(Type type, int affinity, Func<System.Windows.Input.ICommand, object, IObservable<object>, IDisposable> createBinding)
{
config[type] = new CommandBindingInfo { Affinity = affinity, CreateBinding = createBinding };
}

/// <summary>
/// Creates a commands binding from event and a property
/// </summary>
/// <returns>The binding from event.</returns>
/// <param name="command">Command.</param>
/// <param name="target">Target.</param>
/// <param name="commandParameter">Command parameter.</param>
/// <param name="eventName">Event name.</param>
/// <param name="enablePropertyName">Enable property name.</param>
protected static IDisposable ForEvent(System.Windows.Input.ICommand command, object target,
IObservable<object> commandParameter, string eventName, string enablePropertyName)
{
commandParameter = commandParameter ?? Observable.Return(target);

object latestParam = null;
var ctl = (UIRefreshControl)target;

var actionDisp = Observable.FromEventPattern(ctl, eventName).Subscribe((e) => {
if (command.CanExecute(latestParam))
command.Execute(latestParam);
});

var enabledSetter = Reflection.GetValueSetterForProperty(target.GetType(), enablePropertyName);
if(enabledSetter == null) return actionDisp;

// initial enabled state
enabledSetter(target, command.CanExecute(latestParam));

var compDisp = new CompositeDisposable(
actionDisp,
commandParameter.Subscribe(x => latestParam = x),
Observable.FromEventPattern<EventHandler, EventArgs>(x => command.CanExecuteChanged += x, x => command.CanExecuteChanged -= x)
.Select(_ => command.CanExecute(latestParam))
.Subscribe(x => {
enabledSetter(target, x);
}));

return compDisp;
}

/// <summary>
/// Creates a commands binding from event and a property
/// </summary>
/// <returns>The binding from event.</returns>
/// <param name="command">Command.</param>
/// <param name="target">Target.</param>
/// <param name="commandParameter">Command parameter.</param>
/// <param name="enablePropertyName">Enable property name.</param>
protected static IDisposable ForTargetAction(System.Windows.Input.ICommand command, object target,
IObservable<object> commandParameter, string enablePropertyName)
{
commandParameter = commandParameter ?? Observable.Return(target);

object latestParam = null;
var ctlDelegate = new ControlDelegate(x => {
if (command.CanExecute(latestParam))
command.Execute(latestParam);
});

var sel = new Selector("theAction:");

#if UIKIT
IDisposable actionDisp = null;

if(target is UIControl) {
var ctl = (UIControl)target;
ctl.AddTarget(ctlDelegate, sel, UIControlEvent.TouchUpInside);
actionDisp = Disposable.Create(() => ctl.RemoveTarget(ctlDelegate, sel, UIControlEvent.TouchUpInside));
}
#else
Reflection.GetValueSetterOrThrow(target.GetType(), "Action")(target, sel);

var targetSetter = Reflection.GetValueSetterOrThrow(target.GetType(), "Target");
targetSetter(target, ctlDelegate);
var actionDisp = Disposable.Create(() => targetSetter(target, null));
#endif

var enabledSetter = Reflection.GetValueSetterForProperty(target.GetType(), enablePropertyName);
if(enabledSetter == null) return actionDisp;

// initial enabled state
enabledSetter(target, command.CanExecute(latestParam));

var compDisp = new CompositeDisposable(
actionDisp,
commandParameter.Subscribe(x => latestParam = x),
Observable.FromEventPattern<EventHandler, EventArgs>(x => command.CanExecuteChanged += x, x => command.CanExecuteChanged -= x)
.Select(_ => command.CanExecute(latestParam))
.Subscribe(x => {
enabledSetter(target, x);
}));

return compDisp;
}

class ControlDelegate : NSObject
{
readonly Action<NSObject> block;
public ControlDelegate(Action<NSObject> block)
{
this.block = block;
}

[Export("theAction:")]
public void TheAction(NSObject sender)
{
block(sender);
}
}
}
}

10 changes: 0 additions & 10 deletions ReactiveUI.Platforms/Cocoa/TargetActionCommandBinder.cs
Expand Up @@ -53,21 +53,11 @@ public IDisposable BindCommandToObject(ICommand command, object target, IObserva
});

var sel = new Selector("theAction:");
#if UIKIT
IDisposable actionDisp = null;

if(target is UIControl) {
var ctl = (UIControl)target;
ctl.AddTarget(ctlDelegate, sel, UIControlEvent.TouchUpInside);
actionDisp = Disposable.Create(() => ctl.RemoveTarget(ctlDelegate, sel, UIControlEvent.TouchUpInside));
}
#else
Reflection.GetValueSetterOrThrow(target.GetType(), "Action")(target, sel);

var targetSetter = Reflection.GetValueSetterOrThrow(target.GetType(), "Target");
targetSetter(target, ctlDelegate);
var actionDisp = Disposable.Create(() => targetSetter(target, null));
#endif

var enabledSetter = Reflection.GetValueSetterForProperty(target.GetType(), "Enabled");
if(enabledSetter == null) return actionDisp;
Expand Down
Expand Up @@ -10,14 +10,15 @@

namespace ReactiveUI.Cocoa
{
public class UIKitEventBasedCommandBinders : EventBasedCommandBinder
public class UIKitCommandBinders : FlexibleCommandBinder
{
public static Lazy<UIKitEventBasedCommandBinders> Instance = new Lazy<UIKitEventBasedCommandBinders>();
public static Lazy<UIKitCommandBinders> Instance = new Lazy<UIKitCommandBinders>();

public UIKitEventBasedCommandBinders ()
public UIKitCommandBinders ()
{
Register(typeof(UIRefreshControl), 10, (cmd, t, cp)=> CommandBindingFromEvent(cmd, t, cp, "ValueChanged", "Enabled"));
Register(typeof(UIBarButtonItem), 10, (cmd, t, cp)=> CommandBindingFromEvent(cmd, t, cp, "Clicked", "Enabled"));
Register(typeof(UIControl), 9, (cmd, t, cp)=> ForTargetAction(cmd, t, cp, "Enabled"));
Register(typeof(UIRefreshControl), 10, (cmd, t, cp)=> ForEvent(cmd, t, cp, "ValueChanged", "Enabled"));
Register(typeof(UIBarButtonItem), 10, (cmd, t, cp)=> ForEvent(cmd, t, cp, "Clicked", "Enabled"));
}
}
}
Expand Down
16 changes: 10 additions & 6 deletions ReactiveUI.Platforms/Cocoa/UIKitObservableForPropertyBase.cs
Expand Up @@ -19,10 +19,12 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang
if(beforeChanged)
return 0;

Dictionary<string, ObservablePropertyInfo> typeProperties;
if(!config.TryGetValue(type, out typeProperties))
var match = config.Keys.FirstOrDefault(x=> x.IsAssignableFrom(type));
if(match == null)
return 0;

var typeProperties = config[match];

ObservablePropertyInfo info;
if(!typeProperties.TryGetValue(propertyName, out info))
return 0;
Expand All @@ -32,15 +34,17 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang

public IObservable<IObservedChange<object, object>> GetNotificationForProperty(object sender, string propertyName, bool beforeChanged = false)
{
var type = sender.GetType();

if(beforeChanged)
return Observable.Never<IObservedChange<object, object>>();

Dictionary<string, ObservablePropertyInfo> typeProperties;
if(!config.TryGetValue(type, out typeProperties))
var type = sender.GetType();

var match = config.Keys.FirstOrDefault(x=> x.IsAssignableFrom(type));
if(match == null)
throw new NotSupportedException(string.Format("Notifications for {0} are not supported", type.Name));

var typeProperties = config[match];

ObservablePropertyInfo info;
if(!typeProperties.TryGetValue(propertyName, out info))
throw new NotSupportedException(string.Format("Notifications for {0}.{1} are not supported", type.Name, propertyName));
Expand Down
4 changes: 2 additions & 2 deletions ReactiveUI.Platforms/ReactiveUI.Cocoa_Monotouch.csproj
Expand Up @@ -75,8 +75,8 @@
<Compile Include="Cocoa\UIKitObservableForProperty.cs" />
<Compile Include="Cocoa\UIKitObservableForPropertyBase.cs" />
<Compile Include="Cocoa\Converters\DateTimeToNSDateConverter.cs" />
<Compile Include="Cocoa\EventBasedCommandBinder.cs" />
<Compile Include="Cocoa\UIKitEventBasedCommandBinders.cs" />
<Compile Include="Cocoa\FlexibleCommandBinder.cs" />
<Compile Include="Cocoa\UIKitCommandBinders.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ReactiveUI\ReactiveUI_Monotouch.csproj">
Expand Down
7 changes: 5 additions & 2 deletions ReactiveUI.Platforms/Registrations.cs
Expand Up @@ -53,13 +53,16 @@ public void Register(Action<Func<object>, Type> registerFunction)

#if UIKIT
registerFunction(() => UIKitObservableForProperty.Instance.Value, typeof(ICreatesObservableForProperty));
registerFunction(() => UIKitEventBasedCommandBinders.Instance.Value, typeof(ICreatesCommandBinding));
registerFunction(()=> DateTimeNSDateConverter.Instance.Value, typeof(IBindingTypeConverter));
registerFunction(() => UIKitCommandBinders.Instance.Value, typeof(ICreatesCommandBinding));
registerFunction(() => DateTimeNSDateConverter.Instance.Value, typeof(IBindingTypeConverter));
#endif

#if COCOA
registerFunction(() => new KVOObservableForProperty(), typeof(ICreatesObservableForProperty));
registerFunction(() => new CocoaDefaultPropertyBinding(), typeof(IDefaultPropertyBindingProvider));
#endif

#if COCOA && !UIKIT
registerFunction(() => new TargetActionCommandBinder(), typeof(ICreatesCommandBinding));
#endif

Expand Down

0 comments on commit f33907c

Please sign in to comment.