DataGrid的意外行为

时间:2014-11-28 13:26:14

标签: c# wpf xaml

我有一个非常复杂的问题。我试图检查一切,我已经在这个问题上工作了大约6个小时。但我没有成功解决这个问题。所以,问题就在这里。

问题:

最初程序加载时:

enter image description here

当我点击任何一行的编辑按钮时:

enter image description here

当我保存该行时:

enter image description here

当我点击具有不同父组的另一行上的编辑按钮时:

enter image description here

XAML如下所示:

<CollectionViewSource x:Key="GroupsViewSource" Source="{Binding Groups, UpdateSourceTrigger=PropertyChanged}">
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="GroupName"/>
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="ParentGroupsViewSource" Source="{Binding ParentGroups}">
    <CollectionViewSource.SortDescriptions>
        <scm:SortDescription PropertyName="GroupName"/>
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

<DataGrid Grid.Row="0" Grid.Column="0" ItemsSource="{Binding Source={StaticResource GroupsViewSource}}" 
      SelectedItem="{Binding SelectedGroup}" x:Name="dataGrid"
      AutoGenerateColumns="False" CanUserAddRows="False"
      SelectionMode="Single" SelectionUnit="FullRow" IsSynchronizedWithCurrentItem="True"
      EnableRowVirtualization="False" VirtualizingPanel.IsContainerVirtualizable="False" RowEditEnding="DataGrid_RowEditEnding">

    <DataGrid.Resources>
        <Style TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource {x:Type DataGridCell}}">
            <EventSetter Event="PreviewMouseLeftButtonDown" Handler="DataGridCell_PreviewMouseLeftButtonDown" />
        </Style>
    </DataGrid.Resources>

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="RowEditEnding">
            <i:InvokeCommandAction Command="{Binding DataGridRowEditEndingCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <DataGrid.Columns>

        <DataGridTemplateColumn Header="Group Name" Width="*" SortMemberPath="GroupName">
            <DataGridTemplateColumn.HeaderTemplate>
                <DataTemplate>
                    <Grid IsHitTestVisible="True">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{TemplateBinding Content}"/>
                        <!--FILTER EXPANDER-->
                        <Expander Grid.Column="1" IsHitTestVisible="True" VerticalAlignment="Top" Margin="30 0 0 0" ToolTip="Filter">
                            <Border IsHitTestVisible="True" BorderThickness="1" Margin="-160 5 0 0" MinWidth="200" Height="31" >
                                <TextBox Text="{Binding DataContext.SearchGroupName, ElementName=uc, UpdateSourceTrigger=PropertyChanged}" 
                                     TextChanged="SearchTextBox_TextChanged" ToolTip="Enter Group Name to search" FontSize="16" BorderThickness="1" />
                            </Border>
                        </Expander>
                    </Grid>
                </DataTemplate>
            </DataGridTemplateColumn.HeaderTemplate>

            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding GroupName}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding GroupName}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Parent Group" Width="*" SortMemberPath="ParentID">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding ParentID, Converter={StaticResource parentIDToGroupNameConverter}}" />
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <ComboBox ItemsSource="{Binding Source={StaticResource ParentGroupsViewSource}}" 
                          DisplayMemberPath="GroupName"
                          SelectedValue="{Binding ParentID, Converter={StaticResource parentIDToGroupNameConverter}}" SelectedValuePath="GroupName"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Edit" Width="50" IsReadOnly="True">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Button x:Name="btnEdit" Style="{StaticResource ResourceKey=EditButton}" Height="35" Width="35" 
                            Visibility="{Binding DataContext.IsInEdit, 
                                                 RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
                                                 Converter={StaticResource boolToVisibilityInverseConverter}}" 
                            Click="EditButton_Click" 
                            Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
                        <Button x:Name="btnSave" Grid.Row="1" Style="{StaticResource ResourceKey=SaveButton}" Height="35" Width="35" 
                            Visibility="{Binding DataContext.IsInEdit, 
                                                 RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
                                                 Converter={StaticResource boolToVisibilityConverter}}" 
                            Click="SaveButton_Click"
                            Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
                    </Grid>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

        <DataGridTemplateColumn Header="Delete" Width="70" IsReadOnly="True">
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        <Button x:Name="btnDelete" Style="{StaticResource ResourceKey=DeleteButton}" Height="35" Width="35"
                            Visibility="{Binding DataContext.IsInEdit, 
                                                 RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
                                                 Converter={StaticResource boolToVisibilityInverseConverter}}" 
                            Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
                        <Button x:Name="btnCancel" Grid.Row="1" Style="{StaticResource ResourceKey=CancelButton}" Height="35" Width="35" 
                            Visibility="{Binding DataContext.IsInEdit, 
                                                 RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, 
                                                 Converter={StaticResource boolToVisibilityConverter}}" 
                            Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
                            Click="CancelButton_Click"/>
                    </Grid>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
        </DataGridTemplateColumn>

    </DataGrid.Columns>

</DataGrid>

这是代码 - 背后:

public partial class ListView : UserControl
{
    ERPLiteDBContext db = new ERPLiteDBContext();

    public ListView()
    {
        InitializeComponent();
    }

    private void DataGridCell_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        DependencyObject dep = (DependencyObject)e.OriginalSource;

        if (dep == null)
            return;

        while (dep != null && !(dep is DataGridCell))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        if (dep is DataGridCell)
        {
            if (!((DataGridCell)dep).IsReadOnly)
            {
                if (!((DataGridCell)dep).IsEditing)
                    e.Handled = true;
            }
        }

        while (dep != null && !(dep is DataGridRow))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        if (dep is DataGridRow)
        {
            ((DataGridRow)dep).IsSelected = true;
        }

        while (dep != null && !(dep is DataGrid))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        if (dep is DataGrid)
        {
            ((DataGrid)dep).Focus();
        }
    }

    private void EditButton_Click(object sender, RoutedEventArgs e)
    {
        int rowIndex = 0;

        DependencyObject dep = (DependencyObject)e.OriginalSource;
        while (dep != null && !(dep is DataGridCell))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;
        DataGridRow row = null;
        if (dep is DataGridCell)
        {
            while (dep != null && !(dep is DataGridRow))
            {
                dep = VisualTreeHelper.GetParent(dep);
            }

            row = (DataGridRow)dep;
            rowIndex = FindRowIndex(row);
        }

        while (dep != null && !(dep is DataGrid))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        DataGrid dg = (DataGrid)dep;

        dg.CurrentCell = new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[0]);
        dg.BeginEdit();

        for (int column = 0; column <= dg.Columns.Count - 1; column++)
        {
            if (!(GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsReadOnly))
            {
                GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsEditing = true;
            }
        }

        var rows = GetDataGridRows(dg);

        foreach (DataGridRow r in rows)
        {
            if (!(r.IsEditing))
            {
                r.IsEnabled = false;
            }
        }
    }

    private void SaveButton_Click(object sender, RoutedEventArgs e)
    {
        int rowIndex = 0;

        DependencyObject dep = (DependencyObject)e.OriginalSource;
        while (dep != null && !(dep is DataGridCell))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        DataGridRow row = null;

        if (dep is DataGridCell)
        {

            while (dep != null && !(dep is DataGridRow))
            {
                dep = VisualTreeHelper.GetParent(dep);
            }

            row = (DataGridRow)dep;
            rowIndex = FindRowIndex(row);

        }

        while (dep != null && !(dep is DataGrid))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        DataGrid dg = (DataGrid)dep;

        dg.CommitEdit(DataGridEditingUnit.Row, true);

        for (int column = 0; column <= dg.Columns.Count - 1; column++)
        {
            if (!(GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsReadOnly))
            {
                GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsEditing = false;
            }
        }

        var rows = GetDataGridRows(dg);

        foreach (DataGridRow r in rows)
        {
            r.IsEnabled = true;
        }
    }

    private void CancelButton_Click(object sender, RoutedEventArgs e)
    {
        DependencyObject dep = (DependencyObject)e.OriginalSource;

        int rowIndex = 0;

        DataGridRow row = null;

        while (dep != null && !(dep is DataGridRow))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        row = (DataGridRow)dep;
        rowIndex = FindRowIndex(row);

        while (dep != null && !(dep is DataGrid))
        {
            dep = VisualTreeHelper.GetParent(dep);
        }

        if (dep == null)
            return;

        DataGrid dg = (DataGrid)dep;

        var rows = GetDataGridRows(dg);

        dg.CancelEdit(DataGridEditingUnit.Row);

        for (int column = 0; column <= dg.Columns.Count - 1; column++)
        {
            if (!(GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsReadOnly))
            {
                GetDataGridCell(new DataGridCellInfo(dg.Items[rowIndex], dg.Columns[column])).IsEditing = false;
            }
        }

        foreach (DataGridRow r in rows)
        {
            r.IsEnabled = true;
        }
    }

    private void DataGrid_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
    {
        DataGrid dg = (DataGrid)sender;
        foreach (DataGridRow row in GetDataGridRows(dg))
        {
            row.IsEnabled = true;
        }
    }

    private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        if (dataGrid.SelectedItem != null)
        {
            dataGrid.ScrollIntoView(dataGrid.SelectedItem);
        }
    }

    public DataGridCell GetDataGridCell(DataGridCellInfo cellInfo)
    {
        var cellContent = cellInfo.Column.GetCellContent(cellInfo.Item);
        if (cellContent != null)
            return (DataGridCell)cellContent.Parent;

        return null;
    }

    private int FindRowIndex(DataGridRow row)
    {
        DataGrid dataGrid = ItemsControl.ItemsControlFromItemContainer(row) as DataGrid;

        int index = dataGrid.ItemContainerGenerator.IndexFromContainer(row);

        return index;
    }

    public IEnumerable<DataGridRow> GetDataGridRows(DataGrid grid)
    {
        var itemsSource = grid.ItemsSource as IEnumerable;
        if (null == itemsSource) yield return null;
        foreach (var item in itemsSource)
        {
            var row = grid.ItemContainerGenerator.ContainerFromItem(item) as DataGridRow;
            if (null != row) yield return row;
        }
    }
}

At Last这里是ViewModel的代码:

public class ListViewModel : ViewModelBase
{
    ERPLiteDBContext db = new ERPLiteDBContext();

    public ListViewModel()
    {
        Groups = new ObservableCollection<Group>(db.Groups);
        ParentGroups = new ObservableCollection<Group>(db.Groups);

        EditCommand = new RelayCommand(Edit);
        SaveCommand = new RelayCommand(Save);
        DeleteCommand = new RelayCommand(Delete);
        CancelCommand = new RelayCommand(Cancel);
        DataGridRowEditEndingCommand = new RelayCommand(DataGridRowEditEnding);

        SearchGroupName = "";
        IsInEdit = false;
    }

    public RelayCommand EditCommand { get; set; }
    public RelayCommand SaveCommand { get; set; }
    public RelayCommand DeleteCommand { get; set; }
    public RelayCommand CancelCommand { get; set; }
    public RelayCommand DataGridRowEditEndingCommand { get; set; }

    private string _searchGroupName;
    public string SearchGroupName
    {
        get
        {
            return _searchGroupName;
        }
        set
        {
            if (value == null)
            {
                SearchGroupName = "";
            }
            else
            {
                _searchGroupName = value;
            }

            OnPropertyChanged("SearchGroupName");

            SelectedGroup = db.Groups.AsEnumerable().OrderBy(x => x.GroupName).Where(x => x.GroupName.StartsWith(SearchGroupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();

            if (SelectedGroup == null)
            {
                SelectedGroup = db.Groups.AsEnumerable().OrderBy(x => x.GroupName).Where(x => x.GroupName.Contains(SearchGroupName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
            }

        }
    }

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

    private Group _selectedGroup;
    public Group SelectedGroup
    {
        get
        {
            return _selectedGroup;
        }
        set
        {
            _selectedGroup = value;
            OnPropertyChanged("SelectedGroup");

            if (value != null)
            {
                ParentGroups = new ObservableCollection<Group>(db.Groups.Where(x => x.GroupID != value.GroupID));
                ParentGroups.Add(new Group { GroupID = -1, GroupName = "Primary" });
            }
        }
    }

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

    private Group _selectedParentGroup;
    public Group SelectedParentGroup
    {
        get
        {
            return _selectedParentGroup;
        }
        set
        {
            _selectedParentGroup = value;
            OnPropertyChanged("SelectedParentGroup");
        }
    }

    private bool _isInEdit;
    public bool IsInEdit
    {
        get
        {
            return _isInEdit;
        }
        set
        {
            _isInEdit = value;
            OnPropertyChanged("IsInEdit");
        }
    }

    private void Edit(object obj)
    {
        IsInEdit = true;
    }

    private void Save(object obj)
    {
        IsInEdit = false;
        SaveToDataBase();
    }

    private void Delete(object obj)
    {

    }

    private void Cancel(object obj)
    {
        IsInEdit = false;
    }

    private void DataGridRowEditEnding(object obj)
    {
        IsInEdit = false;
    }

    public void SaveToDataBase()
    {
        Group currentGroup = db.Groups.Where(x => x.GroupID == SelectedGroup.GroupID).FirstOrDefault();
        if (currentGroup != null)
        {
            currentGroup.GroupName = SelectedGroup.GroupName;
            if (SelectedGroup.ParentID == -1)
            {
                currentGroup.ParentID = null;
            }
            else
            {
                currentGroup.ParentID = SelectedGroup.ParentID;
            }

            db.SaveChanges();
        }
    }
}

我不确定问题出在哪里,因此,我在这里发布了几乎所有的代码。

我创建了一个示例项目,可以从以下链接下载:

项目:

https://drive.google.com/file/d/0B5WyqSALui0bTTNsMm5ISHV3VEk/view?usp=sharing

数据库:

https://drive.google.com/file/d/0B5WyqSALui0bTXVJanp4TE9iSGs/view?usp=sharing

修改1:

原始问题(如图中所示)已解决

为了解决这个问题,我删除了CollectionViewSource并将ComboBox直接绑定到ViewModel属性。现在我又收到了一个错误:

enter image description here

当我看到InnerException时:

enter image description here

我在网上搜索了这个错误,但随后我发现如果我将NULL值插入外键列,那么我就会收到此错误。但我的列是Nullable,我需要插入空值。

编辑2:

我已解决了编辑1中提到的错误。

在名为Primary的表中添加了一条新记录作为第一条记录。并删除了与Primary Group相关的所有编码。用主组替换数据库中的NULL。

现在唯一的问题是使用CollectionViewSource作为组合框的源来对数据进行排序,而不是将组合框直接绑定到ViewModel中的Property。谁能回答这个问题????

2 个答案:

答案 0 :(得分:3)

您正在混合Commands和事件处理程序 - 从来不是一个好主意。我相信我明白你要做什么。这将是纯MVVM解决方案,没有事件处理程序路由。

在开始之前,我们假设您的DataGrid绑定到代表各行的ObservableCollectionViewModel个对象。这解决了当前在XAML中对RelativeSource绑定到父ViewModel个对象的麻烦和复杂性,并简化了每行Commands的实现(因为数据和命令)如果您使用DelegateCommand,则封装在同一个类中 - 因为您正在使用PRISM我假设您在任何地方都使用它。)

首先,您应该为DataGridCell.IsEditing属性添加一个样式,以将其绑定到IsEditing上的ViewModel布尔属性。更改您的修改按钮以绑定到CommandIsEditing将行ViewModel上的true设置为Mode=TwoWay - 网格应通过进入编辑模式做出反应。

您可能需要考虑将编辑模板绑定到“编辑特定值”,以便在“取消编辑”时不会覆盖原始数据。另请记住将绑定设置为UpdateSourceTrigger=PropertyChanged SaveCommand

然后,您可以将“交互事件到命令”映射器添加到各种“编辑”模板控件,这样,如果您按Enter键调用IsEditing并通过将ViewModel上的{{1}}设置为false来结束编辑模式

答案 1 :(得分:3)

问题解决了。有关解决方案的一半,请参阅相关的编辑1和编辑2.

编辑2之后,我无法使用CollectionViewSource对ComboBox中的数据进行排序。所以,我在查询中使用了OrderBy。现在,ComboBox中的数据按预期排序。现在,我的查询如下:

ParentGroups = new ObservableCollection<Group>(db.Groups.OrderBy(x => x.GroupName));

现在DataGrid按预期工作。

我已经发布了这个解决方案,因为我认为它可能会对将来有所帮助。

感谢大家有兴趣解决我的问题。

在这里您可以找到工作样本:

项目:https://drive.google.com/file/d/0B5WyqSALui0beC05VnpMY1hzV3c/view?usp=sharing

数据库:https://drive.google.com/file/d/0B5WyqSALui0bTXVJanp4TE9iSGs/view?usp=sharing