views:

62

answers:

1

I am trying to refactor my project so that instead of many functions i would have some objects instead.

I am writing an application, which is communicating with a specific optical scanner via Serial port. I use usual SerialPort's class methods

(Write, Open, Close) and some advanced methods, which aren't included in standard class, like ActivateLaser (it writes specific bytes to SerialPort).

I made first step: I created new class, which inherits SerialPort class and added my own methods, like ActivateLaser. This project is compiled to dll and then included in my main project.

Now I'd like to go a step further and include also listening to SeiralPort to my dll. How it works now: first I assign DataReceived event to my scanner (scanner is instance of my own SerialPort class):

scanner.DataReceived += MyDataReceivedEventHandler;

Then in the MyDataReceivedEventHandler I call a delegate, which displays received data in RichTextBox or in DataGridView:

private void MyDataReceivedEventHandler(object sender, SerialDataReceivedEventArgs e)
{
    ...
    this.BeginInvoke(new DisplayDataDelegate(DisplayData), receivedText);
    ...
}

private void DisplayData(string receivedText)
{
    // display received text in RichTextBox in one project, display received text in DataGridView in another project
}

Now I wonder, how could I implement listening to serial port in MySerialPort class. If I just add event handler for DataReceived, then statement which binds MyDataReceivedEventHandler to scanner, would look something like this:

scanner.DataReceived += scanner.MyDataReceivedEventHandler;

I just don't know how to put all this together (should I define MyDataReceivedEventHandler in MySerialPort class, where should I put delegate for displaying text, how can I make possible, to show text in RichTextBox or in DataGridView, ...)

I know my question is a little complex, but I would really like to organize my work better, so that I could use my MySerialPort class in another projects...

Thanks!

+1  A: 

I suggest raising a custom event in your implementation of SerialPort. This event can contain data that's passed to the UI layer of your application where you can display it in a RichTextBox. This will separate the scanner class from the UI controls and means that you don't need to have a reference to a RichTextBox within your scanner class.

As an example you could create a custom event called DocumentScanned and a class derived from EventArgs called DocumentScannedEventArgs to contain the event data.

Within your SerialPort implementation you can then handle the DataReceived event and, in turn, raise a DocumentScanned event when you receive new data. Your UI can subscribe to this event to receive the scanned text and display it however you like.

Something along the lines of (assuming you're using C# 3.0):

public class DocumentScannedEventArgs : EventArgs
{
    public DocumentScannedEventArgs(string scannedText)
    {
        ScannedText= scannedText;
    }

    public string ScannedText { get; set }
}

...

public class MySerialPort : SerialPort
{
    public MySerialPort()
    {
        ...
        this.DataReceived += scanner.MyDataReceivedEventHandler;
        ...
    }

    ...

    public event EventHandler<DocumentScannedEventArgs> DocumentScanned;

    protected void OnDocumentScanned(DocumentScannedEventArgs e)
    {
        var handler = DocumentScanned;

        if (handler != null)
        {
            handler(this, e);
        }
    }

    private void MyDataReceivedEventHandler(object sender, SerialDataReceivedEventArgs e)
    {
        ...
        this.BeginInvoke(new DisplayDataDelegate(DisplayData), receivedText);
        ...
    }

    private void DisplayData(string receivedText)
    {
        // fire DocumentScanned event
        OnDocumentScanned(new DocumentScannedEventArgs(receivedText));
    }
}
dariom
I forgot to mention - I use .NET 2.0, so what should I change in order to make it work in my example?
_simon_
You'll just need to change the ScannedText property in DocumentScannedEventArgs (I've used C# 3.0 automatic properties). For C# 2.0 you'd use a backing field and read/assign to it within the ScannedText getter and setters.
dariom
Thanks :)One more question: if I put all this into my class, then this.BeginInvoke doesn't exist. From which namespace should I use BeginInvoke method?
_simon_
I just copied BeginInvoke from your original code. This method is defined on a Windows Forms control. Sorry, my oversight. You only need BeginInvoke if you want asynchronous processing. You can just call DisplayData(receivedText) synchronously instead of BeginInvoke. If you need to use asynchronous processing you can use a separate thread to run the DisplayData method, but I would try a simple, synchronous operation first :)
dariom