WPF 数据网格 - 当可编辑单元格“打开”时,具有可编辑列的数据网格分组会对组中的行进行重新排序。或编辑过的

发布于 2024-12-13 16:22:56 字数 376 浏览 0 评论 0原文

分组后,我在 WPF 数据网格中遇到一个奇怪的问题。组内的行开始重新排序。我正在使用 codeplex 的 .net 3.5 框架的数据网格。

该问题已在msdn论坛中提出。请找到下面的链接。

http://social.msdn.microsoft.com/Forums/en/wpf/thread/3f915bf9-2571-43e8-8560-3def1d0d65bb

msdn用户在线程末尾说道可能没有解决方法。但我非常需要一个!

I am facing a strange problem in WPF datagrid after grouping. The rows inside groups start reordering. I am using the .net 3.5 framework's datagrid from codeplex.

The problem has been asked in msdn forum. Please find the link below.

http://social.msdn.microsoft.com/Forums/en/wpf/thread/3f915bf9-2571-43e8-8560-3def1d0d65bb

The msdn user has says at the end of the thread that there might be no workarounds. But I badly need one !!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

三岁铭 2024-12-20 16:22:57

您是否尝试过通过 CustomSort 进行排序?我在某个地方看到了这篇文章,它对我有用。虽然需要一些查找,但可以通过 ListCollectionView 类获得。类似于:

ListCollectionView lcv = 
    (ListCollectionView)CollectionViewSource.GetDefaultView(YourObjectCollection);

lcv.CustomSort = new YourObjectComparer();

其中 YourObjectComparer 实现 IComparer 并根据您想要的属性进行排序。

Have you tried doing your sorting via a CustomSort? I saw this posted somewhere and it worked for me. It took a little bit of finding, but it's available via the ListCollectionView class. So something like:

ListCollectionView lcv = 
    (ListCollectionView)CollectionViewSource.GetDefaultView(YourObjectCollection);

lcv.CustomSort = new YourObjectComparer();

Where YourObjectComparer implements IComparer and sorts on the properties that you want to.

っ〆星空下的拥抱 2024-12-20 16:22:57

编辑:

为此,当进行单元格编辑时,我们会删除组并再次添加它们。这使得细胞保持其预期的顺序。但这又带来了另一个问题,即所有扩展器都塌陷了。因此,如果我们记住哪些扩展器在重新组合后仍然保持扩展状态,我们就可以实现您所寻求的目标。


有一些事件可以帮助您实现此功能...

  1. DataGrid.CellEditEnding 事件
  2. Expander.InitializedExpander.Expanded 和 < code>Expander.Collpased 事件
  3. ICollectionView.CurrentChanging 事件

您需要记住的是扩展器展开或折叠时的状态。每个扩展器代表由 Name 属性表示的分组值。这些分组值是唯一的。因此,一个字典,其中 Dictionary.Key 是这个 Name 值,而 Dictionary.ValueExpander.IsExpanded 标志就足够了。

有了这个原材料,下面的代码就可以满足您的需求...

模型类: 我表示 DataGrid 中键值对象的简单列表。 >

public class MyKeyValuePair<TKey, TValue> : INotifyPropertyChanged
{
    private TKey key;
    private TValue value;

    public MyKeyValuePair(TKey k, TValue v)
    {
        key = k;
        value = v;
    }

    public TKey Key
    {
        get { return key; }
        set {
            key = value;
            OnPropertyChanged("Key");
        }
    }

    public TValue Value
    {
        get { return value; }
        set {
            this.value = value;
            OnPropertyChanged("Value");
        }
    }

    public void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged
              (this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler  PropertyChanged;
}

XAML:

 <tk:DataGrid
     ItemsSource="{Binding}"
     CellEditEnding="MyDataGrid_CellEditEnding">
    <tk:DataGrid.GroupStyle>
       <GroupStyle>
          <GroupStyle.HeaderTemplate>
             <DataTemplate>
                <StackPanel>
                   <TextBlock Text="{Binding Path=Name}" />
                </StackPanel>
             </DataTemplate>
          </GroupStyle.HeaderTemplate>
        <GroupStyle.ContainerStyle>
        <Style TargetType="{x:Type GroupItem}">
           <Setter Property="Template">
              <Setter.Value>
                 <ControlTemplate TargetType="{x:Type GroupItem}">
                     <Expander
                        Initialized="Expander_Initialized"
                        Expanded="Expander_Expanded"
                        Collapsed="Expander_Expanded">
                        <Expander.Header>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock
                                   Text="{Binding Path=Name}" />
                                <TextBlock Text=" (" />
                                <TextBlock 
                                   Text="{Binding Path=ItemCount}"/>
                                <TextBlock Text="( " />
                                <TextBlock Text="Items"/>
                            </StackPanel>
                        </Expander.Header>
                        <ItemsPresenter />
                     </Expander>
                 </ControlTemplate>
              </Setter.Value>
           </Setter>
        </Style>
     </GroupStyle.ContainerStyle>
   </GroupStyle>
 </tk:DataGrid.GroupStyle>
</tk:DataGrid>

Window.cs 代码隐藏:

public partial class Window8 : Window
{
    private Dictionary<string, bool> _dict;
    public Window8()
    {
        InitializeComponent();

        _dict = new Dictionary<string, bool>();

        var list1 = new List<MyKeyValuePair<string, int>>();
        var random = new Random();
        for (int i = 0; i < 50; i++)
        {
            list1.Add(new MyKeyValuePair<string, int>(
                i.ToString(), random.Next(300) % 3));
        }

        var colView = new ListCollectionView(list1);
        colView.GroupDescriptions.Add(
            new PropertyGroupDescription("Value"));
        this.DataContext = colView;
    }

    private void MyDataGrid_CellEditEnding
        (object sender, DataGridCellEditEndingEventArgs e)
    {
        var dg = sender as DataGrid;
        var cellInfo = dg.CurrentCell;
        var mySource = dg.ItemsSource as ListCollectionView;
        var oldDlg
            = new CurrentChangingEventHandler((obj, args) => { return; });
        var dlg = new CurrentChangingEventHandler(
            (obj, args) =>
            {
                if (cellInfo.Item == mySource.CurrentItem)
                {
                    var grpDescs = mySource.GroupDescriptions;
                    var oldGrpDescs
                        = grpDescs.Cast<PropertyGroupDescription>().ToList();
                    mySource.Dispatcher.BeginInvoke(
                        new Action(
                            () =>
                            {
                                grpDescs.Clear();

                                foreach (var grdpDesc in oldGrpDescs)
                                {
                                    grpDescs.Add(grdpDesc);
                                }

                                mySource.CurrentChanging -= oldDlg;
                            }));
                }
            });

        oldDlg = dlg;
        mySource.CurrentChanging -= oldDlg;
        mySource.CurrentChanging += oldDlg;
    }

    private void Expander_Expanded(object sender, RoutedEventArgs e)
    {
        var exp = sender as Expander;
        var dc = exp.DataContext as CollectionViewGroup;
        _dict[dc.Name.ToString()] = exp.IsExpanded;
    }

    private void Expander_Initialized(object sender, EventArgs e)
    {
        var exp = sender as Expander;
        var dc = exp.DataContext as CollectionViewGroup;
        if (_dict != null
            && _dict.ContainsKey(dc.Name.ToString())
            && _dict[dc.Name.ToString()])
        {
            exp.IsExpanded = true;
        }
    }
}

但有两个权衡。

  1. 对于数据网格中的大量项目,这将使每个单元格编辑尝试变慢。因为重新分组是在每次单元格编辑尝试之后发生的。
  2. 可能不适用于多个组描述,因为字典中的 Name 键可能/可能不会保持唯一。例如,假设对于员工列表,如果您按 FirstName 进行分组,并且还按 LastName 进行分组,则会有嵌套的扩展器。现在,某些名字分组可能与某些姓氏分组相匹配,例如George。这样字典就会陷入困境,无法正常工作。

希望这有帮助。

EDIT:

For this when a cell edit takes place, we remove groups and add them again. This cuases the cell to stay on its intended order. But this poses another issue that is all the expanders are collapsed. So if we remember which expander(s) remains expanded after regrouping, we achieve what you seek.


There are a few events which can help you with achieving this functionality...

  1. DataGrid.CellEditEnding event
  2. Expander.Initialized, Expander.Expanded and Expander.Collpased events
  3. ICollectionView.CurrentChanging event

All you need to remember is the status of the expanders when they are expanded or collapsed. Each expander represents the grouped value represented by Name property. These grouped values are unique. So a dictionary where Dictionary.Key is this Name value and Dictionary.Value is the Expander.IsExpanded flag would just suffice.

With this raw material following code does what you seek...

Model Class: I represent a simple list of Key-Value objects in the DataGrid.

public class MyKeyValuePair<TKey, TValue> : INotifyPropertyChanged
{
    private TKey key;
    private TValue value;

    public MyKeyValuePair(TKey k, TValue v)
    {
        key = k;
        value = v;
    }

    public TKey Key
    {
        get { return key; }
        set {
            key = value;
            OnPropertyChanged("Key");
        }
    }

    public TValue Value
    {
        get { return value; }
        set {
            this.value = value;
            OnPropertyChanged("Value");
        }
    }

    public void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged
              (this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler  PropertyChanged;
}

XAML:

 <tk:DataGrid
     ItemsSource="{Binding}"
     CellEditEnding="MyDataGrid_CellEditEnding">
    <tk:DataGrid.GroupStyle>
       <GroupStyle>
          <GroupStyle.HeaderTemplate>
             <DataTemplate>
                <StackPanel>
                   <TextBlock Text="{Binding Path=Name}" />
                </StackPanel>
             </DataTemplate>
          </GroupStyle.HeaderTemplate>
        <GroupStyle.ContainerStyle>
        <Style TargetType="{x:Type GroupItem}">
           <Setter Property="Template">
              <Setter.Value>
                 <ControlTemplate TargetType="{x:Type GroupItem}">
                     <Expander
                        Initialized="Expander_Initialized"
                        Expanded="Expander_Expanded"
                        Collapsed="Expander_Expanded">
                        <Expander.Header>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock
                                   Text="{Binding Path=Name}" />
                                <TextBlock Text=" (" />
                                <TextBlock 
                                   Text="{Binding Path=ItemCount}"/>
                                <TextBlock Text="( " />
                                <TextBlock Text="Items"/>
                            </StackPanel>
                        </Expander.Header>
                        <ItemsPresenter />
                     </Expander>
                 </ControlTemplate>
              </Setter.Value>
           </Setter>
        </Style>
     </GroupStyle.ContainerStyle>
   </GroupStyle>
 </tk:DataGrid.GroupStyle>
</tk:DataGrid>

Window.cs Code Behind:

public partial class Window8 : Window
{
    private Dictionary<string, bool> _dict;
    public Window8()
    {
        InitializeComponent();

        _dict = new Dictionary<string, bool>();

        var list1 = new List<MyKeyValuePair<string, int>>();
        var random = new Random();
        for (int i = 0; i < 50; i++)
        {
            list1.Add(new MyKeyValuePair<string, int>(
                i.ToString(), random.Next(300) % 3));
        }

        var colView = new ListCollectionView(list1);
        colView.GroupDescriptions.Add(
            new PropertyGroupDescription("Value"));
        this.DataContext = colView;
    }

    private void MyDataGrid_CellEditEnding
        (object sender, DataGridCellEditEndingEventArgs e)
    {
        var dg = sender as DataGrid;
        var cellInfo = dg.CurrentCell;
        var mySource = dg.ItemsSource as ListCollectionView;
        var oldDlg
            = new CurrentChangingEventHandler((obj, args) => { return; });
        var dlg = new CurrentChangingEventHandler(
            (obj, args) =>
            {
                if (cellInfo.Item == mySource.CurrentItem)
                {
                    var grpDescs = mySource.GroupDescriptions;
                    var oldGrpDescs
                        = grpDescs.Cast<PropertyGroupDescription>().ToList();
                    mySource.Dispatcher.BeginInvoke(
                        new Action(
                            () =>
                            {
                                grpDescs.Clear();

                                foreach (var grdpDesc in oldGrpDescs)
                                {
                                    grpDescs.Add(grdpDesc);
                                }

                                mySource.CurrentChanging -= oldDlg;
                            }));
                }
            });

        oldDlg = dlg;
        mySource.CurrentChanging -= oldDlg;
        mySource.CurrentChanging += oldDlg;
    }

    private void Expander_Expanded(object sender, RoutedEventArgs e)
    {
        var exp = sender as Expander;
        var dc = exp.DataContext as CollectionViewGroup;
        _dict[dc.Name.ToString()] = exp.IsExpanded;
    }

    private void Expander_Initialized(object sender, EventArgs e)
    {
        var exp = sender as Expander;
        var dc = exp.DataContext as CollectionViewGroup;
        if (_dict != null
            && _dict.ContainsKey(dc.Name.ToString())
            && _dict[dc.Name.ToString()])
        {
            exp.IsExpanded = true;
        }
    }
}

But there are two trade offs.

  1. This will make each cell edit attempt slow for large number of items in the datagrid. Because regrouping takes place after each of the cell edit attempt.
  2. May not work for multiple group descriptions as the Name key in the dictionary may / may not stay unique. E.g. Assume for employee list if you group on FirstName and also gropup by LastName there will be nested expanders. Now some first name grouping may match with some last name grouping such as George. So the dictionary will fall into a trick and will not work correctly.

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文