【问题标题】:How to prevent a UserControl from taking focus away from its parent?如何防止 UserControl 将焦点从其父级移开?
【发布时间】:2014-06-06 14:13:41
【问题描述】:

我在我的应用程序中使用 MVVM,并且有一个允许用户输入基本人员信息的表单。该表单包含一个 UserControl,它基本上是一个 ItemsControl,其中包含可以动态创建的文本框。这是一个简化版:

<ItemsControl x:Name="items" ItemsSource="{Binding MyItemsCollection}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid x:Name="row">
                <TextBox x:Name="textBox" Text="{Binding ContactInfo, ValidatesOnExceptions=True}" extensions:FocusExtension.IsFocused="{Binding IsFocused}"/>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
<Button x:Name="NewItemButton" Command="{Binding AddItemToMyCollectionCommand}" />

我希望刚刚创建的 TextBox 接收焦点,因此我添加了一个附加属性。这是其中的一部分:

public static readonly DependencyProperty IsFocusedProperty =
        DependencyProperty.RegisterAttached("IsFocused", typeof(bool), typeof(FocusExtension), new UIPropertyMetadata(false, OnIsFocusedPropertyChanged));

private static void OnIsFocusedPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var uie = (UIElement)d;
    if ((bool)e.NewValue)
    {
        uie.Focus();
    }
}

在包含 UserControl 的表单中,前后还有几个其他文本框。 UserControl 有自己的 ViewModel,我通过容器 ViewModel 中的属性将其设置为控件的 DataContext。基本上,容器的简化版本如下所示:

<StackPanel Orientation="Horizontal" />
    <TextBox x:Name="firstName" />
    <TextBox x:Name="lastName" />
    <local:DynamicFormUserControl
        x:Name="phones"
        DataContext="{Binding PhonesViewModel}" />
    <local:DynamicFormUserControl
        x:Name="emails"
        DataContext="{Binding EmailsViewModel}" />
    <TextBox x:Name="address" />
</StackPanel>

我的问题是我希望 firstName TextBox 在第一次加载表单时获得焦点,但表单继续将焦点放在手机 UserControl 的第一个 TextBox 上。我试图通过在表单的 Loaded 事件上使用 firstName.Focus() 来覆盖它,但这不起作用,无论我尝试什么,焦点仍然在手机 userControl 上,而不是表单中的第一个元素包含它。

有人知道如何解决这个问题吗?

谢谢。

【问题讨论】:

  • 像@pushpraj 建议的那样使用FocusManager.FocusedElement 并删除您的附加属性...如果您不想使用它,为什么还要设置它?我也看不出有任何理由在这里使用UserControl……你可以用简单的Styles 和DataTemplates 来完成所有这些工作。请参阅 MSDN 上的[控制创作概述‎](msdn.microsoft.com/en-us/library/…) 页面,了解何时适合UserControl 派生类。
  • 感谢谢里登,您的反馈。我使用 UserControl 的原因是因为我试图在表单中添加一个部分,该部分可以使用 viewModel 中的特定逻辑动态创建更多文本框。我在这里展示的代码是我实际应用程序的精简版。我试图只使用样式但无法弄清楚,所以我在 StackOverflow 中找到了一个使用 UserControls 的解决方案。我有一个附加属性的原因是当一个新的文本框被动态创建时(通过单击控件中的按钮),我希望该框获得焦点。

标签: c# wpf mvvm user-controls


【解决方案1】:

给你

FocusManager.FocusedElement="{Binding ElementName=firstName}" 添加到您的堆栈面板

<StackPanel Orientation="Horizontal" 
            FocusManager.FocusedElement="{Binding ElementName=firstName}"/>
    <TextBox x:Name="firstName" />
    <TextBox x:Name="lastName" />
    <local:DynamicFormUserControl
        x:Name="phones"
        DataContext="{Binding PhonesViewModel}" />
    <local:DynamicFormUserControl
        x:Name="emails"
        DataContext="{Binding EmailsViewModel}" />
    <TextBox x:Name="address" />
</StackPanel>

还请注意,您可能需要防止用户控件中的项目控件聚焦自身

<ItemsControl x:Name="items" Focusable="False" >
    <ItemsControl.ItemTemplate>

【讨论】:

  • 感谢您的回答,但它不起作用。我已经尝试过这样做,但是一旦创建表单,应用程序就会将焦点放在手机 UserControl 的第一个文本框而不是实体表单的第一个文本框上。在我的实际应用程序中,我实际上使用了网格而不是 StackPanel,当我使用不同类型的面板时焦点会改变吗?
  • 您是否在期望获得焦点之前设置了 IsFocused 属性?对我来说,我复制了您的代码并添加了FocusManager.FocusedElement,它对我有用。我没有在代码中的任何地方设置 IsFocused。也许您提到的代码位于其他没有焦点的可聚焦容器中。
  • 是的,我发现了问题所在。我的错误是,我没有从一开始就解释我构建的整个表单本身就是窗口内的 UserControl,出于某种原因,我不太了解 UserControl 即使您设置了焦点也无法获得焦点() 或 FocusedElement。我为这篇文章写了一个答案,解释了我是如何解决这个问题的。如果您认为您有一个更简单的解决方案,我将不胜感激,因为我找到的解决方案在代码隐藏中添加了一些行。不过,再次感谢您的帮助。
【解决方案2】:

我想我设法找到了解决方案。问题是我创建的表单本身就是一个窗口内的用户控件,并且从未获得焦点。我认为这无关紧要,所以我在之前的帖子中没有提到它——对不起。我在this 中找到了将焦点集中到用户控件的解决方案。

基本上,当我在窗口中有一个 UserControl 时,即使我尝试使用 Focus() 或 FocusedElement 设置焦点,它也不会获得焦点。因此,为了克服这个问题,我在另一篇文章中找到了一种解决方法。基本上,我将它添加到包含 firstName 文本框的 UserControl 的代码隐藏中。如果我们调用 UserControl,比如 PersonalInfoUserControl,控件的构造函数将如下所示:

public PersonalInfoUserControl()
{
    InitializeComponent();
this.IsVisibleChanged += new DependencyPropertyChangedEventHandler(UserControl_IsVisibleChanged); 
}

我向控件的 IsVisibleChanged 事件添加了一个事件处理程序。该方法如下所示:

void UserControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)e.NewValue == true)
{
        Dispatcher.BeginInvoke(
    DispatcherPriority.ContextIdle,
    new Action(delegate()
    {
    firstName.Focus();
    }));
} 
}  

【讨论】:

  • 您能否添加原始帖子中的代码更改。链接可能会断开,而您所解释的内容,用文字解释,修复是什么,这对每个人来说可能并不明显。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 1970-01-01
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多