0

I try to do some simple task (I guess so). I want to change GUI dynamically, from the for loop.
Let's see my XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel Name="MyPanel">
        <TextBlock Text="{Binding MyValue}"></TextBlock>
        <Button Click="Button_Click">OK</Button>
        <ListBox Name="myList" ItemsSource="{Binding MyCollection}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid Margin="10">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="20"/>
                            <ColumnDefinition Width="20"/>
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding A}" Grid.Column="0"/>
                        <TextBlock Text="{Binding B}" Grid.Column="1"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </StackPanel>
</Window>

As you can see, I have the Textblock that shows numbers, button that is starting the program and the listbox, that should show the collection items.

After click on the button, the first textblock (bindes MyValue) shows dynamic values, but on the list box I get the next error:
"This type of CollectionView does not support changes to its SourceCollection from a thread different from the Dispatcher thread."
I saw another answers for the error, but cannot understand how to implement it in my case.

Here the C# code:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static MyModel m;
        public MainWindow()
        {
            m = new MyModel();
            InitializeComponent();
            MyPanel.DataContext = m;
        }

        bool flag = false;
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            flag = !flag;
            Task.Factory.StartNew(() =>
            {
                for (int i = 0; i < 5000000; i++)
                {
                    if (flag == false) break;
                    m.MyValue = i.ToString();
                    m.MyCollection.Add(new ChartPoint { A = i, B = 2 * i });
                }
            });
        }
    }

    public class MyModel : INotifyPropertyChanged
    {
        private string myValue;


        public ObservableCollection<ChartPoint> MyCollection { get; set; }

        public MyModel()
        {
            MyCollection = new ObservableCollection<ChartPoint>();
        }

        public string MyValue
        {
            get { return myValue; }
            set
            {
                myValue = value;
                RaisePropertyChanged("MyValue");
            }
        }

        private void RaisePropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class ChartPoint
    {
        public int A { get; set; }
        public int B { get; set; }
    }
}

Thanks a lot!

Michael Vaysman
  • 277
  • 3
  • 16
  • If your program runs on .NET 4.5 or newer, then there is no need to deal with `Dispatcher.Invoke`/`Dispatcher.BeginInvoke` whenever you want to alter your collection in some other (background) thread. Just instruct the WPF binding engine with help by `BindingOperations.EnableCollectionSynchronization` that other threads than the Dispatcher are allowed to alter your ObservableCollection. Details in [this answer to a related question](http://stackoverflow.com/a/31600890/2819245). –  May 04 '17 at 17:07

1 Answers1

1

I have changed the Button_Click code a bit, is this you want to achieve, please suggest:

private void Button_Click(object sender, RoutedEventArgs e)
    {
      flag = !flag;

      var list = new List <ChartPoint>();

      Task.Factory.StartNew(() =>
      {
        for (int i = 0; i < 50000000; i++)
        {
          if (flag == false) break;
          m.MyValue = i.ToString();
          Dispatcher.BeginInvoke(new Action(() =>
                                            {
                                              m.MyCollection.Add(new ChartPoint
                                                                 {
                                                                   A = i,
                                                                   B = 2 * i
                                                                 });
                                            }),
                                 DispatcherPriority.Background);
        }
      });
    }
Mohit Vashistha
  • 1,824
  • 3
  • 22
  • 49