views:

270

answers:

4

Hey guys. The other day, I asked how to create a message box in your class, but one of the answers stated that was it wasn't the correct approach. I understand that this is because it really defeats the point of a class.

My program reads word by word from a string file, and checks if each word is in the database. I want to put every word not found into a ListBox on the form, which can have multiple selections.

How do i send that data back to the form each time a new word is found?

Thanks,

Ash

+1  A: 

I suggest you make a method something like this:

/* ... */

public IEnumerable<string> FindMissingWords(
                               string fileName, IEnumerable<string> toSearch)
{
    List<string> missingWords = new List<string>();

    // todo: the appropriate code for looking up strings in the file, using 
    // the filename and the strings that we passed into the function.

    // if you find one, add it to missingWords

    return missingWords;
}

Then call that method from your form, and add each string it returns to your box.

(If you're not familiar with IEnumerable, don't worry -- it's just a interface that defines a sequence of things, like an array or a list. You could pass an array of strings instead, but it would be a little less precise.)

mquander
+1  A: 

If the class has a reference to the form, the it can just update the form directly.

someForm.SomeListBox.Items.Add(someWord);

If the form has a reference to the class, you can have the class raise an event like

public delegate string WordNotFoundHandler(string word);
public WordNotFoundHandler event WordNotFound ;

and have the form handle that event

theClass.WordNotFound += AddItemToListBox

void AddItemToListBox(string word)
{
someListBox.Items.Add(word);
}

the advantage of doing it this way instead of one huge call that returns all the words is that it provide a faster ui response time, especially when done by a separate thread

Jacob Adams
A more elegant way of accomplishing this same goal (returning them one at a time as you find them) would probably be to use an iterator block with yield return.
mquander
what do you mean when you say iterator block with yield return? Like store it up and send it in a bulk if you get me?
Ash
"someForm.SomeListBox.Items.Add(someWord);" That never seems like a good idea unless you expose a facade method that hides a direct access to "SomeListBox". Just create "Add" in the form so you can call "someForm.Add(someWord)"
Sung Meister
@Sung Meister - I agree. In reality, I don't like the first approach at all since you're still taking a dependency on a the form from your business class.
Jacob Adams
A: 

This is what I'd do (or similar) :

    bool done = false;
    while(true)
    {
        string foundstring;
        done = searchforstring(out foundstring);
        if(done)
            break;
        // Not done, so take what we found and add it to the listbox
        this.BeginInvoke(new Action<string>(delegate(string input)
            { this.listBox.BeginUpdate(); this.listBox.Items.Add(input); this.listBox.EndUpdate(); }),
            new object[] { foundstring });

        }

Substitute the name of your listbox control and I think that'll work. Or you can break out the anonymous method into its own object. The idea is that every time you find a new string, you dispatch a worker to execute the update in the "main application thread" (hence the BeginInvoke() call). I'm not TOTALLY sue if the begin/endUpdate() calls are strictly necessary, but they might be.

Obviously how you GET the strings is up to you, but this should be the way to get it into the listbox on-the-fly assuming your app is multi-threaded. If it isn't multithreaded, a straight-out Invoke() (instead of BeginInvoke) should work to update the listbox immediately, but that'll probably degrade performance of your search.

Kevin
Hey Kevin, sorry to be a pain but can you explain the difference between multi and singloe threadind please? I know its something to do with he way its processed. Sorry for silly questions, start uni this year loll
Ash
I'll do a little better than that: http://www.albahari.com/threading/ It's an online e-book called "Threading in C#" and is one of the best primers on the concept that I've seen. Very clear, and shows all of the major "gotchas" involved. I hope it helps.
Kevin
A: 

You do not want to couple your form with the class that searches and find words from a file. Here is an Event-based solution

Basically what you have to do is to expose an event in the class that reads and find words from a file (I named it as WordFinder for an illustration.)

WordFinder exposes an event called WordFound that is raised when a new word is found.

public class WordFinder
{
    public event EventHandler<WordFoundEventHandler> WordFound = delegate { };
    public event EventHandler NoWordsFound = delegate { };

    protected virtual void OnWordFound(WordFoundEventHandler e)
    {
        var wordFoundHandler = WordFound;
        wordFoundHandler(this, e);
    }

    private void OnNoWordsFound(EventArgs e)
    {
        var noWordsFoundHandler = NoWordsFound;
        noWordsFoundHandler(this, e);
    }

    public void FindWords(string fileName)
    {
        //.. read file and find word
        //.. When a word is found,
        OnWordFound(new WordFoundEventHandler(foundWord));

        // Keep a counter somewhere and check if any words has been found, 
        // if no words are found, then raise "NoWordsFoundEvent"
        OnNoWordsFound(EventArgs.Empty);
    }
}

public class WordFoundEventHandler : EventArgs
{
    public string FoundWord { get; private set; }

    public WordFoundEventHandler(string foundWord)
    {
        FoundWord = foundWord;
    }
}

Now your form would simply register to the event of WordFinder and add a new item when a new word is found.

public partial class Form1 : Form
{
    private readonly WordFinder _WordFinder;

    public Form1()
    {
        InitializeComponent();

        _WordFinder = new WordFinder();
        _WordFinder.WordFound += WordFinder_WordFound;
        _WordFinder.NoWordsFound += WordFinder_NoWordsFound;
    }

    private void WordFinder_WordFound(object sender, WordFoundEventHandler e)
    {
        // Add item to the list here.
        foundWordsListBox.Items.Add(e.FoundWord);
    }

    private void WordFinder_NoWordsFound(object sender, EventArgs e)
    {
        MessageBox.Show("No words found!");
    }

    private void findWordsButton_Click(object sender, EventArgs e)
    {
        _WordFinder.FindWords(/* pass file name here */);
    }
}
Sung Meister