Hi

I have a program that has Classes

  • GUI
  • Upload
  • and a buffer between the 2 classes - ie used to communicate between the 2 classes .

The Upload class uses Process to run an command line FTP app. I want to return what output produced by the FTP app to be displayed in a textbox in the GUI.
I have tried using the following code that has been truncated.

Upload Class (beginProcess() is a method used to start the Thread (not shown here)):

public delegate void WputOutputHandler(object sender, DataReceivedEventArgs e);
class Upload
{
    private WputOutputHandler wputOutput;

    beginProcess()
    {
        Process pr = new Process();                                                 
        pr.StartInfo.FileName = @"wput.exe";                                        
        pr.StartInfo.RedirectStandardOutput = true;
        pr.StartInfo.UseShellExecute = false;
        pr.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
        pr.OutputDataReceived += new DataReceivedEventHandler(OnDataReceived);
        pr.ErrorDataReceived += new DataReceivedEventHandler(OnDataReceived);
        pr.Start();                                                                 
        pr.BeginOutputReadLine();
        pr.WaitForExit();
    }


    public void OnDataReceived(object sender, DataReceivedEventArgs e)
    {
        if(wputOutput != null)
        {
            wputOutput(sender, e);
        }
    }


    public event WputOutputHandler WputOutput
    {
        add
        {
            wputOutput += value;
        }
        remove
        {
            wputOutput -= value;
        }
    }
}

Buffer Class:

public void EventSubscriber()
{
    uploadSession.WputOutput += Main.writeToTextBoxEvent;
}

Main Class:

public void writeToTextBoxEvent(object sender, DataReceivedEventArgs e)
{
    if(this.textBox1.InvokeRequired)
    {
        MethodInvoker what now?
    }
    else
    {
        textBox1.Text = e.Data;
    }
}

As you can see, when it come to the Main method's writeToTextBoxEvent, I've ran out of ideas. I'm not sure whether doing a UI update using a custom event is even the best way to do it. If someone could point me in the right direction I would be most grateful.

Thanks

Using an event handler is a good approach, primarily because it lets you keep your business logic away from MainForm code, but then again, requirement rules!

In your GUI class, after you check for the InvokeRequired you must then call Invoke and pass it a delegate (they can be anonymous)

I personally suggest Invoking the form, rather than the control as you can then update more than one object at a time and prevents "stuff" from happening that you weren't expecting.

public delegate void UpdateTextBoxDelegate(String message);

private void UpdateTextBox(String message)
{
    if(this.InvokeRequired)
    {
        this.Invoke(new UpdateTextBoxDelegate(UpdateTextBox), new Object[] { message });
        return;
    }
    tbxMyTextBox.Text = message;
}

You were almost there, just the Invoke call you needed.

EDIT: If you want to use MethodInvoker, your target method needs to return void and take no parameters. It's useful for not having to declare a delegate for a void type and for creating anonymous delegates.

eg.

this.Invoke(new MethodInvoker(delegate { tbxUpdate.Text = "Hi"; }));

Edited 6 Years Ago by Ketsuekiame: n/a

You can't update the GUI across multiple threads, you will need to marshal the call over using Control.Invoke/Control.BeginInvoke.

I do something very similar with ngen:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;

namespace testBinarySer
{
  public partial class frmInvoke : Form
  {
    public frmInvoke()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      RunThread();
    }

    void RunThread()
    {
      ThreadPool.QueueUserWorkItem(s => RunUpload());
    }

    void RunUpload()
    {
      using (Upload uploader = new Upload())
      {
        uploader.DataReceived += new EventHandler<DataReceivedEventArgs>(uploader_DataReceived);
        uploader.Start();
      }
    }

    void uploader_DataReceived(object sender, DataReceivedEventArgs e)
    {
      if (InvokeRequired)
      {
        //Marshals to the GUI thread
        Invoke(new Action(() => uploader_DataReceived(sender, e)));
        return;
      }
      textBox1.Text += e.Data + Environment.NewLine;
    }
  }

  public class Upload : IDisposable
  {
    readonly EventHandlerList m_events;
    protected EventHandlerList Events
    {
      get { return m_events; }
    }

    static object EVENT_DataReceived = new object();
    public event EventHandler<DataReceivedEventArgs> DataReceived
    {
      add { m_events.AddHandler(EVENT_DataReceived, value); }
      remove { m_events.RemoveHandler(EVENT_DataReceived, value); }
    }
    protected void OnDataReceived(DataReceivedEventArgs args)
    {
      EventHandler<DataReceivedEventArgs> del = (EventHandler<DataReceivedEventArgs>)m_events[EVENT_DataReceived];
      if (del != null)
      {
        del(this, args);
      }
    }

    public Upload()
    {
      m_events = new EventHandlerList();
    }

    public void Start()
    {
      ProcessStartInfo psi = new ProcessStartInfo()
      {
        Arguments = "update",
        CreateNoWindow = true,
        FileName = @"C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\ngen.exe",
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        UseShellExecute = false,
      };
      using (Process proc = new Process() { StartInfo = psi })
      {
        proc.OutputDataReceived += new DataReceivedEventHandler(proc_DataReceived);
        proc.ErrorDataReceived += new DataReceivedEventHandler(proc_DataReceived);
        proc.Start();
        proc.BeginErrorReadLine();
        proc.BeginOutputReadLine();
        proc.WaitForExit();
      }
    }

    void proc_DataReceived(object sender, DataReceivedEventArgs e)
    {
      OnDataReceived(e);
    }

    
    #region IDisposable Members
    public void Dispose()
    {
      Dispose(true);
    }
    #endregion
    protected void Dispose(bool disposing)
    {
      if (disposing)
      {
        m_events.Dispose();
      }
    }
  }
}

on any methods that might be called from a different thread I always use something like this in the method:

private void UpdateStatusText(string status)
        {
            Action UXUpdate;
            UXUpdate = () => textBoxStatus.AppendText(status);
            this.Invoke(UXUpdate);
        }

and in the thread that calls that method I' just call it as normal:

UpdateStatusText("thread start: OK");

Works like a charm every time.

Dunno if that helps though.

Personally, I'd use a recursive method call to save having to create two methods for all GUI updates.

eg.

private void UpdateTextBox(String message)
{
    if(this.InvokeRequired)
        this.Invoke(new Action<String>(UpdateTextBox), new Object[] { message });
    else
        tbxMyBox.Text = message;
}

or (as shown by sknake);

private void UpdateTextBox(String message)
{
    if(this.InvokeRequired)
        this.Invoke(new Action(() => UpdateTextBox(message)));
    else
        tbxMyBox.Text = message;
}

My personal preference, when dealing with WinForms is my first example as it more closely matches the signature in WPF which is Dispatcher.Invoke(Delegate, params Object[]) and is, to me anyway, clearer to read.

Edited 6 Years Ago by Ketsuekiame: n/a

This article has been dead for over six months. Start a new discussion instead.