简体   繁体   中英

WPF TreeView: bindings not updating when TreeView is virtualizing

In my WPF app I have a TreeView with a lot of elements, so I turned on virtualization to speed up the rendering, like this:

<TreeView VirtualizingPanel.IsVirtualizing="True">
  ...
</TreeView>

However, if I do this it seems controls inside the tree items that are data-bound to properties in my Viewm Model stop reacting to the OnPropertyChanged event.

For example, let's assume I have the following template for my items:

<DataTemplate DataType="{x:Type viewModels:MyViewModel}">
  <TextBlock Text="{Binding ItemName}" />
</DataTemplate>

and the model is something like this:

public class MyViewModel: INotifyPropertyChanged {
  public event PropertyChangedEventHandler PropertyChanged;

  private string itemName;
  public string ItemName {
    get { return itemName; }
    set {
      if(value != itemName) {
        itemName = value;
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(ItemName)));
      }
    }
  }
}

The initial binding to the property works correctly and the item name is displayed in the TextBlock, but if the value of the ItemName property changes after the tree view item has already been rendered it will not update in the UI, as if the PropertyChanged event is being completely ignored.

How can I fix this? Note that if I set VirtualizingPanel.IsVirtualizing="False" the problem disappears, so it is definitely caused by the virtualization.

I've found the problem after further testing and debugging, it is not strictly related to the panel virtualization, I guess that was just a condition in which the bug manifests itself.

Basically, in my full code (that I omitted for brevity) the ItemName property is being set from inside an Event Handler . The problem was that my handler was triggered by events that were raised in a background thread somewhere else in the code and thus it did not run on the UI thread. I was under the impression that this wasn't a problem, since I was not manipulating any UI objects in the model (I was just touching my ViewModel object), however apparently if you're not in the UI thread raising the PropertyChanged becomes unreliable.

To solve the problem, I've wrapped the code in my handler in a dispatcher call, like this:

private void MyEvenHandler(object sender, MyEventArgs e)
{
    Dispatcher.Invoke(() =>
    {
       var myViewModel = FindVm(e);
       myViewModel.ItemName = "updated";
    }
}

Now everything works as expected.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM