1

私は 2 つのテーブルを持っています. ,にすべて とをemployees示しています. 明らかに 1 人の従業員が多くのプロジェクトに関与できます. 1 つのプロジェクトには多くの従業員がいる可能性があります. だから私は、存在する多対多の関係をマッピングするこれを持っています。私が望むのは、ユーザーが最初のリストボックスでプロジェクト名をクリックすると、そのプロジェクトのすべての従業員がリストボックス2で選択されることです。また、ユーザーが listbox2 のアイテムをクリックすると、(従業員) その従業員が所属するすべてのプロジェクトが listbox1 で選択されます。projectlistbox2employeeslistbox1projectsEmployeeProject

しかし、このプロセスに ListBox.SelectedIndexChanged イベントを使用し、listbox2 の値を 1 つでも選択すると、listbox2 の SelectedIndexChagned がトリガーされ、現在の従業員が所属する listbox1 のすべての項目を選択することで動作が開始されますが、再び、listbox1 のアイテムが 1 つでも選択されるとすぐに、その SelectedIndexChanged イベントが発生し、このように永遠に続きます。それで、これの解決策は何ですか?これまでのところ、私はこれをやった..

 private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Load the list of employees
            cmd.CommandText =
                "SELECT EmpName FROM Employee WHERE EmpID IN(SELECT EmpID FROM EmployeeProject WHERE ProjectID =(SELECT ProjectID FROM Project WHERE ProjectName = '" +
                listBox1.SelectedItem.ToString() + "')) ORDER BY EmpId";
            var rdr = cmd.ExecuteReader();
            listBox2.Items.Clear();
            // right now, I am doing this to escape this recursive loop, but thats not what I want
            while (rdr.Read())
            {
                listBox2.Items.Add(rdr.GetString(0));
            }
            rdr.Close();

            this.AutoScroll = true;
        }

        private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
        {
            // Load the list of projects

            cmd.CommandText =
               "SELECT ProjectName FROM Projects WHERE ProjectID IN(SELECT ProjectID FROM EmployeeProject WHERE EmpId=(SELECT EmpId FROM Employee WHERE EmpName= '" +
                listBox2.SelectedItem.ToString() + "')) ORDER BY ProjectID";
            var rdr = cmd.ExecuteReader();
            listBox1.Items.Clear();
            // again, I don't want to clear, but select all those employee in listbox1 that are involved in this selected project, but can't do it because it would cause infinite recursion of these
            while (rdr.Read())
            {
            listBox2.Items.Add(rdr.GetString(0));
            }
            rdr.Close();

            this.AutoScroll = true;
        }

そう?達成したいことを達成するために何をすべきですか?そして、どうすればその再帰を避けることができますか? この方法も今示したように機能することはわかっていますが、クリアして再び表示したくはありません (これは単純なユーザーを混乱させる可能性があります)。選択ごとに、その選択に対応する値が他のリストボックスで選択されるようにします(もちろん再帰は発生しません!)。どうすればいいのですか?

EDITプログラムでリストボックス内の複数の項目を選択する方法がわからないので、それを教えていただければ幸いです!

4

5 に答える 5

5

Balkingというデザインパターンがあり、ここに当てはまると思います。

http://en.wikipedia.org/wiki/Balking_pattern

アイデアは、操作を制御する補助状態変数を導入することです。

  private bool doesProcessing { get; set; }

  private void listBox1_SelectedIndexChanging( ... )
  {
     // signal the beginning of processing
     if ( doesProcessing ) 
        return;
     else
        doesProcessing = true; 

     try
     {
        // your logic goes here
     }
     finally
     {
        // signal the end of processing
        doesProcessing = false;
     }
  }

listBox2 についても同様です。

于 2012-06-21T13:58:43.387 に答える
0

OPコードのロジック(OPが期待するとおりに機能している場合)に問題があります。
2つのリストにすべての可能な値を入力するとします。
次に、ユーザーが1つのアイテムをクリックすると、2番目のリストが空になり、他のリストにリンクされているアイテムのみが入力されます。ただし、2つのリストの間をクリックすると、他のリストから1つ以上のアイテムが完全に削除される可能性があります。2つのリストを埋めたままにして、対応するアイテムの行を強調表示することをお勧めします

たとえば(ListBox.SelectionMode = SelectionMode.MultiSimpleまたはMultiExtendedと仮定)

private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
........
try
{
   ......
   // Remove the event handler, do your selection, No event on the listbox2 will be fired....
   listBox2.SelectedIndexChanged -= new System.EventHandler(this.listBox2_SelectedIndexChanged);

   // Do not remove the items , instead clear previous selection
   listBox2.ClearSelected(); 

   while (rdr.Read()) 
   { 
       int index = listBox2.FindString(rdr.GetString(0), -1);
       if(index != -1) listBox2.SetSelected(index, true)); 
   } 
   ....
}
finally
{
    // before exit, reapply the event handler 
    listBox2.SelectedIndexChanged += new System.EventHandler(this.listBox2_SelectedIndexChanged);
}
}

もちろん、必ずイベントを再適用する必要があるため、try / finalブロックを使用してください。同じアプローチを使用して、listBox2.SelectedIndexChangedイベントに入力したときに、listBox1のイベントの発生を停止できます。

于 2012-06-21T13:59:40.040 に答える
0

eventMask 整数変数を 1 つ持ち、以下のパターンを使用してイベントを相互に保護します。

このシナリオでは、別の回答で提案されているように bool を使用するだけで十分です。ただし、 bool よりも整数を使用することを好みます。これにより、ネストされたスタックされたイベント呼び出しを正しく処理できます。

たとえば、ツリービューやリストビューで選択/選択解除イベントを処理する必要がある場合に特に便利です。

int eventMask = 0;

void items_PropertyChanged(object sender, PropertyChangedEventArgs e) // for example...
{
    if (eventMask > 0)
        return;

    try
    {
        eventMask++;

        // processing
        // it happens some other events can be called. Including this one.
    }
    finally 
    {
        // put this in finally, so there is no lock risk
        eventMask--;
    }
}
于 2012-06-21T14:05:15.007 に答える
0

変更を行う前に、一方のコントロールに接続しているときに、もう一方のコントロールに添付されたイベントを削除できます。最後に再度取り付けます。

于 2012-06-21T13:55:32.560 に答える
0

1 つのリストボックスで選択した項目を変更すると、もう一方のイベントを一時的に無効にすることができます。例えば:

// Store the event handlers in private member variables.
private System.EventHandler selectedEmployeeChanged = new System.EventHandler(this.lbEmployees_SelectedIndexChanged); 

private void lbProjects_SelectedIndexChanged(object sender, EventArgs e)
{
    try
    {
        // Remove your event so that updating lbEmployees doesn't cause
        // lbEmployees_SelectedIndexChanged to get fired.
        lbEmployees.SelectedIndexChanged -= selectedEmployeeChanged;

        // other event handler logic
    }
    finally
    {
        // Ensure that the handler on lbEmployees is re-added,
        // even if an exception was encountered.
        lbEmployees.SelectedIndexChanged += selectedEmployeeChanged;
    }
}

注: listBoxes (および関連するイベント) の名前を読みやすく変更しました。あなたの説明によると、listBox1is nowlbEmployeeslistBox2is nowlbProjectsです。

複数のアイテムをプログラムで選択する場合、それぞれのインデックスがわかっている場合は、ListBox.SetSelected Methodを使用できます。例えば:

listBox1.SetSelected(1, true);
listBox1.SetSelected(3, true);

listBox1 が 1 と 3 の項目を選択するようにします。

あなたの場合、クエリのみを使用して選択する値を取得することをお勧めします(リストボックスをクリアしてから、選択する必要がある値を追加するだけではありません)。以下は、ハンドラーの 1 つをどのように書き換えるかについての私の提案です。

// Store the event handlers in private member variables.
private System.EventHandler selectedEmployeeChanged = new System.EventHandler(this.lbEmployees_SelectedIndexChanged); 

private void lbProjects_SelectedIndexChanged(object sender, EventArgs e)
{
    // Declare this outside of try so that we can close it in finally
    DbDataReader reader = null;
    try
    {
        // Remove your event so that updating lbEmployees doesn't cause
        // lbEmployees_SelectedIndexChanged to get fired.
        lbEmployees.SelectedIndexChanged -= selectedEmployeeChanged;
        // Deselect all items in lbEmployees
        lbEmployees.ClearSelected();

        cmd.CommandText = "SELECT ProjectName FROM Projects WHERE ProjectID IN(SELECT ProjectID FROM EmployeeProject WHERE EmpId=(SELECT EmpId FROM Employee WHERE EmpName= '@EmpName')) ORDER BY ProjectID";
        cmd.Parameters.AddWithValue("@EmpName", lbProjects.SelectedItem.ToString());
        reader = cmd.ExecuteReader();

        // For each row returned, find the index of the matching value
        // in lbEmployees and select it.
        while (rdr.Read())
        {
            int index = lbEmployees.FindStringExact(rdr.GetString(0));
            if(index != ListBox.NoMatches)
            {
                lbEmployees.SetSelected(index, true);
            }
        }

        this.AutoScroll = true;
    }
    finally
    {
        // Ensure that the reader gets closed, even if an exception ocurred
        if(reader != null)
        {
            reader.Close();
        }
        // Ensure that the handler on lbEmployees is re-added,
        // even if an exception was encountered.
        lbEmployees.SelectedIndexChanged += selectedEmployeeChanged;
    }
}

補足として、複数のネストされた SELECT ではなく、クエリ文字列で JOIN 演算子を使用することを検討することをお勧めします。

于 2012-06-21T13:57:48.300 に答える