我正在尝试将键盘焦点设置为默认情况下折叠的stackpanel中包含的文本框。当stackpanel变得可见时,我希望文本框默认为聚焦。
我试过这段代码:
<StackPanel Orientation="Vertical" FocusManager.FocusedElement="{Binding ElementName=TxtB}">
<TextBox x:Name="TxtA" Text="A" />
<TextBox x:Name="TxtB" Text="B" />
</StackPanel>
然而,它没有用。类型光标显示,但它没有闪烁,也不允许写入。
是否可以仅使用XAML解决我的问题?也许触发?
答案 0 :(得分: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>
答案 1 :(得分: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;
}