仅使用XAML聚焦文本框

3
我想将键盘焦点设置到一个默认情况下被折叠的stackpanel中包含的文本框上。当stackpanel变为可见时,我希望该文本框成为默认焦点。
我尝试了以下代码:
<StackPanel Orientation="Vertical" FocusManager.FocusedElement="{Binding ElementName=TxtB}">
  <TextBox x:Name="TxtA" Text="A" />
  <TextBox x:Name="TxtB" Text="B" />
</StackPanel>

然而,它并没有起作用。类型光标出现了,但它没有闪烁,也不允许书写。

是否可能仅使用XAML解决我的问题?也许可以使用触发器?

2个回答

4

是的,正如您所说,简单触发器似乎可以解决问题:

<StackPanel Orientation="Vertical">
    <StackPanel.Style>
       <Style TargetType="StackPanel">
          <Style.Triggers>
              <Trigger Property="Visibility" Value="Visible">
                  <Setter Property="FocusManager.FocusedElement" 
                          Value="{Binding ElementName=TxtA}" />
              </Trigger>
          </Style.Triggers>
       </Style>
    </StackPanel.Style>

    <TextBox x:Name="TxtA" Text="A" />
    <TextBox x:Name="TxtB" Text="B" />
</StackPanel>

你是否知道在 Silverlight 中使用 XAML 实现同样的功能有什么简单的解决方案吗?我尝试了相同的概念,但没有成功,并一直在寻找一个简单的即时解决方案。 - Chris W.
@ChrisW.,据我所知,没有这样简单的Silverlight解决方案。 - icebat

1
你需要创建一个附加属性IsFocused,当设置为true时,会调用附加元素的Focus()方法。等等,我会添加一些代码。
public static class FocusHelper
    {
        static FocusHelper()
        {
            var fpmd = new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, HandleAttachedIsFocusedChanged) { DefaultUpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged };
            IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusHelper), fpmd);
        }

        public static readonly DependencyProperty IsFocusedProperty;

        [Conditional("DEBUG")]
        public static void StartFocusTracing()
        {
            FocusManager.FocusedElementProperty.OverrideMetadata(typeof(FrameworkElement), new PropertyMetadata(HandleFocusedElementChanged));
        }

        private static void HandleFocusedElementChanged(DependencyObject o, DependencyPropertyChangedEventArgs args)
        {
            var element = args.NewValue as FrameworkElement;
            if (element == null)
            {
                Debug.WriteLine("Focus is lost");
                return;
            }

            Debug.WriteLine("Focus moved to {0} type {1}", element.Name, element.GetType().Name);

            var fs = FocusManager.GetFocusScope(element) as FrameworkElement;
            if (fs == null)
                return;

            Debug.WriteLine("Focus scope {0} of type {1}", fs.Name, fs.GetType().Name);
        }

        public static bool? GetIsFocused(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            return (bool?)element.GetValue(IsFocusedProperty);
        }

        public static void SetIsFocused(DependencyObject element, bool? value)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            element.SetValue(IsFocusedProperty, value);
        }

        private static void HandleAttachedIsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            WriteDependencyPropertyBindingInformation(d, IsFocusedProperty);

            var fe = (UIElement)d;

            // значение ранее было не задано
            if (e.OldValue == null)
            {
                var pd = DependencyPropertyDescriptor.FromProperty(UIElement.IsFocusedProperty, typeof(UIElement));
                pd.AddValueChanged(fe, HandleUIElementIsFocusedChanged);
            }

            if (e.NewValue == null)
            {
                var pd = DependencyPropertyDescriptor.FromProperty(UIElement.IsFocusedProperty, typeof(UIElement));
                pd.RemoveValueChanged(fe, HandleUIElementIsFocusedChanged);
                return;
            }

            if ((bool)e.NewValue)
            {
                Action setFocus = () =>
                    {
                        IInputElement elementToBeFocused = null;
                        IInputElement finalyFocusedElement = null;
                        // If current element is Focus Scope we try to restore logical focus
                        if (FocusManager.GetIsFocusScope(fe))
                        {
                            elementToBeFocused = FocusManager.GetFocusedElement(fe);
                            if (elementToBeFocused != null)
                            {
                                finalyFocusedElement = Keyboard.Focus(elementToBeFocused);
                            }
                        }

                        // If focus was not restored we try to focus
                        if (finalyFocusedElement == null
                            || (elementToBeFocused != finalyFocusedElement))
                        {
                            fe.FocusThisOrChild();
                        }
                    };
                if (ReflectionHelper.IsInMethod("MeasureOverride", typeof(FrameworkElement))) // hack of layout issue
                    Dispatcher.CurrentDispatcher.BeginInvoke(setFocus);
                else
                    setFocus();
            }
        }

        [Conditional("DEBUG")]
        private static void WriteDependencyPropertyBindingInformation(DependencyObject d, DependencyProperty property)
        {
            var binding = BindingOperations.GetBindingBase(d, IsFocusedProperty);

            if (binding == null)
            {
                Debug.WriteLine("Property {1} of object {0} has no bindings.", d, property.Name);
            }
            else
            {
                Debug.WriteLine("Property {1} of object {0} has binding.", d, property.Name);
                Debug.WriteLine("Type {0}", binding.GetType());

                var expressionBase = BindingOperations.GetBindingExpressionBase(d, IsFocusedProperty);
                Debug.Assert(expressionBase != null);

                Debug.WriteLine("Status {0}", expressionBase.Status);

                var expression = expressionBase as BindingExpression;
                if (expression != null)
                {
                    Debug.WriteLine("Source type {0}", expression.DataItem.GetType());
                    Debug.WriteLine("Source {0}",expression.DataItem);
                }
            }
        }   


        private static void HandleUIElementIsFocusedChanged(object sender, EventArgs e)
        {
            var uiElement = sender as UIElement;
            var isFocused = uiElement.IsFocused;
            ((DependencyObject)sender).SetCurrentValue(IsFocusedProperty, isFocused);
        }

        /// <summary>
        /// Tries to set focus to the element or any child element inside this one.
        /// Tab index is respected
        /// </summary>
        public static bool FocusThisOrChild(this DependencyObject element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            var inputElement = element as IInputElement;
            var wasFocused = inputElement != null && inputElement.Focus();

            if (!wasFocused)
            {
                element.SetFocusWithin();
            }

            return true;
        }

        public static bool SetFocusWithin(this DependencyObject element)
        {
            if (element == null)
                throw new ArgumentNullException("element");

            var children = element.GetVisualChildrenSortedByTabIndex();
            return children.Any(FocusThisOrChild);
        }
    }

和辅助方法:

public static IEnumerable<DependencyObject> GetVisualChildrenSortedByTabIndex(this DependencyObject parent)
        {
            if (parent == null)
                throw new ArgumentNullException("parent");

            return parent.GetVisualChildren().OrderBy(KeyboardNavigation.GetTabIndex);
        }



public static bool IsInMethod(string methodName, Type ownerType, bool isStatic = false)
        {
            if (string.IsNullOrWhiteSpace(methodName))
                throw new ArgumentNullException("methodName");

            if (ownerType == null)
                throw new ArgumentNullException("ownerType");

            var stackTrace = new StackTrace(false);
            var isInMethod = stackTrace.GetFrames().Skip(1).Any(frame =>
                       {
                           var method = frame.GetMethod();
                           return method.Name == methodName
                                  && method.IsStatic == isStatic
                                  && ownerType.IsAssignableFrom(method.ReflectedType);
                       });

            return isInMethod;
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接