Prevent multiple re-selection of synchronized controls?

Here, the working code example synchronizes the (single) selection in TreeView, ListView, and ComboBox using lambda expressions in the dictionary, where the key in the dictionary is a control and the value of each key is Action<int>.

Where I am stuck is that I get several repetitions of the code that sets the selection in various controls in such an unexpected way: it is not recursive: there is no StackOverFlow error; but I would like to find out why the current strategy for preventing multiple selection of the same control does not work.

Perhaps the real problem is the difference between the selection update initiated by the end user and the selection update initiated by the code that synchronizes the other controls?

Note. I experimented with using delegates and delegate forms, such as Action<T>to insert executable code into dictionaries: “I’ll learn better”, creating “problems” for programming and realizing them while studying the “golden words” of lights like Skeet, MacDonald, Liberty, Troelsen, Sells, Richter.

Note. When adding to this question / code for the "deep background" this statement is about how I did something in pre C # 3.0, when it seemed to me that I needed to use explicit measures to prevent recursion when synchronizing the selection.

Code: Suppose that for standard WinForms, TreeView, ListView, ComboBox all have the same set of records (i.e., TreeView has only root nodes, and ListView has one column in the details view).

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);
    }
}   

background: pre C # 3.0

It seems that in the previous days of C # 3.0, I always used a boolean flag to prevent recursion when updating multiple controls. For example, to synchronize the TreeView and ListView, I usually had this code: provided that each item in the ListView was synchronized with the root level of the TreeView node using a common index:

    // 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). , , .

+3
2

, . - , . Rein "Latch" ,

void TabControl_TabSelected(object sender, TabEventArgs args)
{
    _latch.RunLatchedOperation(
        delegate
        {
            ContentTab tab = (ContentTab)TabControl.SelectedTab;
            activatePresenter(tab.Presenter, tab);                       
        });
}
+1

. , SO . , SO-Meta , , . "".

" " , : " ", " .

: ; ; "" " ": "".

// VS Studio 2010 RC 1, tested under Framework 4.0, 3.5

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace SynchronizationTest_3
{
    public partial class Form1 : Form
    {
        private readonly Dictionary<Control, Action<int>> ControlToAction = new Dictionary<Control, Action<int>>();

        // new code : keep a reference to the control the end-user clicked
        private Control ClickedControl;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            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; }));

            // new code : screen out redundant calls generated by other controls 
            // being updated

            treeView1.AfterSelect += (obj, evt)
            =>
            {
             if (treeView1 == ClickedControl) SynchronizeSelection(evt.Node.Index);
            };

            listView1.SelectedIndexChanged += (obj, evt)
            =>
            {
              if (listView1.SelectedIndices.Count > 0 && listView1 == ClickedControl)
              {
                  SynchronizeSelection(listView1.SelectedIndices[0]);
              }
            };

            comboBox1.SelectedValueChanged += (obj, evt)
            =>
            {
              if (comboBox1 == ClickedControl) SynchronizeSelection(comboBox1.SelectedIndex);
            };

            // new code here : all three controls share a common MouseDownHandler
            treeView1.MouseDown += SynchronizationMouseDown;

            listView1.MouseDown += SynchronizationMouseDown;

            comboBox1.MouseDown += SynchronizationMouseDown;

            // trigger the first synchronization
            ClickedControl = treeView1;
            SynchronizeSelection(0);
        }

        // get a reference to the control the end-user moused down on
        private void SynchronizationMouseDown(object sender, MouseEventArgs e)
        {
            ClickedControl = sender as Control;
        }

        // revised code using state of ClickedControl as a filter

        private void SynchronizeSelection(int i)
        {
            // we're done if the reference to the clicked control is null
            if (ClickedControl == null) return;

            foreach (Control theControl in ControlToAction.Keys)
            {
                if (theControl == ClickedControl) continue;

                // for debugging only
                Console.WriteLine(theControl.Name + " synchronized");

                ControlToAction[theControl](i);
            }

            // set the clicked control to null
            ClickedControl = null;
        }
    }
}

"" :

  • WinForms: , ListView Control Selected ### Events , Click: ComboBox TreeView Click SelectedValueChanged AfterSelect Events : , , MouseDown .

  • " ", , "" "- " : .

0

Source: https://habr.com/ru/post/1737486/


All Articles