【问题标题】:Auto-focus on control in new row自动聚焦新行中的控件
【发布时间】:2014-06-24 20:25:20
【问题描述】:

我有一个带有DataGrid(来自 WPF 工具包)控件的 WPF 应用程序。 ItemsSouce 属性绑定到我的 ViewModel 中的 ObservableCollection

数据网格有一列,其中包含TextBox

<dg:DataGrid.Columns>
    <dg:DataGridTemplateColumn Header="Name">
         <dg:DataGridTemplateColumn.CellTemplate>
               <DataTemplate>
                   <TextBox Text="{Binding UserName}" Width="300"/>
                </DataTemplate>
         </dg:DataGridTemplateColumn.CellTemplate>
    </dg:DataGridTemplateColumn>
...

我还有一个“添加”按钮来创建新用户。当我单击它时,会创建一个新行。但是,我希望上面的文本框能够获得输入焦点(当然是在新行上)。我看过:

WPF MVVM Focus Field on Load

WPF-MVVM: Setting UI control focus from ViewModel

How to set focus to textbox using MVVM?

Set focus on textbox in WPF from view model (C#)

但它们似乎都依赖于“ElementName”绑定的相同变体,并且看起来它们都不会在ItemsControl 中工作。 获得此行为的正确方法是什么?

【问题讨论】:

  • 只需处理TextBox的加载事件并做Keyboard.Focus(sender as TextBox);
  • @HighCore 好主意。在我第一次尝试将焦点放在现有文本框上时,添加新行不会影响它。不过我会尝试更多的版本,谢谢你的建议! 我注意到我在预先设置,可能会导致问题如果可能的话,一个与订单无关的解决方案会很好。
  • @HighCore,插入顺序确实导致了问题。您的解决方案简单有效(如果取决于订单),这将是一个很好的答案。

标签: c# wpf xaml wpftoolkit


【解决方案1】:

我相信您可以做到这一点的一种方法是在处理已加载的文本框上设置一个触发器并设置焦点。像这样的

<dg:DataGridTemplateColumn Header="Name">
    <dg:DataGridTemplateColumn.CellTemplate>
       <DataTemplate>
           <TextBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" Text="{Binding UserName}" Width="300">
            <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Loaded">
                            <local:SetFocusTrigger/>
                        </i:EventTrigger>
                </i:Interaction.Triggers>
           </TextBox>
        </DataTemplate>
    </dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>

SetFocusTrigger 类是:-

    public class SetFocusTrigger : TargetedTriggerAction<Control>
    {
        protected override void Invoke(object parameter)
        {
            if (Target == null) return;

            Target.Focus(); 
        }
    }

请注意,我还没有尝试过。

【讨论】:

  • 一种有趣的方法,似乎是 HighCore 解决方案的无代码隐藏版本。有没有办法让它不依赖于新项目在列表中的插入位置?
  • 我很惊讶订单会出现问题,因为当您添加新行时,加载应该会导致新文本框加载,而这反过来又会让触发器起作用。我能想到的唯一另一件事是使用 FocusManager.FocusedElement 设置正确的文本框。
  • 也许每个文本框都因为 CollectionChanged 事件触发而重新加载?您的替代方案将如何运作?
  • 是的,除非你有一些属性指定哪一行应该有焦点,否则我的替代将无法工作。
  • 鉴于它是我实际在做的唯一 XAML 版本,我会接受你的作为答案。感谢您的帮助!
【解决方案2】:

我尝试在纯 XAML 中做

<DataGrid CanUserAddRows="True"
          ItemsSource="{Binding List}"
          AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Name">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"
                               Width="300" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Name}"
                             FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
                             Width="300" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

我添加了两个模板 1 用于正常模式和其他用于编辑模式。所以一旦单元格进入编辑模式,它将聚焦文本框。

如果您总是希望文本框可见,那么它将具有不同的行为,因为新行绑定到占位符并且值为文本框将持续存在并移至除实际行之外的每个新行。

ListView 解决方案 (MVVM)

示例 XAML

<StackPanel>
    <Button Command="{Binding AddItem}"
            Content="Add Item"/>
    <ListView ItemsSource="{Binding List}"
              Grid.IsSharedSizeScope="True">
        <ListView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="name" />
                        <!--other columns-->
                        <ColumnDefinition  />
                    </Grid.ColumnDefinitions>
                    <TextBox Text="{Binding Name}"
                             FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" />
                    <TextBox Text="other column" Grid.Column="1"/>
                </Grid>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>

示例虚拟机

    public ViewModel()
    {
        AddItem = new SimpleCommand(i => List.Add(new Person()));
        List = new ObservableCollection<object>(new[] { new Person() { Name = "a person"} });
    }

    public ObservableCollection<object> List { get; set; }

    class Person
    {
        public string Name { get; set; }
    }

    public ICommand AddItem { get; set; }

因此,在添加新项目时,将通过向列表中添加新人员来创建新行,并将焦点显示在文本框上。

运行上面的示例并查看行为,不涉及任何代码。

【讨论】:

  • 这很有趣,如何在添加单元格后立即将其标记为编辑?我确实希望它们也始终是可编辑的。
  • grid.CurrentCell= newly added cell; & grid.BeginEdit(); 将使单元格可编辑。
  • 但是我无法访问新添加的单元格...我只是将一个项目添加到绑定的 ItemsSource 中。当然我可以通过索引等搜索它,但这似乎比其他答案复杂得多(并且仍然依赖于顺序)是否有一些我错过的简单方法?
  • 如果您希望所有这些都可以一直编辑,也许 datagrid 不适合您,您可以使用带有模板的普通项目控件。这样就可以了。让我试着想出一个相同的解决方案。
  • ListView 可能有效(绝对不是 ItemsControl,因为我需要“DataGrid”外观)但我的样式目前适用于 DataGrid。无论如何,它都允许它们都可以编辑(也许是使用 WPF Toolkit 版本的副作用?)无论如何,这并不是真正的问题所在。不过,我很想看看您的 ListView 解决方案,也许它会给我一个想法。
猜你喜欢
  • 2020-08-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-13
  • 1970-01-01
  • 2020-08-16
相关资源
最近更新 更多