C# 确定谁触发了事件

C# 确定谁触发了事件,c#,events,C#,Events,背景: 在我的winforms表单中,我有一个选中的ListView和一个名为checkBoxAll的“主”复选框。 船长的行为如下: 如果选中或取消选中主控形状,则所有ListViewItems都必须相应更改 如果用户取消选中ListViewItem,则主控形状必须相应更改 如果用户检查了一个ListViewItem,并且还检查了所有其他ListViewItems,则主控形状必须相应地更改 我编写了以下代码来模拟这种行为: private bool byProgram = false; /

背景:

在我的winforms表单中,我有一个选中的ListView和一个名为checkBoxAll的“主”复选框。 船长的行为如下:

  • 如果选中或取消选中主控形状,则所有ListViewItems都必须相应更改

  • 如果用户取消选中ListViewItem,则主控形状必须相应更改

  • 如果用户检查了一个ListViewItem,并且还检查了所有其他ListViewItems,则主控形状必须相应地更改

我编写了以下代码来模拟这种行为:

private bool byProgram = false; //Flag to determine the caller of the code. True for program, false for user.

private void checkBoxAll_CheckedChanged(object sender, EventArgs e)
{
    //Check if the user raised this event.
     if (!byProgram)
     {
         //Event was raised by user!

         //If checkBoxAll is checked, all listviewitems must be checked too and vice versa.

         //Check if there are any items to (un)check.
         if (myListView.Items.Count > 0)
         {
             byProgram = true; //Raise flag.

             //(Un)check every item.
             foreach (ListViewItem lvi in myListView.Items)
             {
                 lvi.Checked = checkBoxAll.Checked;
             }

             byProgram = false; //Lower flag.
         }
     }
}

private void myListView_ItemChecked(object sender, ItemCheckedEventArgs e)
{
    //Get the appropiate ListView that raised this event
    var listView = sender as ListView;

    //Check if the user raised this event.
    if (!byProgram)
    {
        //Event was raised by user!

        //If all items are checked, set checkBoxAll checked, else: uncheck him!

        bool allChecked = true; //This boolean will be used to set the value of checkBoxAll

        //This event was raised by an ListViewItem so we don't have to check if any exist. 
        //Check all items untill one is not checked.
        foreach (ListViewItem lvi in listView.Items)
        {
            allChecked = lvi.Checked;
            if (!allChecked) break;
        }

        byProgram = true; //Raise flag.

        //Set the checkBoxAll according to the value determined for allChecked.
        checkBoxAll.Checked = allChecked;

        byProgram = false; //Lower flag.
    }
}
在本例中,我使用一个标志(byProgram)来确保事件是否由用户引起,从而防止无限循环(一个事件可以触发另一个事件,它可以再次触发第一个事件等)。这是一个骇人的解决方案。 我四处搜索,但找不到MSDN文档化的方法来确定是否由于用户的原因直接触发了用户控制事件。这让我觉得很奇怪(又一次,IMHO)

我知道FormClosingEventArgs有一个字段,我们可以使用它来确定用户是否正在关闭表单。但据我所知,这是唯一一个提供这种功能的EventArg

总而言之:

是否有方法(我的示例除外)确定事件是否由用户直接触发?

请注意:我不是指活动的发送者!我是否编写someCheckBox并不重要。Checked=true;或者手动设置someCheckBox,事件的发送者将始终是someCheckBox。我想知道是否可以确定是通过用户(单击)还是通过程序(.Checked=true)


aa另外:30%的时间用来写这个问题,是为了正确地表达问题和标题。仍然不确定是否100%清除,因此如果您认为可以做得更好,请进行编辑:)

您可以使用单击的事件将更改级联到相关控件,而不是使用更改的事件。这将是对用户单击的响应,而不是通过编程更改的值。

不,没有实用的方法来确定更改是来自GUI还是由程序完成的(事实上,您可以分析调用堆栈-但不建议这样做,因为它非常慢且容易出错)

顺便说一句,还有一件事你可以做,而不是设置
byProgram
。您可以分别在更改控件之前或之后删除和添加事件处理程序:

checkBoxAll.CheckedChanged -= checkBoxAll_CheckedChanged;
// do something
checkBoxAll.CheckedChanged += checkBoxAll_CheckedChanged;

这是我经常遇到的事情,我倾向于尝试不将其划分为用户交互和程序交互-我使用更通用的代码,即UI正在更新,不需要处理任何事件。我通常通过
BeginUpdate
/
EndUpdate
方法将其打包,例如

private int updates = 0;

public bool Updating { get { return updates > 0; } }

public void BeginUpdate()
{
    updates++;
}

public void EndUpdate()
{
    updates--;
}

public void IndividualCheckBoxChanged(...)
{
    if (!Updating)
    {
        // run code
    }
}

public void CheckAllChanged(...)
{
    BeginUpdate();
    try
    {
        // run code
    }
    finally
    {
        EndUpdate();
    }
}

这只是一个猜测,但事件中确实应该有一些东西。您是否在运行时检查过它们(调试)?On click事件设置一个标志,表明此调用是通过单击而不是通过代码进行的?@Romiox,检查我的问题,它说我没有这种功能(据我所知,只有FormClosing有这种功能)。@MEYWD,我考虑过,但这仍然是一个标志,现在还有另一个混合事件。这似乎不是理想的解决方案。这是我经常做的。+1用于提供不涉及额外事件或标志的解决方案!我认为以这种方式分离/重新连接事件处理程序不是一个好主意。除了轻微的开销,如果您有20个事件处理程序呢?我觉得阻塞代码更好,因为您是对“状态”做出反应,而不是对事件是否已连接做出反应,因为您总是有机会忘记重新连接或分离。对于提供不涉及额外标志的解决方案,请使用+1。但是你知道这房子实际上变了吗?这将涉及到存储旧值,对吗?如果我在“全选”框中获得单击事件,那么我可以设置所有复选框的值。如果我在其中一个选项项上得到一个单击事件,我只会根据选项的状态更新“全选”复选框。你说得对!我不在乎值是否改变。我可以看看它目前的价值。一个警告是时机。我相信单击事件可能在项目更改之前发生,因此您必须对此进行解释。+1,仍然是一个标志,但我将在下次需要升起标志时使用此结构。@Jordy这不是另一个标志,它是对您现有标志的改进,添加了更多结构和意义。
BeginUpdate
/
EndUpdate
模式是解决此类问题的一种常用方法,适用于各种不同的平台(.NET包括在内)。其思想是,无论事件是否附加,您都会对“状态”做出反应,这使得代码更具可读性。。。我可能不应该把它描述为“仍然是一面旗帜”。但由于某些原因,我不能在评论太旧或其他什么的时候编辑它:\n别担心,我明白你的意思了。然而,我要强调的是,不要把它当作“哦,不,另一个标志”——从应用程序架构的角度来考虑,即我的应用程序必须处理UI更新可能导致触发事件的场景,在某些情况下我想忽略这些事件,我如何解释这一点?基本上,您在这里所做的是采用一种通用模式&将其应用到您自己的代码中,从而保持代码的一致性。