令人困惑的DataGrid行为

时间:2014-04-14 06:23:37

标签: c# wpf xaml datagrid

我有一个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>

以下是我的Page的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());
    }
}

问题:

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

2 个答案:

答案 0 :(得分:1)

CanUserAddRows会引起一些混乱吗?

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

此行将始终位于ObservableCollection提供的行下方。我把一些虚拟数据放入NewRole,如下所示:

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

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

答案 1 :(得分:0)

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

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

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

对于您的示例项目,我删除了您的KeyBinding&amp;在您的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>