【问题标题】:When populating View Y from ListView selection in View X, why is View Y showing stale results?从 View X 中的 ListView 选择填充 View Y 时,为什么 View Y 显示陈旧的结果?
【发布时间】:2016-10-18 09:58:10
【问题描述】:

我正在做一个项目,其中主窗口有 4 个视图。其中有两种观点让我很烦恼。

It helps to think of the project as something like MS Outlook, where you have a list on the left and, when one entry is selected, the right pane shows the details of that view.

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

当前发生的情况是,左窗格中的选定条目显示在右窗格中,但 单击后有一个选择。所以第一次选择什么都不显示,下一次选择显示之前选择的项目,下一个显示之前选择的项目,等等。

我正在使用 Prism 6。我还使用名为 ObservableImmutableList 而不是 ObservableCollection 的 this library 来处理我的视图的 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);
            }
        }
    }
}

此外,我有一个单独的 ViewModel,称为 ActiveMyListItem,视图绑定到它。它订阅了 eventtaggregator

相关代码:

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 的类组成的。所选项目使用名为 ActiveListItemViewModel 的视图模型。

在我的代码 sn-p 中,我没有显示这个,但在实际代码中,它们都继承自一个基类,因为它们有很多相似的特性。反过来,基类继承自 BindableBase。

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

更新

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

    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 所做的大量工作。有什么更好的方法来解决这个问题?

【问题讨论】:

  • 创建区域视图X.当列表框选择是使用Prism IregionManager.RequestNavigate到其他视图时。 span>
  • 非常感谢您的回复。条目详细信息视图始终可见。我没有导航到它,我只是填充它。为什么要使用 RegionManager Navigate 命令?
  • 为什么 selectItems 是异步的?
  • 主要是因为我最初在早期版本的代码中从其他地方获取了 AbstractRepresentation。但是,我确实在没有 async await 功能的情况下测试了代码,并且它的行为方式完全相同。
  • 您能否在一个小示例项目中重现该问题?还要记住在 cmets 中提及用户名,以便用户收到通知。例如:如果您想通知我某事,只需使用@Vishal,然后输入您的评论。这将通知我您已回复我。

标签: c# wpf mvvm async-await prism


【解决方案1】:

您的问题是您在广播属性已更改的事实,但您仍在进行更改时正在这样做。

例如:

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,允许您在属性名称与当前属性相同的情况下省略它。

【讨论】:

  • 在您的第一个解决方案中,您要使用_myModelItem.Name = value;(而不是_myModelItem.Name = _txtDocName;),否则您将获得旧值...
  • @Haukinger 问题海报在这里。很好,我自己抓住了:) 明天我会发布更多信息和更多我的想法。 Mackie,很好的分析,非常感谢!
  • 您的回答是正确的,并允许我重新引入 Prism 事件通知器。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多