2013-04-03 81 views
3

我試圖將鍵盤焦點設置爲包含在默認情況下摺疊的stackpanel中的文本框。當堆疊面板變得可見時,我希望默認情況下文本框成爲焦點。焦點文本框僅使用XAML

我試過這段代碼:

<StackPanel Orientation="Vertical" FocusManager.FocusedElement="{Binding ElementName=TxtB}"> 
    <TextBox x:Name="TxtA" Text="A" /> 
    <TextBox x:Name="TxtB" Text="B" /> 
</StackPanel> 

然而,它沒有工作。類型光標出現,但不閃爍,不允許寫入。

僅使用XAML可以解決我的問題嗎?也許是觸發器?

回答

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; 
     } 
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> 
+0

我想你不會知道有這麼一個簡單的解決方案只能在Silverlight中用xaml做同樣的事情嗎?我嘗試了相同的概念,但沒有這樣的運氣,並一直在尋找一個簡單的adhoc解決方案。 –

+0

@ChrisW。,我沒有意識到這種簡單的Silverlight解決方案。 – icebat