0

私は単純な Windows フォーム アプリケーションを作成して、Threads を使った作業に取り掛かります。これまでのところ、私が持っているものは機能していますが、私がやりたいのは、フォーム コードに直接ではなく、すべてを別のクラスに含めることです。

データベースからデータを開始して取得するバックグラウンド スレッドがあります。次に、そのデータをリストボックスに表示します。

private delegate void UpdateListValues(List<ListBoxItem> itemList);

private void form_main_Shown(object sender, EventArgs e)
{            
    // Set the loading text.
    list_selection.Items.Add(ListHelpers.LoadingItem());

    // Start the data access on a seperate thread.
    Thread worker = new Thread(GetInvoicingData);
    worker.IsBackground = true;
    worker.Start();
}

private void GetInvoicingData()
{
    // Query database
    List<ListBoxItem> values = DAC.GetInvoicingAccounts();

    // Display results
    BeginInvoke(new UpdateListValues(DisplayList), new object[] { values });
}

private void DisplayList(List<ListBoxItem> itemList)
{
    // Display each result
    list_selection.Items.Clear();
    for (int i = 0; i < itemList.Count; i++)
    {
        list_selection.Items.Add(itemList[i]);
    }
}

問題は、DisplayList メソッドでは、フォーム クラスの一部であるため、リスト ボックス (list_selection) にアクセスできないことです。私がこれを行う方法について何か提案はありますか?

また、私はスレッド化が初めてなので、まったく間違っていることを教えてください。http://www.codeproject.com/Articles/23517/How-to-Properly-Handle-Cross-thread-Events-and-Updの例を使用して、現在の場所にたどり着きました。

ありがとう

4

1 に答える 1

2

このようなものはどうですか:

// Added the form's class declaration to highlight separation of thread code into a separate class, but may not be exactly the same as yours depending on naming
public class Form1 : Form
{
    private readonly DataRetriever _dataRetriever;

    private void form_main_Shown(object sender, EventArgs e)
    {            
        // Set the loading text.
        list_selection.Items.Add(ListHelpers.LoadingItem());

        // Create the DataRetriever, and provide it with a delegate to DisplayList for returning data
        _dataRetriever = new DataRetriever(DisplayList);
        // Start retrieving data on a separate thread...
        _dataRetriever.GetData();
    }

    private void DisplayList(List<ListBoxItem> itemList)
    {
        if (InvokeRequired)
        {
            // Ensure the update occurs on the UI thread
            Invoke((Action)(() => DisplayList(itemList)));
            return;
        }
        // Display each result
        list_selection.Items.Clear();
        foreach (var item in itemList)
        {
            list_selection.Items.Add(item);
        }
    }
}

// Separate class to hold thread code
public class DataRetriever
{
    public delegate void UpdateCallbackDelegate(List<ListBoxItem> itemList);

    private readonly UpdateCallbackDelegate _updateCallback;

    public DataRetriever(UpdateCallbackDelegate updateCallback)
    {
        _updateCallback = updateCallback;
    }

    public void GetData()
    {
        var thread = new Thread(GetInvoicingData)
        {
            IsBackground = true
        };
        thread.Start();
    }

    private void GetInvoicingData()
    {
        // Not sure whether "DAC" is a static class, if it needs to be constructed
        // in the DataRetriever's constructor, or passed to it as a parameter
        _updateCallback(DAC.GetInvoicingAccounts());
    }
}

ご覧のとおり、すべてのスレッド コードは別のクラスDataRetrieverになり、取得が完了したら取得したデータをフォームに戻すことができるように、それを構築するときにデリゲートが提供されます。コールバックを処理するメソッドは、クロススレッド例外を防ぐために呼び出しが UI スレッドにマーシャリングされることを保証します。

これはこれを行うための「最良の」方法として提示されているのではなく、単に質問 (スレッドコードを別のクラスに分離する方法) に対する答えとして提示されていることを指摘したいと思います。他の人が述べたように、この種のことを行うためのメカニズムがすでに用意されています (たとえば、BackgroundWorker)。わかりやすくするために、一部の複雑さは省略されています。たとえば、ここに示す実装では、GetData()(前の呼び出しがデータを返す前に各呼び出しが発生する) 複数回呼び出した場合、複数のクエリが同時に発生し、非同期で実行されているため、データが返される可能性があります。任意の順序で。これは、あなたのケースでは問題になる場合とそうでない場合があります。

于 2012-06-22T03:16:00.080 に答える