新しい状態でCheckedItemsを使用できるように、アイテムがチェックされた後にイベントが必要なCheckedListBoxがあります。
ItemCheckedはCheckedItemsが更新される前に発生するため、そのままでは機能しません。
CheckedItemsが更新されたときに通知を受けるには、どのようなメソッドまたはイベントを使用できますか?
回答:
ItemCheck
クリックされているアイテムの新しい状態も確認する場合は、イベントを使用できます。これは、イベント引数で使用できますe.NewValue
。NewValue
がチェックされている場合、現在のアイテムと適切なコレクションをロジックに含めます。
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
List<string> checkedItems = new List<string>();
foreach (var item in checkedListBox1.CheckedItems)
checkedItems.Add(item.ToString());
if (e.NewValue == CheckState.Checked)
checkedItems.Add(checkedListBox1.Items[e.Index].ToString());
else
checkedItems.Remove(checkedListBox1.Items[e.Index].ToString());
foreach (string item in checkedItems)
{
...
}
}
別の例として、この項目が(非)チェックされた後にコレクションが空になるかどうかを判別するには、次のようにします。
private void ListProjects_ItemCheck(object sender, ItemCheckEventArgs args)
{
if (ListProjects.CheckedItems.Count == 1 && args.NewValue == CheckState.Unchecked)
// The collection is about to be emptied: there's just one item checked, and it's being unchecked at this moment
...
else
// The collection will not be empty once this click is handled
...
}
これには、関連するStackOverflowの投稿がたくさんあります... Branimirのソリューションと同様に、さらに2つの簡単なものがあります。
ItemCheckの実行の遅延(ここにもあります):
void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
this.BeginInvoke((MethodInvoker) (
() => Console.WriteLine(checkedListBox1.SelectedItems.Count)));
}
void checkedListBox1_MouseUp(object sender, MouseEventArgs e)
{
Console.WriteLine(checkedListBox1.SelectedItems.Count);
}
2番目のオプションは誤検知(つまり、頻繁に発火する)になるため、最初のオプションを選択します。
私はこれを試してみましたがうまくいきました:
private void clbOrg_ItemCheck(object sender, ItemCheckEventArgs e)
{
CheckedListBox clb = (CheckedListBox)sender;
// Switch off event handler
clb.ItemCheck -= clbOrg_ItemCheck;
clb.SetItemCheckState(e.Index, e.NewValue);
// Switch on event handler
clb.ItemCheck += clbOrg_ItemCheck;
// Now you can go further
CallExternalRoutine();
}
ItemChecked
イベントを実装するのを忘れたために動作するばかげたハッキングであり、誰もそれが存在しないことについて対処したことがありません。
ItemChecking
、ItemChecked
、あなたは後者を使用することができます。しかし、1つだけが実装されている場合(ItemCheck
)は、物事を正しく実行しています。つまり、パラメーターとして指定された新しい値とインデックスで値がチェックされる前にイベントを発生させます。「変更後」のイベントを希望する人は誰でも、簡単に上記を使用できます。Microsoftに提案する場合は ItemChecked
、既存のイベントを変更せずに、新しいイベントを提案します。diimdeepの回答を
から派生しCheckedListBox
て実装する
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.CheckedListBox.ItemCheck"/> event.
/// </summary>
/// <param name="ice">An <see cref="T:System.Windows.Forms.ItemCheckEventArgs"/> that contains the event data.
/// </param>
protected override void OnItemCheck(ItemCheckEventArgs e)
{
base.OnItemCheck(e);
EventHandler handler = AfterItemCheck;
if (handler != null)
{
Delegate[] invocationList = AfterItemCheck.GetInvocationList();
foreach (var receiver in invocationList)
{
AfterItemCheck -= (EventHandler) receiver;
}
SetItemCheckState(e.Index, e.NewValue);
foreach (var receiver in invocationList)
{
AfterItemCheck += (EventHandler) receiver;
}
}
OnAfterItemCheck(EventArgs.Empty);
}
public event EventHandler AfterItemCheck;
public void OnAfterItemCheck(EventArgs e)
{
EventHandler handler = AfterItemCheck;
if (handler != null)
handler(this, e);
}
いくつかのテストの後、SelectedIndexChangedイベントがItemCheckイベントの後にトリガーされることがわかりました。プロパティCheckOnClick Trueを維持する
最高のコーディング
これは機能しますが、どれほどエレガントかはわかりません!
Private Sub chkFilters_Changed(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles chkFilters.ItemCheck
Static Updating As Boolean
If Updating Then Exit Sub
Updating = True
Dim cmbBox As CheckedListBox = sender
Dim Item As ItemCheckEventArgs = e
If Item.NewValue = CheckState.Checked Then
cmbBox.SetItemChecked(Item.Index, True)
Else
cmbBox.SetItemChecked(Item.Index, False)
End If
'Do something with the updated checked box
Call LoadListData(Me, False)
Updating = False
End Sub
これが当てはまるかどうかはわかりませんが、チェックリストボックスを使用して結果をフィルタリングしたいと思いました。そのため、ユーザーがアイテムをオンまたはオフにしたときに、リストにアイテムを表示/非表示にしたいと考えました。
この投稿につながったいくつかの問題がありました。特別なことをせずに、私がどのようにそれをしたかを共有したかっただけです。
注:CheckOnClick = trueですが、それでもおそらく動作します
私が使用するイベントは「SelectedIndexChanged」です
私が使用する列挙は " .CheckedItems」です
これは私たちが期待するかもしれないと思う結果を与えます。単純化すると、次のようになります....
private void clb1_SelectedIndexChanged(object sender, EventArgs e)
{
// This just spits out what is selected for testing
foreach (string strChoice in clb1.CheckedItems)
{
listBox1.Items.Add(strChoice);
}
//Something more like what I'm actually doing
foreach (object myRecord in myRecords)
{
if (clb1.CheckItems.Contains(myRecord["fieldname"])
{
//Display this record
}
}
}
からの引数を保持したいItemCheck
が、モデルが変更された後に通知を受けたいとすると、次のようになります。
CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));
どこにありCheckedItemsChanged
ますか:
private void CheckedItemsChanged(object sender, EventArgs e)
{
DoYourThing();
}
タイマーを使用してこの問題を解決しています。ItemCheckイベントを介してタイマーを有効にします。Timer's Tickイベントでアクションを実行します。
これは、マウスクリックで、またはスペースバーを押して項目をチェックするかどうかに関係なく機能します。チェックされた(またはチェックされていない)アイテムは常にSelected Itemであるという事実を利用します。
タイマーの間隔は1まで低くできます。Tickイベントが発生するまでに、新しいCheckedステータスが設定されます。
このVB.NETコードは、概念を示しています。あなたが採用できる多くのバリエーションがあります。タイマーの間隔を長くして、ユーザーがアクションを実行する前にいくつかのアイテムのチェックステータスを変更できるようにすることができます。次に、Tickイベントで、リスト内のすべての項目の順次パスを作成するか、そのCheckedItemsコレクションを使用して適切なアクションを実行します。
そのため、最初にItemCheckイベントでタイマーを無効にします。無効にしてから有効にすると、インターバル期間が再開されます。
Private Sub ckl_ItemCheck(ByVal sender As Object, _
ByVal e As System.Windows.Forms.ItemCheckEventArgs) _
Handles ckl.ItemCheck
tmr.Enabled = False
tmr.Enabled = True
End Sub
Private Sub tmr_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) _
Handles tmr.Tick
tmr.Enabled = False
Debug.Write(ckl.SelectedIndex)
Debug.Write(": ")
Debug.WriteLine(ckl.GetItemChecked(ckl.SelectedIndex).ToString)
End Sub
通常の動作では、1つのアイテムをチェックすると、イベントハンドラーが発生する前にアイテムのチェック状態が変化します。ただし、CheckListBoxの動作は異なります。アイテムのチェック状態が変化する前にイベントハンドラーが発生するため、ジョブの修正が困難になります。
私の意見では、この問題を解決するには、イベントハンドラーを遅延させる必要があります。
private void _clb_ItemCheck(object sender, ItemCheckEventArgs e) {
// Defer event handler execution
Task.Factory.StartNew(() => {
Thread.Sleep(1000);
// Do your job at here
})
.ContinueWith(t => {
// Then update GUI at here
},TaskScheduler.FromCurrentSynchronizationContext());}
私はこれを試してみましたがうまくいきました:
private List<bool> m_list = new List<bool>();
private void Initialize()
{
for(int i=0; i < checkedListBox1.Items.Count; i++)
{
m_list.Add(false);
}
}
private void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.NewValue == CheckState.Checked)
{
m_list[e.Index] = true;
checkedListBox1.SetItemChecked(e.Index, true);
}
else
{
m_list[e.Index] = false;
checkedListBox1.SetItemChecked(e.Index, false);
}
}
リストのインデックスによって決定します。
if not item = checkedListBox1.Items[e.Index].ToString()