In WPF, how do you set the target of a label so that the access key will set focus on the control inside a ContentControl?
I am using MVVM and so I do not want to 开发者_开发百科add any code to the code behind in order to solve this.
I have already tried setting the path to "Content" and at runtime an exception was thrown because there is no converter for the data type which is set to the content of the ContentControl. If I don't set the path, then focus is set to the ContentControl itself.
<Label Target="{Binding ElementName=_myContentControl, Path=Content}"/>
Use GotFocus event.
<Label Target="myContentControl" >_Content</Label>
<ContentControl x:Name="myContentControl" GotFocus="myContentControl_GotFocus">
private void myContentControl_GotFocus(object sender, RoutedEventArgs e)
{
var cc = sender as ContentControl;
if (cc != null && cc.Content is UIElement)
((UIElement)cc.Content).Focus();
}
Another solution using the separated class FocusBehavior:
class FocusBehaviour : Behavior<ContentControl>
{
protected override void OnAttached()
{
base.OnAttached();
this.AssociatedObject.GotFocus += new System.Windows.RoutedEventHandler(AssociatedObject_GotFocus);
}
void AssociatedObject_GotFocus(object sender, System.Windows.RoutedEventArgs e)
{
var c = this.AssociatedObject.Content as UIElement;
if (c != null)
c.Focus();
}
protected override void OnDetaching()
{
base.OnDetaching();
this.AssociatedObject.GotFocus -= new System.Windows.RoutedEventHandler(AssociatedObject_GotFocus);
}
}
XAML:
<ContentControl x:Name="myContentControl">
<i:Interaction.Behaviors>
<local:FocusBehaviour />
</i:Interaction.Behaviors>
</ContentControl>
This way requires a dll that is called System.Windows.Interactivity and is installed with Expression Blend SDK.
You could also use a converter to bind the label's Target
to the Content
of the ContentControl
:
[ValueConversion(typeof(ContentControl), typeof(UIElement))]
public class ToContentConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var source = value as ContentControl;
if (source == null)
{
throw new ArgumentException("ToContentConverter source must be a ContentControl.");
}
return source.Content;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Declare the converter:
<Converters:ToContentConverter x:Key="ToContentConverter" />
And use it:
<Label Content="_Thing:" Target="{Binding ElementName=TheContentControl, Converter={StaticResource ToContentConverter}}" />
<ContentControl Name="TheContentControl" />
The approach I went with was similar to vorrtex's idea but doesn't require adding a reference to System.Windows.Interactivity
You create a boolean attached property with an event handler for when it changes. Add this property to your content control in the xaml. When the property is added, the event handler fires and here you can subscribe to the got focus event on your content control.
In the got focus event handler, you move focus to the next object which will be the content! Be sure that you set IsTabStop=False on the content control or you won't be able to Shift+Tab out of the content.
public static bool? GetFocusContent(DependencyObject obj)
{
return (bool?)obj.GetValue(FocusContentProperty);
}
public static void SetFocusContent(DependencyObject obj, bool? value)
{
obj.SetValue(FocusContentProperty, value);
}
public static readonly DependencyProperty FocusContentProperty =
DependencyProperty.RegisterAttached("FocusContent", typeof(bool?), typeof(MyClassName),
new UIPropertyMetadata(OnFocusContentChanged));
static void OnFocusContentChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
if (((bool?)e.NewValue).Value == true)
{
ContentControl cControl = obj as ContentControl;
if (cControl!= null)
{
cControl.GotFocus += OnGotFocus;
}
}
}
static void OnGotFocus(object sender, RoutedEventArgs e)
{
ContentControl cControl = sender as ContentControl;
// You should check the original source against the sender to make sure that
// you don't prevent focus from getting to a child of your content.
if (cControl != null && e.OriginalSource == sender)
{
cControl.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}
}
精彩评论