【问题标题】:A better way of forcing data bound WPF ListBox to update?强制数据绑定 WPF ListBox 更新的更好方法?
【发布时间】:2010-09-20 03:54:51
【问题描述】:

我有绑定到 ObservableCollection 的 WPF ListBox, 当集合发生变化时,所有项目都会更新它们的位置。

新位置存储在集合中,但 UI 不会更新。 所以我添加了以下内容:

    void scenarioItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        ToolboxListItem.UpdatePositions();
        lstScenario.ItemsSource = null;
        lstScenario.ItemsSource = ToolboxListItem.ScenarioItems;
        this.lstScenario.SelectedIndex = e.NewStartingIndex;
    }

通过将 ItemsSource 设置为 null 然后再次绑定,更新 UI,

但这可能是非常糟糕的编码:p

建议?

【问题讨论】:

  • 您能否详细说明“当集合更改时,所有项目都会更新其位置”的含义,以便我确定我正确回答了您的问题?

标签: wpf data-binding listbox observablecollection


【解决方案1】:

我昨天遇到了同样的问题,这完全是一团糟:) ...不过,我不再将我的设置为 null 了。在我的场景中,我将其设置为 MyList.ToArray() (每次添加到列表之后)。

我见过多个“哦,你需要使用 ObservableList”

我见过多个“哦,调用'刷新'”

请原谅我的不安,但我也希望这能奏效:)

【讨论】:

  • 相信我,我能理解你的沮丧,所以没有冒犯。我曾尝试使用 GetBindingExpression,但结果为空:p
  • 我的“GetBindingExpression”不返回空值......但即使我调用“UpdateTarget”它什么也不做:(......仅在这种情况下......它适用于其他一切。
【解决方案2】:

我有一个绑定到 List<MyCustomType>() 类型的对象属性的列表框,并且我验证了以下代码会在更新列表时更新列表框。

void On_MyObjProperty_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
   MyListBox.Items.Refresh();
}

如果您仍然遇到问题,请扫描 VS IDE 输出窗口(Ctrl+W、O)并查看是否可以发现任何报告的绑定错误。

【讨论】:

  • 这似乎确实解决了问题,所以我将把错误问题移到另一篇文章中。谢谢!
  • 两年多没有人评论这个了...非常感谢!
  • 如果 ListBox 绑定到公共属性“MyItems”并且具有 tihs 属性的类实现了 INotifyPropertyChanged,为什么不直接引发事件 PropertyChanged(this, "MyItems")?它将通知 UI 该对象已更改并自动更新其值。
  • @JoanComasFdz 这个解决方案适用于我的情况。在我的情况下,列表框的 ItemsSource 数据绑定到 ObservableCollection。当 BusinessObject.SomeProperty 发生更改时,列表没有反映该更改,因为 BusinessObject 仍然是同一个实例(因此没有通知)。上述刷新方法导致列表重新评估绑定/转换器输出。
【解决方案3】:

如果您有一个 ObservableList 对象,并且您正在更改这些对象内的属性,则通知不适用,因为该集合没有直接更改。更改对象属性后,我一直在强制通知,方法是使用 Insert() 将更改的对象重新添加到集合中,然后使用 RemoveAt() 删除旧副本。它不漂亮,但它有效。

【讨论】:

    【解决方案4】:

    WPF 将一个列表/项目集合绑定到 ListBox,但在项目更新后 UI 不刷新,已解决

    我只是愚蠢。虽然我读过很多关于使用ObservableCollection<> 而不是List<> 的信息,但我只是继续忽略这个建议并遵循其他建议,但无济于事。回到我的书,重读。很好地解释了ObservableCollection<> 是必须使用的,因为List<> 不提供ListBox 所需的INotifyCollectionChange 接口,以便在集合中的项目更改时更新其显示。

    这是更新后的代码:

    private ObservableCollection<StringWrapper> m_AppLog;
    ObservableCollection<StringWrapper> Log { get { return m_AppLog; } }
    

    非常简单,不需要其他任何东西(例如 Refresh())。因为 ObservableCollection 自己负责触发更改事件,所以我能够删除不必要的调用:

    // notify bound objects
    OnPropertyChanged("Log");
    

    ObservableCollection 不支持由没有创建它的线程进行的更新。因为我的列表(显示最近错误/信息消息的可视日志)可以从不同的线程更新,所以我添加以这种方式调整我的代码,以确保使用列表自己的调度程序完成更新:

    public void AddToLog(string message) {
        if (Thread.CurrentThread != Dispatcher.Thread) {
            // Need for invoke if called from a different thread
            Dispatcher.Invoke(
                DispatcherPriority.Normal, (ThreadStart)delegate() { AddToLog(message); });
        }
        else {
            // add this line at the top of the log
            m_AppLog.Insert(0, new StringWrapper(message));
            // ...
    

    还要注意 ObservableCollection&lt;&gt; 不支持 RemoveRange()List&lt;&gt; 相反。这是从 List 切换到 ObservableCollection 时可能需要进行的调整的一部分。

    【讨论】:

    • 我也犯了这个错误,我花了很长时间才找到解决方案。 +1。
    【解决方案5】:

    这是旧的东西,但使用 ObservableCollection。如果您希望 UI 看到 ObservableCollection 的对象中属性的更新,则需要在该对象的类定义中实现 INotifyPropertyChanged。然后在每个属性的 setter 中引发属性更改事件。

    Public Class Session
    Implements INotifyPropertyChanged
    
    Public Event PropertyChanged As PropertyChangedEventHandler _
       Implements INotifyPropertyChanged.PropertyChanged
    
    Private Sub NotifyPropertyChanged(ByVal info As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
    End Sub
    
    Private _name As String = "No name"
    ''' <summary>
    ''' Name of Session
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Property Name() As String
        Get
            Return _name
        End Get
        Set(ByVal value As String)
            _name = value
            NotifyPropertyChanged("Name")
        End Set
    End Property
    

    【讨论】:

      【解决方案6】:

      我可能遇到了与您遇到的类似的问题,但我不确定。

      我绑定了一个ObservableCollection&lt;MyEntity&gt; 和一个ListBox。但由于某些奇怪的原因,当我更改列表中 MyEntity 对象的属性时,我的 ListBox 没有更新。

      搜索了一段时间后,我找到了以下页面,我只需要让您知道:

      http://www.wblum.org/listbind/net3/index.html

      它很好地描述了当列表或其中的对象发生更改时要更新ListBox 必须做什么。希望您能从中受益。

      【讨论】:

      • 这非常棒,非常感谢!特别是你只需要为 ListBox 中包含的类实现 INotifyPropertyChanged 对我来说很有趣。
      • 此域名出售。
      【解决方案7】:

      在我看来,它更像是 ListBox 和 ListView 中的一个错误。我绑定到 ObservableCollection,集合中的项目实现 INotifyPropertyChanged。当我动态按下“添加项目”按钮时,UI 未显示任何添加的项目,但是我有一个绑定到 MyCollection.Count 的计数器控件。每次按下“添加项目”按钮时,此计数器控件都会增加。如果我调整视图大小,列表框会显示我添加的所有项目。因此,ListBox 控件上的 ItemSource 绑定已损坏。我还注意不要在任何时候创建新的 MyCollection,这会破坏绑定。嘘。

      【讨论】:

      • Lolz,在我的情况下,问题是有人实际上实现了一个名为 ObservableCollection 的类!因此,我的集合实际上并没有实现 INotifyCollectionChanged,即使它被称为 ObservableCollection。天哪。一旦我删除了有问题的命名空间,并使用 System.Collections.ObjectModel 一切都很好。哦!
      【解决方案8】:

      我知道它已经有点老了,但今天我遇到了同样的问题。我在 ObservableCollection 中更新了一个对象的属性,但 View 没有更新,但后来我发现 this 很棒的文章。

      我认为手动触发 ObservableCollection 的更新是一个非常干净的解决方案:

      CollectionViewSource.GetDefaultView(this.myObservableCollection).Refresh();
      

      【讨论】:

      • 酷。这对我来说是最好的答案。
      • 这也是我所做的,并且成功了。如果你有一个常规列表而不是 ObservableCollection,它甚至可以工作
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-28
      • 2013-09-01
      • 1970-01-01
      • 2013-02-03
      • 2012-07-27
      • 1970-01-01
      相关资源
      最近更新 更多