Skip to content
This repository has been archived by the owner on Feb 20, 2022. It is now read-only.

Support auto focus on dedicated field #56

Merged
merged 4 commits into from
Feb 16, 2015
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
1 change: 1 addition & 0 deletions NZazu.Contracts/FormDefinition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ public class FormDefinition
{
public FieldDefinition[] Fields { get; set; }
public string Layout { get; set; }
public string FocusOn { get; set; }
}
}
53 changes: 53 additions & 0 deletions NZazu/Extensions/FocusExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

namespace NZazu.Extensions
{
public static class FocusExtensions
{
// cf.: https://wpf.codeplex.com/workitem/13476
public static Task DelayedFocus(this UIElement uiElement)
{
if (uiElement == null) throw new ArgumentNullException("uiElement");

return uiElement.Dispatcher.BeginInvoke(SetFocusAction(uiElement), DispatcherPriority.Render).Task;
}

static Action SetFocusAction(UIElement uiElement)
{
return () => SetFocus(uiElement);
}

public static void SetFocus(this UIElement uiElement)
{
RemoveFocus(uiElement);

uiElement.Focusable = true;
FocusManager.SetFocusedElement(uiElement, uiElement);
uiElement.Focus();
Keyboard.Focus(uiElement);
}

public static void RemoveFocus(this UIElement uiElement)
{
// cf.: http://stackoverflow.com/questions/2914495/wpf-how-to-programmatically-remove-focus-from-a-textbox
//Keyboard.ClearFocus();

var frameWorkElement = uiElement as FrameworkElement;
if (frameWorkElement == null) return;

// Move to a parent that can take focus
var parent = frameWorkElement.Parent as FrameworkElement;
while (parent != null && !parent.Focusable)
{
parent = parent.Parent as FrameworkElement;
}

var scope = FocusManager.GetFocusScope(uiElement);
FocusManager.SetFocusedElement(scope, parent);
}
}
}
6 changes: 3 additions & 3 deletions NZazu/FieldBehavior/EmptyNZazuFieldBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System;
using System.Windows.Controls;

namespace NZazu.FieldBehavior
{
internal class EmptyNZazuFieldBehavior : NZazuFieldBehavior
{
public override void AttachTo(Control valueControl)
public override void AttachTo(INZazuWpfField field)
{
if (valueControl == null) throw new ArgumentNullException("valueControl");
if (field == null) throw new ArgumentNullException("field");
}

public override void Detach() { }
}
}
6 changes: 3 additions & 3 deletions NZazu/FieldBehavior/EmptyNZazuFieldBehavior_Should.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ public void Throw_Excepion_If_No_Control_Provided()
[Test]
public void Do_Nothing()
{
var ctrl = Substitute.For<Control>();
var field = Substitute.For<INZazuWpfField>();
var sut = new EmptyNZazuFieldBehavior();

sut.AttachTo(ctrl);
sut.AttachTo(field);
sut.Detach();

ctrl.ReceivedCalls().Should().BeEmpty();
field.ReceivedCalls().Should().BeEmpty();
}
}
}
4 changes: 1 addition & 3 deletions NZazu/FieldBehavior/NZazuFieldBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System.Windows.Controls;

namespace NZazu.FieldBehavior
{
internal abstract class NZazuFieldBehavior : INZazuWpfFieldBehavior
{
public abstract void AttachTo(Control valueControl);
public abstract void AttachTo(INZazuWpfField field);
public abstract void Detach();
}
}
3 changes: 1 addition & 2 deletions NZazu/FieldBehavior/NZazuFieldBehaviorFactory_Should.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Windows.Controls;
using FluentAssertions;
using NUnit.Framework;
using NZazu.Contracts;
Expand Down Expand Up @@ -40,7 +39,7 @@ public void Verify_Parameter_On_CreateFieldBehavior()
[ExcludeFromCodeCoverage]
private class SimpleInterfaceImplementation : INZazuWpfFieldBehavior
{
public void AttachTo(Control valueControl) { }
public void AttachTo(INZazuWpfField field) { }
public void Detach() { }
}

Expand Down
6 changes: 2 additions & 4 deletions NZazu/INZazuWpfFieldBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
using System.Windows.Controls;

namespace NZazu
namespace NZazu
{
public interface INZazuWpfFieldBehavior
{
void AttachTo(Control valueControl);
void AttachTo(INZazuWpfField field);
void Detach();
}
}
3 changes: 3 additions & 0 deletions NZazu/INZazuWpfView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ public interface INZazuWpfView
IResolveLayout ResolveLayout { get; set; }

INZazuWpfField GetField(string key);
bool TryGetField(string key, out INZazuWpfField field);
bool TrySetFocusOn(string key);

Dictionary<string, string> GetFieldValues();
void ApplyChanges();

Expand Down
1 change: 1 addition & 0 deletions NZazu/NZazu.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Extensions\FieldExtensions.cs" />
<Compile Include="Extensions\FocusExtensions.cs" />
<Compile Include="FieldBehavior\BehaviorExtender.cs" />
<Compile Include="FieldBehavior\EmptyNZazuFieldBehavior.cs" />
<Compile Include="FieldBehavior\EmptyNZazuFieldBehavior_Should.cs" />
Expand Down
32 changes: 27 additions & 5 deletions NZazu/NZazuView.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using NZazu.Contracts;
using NZazu.Contracts.Checks;
using NZazu.Extensions;
Expand Down Expand Up @@ -30,6 +32,7 @@ private static void FormDefinitionChanged(DependencyObject d, DependencyProperty
var view = (NZazuView)d;
var formDefinition = (FormDefinition)e.NewValue;
view.UpdateFields(formDefinition, view.FieldFactory, view.FieldBehaviorFactory, view.ResolveLayout);
view.TrySetFocusOn(formDefinition.FocusOn);
}

// ############# FieldFactory
Expand Down Expand Up @@ -151,10 +154,15 @@ public void ApplyChanges()
public INZazuWpfField GetField(string key)
{
INZazuWpfField field;
if (!_fields.TryGetValue(key, out field)
&& !_groupFields.TryGetValue(key, out field))
throw new KeyNotFoundException();
return field;
if (TryGetField(key, out field))
return field;
throw new KeyNotFoundException();
}

public bool TryGetField(string key, out INZazuWpfField field)
{
return _fields.TryGetValue(key, out field)
|| _groupFields.TryGetValue(key, out field);
}

public Dictionary<string, string> GetFieldValues()
Expand Down Expand Up @@ -195,6 +203,20 @@ private void UpdateFields(
this.SetFieldValues(FormData.Values);
}

public bool TrySetFocusOn(string focusOn)
{
INZazuWpfField field;
if (String.IsNullOrWhiteSpace(focusOn) || !TryGetField(focusOn, out field)) return false;

var control = field.ValueControl;
if (control == null) return false;

control.SetFocus();
control.DelayedFocus();

return true;
}

private void CreateFields(FormDefinition formDefinition, INZazuWpfFieldFactory fieldFactory)
{
formDefinition.Fields.ToList().ForEach(f =>
Expand Down Expand Up @@ -232,7 +254,7 @@ private void AttachBehavior(FormDefinition formDefinition, INZazuWpfFieldBehavio
var field = GetField(fieldDefinition.Key) as NZazuField;
if (field == null) return;

behavior.AttachTo(field.ValueControl);
behavior.AttachTo(field);
field.Behavior = behavior;
}
}
Expand Down
30 changes: 29 additions & 1 deletion NZazu/NZazuView_Should.cs
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ public void Attach_And_Detach_Behavior_To_Field()
var sut = new NZazuView { FieldBehaviorFactory = behaviorFactory, FormDefinition = formDefinition, FormData = formData };
sut.Should().NotBeNull();

behavior.ReceivedWithAnyArgs().AttachTo(Arg.Any<Control>());
behavior.ReceivedWithAnyArgs().AttachTo(Arg.Any<INZazuWpfField>());
behavior.ClearReceivedCalls();

// now lets create a ner form and detach the existing behavior
Expand Down Expand Up @@ -371,5 +371,33 @@ public void Skip_skip_fixed_fields_in_GetFieldValues()
var actual = view.GetFieldValues();
actual.ShouldBeEquivalentTo(expected);
}

[Test]
public void Focus_specified_field_after_changing_FormDefinition()
{
var view = new NZazuView
{
FormDefinition = new FormDefinition
{
Fields = new[]
{
new FieldDefinition {Key = "other", Type = "string"},
new FieldDefinition {Key = "focus", Type = "string"}
},
FocusOn = "focus"
}
};

var otherCtrl = view.GetField("other").ValueControl;
var keyCtrl = view.GetField("focus").ValueControl;

otherCtrl.IsFocused.Should().BeFalse();
keyCtrl.IsFocused.Should().BeTrue();

view.TrySetFocusOn("other").Should().BeTrue();

keyCtrl.IsFocused.Should().BeFalse();
otherCtrl.IsFocused.Should().BeTrue();
}
}
}
1 change: 1 addition & 0 deletions NZazuFiddle/NZazuFiddle.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@
<Compile Include="AppBootstrapper.cs" />
<Compile Include="IFiddle.cs" />
<Compile Include="Samples\BehaviorSample.cs" />
<Compile Include="Samples\FocusSample.cs" />
<Compile Include="Samples\IHaveSample.cs" />
<Compile Include="Samples\MixedGroupLayout.cs" />
<Compile Include="Samples\PrimitivesSample.cs" />
Expand Down
10 changes: 10 additions & 0 deletions NZazuFiddle/PreviewViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,16 @@ private set
}
}

protected override void OnActivate()
{
base.OnActivate();

var view = GetView() as PreviewView;
if (view == null) return;

view.View.TrySetFocusOn(Definition.FocusOn);
}

public void Handle(FormDefinition definition)
{
Definition = definition;
Expand Down
5 changes: 3 additions & 2 deletions NZazuFiddle/Samples/BehaviorSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ private class OpenUrlOnStringEnterBehavior : INZazuWpfFieldBehavior
private Control _control;
private KeyEventHandler _handler;

public void AttachTo(Control valueControl)
public void AttachTo(INZazuWpfField field)
{
if (valueControl == null) throw new ArgumentNullException("valueControl");
if (field == null) throw new ArgumentNullException("field");
var valueControl = field.ValueControl;

_control = valueControl;

Expand Down
33 changes: 33 additions & 0 deletions NZazuFiddle/Samples/FocusSample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Collections.Generic;
using NZazu.Contracts;

namespace NZazuFiddle.Samples
{
// ReSharper disable once UnusedMember.Global
class FocusSample : SampleBase
{
public FocusSample() : base(50)
{
Sample = new SampleViewModel
{
Name = "Focus",
Fiddle = ToFiddle(new FormDefinition
{
Fields = new []
{
new FieldDefinition { Key="label", Type = "label", Prompt = "Focus should be on 'value2'"},
new FieldDefinition { Key="key1", Type="string", Prompt="key1"},
new FieldDefinition { Key="key2", Type="string", Prompt="key2"}
},
FocusOn = "key2"
},
new Dictionary<string, string>
{
{"key1", "value1"},
{"key2", "value2"}
})
};

}
}
}
1 change: 1 addition & 0 deletions NZazuFiddle/Samples/PrimitivesSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace NZazuFiddle.Samples
{
// ReSharper disable once UnusedMember.Global
class PrimitivesSample : SampleBase
{
public PrimitivesSample() : base(10)
Expand Down
1 change: 1 addition & 0 deletions NZazuFiddle/Samples/ValidationSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace NZazuFiddle.Samples
{
// ReSharper disable once UnusedMember.Global
class ValidationSample : SampleBase
{
public ValidationSample() : base(30)
Expand Down
2 changes: 1 addition & 1 deletion NZazuFiddle/ShellViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public IEnumerable<ISample> Samples
{
if (Equals(value, _samples)) return;
_samples.Clear();
if (value != null) _samples.AddRange(value.OrderBy(s => s.Name));
if (value != null) _samples.AddRange(value);
SelectedSample = _samples.FirstOrDefault();
}
}
Expand Down