从视图 X 中的 ListView 选择填充视图 Y 时,为什么视图 Y 显示陈旧的结果?

When populating View Y from ListView selection in View X, why is View Y showing stale results?

我正在做一个主要 window 有 4 个视图的项目。其中有两个观点让我很烦恼。

将项目想象成类似 MS Outlook 的东西会有所帮助,其中左侧有一个列表,当一个条目被 selected 时,右侧窗格会显示该视图的详细信息。

不过,我的项目将有多个 MyList。所以就像在 MS Word 中一样,您一直在打开和编辑新文档,因此用户也将浏览多个 MyLists。

当前发生的情况是,左窗格中的 selected 条目显示在右窗格中,但 selection 在点击后 .所以第一个 select 什么都不显示,下一个 select 显示之前的 selected 项目,下一个显示之前的 selected 项目,等等

我正在使用 Prism 6。我还使用 this library 称为 ObservableImmutableList 而不是 ObservableCollection 来处理我的视图的 Listview 对象。

相关代码:

我的列表视图模型:

public class MyListViewModel : BindableBase
{
    private MyListModel activeMyList;
    private IEventAggregator _eventAggregator;

    public MyListViewModel(IEventAggregator eventAggregator)
    {
        _eventAggregator = eventAggregator;

        _selectItemDelegateCommand = DelegateCommand<IList>.FromAsyncHandler(selectItem);

        //Simplified version of real method
        activeList = MyListModel.MyList.GetActiveListItem(_eventAggregator);
        ...
    }

    private DelegateCommand<System.Collections.IList> _selectItemDelegateCommand;

    /// <summary>
    /// Command associated with the selection of an item in the observableimmutablelist
    /// </summary>
    public DelegateCommand<System.Collections.IList> SelectItemDelegateCommand
    {
        get
        {
            return _selectItemDelegateCommand;
        }
        private set { _selectItemDelegateCommand = value; }
    }

    private async Task<int> selectItem(System.Collections.IList items)
    {
        var id = items.Cast<MyListItemViewModel>();

        _eventAggregator.GetEvent<ItemUpdateEvent>().Publish(MyListItems.FirstOrDefault(x => x.ItemID == id.First<MyListItemViewModel>().ID).toAbstractRepresentation());     

        return await Task.FromResult(1);
    }

    private readonly object _myListItemsLock;
    private ObservableImmutableList<MyListItemViewModel> _myListItems;
    public ObservableImmutableList<MyListItemViewModel> MyListItems
    {
        get { return _myListItems; }
        private set
        {
            lock (_myListItemsLock)
            {
                SetProperty(ref _myListItems, value);
            }
        }
    }
}

此外,我有一个名为 ActiveMyListItem 的单独 ViewModel,View 绑定到该 ViewModel。它订阅了 eventaggregator

相关代码:

public ActiveMyListItemViewModel(IEventAggregator eventAgg) : BindableBase
{
    if (_eventAggregator == null && eventAgg != null)
    {
        _eventAggregator = eventAgg;
        _eventAggregator.GetEvent<ItemUpdateEvent>().Subscribe(setProperties);
    }
}

protected void setProperties(AbstractRepresentation abstract)
{
    try
    {
        setProperties(abstract.ID, abstract.name, [...]);
    }
    catch (NullReferenceException nre)
    {
        throw nre;
    }
}

如果我还需要提供任何其他信息,请告诉我。干杯!

编辑

我想澄清一下,MyListViewModel 中的 属性 MyListItems 由一个名为 MyListItemViewModel 的 class 组成。 selected 项目使用名为 ActiveListItemViewModel 的视图模型。

在我的代码片段中,我没有显示它,但在实际代码中,两者都继承自基础 class,因为它们有很多相似的功能。基础 class 又继承自 BindableBase.

我不确定这是否是问题的一部分,所以把它放在那里。

更新

所以我能够确定问题所在。在我的基础 class 视图模型中,属性代码如下所示

    protected String _txtDocName;
    public String TxtDocName
    {
        get { return _myModelItem.Name; }
        set
        {
            SetProperty(ref _txtDocName, value);
            _myModelItem.Name = _txtDocName;
        }

    }

将其更改为此设置可立即生效。

    protected String _txtDocName;
    public String TxtDocName
    {
        get { return _myModelItem.Name; }
        set
        {
            _txtDocName = value;
            _myModelItem.Name = value;
            OnPropertyChanged("TxtDocName");

        }
    }

但是,这太笨重了,并且删除了 Prism 所做的很多工作。解决此问题的更好方法是什么?

你的问题是你传播了 属性 已经改变的事实,但是当你还在进行改变的时候你就这样做了。

例如:

protected String _txtDocName;
public String TxtDocName
{
    get { return _myModelItem.Name; }
    set
    {
        SetProperty(ref _txtDocName, value);
        _myModelItem.Name = _txtDocName;
    }
}

SetProperty 触发 属性 更改通知,WPF 获取通知,查询值,它仍然是旧值。只有 WPF 查询新值后,您是否真的更改了您说已经更改的值。

那你是怎么解决的呢?最好的选择是在设置所有字段后重写 属性 以触发 属性 更改。例如:

public String TxtDocName
{
    get { return _myModelItem.Name; }
    set
    {
        _myModelItem.Name = value;
        SetProperty(ref _txtDocName, value);
    }
}

或者,您可以在完成更改字段后简单地调用 OnPropertyChanged

public String TxtDocName
{
    get { return _myModelItem.Name; }
    set
    {
        _txtDocName = value;
        _myModelItem.Name = _txtDocName;
        OnPropertyChanged();
    }
}

请注意,您不必将任何内容传递给 OnPropertyChanged(),因为该方法在 propertyName 参数上有 CallerMemberNameAttribute,允许您在 属性 名称与您当前的 属性.

相同