BindingList<T> привязан к источнику данных сетки через контроллер, затем обновлен из другого потока, исключение между потоками

Работая над JavaScript и Angular в течение последнего года, я сейчас работаю над проектом в Winforms. Я понимаю проблему перекрестных потоков с элементами управления пользовательского интерфейса (InvokeRequired), но раньше я не работал с BindingList, поэтому для эксперимента у меня есть базовое приложение с Form, Controller и POCO, которое реализует INotifyPropertyChanged. Форма вводится с контроллером, который имеет BindingList. Для имитации внешнего потока, манипулирующего данными, у меня есть метод Start внутри контроллера, который создает поток, запускает цикл и обновляет список акций. Очевидно, что это вызовет событие свойств измененного, которое уведомляет Grid и его источник данных, однако я получаю исключение между потоками. Что я хочу знать, что является обычной практикой для этого? Мой прошлый опыт был связан с потоками из GUI (фоновый рабочий), поэтому код обычно уже находится в форме и имеет доступ к "this" для вызова InvokeRequired. Однако при использовании BindingList из контроллера у меня этого нет.

Form1Controller

public class Form1Controller
{
    public bool IsRunning { get; private set; }

    private readonly Random _Random = new Random();

    public Form1Controller()
    {
        Stocks = new BindingList<Stock>();           
        Stocks.Add(new Stock()
        {
            Name = "Microsoft",
            Ticker = "MSFT",
            Price = 25,
            Volume = 100000,
            LastTraded = DateTime.Now
        });
        Stocks.Add(new Stock()
        {
            Name = "Google",
            Ticker = "GOOG",
            Price = 450,
            Volume = 100000,
            LastTraded = DateTime.Now
        });
        Stocks.Add(new Stock()
        {
            Name = "Apple",
            Ticker = "AAPL",
            Price = 750,
            Volume = 100000,
            LastTraded = DateTime.Now
        });
        Stocks.Add(new Stock()
        {
            Name = "Oracle",
            Ticker = "ORCL",
            Price = 80,
            Volume = 100000,
            LastTraded = DateTime.Now
        });
    }

    public BindingList<Stock> Stocks { get; set; }

    public void Start()
    {
        IsRunning = true;

        new Thread(() => 
        {
            while (IsRunning)
            {
                var index = _Random.Next(0, Stocks.Count);

                Debug.WriteLine(index);

                var stock = Stocks[index];
                stock.Price += 1;
                stock.Volume += 500;
                stock.LastTraded = DateTime.Now;

                Thread.Sleep(100);
            }                
        }).Start();            
    }

    public void Stop()
    {
        IsRunning = false;
    }
}

Склад

public class Stock : DependencyObject
{        
    private string name;
    public string Name
    {
        get { return name; }
        set { SetField(ref name, value, () => Name); }
    }

    private string ticker;
    public string Ticker
    {
        get { return ticker; }
        set { SetField(ref ticker, value, () => Ticker); }
    }

    private int price;
    public int Price
    {
        get { return price; }
        set { SetField(ref price, value, () => Price); }
    }

    private long volume;
    public long Volume
    {
        get { return volume; }
        set { SetField(ref volume, value, () => Volume); }
    }

    private DateTime lastTraded;
    public DateTime LastTraded
    {
        get { return lastTraded; }
        set { SetField(ref lastTraded, value, () => LastTraded); }
    }   
}

DependencyObject

public class DependencyObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) 
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    protected bool SetField<T>(ref T field, T value, string propertyName)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }

    protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(selectorExpression);
        return true;
    }
}

Form1

public partial class Form1 : Form
{        
    private readonly Form1Controller _Controller = new Form1Controller();

    public Form1(Form1Controller controller)
    {
        InitializeComponent();

        _Controller = controller;
        _MainGrid.DataSource = _Controller.Stocks;
    }

    private void _StartButton_Click(object sender, EventArgs e)
    {
        _Controller.Start();
    }

    private void _StopButton_Click(object sender, EventArgs e)
    {
        _Controller.Stop();
    }

    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        _Controller.Stop();
    }
}

Как перенести измененное свойство обратно в поток пользовательского интерфейса из BindingList (при условии, что это то, что я должен просить)?

0 ответов

Другие вопросы по тегам