Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 62 additions & 14 deletions ReactiveUI.Tests/Winforms/CommandBindingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Windows.Forms;
using ReactiveUI.Winforms;
using Xunit;
using System.ComponentModel;

namespace ReactiveUI.Tests.Winforms
{
Expand All @@ -25,14 +26,12 @@ public void CommandBinderBindsToButton()
commandExecuted = true;
});

var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5));

input.PerformClick();

Assert.True(commandExecuted);
Assert.NotNull(ea);
disp.Dispose();
using (var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5))) {
input.PerformClick();

Assert.True(commandExecuted);
Assert.NotNull(ea);
}
}

[Fact]
Expand All @@ -51,14 +50,36 @@ public void CommandBinderBindsToCustomControl()
commandExecuted = true;
});

var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5));
using (var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5))) {
input.PerformClick();

input.PerformClick();
Assert.True(commandExecuted);
Assert.NotNull(ea);
}
}

Assert.True(commandExecuted);
Assert.NotNull(ea);
disp.Dispose();
[Fact]
public void CommandBinderBindsToCustomComponent()
{
var fixture = new CreatesWinformsCommandBinding();
var cmd = ReactiveCommand.Create();
var input = new CustomClickableComponent { };

Assert.True(fixture.GetAffinityForObject(input.GetType(), true) > 0);
Assert.True(fixture.GetAffinityForObject(input.GetType(), false) > 0);
bool commandExecuted = false;
object ea = null;
cmd.Subscribe(o => {
ea = o;
commandExecuted = true;
});

using (var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5))) {
input.PerformClick();

Assert.True(commandExecuted);
Assert.NotNull(ea);
}
}

[Fact]
Expand All @@ -73,16 +94,44 @@ public void CommandBinderAffectsEnabledState()

using (var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5))) {
canExecute.OnNext(true);

Assert.True(input.Enabled);

canExecute.OnNext(false);
Assert.False(input.Enabled);
}
}

[Fact]
public void CommandBinderAffectsEnabledStateForComponents()
{
var fixture = new CreatesWinformsCommandBinding();
var canExecute = new Subject<bool>();
canExecute.OnNext(true);

var cmd = ReactiveCommand.Create(canExecute);
var input = new ToolStripButton { }; // ToolStripButton is a Component, not a Control

using (var disp = fixture.BindCommandToObject(cmd, input, Observable.Return((object)5))) {
canExecute.OnNext(true);
Assert.True(input.Enabled);

canExecute.OnNext(false);
Assert.False(input.Enabled);
}
}
}

public class CustomClickableComponent : Component
{
public event EventHandler Click;

public void PerformClick()
{
if (Click != null)
Click(this, EventArgs.Empty);
}
}

public class CustomClickableControl : Control
{
public void PerformClick()
Expand All @@ -99,7 +148,6 @@ public void RaiseMouseUpEvent(System.Windows.Forms.MouseEventArgs args)
{
this.OnMouseUp(args);
}

}

public class CommandBindingImplementationTests
Expand Down
38 changes: 23 additions & 15 deletions ReactiveUI.Winforms/Winforms/CreatesCommandBinding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Windows.Input;
using ReactiveUI;
using System.Reflection;
using System.ComponentModel;

namespace ReactiveUI.Winforms
{
Expand All @@ -23,8 +24,6 @@ public class CreatesWinformsCommandBinding : ICreatesCommandBinding
Tuple.Create("MouseUp", typeof (MouseEventArgs)),
};

static readonly PropertyInfo enabledProperty = typeof(Control).GetRuntimeProperty("Enabled");

public int GetAffinityForObject(Type type, bool hasEventTarget)
{
bool isWinformControl = typeof(Control).IsAssignableFrom(type);
Expand All @@ -33,7 +32,7 @@ public int GetAffinityForObject(Type type, bool hasEventTarget)

if (hasEventTarget) return 6;

return defaultEventsToBind.Any(x =>{
return defaultEventsToBind.Any(x => {
var ei = type.GetEvent(x.Item1, BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.Instance);
return ei != null;
}) ? 4 : 0;
Expand Down Expand Up @@ -76,18 +75,27 @@ public IDisposable BindCommandToObject<TEventArgs>(ICommand command, object targ
command.Execute(useEventArgsInstead ? ea : latestParameter);
}
}));

//only controls have Enabled
if (typeof(Control).IsAssignableFrom(target.GetType())) {
object latestParam = null;
commandParameter.Subscribe(x => latestParam = x);

ret.Add(Observable.FromEventPattern<EventHandler, EventArgs>(x => command.CanExecuteChanged += x, x => command.CanExecuteChanged -= x)
.Select(_ => command.CanExecute(latestParam))
.StartWith(command.CanExecute(latestParam))
.Subscribe(x => {
enabledProperty.SetValue(target, x, null);
}));

Type targetType = target.GetType();

// We initially only accepted Controls here, but this is too restrictive:
// there are a number of Components that can trigger Commands and also
// have an Enabled property, just like Controls.
// For example: System.Windows.Forms.ToolStripButton.
if (typeof(Component).IsAssignableFrom(targetType)) {
PropertyInfo enabledProperty = targetType.GetRuntimeProperty("Enabled");

if (enabledProperty != null) {
object latestParam = null;
commandParameter.Subscribe(x => latestParam = x);

ret.Add(Observable.FromEventPattern<EventHandler, EventArgs>(x => command.CanExecuteChanged += x, x => command.CanExecuteChanged -= x)
.Select(_ => command.CanExecute(latestParam))
.StartWith(command.CanExecute(latestParam))
.Subscribe(x => {
enabledProperty.SetValue(target, x, null);
}));
}
}

return ret;
Expand Down