在以下情况下从组合框中在我的应用程序中触发SelectedIndexChanged
事件:
SelectedItem
来反映这一点
组合框现在正在显示
不同对象的属性。我对案例1的SelectedIndexChanged
事件感兴趣,以便我可以更新当前对象的属性。但是在第2种情况下,我不希望触发事件,因为对象的属性没有改变。
一个例子可能有所帮助。让我们考虑一下,我有一个包含人员列表的列表框,我有一个组合框,表示列表中当前所选人员的国籍。如果当前在列表中选择了Fred,则可能发生情况1,并且我使用组合框将他的国籍从英语更改为威尔士语。如果我在列表中选择苏格兰人Bob,则可能发生案例2。在这里,我的列表更新事件处理程序代码看到Bob现在被选中,并更新组合框,以便苏格兰语现在是所选项目。这导致组合框的SelectedIndexChanged
事件被触发以将Bob的国籍设置为苏格兰语,即使它已经是苏格兰语。
如何在不导致SelectedItem
事件触发的情况下更新我的组合框的SelectedIndexChanged
属性?一种方法是取消注册事件处理程序,设置{{1}然后重新注册事件处理程序,但这似乎很乏味且容易出错。必须有更好的方法。
答案 0 :(得分:7)
我创建了一个名为SuspendLatch
的课程。欢迎提供更好名称的优惠,但它可以满足您的需求,您可以像这样使用它:
void Method()
{
using (suspendLatch.GetToken())
{
// Update selected index etc
}
}
void listbox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (suspendLatch.HasOutstandingTokens)
{
return;
}
// Do some work
}
它不漂亮,但确实有效,与注销事件或布尔标志不同,它支持嵌套操作,有点像TransactionScope。你继续从锁存器获取令牌,只有当最后一个令牌被处理时HasOutstandingTokens
返回false时才会这样做。很好,很安全。虽然不是线程安全的......
这是SuspendLatch的代码:
public class SuspendLatch
{
private IDictionary<Guid, SuspendLatchToken> tokens = new Dictionary<Guid, SuspendLatchToken>();
public SuspendLatchToken GetToken()
{
SuspendLatchToken token = new SuspendLatchToken(this);
tokens.Add(token.Key, token);
return token;
}
public bool HasOutstandingTokens
{
get { return tokens.Count > 0; }
}
public void CancelToken(SuspendLatchToken token)
{
tokens.Remove(token.Key);
}
public class SuspendLatchToken : IDisposable
{
private bool disposed = false;
private Guid key = Guid.NewGuid();
private SuspendLatch parent;
internal SuspendLatchToken(SuspendLatch parent)
{
this.parent = parent;
}
public Guid Key
{
get { return this.key; }
}
public override bool Equals(object obj)
{
SuspendLatchToken other = obj as SuspendLatchToken;
if (other != null)
{
return Key.Equals(other.Key);
}
else
{
return false;
}
}
public override int GetHashCode()
{
return Key.GetHashCode();
}
public override string ToString()
{
return Key.ToString();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Dispose managed resources.
parent.CancelToken(this);
}
// There are no unmanaged resources to release, but
// if we add them, they need to be released here.
}
disposed = true;
// If it is available, make the call to the
// base class's Dispose(Boolean) method
//base.Dispose(disposing);
}
}
}
答案 1 :(得分:4)
我认为最好的方法是使用标志变量:
bool updatingCheckbox = false;
void updateCheckBox()
{
updatingCheckBox = true;
checkbox.Checked = true;
updatingCheckBox = false;
}
void checkbox_CheckedChanged( object sender, EventArgs e )
{
if (!updatingCheckBox)
PerformActions()
}
[编辑:只发布代码并不是很清楚]
在这种情况下,当通过updateCheckBox()更改复选框时,事件处理程序不会执行其正常操作。
答案 2 :(得分:3)
我总是使用布尔标志变量来防止不需要的事件处理程序。 TaskVision示例应用程序教我如何执行此操作。
您所有活动的事件处理程序代码如下所示:
private bool lockEvents;
protected void MyEventHandler(object sender, EventArgs e)
{
if (this.lockEvents)
{
return;
}
this.lockEvents = true;
//Handle your event...
this.lockEvents = false;
}
答案 3 :(得分:1)
我让事件开火了。但是,我在更改索引之前设置了一个标志,然后将其翻转。在事件处理程序中,我检查是否设置了标志并退出处理程序(如果是)。
答案 4 :(得分:1)
我认为你的重点应放在对象而不是发生的事件上。
比如说你有事件
void combobox_Changed( object sender, EventArgs e )
{
PerformActions()
}
和PerformActions做了一些事情
void PerformActions()
{
(listBox.SelectedItem as IPerson).Nationality =
(comboBox.SelectedItem as INationality)
}
然后在人物内部你会看到一些影响
的东西class Person: IPerson
{
INationality Nationality
{
get { return m_nationality; }
set
{
if (m_nationality <> value)
{
m_nationality = value;
this.IsDirty = true;
}
}
}
}
这里的要点是,让对象跟踪自身发生的事情,而不是UI。这还可以让您跟踪对象上的脏标记跟踪,这对以后的持久性非常有用。
这也可以保持您的用户界面清洁,并防止它出现很可能容易出错的奇怪事件注册码。
答案 5 :(得分:0)
我终于找到了一个解决方案,以避免被激活的事件发生太多次。
我使用了一个计数器,我只在不需要的时候挂钩/取消挂钩我想要屏蔽的事件,以及再次需要它时。
下面的示例显示了如何隐藏数据网格的CellValueChanged事件。
EventMask valueChangedEventMask;
// In the class constructor
valueChangedEventMask = new EventMask(
() => { dgv.CellValueChanged += new DataGridViewCellEventHandler(dgv_CellValueChanged); },
() => { dgv.CellValueChanged -= new DataGridViewCellEventHandler(dgv_CellValueChanged); }
);
// Use push to hide the event and pop to make it available again. The operation can be nested or be used in the event itself.
void changeCellOperation()
{
valueChangedEventMask.Push();
...
cell.Value = myNewCellValue
...
valueChangedEventMask.Pop();
}
// The class
public class EventMask
{
Action hook;
Action unHook;
int count = 0;
public EventMask(Action hook, Action unHook)
{
this.hook = hook;
this.unHook = unHook;
}
public void Push()
{
count++;
if (count == 1)
unHook();
}
public void Pop()
{
count--;
if (count == 0)
hook();
}
}