【问题标题】:Selected Item WPF DataGrid With Selection Mode Extended扩展了选择模式的选定项目 WPF DataGrid
【发布时间】:2014-01-09 21:18:00
【问题描述】:

我有一个使用 mvvm 的应用程序,它有一个将 SelectionMode 设置为 Extended 的 DataGrid。我有两个按钮“上移”和“下移”,单击它们时应该将所选项目的位置向上或向下移动一个点(仅当只选择一个项目时)。

这工作正常(代码遵循),但是,在执行移动后,它似乎会破坏选定的项目触发器。例如,如果我单击一个项目,将其向下移动 4 个点,单击另一个项目并尝试将其向上移动一个点,之前移动的项目将再次被选中并移动。即使在清除所有选择并选择一个全新的行之后,在 VM 中的“SelectItem”上设置的断点也会停止。如果选择模式设置为单一,则不会发生此问题。

我尝试启用和禁用虚拟化、IsSynchronisedWIthCurrentItem、不同的列表类型等,没有任何改变这种行为。我正在拔头发。任何人都可以提出解决这个问题的方法吗?

我的网格和按钮...

<StackPanel Orientation="Horizontal">
                <Button Margin="3" Content="Move Up">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Click">
                            <ei:CallMethodAction TargetObject="{Binding}"
                                                 MethodName="MoveUp" />
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </Button>
                <Button Margin="3" Content="Move Down">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Click">
                            <ei:CallMethodAction TargetObject="{Binding}"
                                                 MethodName="MoveDown" />
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                </Button>
            </StackPanel>

            <DataGrid Margin="3"
                      x:Name="DataGrid"
                      AutoGenerateColumns="False"
                      ItemsSource="{Binding Items, Mode=OneWay}"
                      SelectedItem="{Binding SelectedItem, Mode=TwoWay}"
                      CanUserAddRows="False"
                      CanUserDeleteRows="False"
                      IsReadOnly="True"
                      SelectionMode="Extended">
                    <DataGrid.RowStyle>
                    <Style TargetType="{x:Type DataGridRow}">
                        <Setter Property="IsSelected"
                                Value="{Binding Path=IsSelected, Mode=TwoWay}" />
                    </Style>
                </DataGrid.RowStyle>

                <DataGrid.RowHeaderTemplate>
                    <DataTemplate>
                        <Grid>
                            <CheckBox Margin="3"
                                      IsChecked="{Binding Path=IsSelected, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}}"
                                      IsTabStop="False" />
                        </Grid>
                    </DataTemplate>
                </DataGrid.RowHeaderTemplate>
            </DataGrid>
        </StackPanel>

我的相关虚拟机代码...

public ObservableCollection<ListItemViewModelBase<Person>> Items
    {
        get { return _items ?? (_items = new ObservableCollection<ListItemViewModelBase<Person>>()); }
    }

    public ListItemViewModelBase<Person> SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            if (_selectedItem == value)
                return;

            _selectedItem = value;
            RaisePropertyChanged(() => SelectedItem);
        }
    } 

public void MoveUp()
    {
        if (Items.Count(item => item.IsSelected) == 1)
        {
            // get it's index
                int toMoveIndex = Items.IndexOf(SelectedItem);

                // move up if not at zero
                if (toMoveIndex > 0)
                {
                    Items.Move(toMoveIndex, toMoveIndex - 1);
                }
        }
    }

    public void MoveDown()
    {
        if (Items.Count(item => item.IsSelected) == 1)
        {
            // get it's index
                int toMoveIndex = Items.IndexOf(SelectedItem);

                // move down if not at zero
                if (toMoveIndex < Items.Count - 1)
                {
                    Items.Move(toMoveIndex, toMoveIndex + 1);
                }
        }
    }

还有 Person 和 ListItemBase 类...

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnProperyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }

    #endregion

    #region Fields

    private string _firstName;
    private Guid _id;
    private string _lastName;
    private int _sortOrder;

    public int SortOrder
    {
        get { return _sortOrder; }
        set
        {
            if (_sortOrder == value)
                return;

            _sortOrder = value;
            OnProperyChanged("SortOrder");
        }
    }

    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (_firstName == value)
                return;

            _firstName = value;
            OnProperyChanged("FirstName");
        }
    }

    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (_lastName == value)
                return;

            _lastName = value;
            OnProperyChanged("LastName");
        }
    }

    public Guid Id
    {
        get { return _id; }
        set
        {
            if (_id == value)
                return;

            _id = value;
            OnProperyChanged("Id");
        }
    }

    public static bool operator ==(Person item1, Person item2)
    {
        // if both null, equal
        if (ReferenceEquals(item1, null) && ReferenceEquals(item2, null))
            return true;

        // if one null, one not, not equal
        if (!ReferenceEquals(item1, null) && ReferenceEquals(item2, null))
            return false;

        if (ReferenceEquals(item1, null))
            return false;

        return item1.Equals(item2);
    }

    public static bool operator !=(Person item1, Person item2)
    {
        return !(item1 == item2);
    }

    public override string ToString()
    {
        return string.Format("{0} {1}: {2}", FirstName, LastName, SortOrder);
    }

    public override bool Equals(object obj)
    {
        var item = obj as Person;

        if (item != null)
        {
            return Equals(item);
        }

        return base.Equals(obj);
    }

    public bool Equals(Person item)
    {
        if (item != null)
        {
            return GetHashCode() == item.GetHashCode();
        }

        return false;
    }

    public override int GetHashCode()
    {
        return Id.GetHashCode();
    }
}

public class ListItemViewModelBase<TModel> : ViewModelBaseExtended, IEquatable<ListItemViewModelBase<TModel>>
{
    private bool _isSelected;
    private TModel _model;

    public ListItemViewModelBase(TModel model)
    {
        if (model == null)
        {
            throw new ArgumentNullException("model", "The model cannot be null.");
        }

        // set initial values
        Model = model;
    }

    public static bool operator ==(ListItemViewModelBase<TModel> item1, ListItemViewModelBase<TModel> item2)
    {
        // if both null, equal
        if (ReferenceEquals(item1, null) && ReferenceEquals(item2, null))
            return true;

        // if one null, one not, not equal
        if (!ReferenceEquals(item1, null) && ReferenceEquals(item2, null))
            return false;

        if (ReferenceEquals(item1, null))
            return false;

        return item1.Equals(item2);
    }

    /// <returns></returns>
    public static bool operator !=(ListItemViewModelBase<TModel> item1, ListItemViewModelBase<TModel> item2)
    {
        return !(item1 == item2);
    }

    public virtual bool IsSelected
    {
        get { return _isSelected; }
        set { Set(() => IsSelected, ref _isSelected, value); }
    }

    public TModel Model
    {
        get { return _model; }
        set { Set(() => Model, ref _model, value); }
    }

    public override string ToString()
    {
        return Model.ToString();
    }

    public override int GetHashCode()
    {
        return Model.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        var item = obj as ListItemViewModelBase<TModel>;

        if (item != null)
            return Equals(item);

        return base.Equals(obj);
    }

    public bool Equals(ListItemViewModelBase<TModel> other)
    {
        if (other != null)
        {
            // use Equals() here, not == because it's a Generic, == won't work
            return Model.Equals(other.Model);
        }

        return false;
    }

    }

任何见解将不胜感激。我要努力解决这个问题。我在 ListBox、ListView 等中看到了相同的结果。

【问题讨论】:

    标签: wpf mvvm


    【解决方案1】:

    嗯,我找到了一个“解决方案”。我讨厌它,因为它似乎与使 WPF 如此吸引人的绑定引擎背道而驰,但这是我发现的唯一可行的方法。基本上,我必须创建一个临时列表,对其重新排序,清除我的可观察集合并重新填充它...

    public void MoveUp()
            {
                if (Items.Count(item => item.IsSelected) == 1)
                {
                    // create a list with the current items
                    var items = new List<ListItemViewModelBase<Person>>(Items);
                    // get the index of the item being moved
                    int oldIndex = items.IndexOf(SelectedItem);
    
                    // only move if the item isn't already at 0
                    if (oldIndex > 0)
                    {
                        // get the item we're moving
                        var item = items[oldIndex];
                        // remove the item from the list
                        items.RemoveAt(oldIndex);
                        // add the item back into the list at it's new position
                        items.Insert(oldIndex - 1, item);
    
                        // clear items list, and re-add items from our temp list
                        Items.Clear();
                        foreach (var toAdd in items)
                        {
                            Items.Add(toAdd);
                        }
                    }
                }
            }
    
            public void MoveDown()
            {
                if (Items.Count(item => item.IsSelected) == 1)
                {
                    // create a list with the current items
                    var items = new List<ListItemViewModelBase<Person>>(Items);
                    // get the index of the item being moved
                    int oldIndex = items.IndexOf(SelectedItem);
    
                    // only move if the item isn't already the last item
                    if (oldIndex < items.Count - 1)
                    {
                        // get the item we're moving
                        var item = items[oldIndex];
                        // remove the item from the list
                        items.RemoveAt(oldIndex);
                        // add the item back into the list at it's new position
                        items.Insert(oldIndex + 1, item);
    
                        // clear items list, and re-add items from our temp list
                        Items.Clear();
                        foreach (var toAdd in items)
                        {
                            Items.Add(toAdd);
                        }
                    }
                }
            }
    

    如果有人有更好的想法,我仍然很乐意听到它!

    【讨论】:

      【解决方案2】:

      我也遇到过这样的错误,不幸的是我没有关于如何正确避免这个问题的答案,尽管我可以添加一些细节。

      首先 - 仅当系统中安装了 4.5 框架时才会出现有关旧移动项目的多选问题,尽管应用程序针对 4.0。

      第二 - Microsoft 修复了一些与 DataGrid 中的选择相关的问题:

      https://connect.microsoft.com/VisualStudio/feedback/details/565153/wpf-datagrid-vs2010-net-4-0-datagrid-selecteditem-isenabled-bug

      看起来它已在 4.5 中修复,导致另一个帖子:

      http://www.beta.microsoft.com/VisualStudio/feedback/details/742545/net-4-5-changes-4-0-wpf-datagrid-behaviour

      也许这些修复导致了新的错误。

      至于我,我想出了与您类似的解决方法,但我不是创建一个新的临时列表,而是使用

      ListViewAudio.RemoveAt(oldIndex);
      ListViewAudio.Insert(newIndex, selectedItem); 
      

      在我的名单上而不是

      ListViewAudio.Move(oldIndex, newIndex);
      

      我的新行为以及您对新临时列表的行为导致另一个错误。如果我在列表中的最后一个项目上使用 MoveUp 或在列表中的第一个项目上使用 MoveDown,它们会被移动和松散的选择。您是否遇到同样的问题?

      【讨论】:

      • 我没有这个问题。也许是因为我的临时清单和娱乐活动?
      • 我尝试创建一个临时列表,但仍然有问题,虽然现在(使用临时列表)问题仅从上一个条目开始出现在 MoveUp 中。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-03
      • 2023-03-16
      • 2017-08-27
      • 1970-01-01
      相关资源
      最近更新 更多