Пример рабочего кода здесь синхронизирует (одиночный) выбор в TreeView, ListView и ComboBox посредством использования лямбда-выражений в словаре, где ключом в словаре является элемент управления, а значением каждого ключа является * 1001. *>.
Где я застрял - это то, что я получаю несколько повторений выполнения кода, который устанавливает выбор в различных элементах управления неожиданным образом: это не повторяется: ошибки StackOverFlow не происходит; но я хотел бы выяснить, почему текущая стратегия предотвращения многократного выбора одних и тех же элементов управления не работает.
Возможно, настоящая проблема здесь заключается в различении обновления выбора , запускаемого конечным пользователем , и обновления выбора , запускаемого кодом, синхронизирующим другие элементы управления ?
Примечание: я экспериментировал с использованием делегатов и форм делегатов, таких как Action<T>
, для вставки исполняемого кода в словари: я "учусь лучше всего", ставя перед собой задачи "программирования" и реализуя их Кроме того, одновременно изучая «золотые слова» таких светил, как Скит, Макдональд, Либерти, Троелсен, Селлс, Рихтер.
Примечание: к этому вопросу / коду добавлено «глубокое предыстория», это заявление о том, как я привык делать вещи до C # 3.0 дней, когда казалось, что мне нужно было использовать явные меры для предотвращения рекурсия при синхронизации выделения.
Код: Предположим, что стандартное дерево WinForms TreeView, ListView, ComboBox имеют одинаковый набор записей (т. Е. TreeView имеет только корневые узлы; ListView, в View Details, имеет один столбец).
private Dictionary<Control, Action<int>> ControlToAction = new Dictionary<Control, Action<int>>();
private void Form1_Load(object sender, EventArgs e)
{
// add the Controls to be synchronized to the Dictionary
// with appropriate Action<int> lambda expressions
ControlToAction.Add(treeView1, (i => { treeView1.SelectedNode = treeView1.Nodes[i]; }));
ControlToAction.Add(listView1, (i => { listView1.Items[i].Selected = true; }));
ControlToAction.Add(comboBox1, (i => { comboBox1.SelectedIndex = i; }));
// optionally install event handlers at run-time like so :
// treeView1.AfterSelect += (object obj, TreeViewEventArgs evt)
// => { synchronizeSelection(evt.Node.Index, treeView1); };
// listView1.SelectedIndexChanged += (object obj, EventArgs evt)
// => { if (listView1.SelectedIndices.Count > 0)
// { synchronizeSelection(listView1.SelectedIndices[0], listView1);} };
// comboBox1.SelectedValueChanged += (object obj, EventArgs evt)
// => { synchronizeSelection(comboBox1.SelectedIndex, comboBox1); };
}
private void synchronizeSelection(int i, Control currentControl)
{
foreach(Control theControl in ControlToAction.Keys)
{
// skip the 'current control'
if (theControl == currentControl) continue;
// for debugging only
Console.WriteLine(theControl.Name + " synchronized");
// execute the Action<int> associated with the Control
ControlToAction[theControl](i);
}
}
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
synchronizeSelection(e.Node.Index, treeView1);
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
// weed out ListView SelectedIndexChanged firing
// with SelectedIndices having a Count of #0
if (listView1.SelectedIndices.Count > 0)
{
synchronizeSelection(listView1.SelectedIndices[0], listView1);
}
}
private void comboBox1_SelectedValueChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex > -1)
{
synchronizeSelection(comboBox1.SelectedIndex, comboBox1);
}
}
фон: до C # 3.0
Похоже, еще в дни до C # 3.0 я всегда использовал логический флаг для предотвращения рекурсии при обновлении нескольких элементов управления. Например, у меня обычно был бы такой код для синхронизации TreeView и ListView: предполагая, что каждый элемент в ListView был синхронизирован с узлом корневого уровня TreeView через общий индекс:
// assume ListView is in 'Details View,' has a single column,
// MultiSelect = false
// FullRowSelect = true
// HideSelection = false;
// assume TreeView
// HideSelection = false
// FullRowSelect = true
// form scoped variable
private bool dontRecurse = false;
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
{
if(dontRecurse) return;
dontRecurse = true;
listView1.Items[e.Node.Index].Selected = true;
dontRecurse = false;
}
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if(dontRecurse) return
// weed out ListView SelectedIndexChanged firing
// with SelectedIndices having a Count of #0
if (listView1.SelectedIndices.Count > 0)
{
dontRecurse = true;
treeView1.SelectedNode = treeView1.Nodes[listView1.SelectedIndices[0]];
dontRecurse = false;
}
}
Тогда, кажется, где-то около FrameWork 3 ~ 3.5 я мог бы избавиться от кода для подавления рекурсии, и рекурсии не было (по крайней мере, при синхронизации TreeView и ListView). К тому времени это стало «привычкой» использовать логический флаг для предотвращения рекурсии, и это, возможно, было связано с использованием определенного стороннего элемента управления.