【问题标题】:Setting SelectedItem of Listbox or ComboBox through ViewModel MVVM WPF通过 ViewModel MVVM WPF 设置 Listbox 或 ComboBox 的 SelectedItem
【发布时间】:2017-03-20 15:37:47
【问题描述】:

目标:通过 xaml 将 ListBox(或 ComboBox)的 SelectedItem 绑定到对象的实例后,我想通过视图模型设置对象的选定实例并让它反映在 ListBox 或 ComboBox 上。

<ComboBox x:Name="cboServers" HorizontalAlignment="Left" Margin="535,694,0,0" VerticalAlignment="Top" Width="225"
          ItemsSource="{Binding Settings.Servers}"
          SelectedItem="{Binding Settings.SelectedServer, Mode=TwoWay}"
          DisplayMemberPath="UserFriendlyName">

C#模型查看代码

public ObservableCollection<AutoSyncServer> Servers { get; set; }

private AutoSyncServer _selectedServer;

public AutoSyncServer SelectedServer
{
    get { return _selectedServer;}
    set
    {
        _selectedServer = value;
        OnPropertyChanged("SelectedServer");
    }
}

列表或组合框正确填充。选择 ListBox 或 ComboBox 上的项目将正确设置 SelectedServer 对象。

但是,如果我尝试在 C# 中编写 set 语句,例如:

Servers.Add(newServer);
SelectedServer = newServer;

ListBox 或 ComboBox 会正确添加项,并且在 MVVM 模型上会正确设置 SelectedServer 对象,但前端不会反映此选择。

在这种特定情况下,会读取一个 xml 文件,说明用户最后选择的内容,当窗口打开时,ComboBox 没有选择任何内容(尽管服务器都正确加载在其中)

这里缺少什么?

【问题讨论】:

  • 调试时检查任何绑定问题错误。
  • 什么是“OnServerChanged”?您的视图模型类应该实现 INotifyPropertyChanged 接口并引发 PropertyChanged 事件。是吗?
  • OnServerChanged 与此问题无关,因为此问题也出现在其他领域。 OnServerChange 让我知道服务器已更改,以便可以检索服务器对象。我将编辑这个问题。
  • @mm8 有一种常见的模式,即对特定属性及其值的变化特别感兴趣,因此您编写带有专用 EventArgs 的 FooChanged 事件,以避免消费者在处理时相对烦恼PropertyChanged 并检查属性名称。
  • 您实际上是如何设置 SelectedServer 属性的?

标签: wpf mvvm selecteditem


【解决方案1】:

SelectedItem 中的实际对象必须是在Object.ReferenceEquals(a, b) 意义上的Servers 集合中找到的对象实例。不仅仅是相同的名称和 ID(或其他)属性;相同的确切类实例。

人们遇到这种情况的典型案例是在多个地方反序列化等效项。 Servers 有一组反序列化的AutoSyncServer 实例,Settings.SelectedServer 是一个单独反序列化的AutoSyncServer 实例,其属性值与Servers 中的一项相同。但它仍然是一个不同的对象,ComboBox 无法知道您是否打算这样做。

如果AutoSyncServer 的两个实例逻辑上 等效,您可以覆盖AutoSyncServer.Equals() 以返回true。我不喜欢这样做,因为它改变了该类的 = 运算符的语义,这在我之前曾被我咬过。但这是一个选择。

另一种选择是拥有一个AutoSyncServer 的规范静态集合,并确保每个类都从中获取其实例。


鉴于上述情况,我不明白为什么这段代码不起作用:

Servers.Add(newServer);
SelectedServer = newServer;

一旦newServerServers 中,它应该是可选的。我测试了它,它对我有用,正如你所期望的那样。

【讨论】:

  • 我想我不明白它是如何工作的。已经有一个 ObservableCollection 类型的主集合。我不明白您如何将收藏放在此收藏之上,或者您如何知道从该主收藏中选择了什么。可观察的集合效果很好。从列表中选择时,它工作正常。问题仅在您尝试从模型视图上的后端代码设置 SelectedItem 时出现。然后 SelectedItem 仍在设置中,但无论出于何种原因,该列表都没有反映该更改。 equals 覆盖对我来说确实有意义。
  • @auticus 抱歉,我似乎没有仔细阅读您的问题。如果我引用的 C# sn-p 是 only 不起作用的情况,我怀疑OnPropertyChanged 可能在设置中实现错误。 Settings.Server 是静态类的静态属性吗?
  • Ed,没有什么是静态的。 Settings 是一个实例化的类,所有属性都在其中实例化。您的等号覆盖建议确实解决了问题,并且当覆盖等号时,我确实让我的列表框在反序列化时选择了正确的项目。这对我来说很有意义,服务器的初始负载与所选服务器对象不是同一个对象,因此在加载它们时哈希码将不匹配,并且因此永远不会在后端选择列表项。
  • @auticus 现在我很困惑,但如果它解决了问题,它就解决了问题。我将更新答案并删除我误解您的部分。
  • 我对哈希码的评论更适合普通观众,以防其他人进来阅读回复以帮助他们。我不知道我的代码有什么不同。就像我介绍的那样,尽管现在我在 AutoSyncServer 类上添加了对 Equals 的覆盖,从而解决了这个问题。您想知道为什么它不起作用的代码并不是真正的问题,它是窗口加载时对象的初始反序列化。 (由于反序列化,问题是集合中的对象!= selectedItem 属性中的对象)您的回答澄清了问题。
【解决方案2】:

我认为你必须避免“子绑定”,它们在视图请求时工作一次,但在之后就不行了 Settings.SelectedServer ==> SelectedServer

如果您评论 OnServerChanged?.Invoke(this, _selectedServer); 会发生什么?有用吗?

【讨论】:

  • OnServerChanged?.Invoke 与该问题无关。我在其他没有实现该功能的 ListBox 的代码的其他区域也有同样的问题,这只是从列出的服务器中检索服务器对象的调用。
猜你喜欢
  • 2012-06-19
  • 1970-01-01
  • 2017-02-20
  • 1970-01-01
  • 2014-03-18
  • 2014-03-08
  • 2023-03-09
  • 2010-10-14
  • 1970-01-01
相关资源
最近更新 更多