From f004a0a778e9779a01f81418435fedfd79b5fa29 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Wed, 10 Feb 2016 22:31:12 +0100 Subject: [PATCH] Added IsTemplateFocusTarget attached property. And use it in TreeViewItem's data template to place the focus adorner around the correct control. Closes #294. --- src/Perspex.Controls/Control.cs | 21 ++++++-- .../Primitives/TemplatedControl.cs | 48 +++++++++++++++++++ .../Templates/TemplateExtensions.cs | 6 +-- src/Perspex.Themes.Default/TreeViewItem.paml | 8 +--- 4 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/Perspex.Controls/Control.cs b/src/Perspex.Controls/Control.cs index 74d67e0f4a..8a70ba201f 100644 --- a/src/Perspex.Controls/Control.cs +++ b/src/Perspex.Controls/Control.cs @@ -69,8 +69,7 @@ namespace Perspex.Controls /// Defines the property. /// public static readonly StyledProperty ContextMenuProperty = - PerspexProperty.Register(nameof(ContextMenu)); - + PerspexProperty.Register(nameof(ContextMenu)); /// /// Event raised when an element wishes to be scrolled into view. @@ -393,6 +392,15 @@ namespace Perspex.Controls }); } + /// + /// Gets the element that recieves the focus adorner. + /// + /// The control that recieves the focus adorner. + protected virtual IControl GetTemplateFocusTarget() + { + return this; + } + /// /// Called when the control is added to a logical tree. /// @@ -488,8 +496,13 @@ namespace Perspex.Controls if (_focusAdorner != null) { - AdornerLayer.SetAdornedElement((Visual)_focusAdorner, this); - adornerLayer.Children.Add(_focusAdorner); + var target = (Visual)GetTemplateFocusTarget(); + + if (target != null) + { + AdornerLayer.SetAdornedElement((Visual)_focusAdorner, target); + adornerLayer.Children.Add(_focusAdorner); + } } } } diff --git a/src/Perspex.Controls/Primitives/TemplatedControl.cs b/src/Perspex.Controls/Primitives/TemplatedControl.cs index 0941864ae4..7c3d7dd7cf 100644 --- a/src/Perspex.Controls/Primitives/TemplatedControl.cs +++ b/src/Perspex.Controls/Primitives/TemplatedControl.cs @@ -74,6 +74,12 @@ namespace Perspex.Controls.Primitives public static readonly StyledProperty TemplateProperty = PerspexProperty.Register("Template"); + /// + /// Defines the IsTemplateFocusTarget attached property. + /// + public static readonly AttachedProperty IsTemplateFocusTargetProperty = + PerspexProperty.RegisterAttached("IsTemplateFocusTarget"); + /// /// Defines the routed event. /// @@ -198,6 +204,33 @@ namespace Perspex.Controls.Primitives set { SetValue(TemplateProperty, value); } } + /// + /// Gets the value of the IsTemplateFocusTargetProperty attached property on a control. + /// + /// The control. + /// The property value. + /// + public bool GetIsTemplateFocusTarget(Control control) + { + return control.GetValue(IsTemplateFocusTargetProperty); + } + + /// + /// Sets the value of the IsTemplateFocusTargetProperty attached property on a control. + /// + /// The control. + /// The property value. + /// + /// When a control is navigated to using the keyboard, a focus adorner is shown - usually + /// around the control itself. However if the TemplatedControl.IsTemplateFocusTarget + /// attached property is set to true on an element in the control template, then the focus + /// adorner will be shown around that control instead. + /// + public void SetIsTemplateFocusTarget(Control control, bool value) + { + control.SetValue(IsTemplateFocusTargetProperty, value); + } + /// public sealed override void ApplyTemplate() { @@ -224,6 +257,7 @@ namespace Perspex.Controls.Primitives } } + /// protected sealed override IndexerDescriptor CreateBindingDescriptor(IndexerDescriptor source) { var result = base.CreateBindingDescriptor(source); @@ -240,6 +274,20 @@ namespace Perspex.Controls.Primitives return result; } + /// + protected override IControl GetTemplateFocusTarget() + { + foreach (Control child in this.GetTemplateChildren()) + { + if (GetIsTemplateFocusTarget(child)) + { + return child; + } + } + + return this; + } + /// /// Called when the control's template is applied. /// diff --git a/src/Perspex.Controls/Templates/TemplateExtensions.cs b/src/Perspex.Controls/Templates/TemplateExtensions.cs index 40672f873e..dbd5003b2d 100644 --- a/src/Perspex.Controls/Templates/TemplateExtensions.cs +++ b/src/Perspex.Controls/Templates/TemplateExtensions.cs @@ -12,7 +12,7 @@ namespace Perspex.Controls.Templates { public static class TemplateExtensions { - public static IEnumerable GetTemplateChildren(this ITemplatedControl control) + public static IEnumerable GetTemplateChildren(this ITemplatedControl control) { var visual = control as IVisual; @@ -21,12 +21,12 @@ namespace Perspex.Controls.Templates // TODO: This searches the whole descendent tree - it can stop when it exits the // template. return visual.GetVisualDescendents() - .OfType() + .OfType() .Where(x => x.TemplatedParent == control); } else { - return Enumerable.Empty(); + return Enumerable.Empty(); } } } diff --git a/src/Perspex.Themes.Default/TreeViewItem.paml b/src/Perspex.Themes.Default/TreeViewItem.paml index 5758f598bb..9df1ec1545 100644 --- a/src/Perspex.Themes.Default/TreeViewItem.paml +++ b/src/Perspex.Themes.Default/TreeViewItem.paml @@ -11,18 +11,12 @@ Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" + TemplatedControl.IsTemplateFocusTarget="True" Grid.Column="1"> -