【问题标题】:Confusing Behavior of DataGridDataGrid 的令人困惑的行为
【发布时间】:2014-04-14 06:23:36
【问题描述】:

我有一个 DataGrid 如下:

<DataGrid CanUserAddRows="True" CanUserReorderColumns="False" CanUserSortColumns="False" CanUserDeleteRows="True"
          ItemsSource="{Binding Groups}" AutoGenerateColumns="False">
    <DataGrid.InputBindings>
        <KeyBinding Key="Enter" Command="{Binding DataContext.NewRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"/>
    </DataGrid.InputBindings>
    <DataGrid.Resources>
        <CompositeCollection x:Key="Items">
            <ComboBoxItem IsEnabled="False" Background="#FF2A2A2A" Foreground="White">
                <Grid TextElement.FontWeight="Bold" >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition SharedSizeGroup="A" />
                        <ColumnDefinition Width="50" />
                        <ColumnDefinition SharedSizeGroup="B" />
                    </Grid.ColumnDefinitions>
                    <Grid.Children>
                        <TextBlock Grid.Column="0" Text="Group Name" />
                        <TextBlock Grid.Column="2" Text="Effect" />
                    </Grid.Children>
                </Grid>
            </ComboBoxItem>
            <CollectionContainer Collection="{Binding Source={StaticResource GroupNamesWithCorrespondingEffectsCollection}}" />
        </CompositeCollection>

        <DataTemplate DataType="{x:Type helpers:GroupNameWithCorrespondingEffect}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition SharedSizeGroup="A" />
                    <ColumnDefinition Width="50" />
                    <ColumnDefinition SharedSizeGroup="B" />
                </Grid.ColumnDefinitions>
                <Grid.Children>
                    <TextBlock Grid.Column="0" Text="{Binding GroupName}" />
                    <TextBlock Grid.Column="2" Text="{Binding CorrespondingEffect}" />
                </Grid.Children>
            </Grid>
        </DataTemplate>
    </DataGrid.Resources>
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Name" Width="2*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding GroupName}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Group" Width="2*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{DynamicResource Items}" 
                              SelectedValue="{Binding ParentID}"
                              SelectedValuePath="GroupID" Grid.IsSharedSizeScope="True" TextSearch.TextPath="GroupName">
                    </ComboBox>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Effect" Width="*" >
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding DataContext.Effects, RelativeSource={RelativeSource AncestorType={x:Type Page}}}" DisplayMemberPath="Effect" 
                                                    SelectedValue="{Binding EffectID}" SelectedValuePath="EffectID"
                              Visibility="{Binding Path=DataContext.SelectedGroupID, 
                                                     RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}},
                                                     Converter={StaticResource effectsVisibilityConverter}}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

这是我的页面的 DataContext 绑定到的 GroupsViewModel:

public class GroupsViewModel : ViewModelBase
{
    public GroupsViewModel()
    {
        Groups = new ObservableCollection<Group>();

        NewRowCommand = new RelayCommand(NewRow);

        using (Entities db = new Entities())
        {
            List<GroupNameWithCorrespondingEffect> _GroupNamesWithCorrespondingEffects = (
                                                                                             from g in db.Groups
                                                                                             select new GroupNameWithCorrespondingEffect
                                                                                             {
                                                                                                 GroupID = g.GroupID,
                                                                                                 GroupName = g.GroupName,
                                                                                                 CorrespondingEffect = g.Master_Effects.Effect
                                                                                             }
                                                                                         ).ToList();

            GroupNamesWithCorrespondingEffects
                = new ObservableCollection<GroupNameWithCorrespondingEffect>(
                                                                                _GroupNamesWithCorrespondingEffects.Where
                                                                                    (
                                                                                        u => !StaticMethods.GetAllChildren(25)
                                                                                                .Select(x => x.GroupID)
                                                                                                .Contains(u.GroupID)
                                                                                    ).ToList()
                                                                            );

            Effects = new ObservableCollection<Master_Effects>(from m in db.Master_Effects
                                                               select m);
        }
    }

    private ObservableCollection<Group> _groups;
    public ObservableCollection<Group> Groups
    {
        get
        {
            return _groups;
        }
        set
        {
            _groups = value;
            OnPropertyChanged("Groups");
        }
    }

    public ICommand NewRowCommand { get; set; }

    private void NewRow(object obj)
    {
            Groups.Add(new Group());
    }
}

问题:

我在数据网格中输入了一些数据,然后按 Enter 向数据网格添加了一个新行,这是预期的。但是新行被添加到 DataGrid 的顶部,而不是我希望它被添加到最后一个位置。其他行中的数据也被清除,但我希望它保持原样。

【问题讨论】:

  • NewRowCommand 代码在哪里?
  • 它在问题代码中提到的 ViewModel 中。
  • 我已在示例应用程序中成功重现了该问题。您可以在这里下载示例应用程序:drive.google.com/file/d/0B5WyqSALui0bTDhlc2JGTThiVlU/…
  • +1 用于提供样品。让生活变得如此简单!

标签: c# wpf xaml datagrid


【解决方案1】:

CanUserAddRows 是否引起了一些混乱?

"当此属性设置为 true 时,DataGrid 底部会显示一个空白行。"

此行将始终位于 ObservableCollection 提供的行下方。我像这样将一些虚拟数据放入 NewRole:

var p = new Person() {Name = "New " + DateTime.Now.TimeOfDay.TotalMilliseconds};
People.Add(p);

使结果更清晰。当您添加几行时,TotalMilliseconds 的最大值将位于集合的末尾,并且将是 DataGrid 中的倒数第二行。

【讨论】:

  • 感谢您的回答。您的回答有助于确定问题。但是 Maverick 的回答更有帮助,因为我不必再使用 KeyBindings 了。所以我接受了他的回答。再次感谢。
【解决方案2】:

罪魁祸首是你的KeyBinding。根据您的示例项目,当您按 Enter 时,您永远不会将 DataGrid 中的值保存回集合中。它通常发生在失去焦点的情况下,但是由于您的 Enter 在失去焦点之前添加了一行,因此您在空集合中添加了一个空白行。

DataGrid 检测到更改并使用此新行更新视图,同时保留正在进行的更改(您按 Enter 的行尚未完成更新)。结果显然是你所看到的。

我不知道你为什么使用 Enter 作为KeyBinding 来添加行,这应该在什么时候发生?如果没有发生,那是因为 DataGrid 无法创建您的模型(也许它不是公开的?或者您可能没有定义默认的无参数 ctor?)

对于您的示例项目,我删除了您的 KeyBinding 并在您的 Person 模型上实现了 INotifyChanged,它可以正常工作。

如果您使用的是 CellTemplates,则还需要实现 CellEditingTemplate。

<DataGrid CanUserAddRows="True" CanUserDeleteRows="True" CanUserReorderColumns="False" CanUserResizeColumns="False" AutoGenerateColumns="False" ItemsSource="{Binding People}">
    <DataGrid.Columns>
        <DataGridTemplateColumn Header="Name" Width="*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Name}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
        <DataGridTemplateColumn Header="Age" Width="*">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Age}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding Age}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

【讨论】:

  • 谢谢特立独行。它在使用 cellEditingTemplate 后工作。现在我想再做 1 件事。你能帮助我吗? 1. 输入人名后按TAB输入年龄时,焦点不显示在年龄单元格上,而是按两次TAB。
  • @Vishal 那是因为一个选项卡正在从编辑模式切换到正常模式。其他选项卡然后更改为下一个单元格编辑模式。我不确定如何解决。你真的需要使用模板列吗?如果您可以使用DataGridTextColumn 等,您可能会有更好的运气?
猜你喜欢
  • 2011-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多