Home > Mobile >  How should I use control.invoke correctly to exclude a CS0120?
How should I use control.invoke correctly to exclude a CS0120?

Time:07-20

I wrote simple application where C# (winforms) call Python ML script (so I cannot use IronPython etc.) All worked fine and I used "invoke" correctly (I had only one process and not used class):

public static void appendTextData(string text)
{
    if (richTextBox1.InvokeRequired)
    {
        richTextBox1.Invoke(new AppendTextDelegate(appendTextData), new object[] { text });
    }
        else
    {
        richTextBox1.AppendText(text);
    }
}

But when I move process creating in class (InnerPerson) then I get error on richTextBox1: "CS0120: An object reference is required for the nonstatic field, method, or property":

public static void appendTextData(string text)
{
    ERROR ->    if (richTextBox1.InvokeRequired) 
    {
    ERROR ->       richTextBox1.Invoke(new AppendTextDelegate(appendTextData), new object[] { text });
    }
        else
    {
    ERROR ->        richTextBox1.AppendText(text);
    }
}

enter image description here

For temporary solution I simple moved richBox declaration from Designer to Aplication and all works fine again. But am I right? Is it possible work with richBox defined in Designer?

This is my working code:

    public partial class Form1 : Form
    {
        delegate void                   AppendTextDelegate(string text);
        public static string            err = "";
        public static InnerPerson[]     innerPerson = new InnerPerson[16];
        public static System.Windows.Forms.RichTextBox richTextBox1;
  
        public Form1()
        {
            richTextBox1 = new System.Windows.Forms.RichTextBox();
            richTextBox1.Location = new System.Drawing.Point(25, 12);
            richTextBox1.Name = "richTextBox1";
            richTextBox1.Size = new System.Drawing.Size(400, 350);
            Controls.Add(richTextBox1);

            InitializeComponent();
        }
        void Form1_Shown(object sender, EventArgs e)
        {
             Start16();
        }
        void Start16()
        {
            innerPerson[1] = new InnerPerson();
            innerPerson[2] = new InnerPerson(); 
            innerPerson[1].Init("Uncle_Bob", 1);
            innerPerson[2].Init("Aunt_Julia", 2);

        }
        public static void OnDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (e.Data != null)
            {
               appendTextData(e.Data   "\n");
            }
        }
        public static void appendTextData(string text)
        {
            
            if (richTextBox1.InvokeRequired)
            {
                richTextBox1.Invoke(new AppendTextDelegate(appendTextData), new object[] { text });
            }
            else
            {
                richTextBox1.AppendText(text);
            }
        }

        void textBox1_KeyDown(object sender, KeyEventArgs e)
        {
            string txt = textBox1.Text;
            if (e.KeyCode == Keys.Enter)
            {
                richTextBox1.Text  = "Me: "   txt   "";
                richTextBox1.Text  = innerPerson[1].Dialog(txt, 1)   "";
                richTextBox1.Text  = innerPerson[2].Dialog(txt, 2)   "";

                richTextBox1.Text  = "\n";
                richTextBox1.SelectionStart = richTextBox1.Text.Length;
                richTextBox1.ScrollToCaret();
                textBox1.Text = "";
            }
        }

    }

    public class InnerPerson
    {
         public string Name = "";
 
        // Constructor
        public InnerPerson()
        {
             Name = "NoName";
        }

        public void SetName(string n) { Name = n; }
        public StreamWriter sw = null;


        public bool Init(string n, int i)
        {
            bool successRunPythonFlag = false;
            SetName(n);
            string executable = @"C:\Users\user\AppData\Local\Programs\Python\Python37\Python.exe";
            string script = @"..\BOTS\chatbot.py "   i; 
            ProcessStartInfo startInfo = new ProcessStartInfo(executable, script);
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardInput = true;
            startInfo.RedirectStandardError = true;

            Process p = new Process();

            try
            {
                p.StartInfo = startInfo;
                p.EnableRaisingEvents = true;
                p.OutputDataReceived  = new DataReceivedEventHandler(Form1.OnDataReceived);
               // p.ErrorDataReceived   = new DataReceivedEventHandler(Form1.OnErrorReceived);
                p.Start();
                p.BeginOutputReadLine();
                p.BeginErrorReadLine();
                sw = p.StandardInput;
                successRunPythonFlag = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
                successRunPythonFlag = false;
            }
            finally
            {
                p.Close();
            }

            return successRunPythonFlag;
        }

        public string Dialog(string d, int i)
        {
            sw.WriteLine(Name   " "   d);
            return "";
        }

    } 


So my problem is why I get errors on richbox when I define it in Constructor\Designer.

CodePudding user response:

Add a SynchronizationContext to your Form

private static SynchronizationContext Context;

And initialize in the constructor:

Context = SynchronizationContext.Current;

This allow you run code in your gui thread easily:

Context.Post(state => { /* this code run in your gui thread */ }, null);

So, in that part you can set values in any control of the form, not only in the RichTextBox. You can also redirect the event to the gui thread at first and forget this problem.

I did another change in your code, creating a Message event in the InnerPerson class.

public event EventHandler<MessageEventArgs> Message;

With this EventArgs:

public class MessageEventArgs : EventArgs
{
    public MessageEventArgs(string message)
    {
        this.Message = message;
    }

    public string Message { get; }
}

Now, your InnerPerson has an own message/event.

Check the full class:

public class InnerPerson
{
    private StreamWriter _writer = null;

    public InnerPerson()
        : this("NoName")
    {
    }

    public InnerPerson(string name)
    {
        this.Name = name;
    }

    public string Name { get; set; }

    public event EventHandler<MessageEventArgs> Message;

    public bool Execute(int index)
    {
        bool successRunPythonFlag = false;
        string executable = @"C:\Users\user\AppData\Local\Programs\Python\Python37\Python.exe";
        string script = @"..\BOTS\chatbot.py "   index;
        
        var startInfo = new ProcessStartInfo(executable, script)
        {
            CreateNoWindow = true,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            RedirectStandardInput = true,
            RedirectStandardError = true
        };

        Process p = new Process();

        try
        {
            p.StartInfo = startInfo;
            p.EnableRaisingEvents = true;
            p.OutputDataReceived  = this.OnDataReceived;
            // p.ErrorDataReceived   = new DataReceivedEventHandler(Form1.OnErrorReceived);
            p.Start();
            p.BeginOutputReadLine();
            p.BeginErrorReadLine();
            _writer = p.StandardInput;
            successRunPythonFlag = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.ToString());
            successRunPythonFlag = false;
        }
        finally
        {
            p.Close();
        }

        return successRunPythonFlag;
    }

    protected virtual void OnDataReceived(object sender, DataReceivedEventArgs e)
    {
        if (!string.IsNullOrEmpty(e.Data) && this.Message != null)
        {
            var e2 = new MessageEventArgs(e.Data   "\n");
            this.Message(this, e2);
        }
    }

    public string Dialog(string d, int i)
    {
        this._writer.WriteLine(this.Name   " "   d);
        return string.Empty;
    }
}

And now, in the form, you need to do some adjustments:

public partial class Form1 : Form
{
    private static SynchronizationContext Context;

    public Form1()
    {
        this.InitializeComponent();

        Context = SynchronizationContext.Current;
    }

    public static string err = "";
    public static InnerPerson[] innerPerson = new InnerPerson[16];

    void Form1_Shown(object sender, EventArgs e)
    {
        Start16();
    }

    void Start16()
    {
        string[] names = new[] { "Uncle_Bob", "Aunt_Julia" };

        for (int i = 0; i < names.Length; i  )
        {
            var person = innerPerson[i] = new InnerPerson(names[i]);

            person.Message  = (sender, e) =>                 
                Context.Post(state => this.OnPerson_Message(sender, e), null);

            person.Execute(i   1);
        }
    }

    private void OnPerson_Message(object sender, MessageEventArgs e)
    {
        richTextBox1.AppendText(e.Message);
    }

    void textBox1_KeyDown(object sender, KeyEventArgs e)
    {
        string txt = textBox1.Text;
        if (e.KeyCode == Keys.Enter)
        {
            richTextBox1.Text  = "Me: "   txt   "";
            richTextBox1.Text  = innerPerson[1].Dialog(txt, 1)   "";
            richTextBox1.Text  = innerPerson[2].Dialog(txt, 2)   "";

            richTextBox1.Text  = "\n";
            richTextBox1.SelectionStart = richTextBox1.Text.Length;
            richTextBox1.ScrollToCaret();
            textBox1.Text = string.Empty;
        }
    }
}

As you see, we use the Message event of InnerPerson to get the messages and Context to redirect these messages to GUI thread: you don't need to do nothing about BeginInvoke in OnPerson_Message, just work with your controls.

  • Related