-1

In a simple WPF XAML UI a list of items is shown:

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Height="450" Width="800">
    <ItemsControl ItemsSource="{Binding Items}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Text}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Window>

The data is in the C# view model:

using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Items.Add(new MyItem());
        Items.Add(new MyItem());
        Items.Add(new MyItem());
        Items.Add(new MyItem());

        Loaded += async (s, e) =>
            {
                while (true)
                {
                    await Task.Delay(1000); // Items could be changed here instead
                    Items.ResetBindings(); // Let UI know about the added/removed/reordered/modified items
                }
            };
    }

    public BindingList<MyItem> Items { get; } = new BindingList<MyItem>();
}

public class MyItem : INotifyPropertyChanged
{
    public string Text { get; set; }
    public event PropertyChangedEventHandler PropertyChanged;
}

The items can change automatically, so Items.ResetBindings(); is called to makes these changes appear in the UI. But this also breaks the UI: The focus and any modification of the current item by the user just disappear!

How can the binding list be updated without resetting the UI controls?

Peter
  • 3,322
  • 3
  • 27
  • 41
  • When you say that "the items can change automatically", do you mean that items are added or removed from the `BindingList` or that the `Text` property of already existing `MyItem` objects change? – mm8 Sep 05 '19 at 13:51
  • @mm8 Yes. (All of those can happen.) – Peter Sep 05 '19 at 13:53
  • @Peter: Are you raising the `PropertyChanged` event in your `Text` property? You don't in the code you have posted. – mm8 Sep 05 '19 at 13:54
  • @mm8 I included only the relevant code to the problem. The problem is that the UI resets. Adding the events does not help and is therefore irrelevant to the question, no? – Peter Sep 05 '19 at 13:58
  • 2
    Bindinglist is still not properly supported in wpf AFAIK. https://stackoverflow.com/questions/9532629/why-not-bindinglist-in-wpf You should definitely use Observablecollection instead. – Andy Sep 05 '19 at 14:14
  • 1
    Like Andy pointed out don't use `BindingList` it's a leftover rubbish from rubbish `WinForms`. `ObservableCollection` is the way to go. – XAMlMAX Sep 05 '19 at 14:36
  • I will accept an answer suggesting ObservableCollection, because that indeed solved the problem. (LiveGrouping was also needed, and the group objects must be reused or it breaks selection and is very slow.) – Peter Sep 26 '19 at 09:44

1 Answers1

1

You need to actually raise the PropertyChanged event whenever your property is set to a new value. Compare this implementation of the Text property to your one:

public class MyItem : INotifyPropertyChanged
{
    private string _text;
    public string Text
    {
        get { return _text; }
        set { _text = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Text))); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

You don't need to call Items.ResetBindings(). This works:

public partial class MainWindow: Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
        Items.Add(new MyItem());
        Items.Add(new MyItem());
        Items.Add(new MyItem());
        Items.Add(new MyItem());

        Loaded += async (s, e) =>
        {
            while (true)
            {
                await Task.Delay(1000);
                Items.Add(new MyItem() { Text = "new" });

            }
        };
    }

    public BindingList<MyItem> Items { get; } = new BindingList<MyItem>();
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • 2
    What problem are you referring to then? Please provide [repo](https://stackoverflow.com/help/minimal-reproducible-example) of your issue when asking a question. – mm8 Sep 05 '19 at 14:08
  • Updated the question to incude the full example code – Peter Sep 05 '19 at 14:28
  • @Peter: Your example works as expected if you replace your `MyItem` implementation with mine and remove replace the call to `Items.ResetBindings()` with for example `Items.Add(new MyItem() { Text = "new" });`. So what's your issue? – mm8 Sep 05 '19 at 14:31
  • So you say `Items.ResetBindings()` should not be used? It is the only solution that so far worked well for many other complex scenarios: add / remove / change / reorder / items arbitrarily, update UI, but don't lose e.g. the selected item). Imageine Task.Delay is instead e.g. a network request that contains returns a new (similar or completely different) version of the List. – Peter Sep 05 '19 at 14:46
  • Yes, it makes no sense to call `Items.ResetBindings()` here. Did you try my suggestion? – mm8 Sep 05 '19 at 14:47