0

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

5
Contributors
6
Replies
7
Views
7 Years
Discussion Span
Last Post by Ketsuekiame
0

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!

0

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 by Ketsuekiame: n/a

0

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();
      }
    }
  }
}
0

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.

0

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 by Ketsuekiame: n/a

This topic has been dead for over six months. Start a new discussion instead.
Have something to contribute to this discussion? Please be thoughtful, detailed and courteous, and be sure to adhere to our posting rules.