在用户控件中更新 ItemsSource 后查看模型项目为空

View Model Items is Null after updating ItemsSource in User Control

我正在开发 WPF MVVM 应用程序,运行 遇到了问题。我熟悉 WPF 本身,但是我很少使用 MVVM,我怀疑我正在做一些 MVVM 不支持的事情,但是我不知道还有什么方法可以完成我想做的事情。

在应用程序中,我有一个名为 Agenda 的用户控件。它由几个控件组成,包括一个文本框、一个用于添加新议程项目的按钮和一个带有自定义模板的列表框。该模板包括一个扩展器,其中 header 是议程项目标题,up/down 箭头用于重新排序项目,以及一个用于删除项目的按钮。扩展器内容包含一个工具栏和一个富文本框。在 UC 议程中,我有一个名为 ItemsSource 的依赖项 属性,它是一个 IEnumerable<AgendaItem>.

现在,我有一个名为 Appointment 的视图、其关联的 VM (AppointmentViewModel) 及其模型 (AppointmentModel)。在模型中,有一个名为 AgendaItems 的字段,它是一个 ObservableCollection<AgendaItem>。议程 UC 在约会视图中使用,UC 的 ItemsSource 绑定到 Model.AgendaItems(可观察的 collection)。

我遇到的问题是当我尝试处理按钮以重新排序 UC 中的议程项目时。例如,对于将议程项目向上移动列表的按钮,这是 UC 中的代码:

var tb = sender as Button;
var tag = tb.Tag as AgendaItem;
var lst = ItemsSource.ToList();
var index = lst.IndexOf(tag);
if(index > 0)
{
    lst.RemoveAt(index);
    lst.Insert(index - 1, tag);
    ItemsSource = lst;
}

向上箭头的标签绑定到列表中的特定议程项目,因此我知道正在移动哪个项目。问题出现在我更新 ItemsSource 属性 做 ItemsSource = lst 之后。该行执行后,VM 中的 AgendaItems ObservableCollection 为空。绑定模式设置为TwoWay.

由于约会 UC 在应用程序中的各种 windows 中使用,对我来说,议程项目的重新排序应该由我的 UC 处理而不是在每个 window 使用了 UC。但是更新 UC 中的 ItemsSource 属性 会导致 VM 中的 collection 为空。

供参考,UC中的ItemsSource 属性定义为:

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IEnumerable<AgendaItem>), typeof(Agenda), new PropertyMetadata(null, new PropertyChangedCallback(OnItemsSourceChanged)));

有常规的 .NET 属性:

public IEnumerable<AgendaItem> ItemsSource
{
    get => (IEnumerable<AgendaItem>)GetValue(ItemsSourceProperty);
    set => SetValue(ItemsSourceProperty, value);
}

OnItemsSourceChanged方法是:

private static void OnItemsSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    if (obj is Agenda control)
    {
        if (e.OldValue is INotifyCollectionChanged oldValueINotifyCollectionChanged)
        {
            oldValueINotifyCollectionChanged.CollectionChanged -= control.ItemsSource_CollectionChanged;
        }
        
        if (e.NewValue is INotifyCollectionChanged newValueINotifyCollectionChanged)
        {
            newValueINotifyCollectionChanged.CollectionChanged += control.ItemsSource_CollectionChanged;
        }
    }
}

任何关于如何在不破坏 VM 的情况下重新排序 UC 中的 ItemsSource collection 的 help/guidance 将不胜感激。提前谢谢你。

由于在 Enumerable 上调用 .ToList() 会创建新列表并将其重新分配给 ItemsSource 不起作用,因此可以将 ItemsSourceProperty 定义为 IList<AgendaItem> 因为这是接口您需要重新订购商品。

public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(IList<AgendaItem>), typeof(Agenda), new PropertyMetadata(null, new PropertyChangedCallback(OnItemsSourceChanged)));

(不要忘记更改 ItemsSource 属性 的签名)。

您还可以创建列表扩展方法以在您的代码中调用它,如下所示:

ItemsSource.Move(ItemsSource.IndexOf(tag), MoveDirection.Up);
public static void Move<T>(this IList<T> list, int iIndexToMove,
    MoveDirection direction)
{
    if (list.Count > 0 && (direction == MoveDirection.Down && iIndexToMove < list.Count - 1)
                    || (direction == MoveDirection.Up && iIndexToMove > 0))
    {
        if (direction == MoveDirection.Up)
        {
            var old = list[iIndexToMove - 1];
            list[iIndexToMove - 1] = list[iIndexToMove];
            list[iIndexToMove] = old;
        }
        else
        {
            var old = list[iIndexToMove + 1];
            list[iIndexToMove + 1] = list[iIndexToMove];
            list[iIndexToMove] = old;
        }
    }
}


    public enum MoveDirection
    {
        Up,
        Down
    }

通过将 ItemsSource 的数据类型从 IEnumerable<AgendaItem> 更改为 ObservableCollection<AgendaItem> 解决了该问题。感谢所有回复的人。非常感谢。