我有两个表,分别在employees
和project
中,在listbox2
中显示所有employees
,在listbox1
中显示所有projects
,现在显然一位员工可以参与许多项目,而一个项目可以有很多员工。因此,我有一个EmployeeProject
映射了存在的多对多关系。我想要的是,如果用户在第一个列表框中单击项目名称,则应在listbox2中选择该项目中的所有员工。另外,当用户单击listbox2中的项目时,(雇员)应在listbox1中选择该雇员所属的所有项目。
但是,如果我在此过程中使用ListBox.SelectedIndexChanged事件,甚至在listbox2中选择一个值,那么它将触发listbox2的SelectedIndexChagned,这将通过选择listbox1中当前雇员所属的所有项目而开始工作,但是再次,即使选择了listbox1中的一项,它也会触发其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;
}
所以?我应该怎么做才能达到我想要的目标?我将如何避免这种递归?我知道这种方式也可以像我刚刚显示的那样工作,但是我不想清理并再次显示(这可能会使一个简单的用户感到困惑)。我要为每个选择在其他列表框中选择与该选择相对应的值(当然不会造成递归!)。我该怎么做?
编辑我不知道如何以编程方式选择列表框中的多个项目,因此,如果您能说出来,那就太好了!
最佳答案
更改一个列表框的选定项目时,可以暂时禁用另一个列表框的事件。例如:
// 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(及相关事件)重命名为更易读。根据您的描述,
listBox1
现在是lbEmployees
,而listBox2
现在是lbProjects
。至于以编程方式选择多个项目,如果您知道每个项目的索引,则可以使用ListBox.SetSelected Method。例如:
listBox1.SetSelected(1, true);
listBox1.SetSelected(3, true);
使listBox1选择1和3处的项目。
在您的情况下,我只建议使用查询来获取要选择的值(不清除列表框,然后仅添加应选择的值)。以下是我对如何重写其中一个处理程序的建议:
// 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;
}
}
附带说明一下,您可能希望研究在查询字符串中使用JOIN运算符,而不是使用多个嵌套的SELECTS。
关于c# - 如何阻止方法递归调用自己?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11139674/