【问题标题】:Prism MVVM pattern: View is always one step behind on property change in ViewModelPrism MVVM 模式:View 总是落后于 ViewModel 中的属性更改
【发布时间】:2014-08-30 12:00:10
【问题描述】:

在 Prism、MVVM、Windows 8.1 StoreApp 中,我希望 ViewModel 捕获 ListView 中的 SelectItem。 ListView 包含一个 ObservableCollection 对象。 ViewModel 需要查找所选对象的更多详细信息并通知 View。视图应依次显示对象的详细信息。

我已经实现了这个,但是视图总是显示以前的对象(在选择一个新对象之后)

当然,我正在寻找的是在选择对象时视图中的即时且正确的反应。这是我的coden-ps,都是VB代码。

编辑:我已经提出了另一个 - 较小的例子,使用这种方法。我记录了这个过程in this video.请在阅读之前先看看!!

对象来自 ViewModel:

Public Property Persons 作为新的 ObservableCollection(Of Person)

它们被绑定到一个用户控件:

<Grid>
    <ListView 
                ItemsSource="{Binding Persons}" 
                ItemTemplate="{StaticResource BusinessCard}">
        <Interactivity:Interaction.Behaviors>
            <Core:EventTriggerBehavior EventName="SelectionChanged">
                <Behaviors:ListViewSelectionChangedAction/>
            </Core:EventTriggerBehavior>
        </Interactivity:Interaction.Behaviors>
    </ListView>
</Grid>

通过 Behavior 最终通过以下代码在 ViewModel 中结束:

Sub New(InjectedEventAggregator As IEventAggregator)

        LocalEventAggregator = InjectedEventAggregator
        LocalEventAggregator.GetEvent(Of PersonIsSelectedEvent)().Subscribe(AddressOf HandlePersonIsSelected, True)

这个事件由这个例程处理

    Public Sub HandlePersonIsSelected(ByVal SelectedPerson As Person)
        ActualPerson = SelectedPerson
    End Sub

所有这一切的最后一部分是包含 ActualPerson 的属性,如下所示:

    Private Property _ActualPerson As Person
    Public Property ActualPerson As Person
        Get
            Return _ActualPerson
        End Get
        Set(value As Person)
            SetProperty(_ActualPerson, value)
        End Set
    End Property

编辑: 这是应显示所选 ActualPerson 的 XAML:

<StackPanel DataContext="{Binding ActualPerson}" >

       <Image Source="{Binding Photo}" Stretch="Fill" />

       <StackPanel Orientation="Horizontal">
           <TextBlock Text="{Binding FirstName}" />
           <TextBlock Text="{Binding FamilyName}" />
           <TextBlock Text="{Binding Gender}" />
       </StackPanel>

</StackPanel>

当我单步执行代码时,我可以看到 ViewModel 中捕获了 SelectedItem 事件,调用了所选人员的处理程序,更新了属性。使用 Prism 这也意味着触发 NotifyPropertyChanged 事件。它确实被解雇了,否则我猜前一个对象也不会显示。

但是为什么视图没有立即用正确的(人)对象更新?

如果你有线索....做我的贵宾!

问候

【问题讨论】:

  • 您尚未显示“视图应依次显示对象的详细信息”的代码。如果 XAML 代码绑定到 ActualPerson 对象,它应该显示 ActualPerson 的详细信息。您可以尝试将 TextBlock 的 Text 属性绑定到 ActualPerson. 并检查您所看到的内容。
  • @Wonderfulworld 感谢您的反应。我在原始帖子中进行了编辑。从列表视图中选择的对象是一个人。基于此,将刷新 ActualPerson 属性。问题不在于绑定本身,而在于时机:只有前一个对象被完全显示。绑定到其他属性有效,但反应滞后:始终是上次单击时选择的对象。
  • 您的代码在我看来是正确的。只要调用了“ActualPerson = SelectedPerson”,UI 就会更新。除非 ActualPerson 是以前的对象,否则您应该会看到最近点击的人。在 ActualPerson 的 Set 操作里面放一个断点,看它是否被调用了两次,是否有人将最新的对象设置回之前的对象。
  • @wonderfulworld 感谢您的回复。我完全同意你的观点,代码看起来不错,但这正是我的问题!!我不明白为什么它不能正常工作。按照你的建议,我已经在二传手上休息了一下,并看到它通过了一次。更进一步,Prism 库函数接管(通过 SetProperty),我可以确保 BindableBase 类引发 PropertyChanged。仍然……对视图没有反应。

标签: vb.net mvvm windows-store-apps prism inotifypropertychanged


【解决方案1】:

让我试着理解你在“对视图没有反应”中所说的。您是说 UI 没有改变,即使调用了“ActualPerson = SelectedPerson”?

控件有一个称为 MODE 的绑定属性,它决定数据流。显示人员信息的 TextBlock 的 MODE 可以是 OneWay。绑定可能是导致问题的 OneTime

【讨论】:

  • 你理解的没错!视图上没有任何反应。是的,ActualPerson = SelectedPerson 被调用,是的,ActualPerson 属性中的 Set - SetProperty 也被调用。这就是为什么我要把头发拉出来然后把它撞到墙上。我无法弄清楚出了什么问题。据我所知,OneWay 绑定是默认设置。我已更改为 OneWay。行为上没有区别。
  • @wonderfulworld 为了更容易看到正在发生的事情,我构建了另一个小例子。请参阅原始帖子中的第一个编辑。您也可以查看 [此处] (prismwindowsruntime.codeplex.com/discussions/564825) 以查看有关此问题的完整讨论。
  • 您是否尝试过调用 SetProperty(_Messages, value, "Messages")?最后一个参数是属性而不是字段。
【解决方案2】:

您能否确认一下您的存储库中的内容?我假设它不是一个可观察的集合。至少它不应该是,我认为这将是一个可序列化的 POCO 对象。这意味着您需要将项目添加到可观察集合中。如果不使用 CollectionView,我这样做的方法是拥有一个 ObservableCollection 类型的只读变量,它永远不会改变。然后,当您向消息发出请求时,我会确保收集已清除,为新项目做好准备。返回消息后,遍历响应中的每个模型项(消息)并将它们转换为 MessageViewModels(一个包含可绑定属性和验证(数据注释)的新类)。在创建每个 ViewModel 项时,它被添加到observablecollection。将项目添加到集合的行为将引发列表视图正在侦听的事件,因此将显示项目(只要 MessageViewModel 具有关联的数据模板。

private readonly _messages = new ObservableCollection<MessageViewModel>(); 

Public ObservableCollection<MessageViewModel> Messages {get { return _messages;}} 

OnNavigateTo

Messages.Clear; 
foreach(var message in await _messageRepository,GetMessagesAsync()) 
{ 
    Messages.Add(new MessageViewModel(){Name = message.Name, Text = message.Text}); 
} 

这有意义吗?

【讨论】:

  • 您可以确定来自存储库的内容是一个 observablecollection。从原始 json 数据到 ViewModel 层可用数据的转换完全在存储库中完成。我从您在这里提出的这个建议中得到了很多帮助,并提出了最终的可行解决方案。谢谢!
【解决方案3】:

一个糟糕的解决方案,但也许它会有所帮助。您可以“再次”调用 OnProperyChanged。我说“再次”是因为它应该是 SetProperty 调用它,但我也遇到了 VB.NET 和 SetProperty 的一些问题。

私有财产_ActualPerson As Person 公共财产 ActualPerson As Person 得到 返回_ActualPerson 结束获取 设置(值作为人) 设置属性(_ActualPerson,值) OnPropertyChanged("ActualPerson") 结束集 结束属性

2015 年 4 月 2 日编辑:好的解决方案在这里:OnPropertyChanged not fired when SetProperty is called

就像删除私有声明中的“属性”一词一样简单。因为是ByRef传递的,所以不能是属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-06
    • 1970-01-01
    • 1970-01-01
    • 2011-01-17
    相关资源
    最近更新 更多