开发者

Singleton Bindable Controls While Tasking/Multi-Threading

开发者 https://www.devze.com 2023-03-20 14:20 出处:网络
before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.

before posting the question i did my research for 10 days so really hope someone can shed some light into solving this issue.

The issue is that any bindable control, does not update once the binding list from singleton class is changed. This is a common issue on multi-threaded apps. Most if not all solutions offer suggestions where the bindlinglist or collection is initialized from parent thread, and then some invocation to be made. Not what i'm looking for. The same issue persist if static class is used instead of singleton.

Basically, the application triggers some Tasks, which in turn create object(s) on different business classes. These objects post messages into the bindinglist, which should update the UI listbox, but does not. And yes, the message object is in the list, and binding after the TASK finished works (items displayed). Locking/unlocking object(s) access is also not an issue.

Appreciate any suggestions/solutions

A trimmed down version of business objects:

namespace MyNameSpace
{
    public class Message
    {
        private string messageSummary;
        pu开发者_如何学Cblic Message() { }
        public string MessageSummary
        {
            set { messageSummary = value; }
            get { return messageSummary; }
        }
    }
}

A trimmed down version of another class doing some ops:

namespace MyNameSpace
{
    public class WorkDoingClass
    {
        public WorkDoingClass() { }
        public void DoSomeWork()
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSOmrWork Finished";
        }

        public void DoSomeOtherWork()
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSomeOtherWork Finished";
            AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
        }
    }
}

Singleton:

namespace MyNameSpace
{
    public sealed class AllMessages
    {
        private static readonly AllMessages _instance = new AllMessages();
        private BindingList<Message> _allMessagesBL;

        public WorkDoingClass() { _allMessagesBL = new BindingList<Message>(); }

        public static AllMessages Instance
        {
            get { return _instance; }
        }

        public BindingList<Message> AllMessagesBindingList
        {
            get { return _allMessagesBL};
        }
    }
}

This is also a trimmed down version from where calls start:

namespace MyNameSpace
{
    public partial class Form1 : Form
    {
        private Task _TaskSqlData;
        private CancellationTokenSource cTokenSourceSql;

        public Form1()
        {
            InitializeComponent();
            listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
            listBox1.DisplayMember = "MessageSummary";
        }

    private void button1_Click(object sender, EventArgs e)
    {
            cTokenSourceSql = new CancellationTokenSource();
            var tokenSqlData = cTokenSourceSql.Token;
            if (this._TaskSqlData != null)
            {
                if (this._TaskSqlData.Status == TaskStatus.Running)
                    this.cTokenSourceSql.Cancel();
                this._TaskSqlData.Dispose();
                this._TaskSqlData = null;
            }
            _TaskSqlData = Task.Factory.StartNew(()
                            => StartDoingWork(this, tokenSqlData, null), tokenSqlData);
    }

    public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
    {
            if (ct.IsCancellationRequested)
                ct.ThrowIfCancellationRequested();
            WorkDoingClass work = new WorkDoingClass();
            work.DoSomeOtherWork();
    }


Your problem is that the thread(the main UI thread) making the listbox is different from the thread(the worker thread) modifying the collection.

Try the following code. It could solve your issue. I use SynchronizationContext to synchronize the two threads, which serves as the same function with Control.Invoke().

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        private Task _TaskSqlData;
        private CancellationTokenSource cTokenSourceSql;
        WorkDoingClass _work;

        public Form1()
        {
            InitializeComponent();
            listBox1.DataSource = AllMessages.Instance.AllMessagesBindingList;
            listBox1.DisplayMember = "MessageSummary";
            _work = new WorkDoingClass(SynchronizationContext.Current);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            cTokenSourceSql = new CancellationTokenSource();
            var tokenSqlData = cTokenSourceSql.Token;
            if (this._TaskSqlData != null)
            {
                if (this._TaskSqlData.Status == TaskStatus.Running)
                    this.cTokenSourceSql.Cancel();
                this._TaskSqlData.Dispose();
                this._TaskSqlData = null;
            }
            _TaskSqlData = Task.Factory.StartNew(()
                            => StartDoingWork(this, tokenSqlData, null), tokenSqlData);
        }

        public void StartDoingWork(object sender, CancellationToken ct, EventArgs e)
        {
            if (ct.IsCancellationRequested)
                ct.ThrowIfCancellationRequested();

            _work.DoSomeOtherWork();
        }
    }

    public class Message
    {
        private string messageSummary;
        public Message() { }
        public string MessageSummary
        {
            set { messageSummary = value; }
            get { return messageSummary; }
        }
    }

    public class WorkDoingClass
    {
        private SynchronizationContext _syncContext;

        public WorkDoingClass() { }

        public WorkDoingClass(SynchronizationContext _syncContext)
        {
            // TODO: Complete member initialization
            this._syncContext = _syncContext;
        }
        public void DoSomeWork()
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSOmrWork Finished";
        }

        public void DoSomeOtherWork()
        {
            _syncContext.Send(DoWork, null);
        }

        private static void DoWork(object arg)
        {
            //some routines
            Message messageObj = new Message();
            messageObj.MessageSummary = "DoSomeOtherWork Finished";
            AllMessages.Instance.AllMessagesBindingList.Add(messageObj);
        }
    }

    public sealed class AllMessages
    {
        private static readonly AllMessages _instance = new AllMessages();
        private BindingList<Message> _allMessagesBL;

        public AllMessages() { _allMessagesBL = new BindingList<Message>(); }

        public static AllMessages Instance
        {
            get { return _instance; }
        }

        public BindingList<Message> AllMessagesBindingList
        {
            get { return _allMessagesBL; }
        }
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消