新しい状態でCheckedItemsを使用できるように、アイテムがチェックされた後にイベントが必要なCheckedListBoxがあります。
ItemCheckedは、CheckedItemsが更新される前に起動されるため、そのままでは機能しません。
CheckedItemsが更新されたときに通知を受け取るために、どのようなメソッドまたはイベントを使用できますか?
新しい状態でCheckedItemsを使用できるように、アイテムがチェックされた後にイベントが必要なCheckedListBoxがあります。
ItemCheckedは、CheckedItemsが更新される前に起動されるため、そのままでは機能しません。
CheckedItemsが更新されたときに通知を受け取るために、どのようなメソッドまたはイベントを使用できますか?
ItemCheck
クリックされているアイテムの新しい状態も確認すれば、イベントを利用できます。これは、イベントargsで。として利用できます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();
}
派生し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);
}
理想的ではありませんが、イベントに渡される引数を使用してCheckedItemsを計算できますItemCheck
。MSDNでこの例を見ると、新しく変更されたアイテムがチェックされているかどうかがわかります。これにより、アイテムを操作するのに適した位置になります。
アイテムがチェックされた後に発生する新しいイベントを作成することもできます。これにより、必要に応じて必要なものを正確に提供できます。
いくつかのテストの後、イベント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();
}
私はこれを試しましたが、うまくいきました:
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);
}
}
リストのインデックスによって決定します。
チェックリストボックスではなく、チェックボックスリストを意味しますか?その場合、関連するイベントはSelectedIndexChangedになります。
例:VBのハンドラー定義ヘッド:
Protected Sub cblStores_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cblStores.SelectedIndexChanged
コントロールの感覚を気にせず、チェックボックスの変更が実際にすでに行われたかのようにイベントを処理することにしました。CheckedIndices
リストを取得し、オブジェクト内の情報を使用しItemCheckEventArgs
て新しい状態に調整するだけです。
Items
次に、そのリストをループして、コントロールのプロパティから指定されたアイテムを取得するだけで、リストが作成されますCheckedItems
。
private void CheckedList_ItemCheck(Object sender, ItemCheckEventArgs e)
{
CheckedListBox checkedList = sender as CheckedListBox;
if (checkedList == null)
return;
// Somehow this still returns the state before the check, so update it manually.
List<Int32> checkedIndices = checkedList.CheckedIndices.Cast<Int32>().ToList();
if (e.NewValue == CheckState.Unchecked)
checkedIndices.Remove(e.Index);
else if (e.NewValue == CheckState.Checked)
checkedIndices.Add(e.Index);
checkedIndices.Sort()
Int32 checkedItemCount = checkedIndices.Length;
Object[] checkedItems = new Object[checkedItemCount]
for (Int32 i = 0; i < checkedItemCount; i++)
checkedItems[i] = checkedList.Items[checkedIndices[i]];
this.UpdateAfterCheckChange(checkedItems);
}
結果は、変更後にのみイベントがトリガーされるという仮想の望ましいケースと機能的に同じです。
アイテムがチェックされるように、ハンドラーに対するDuncの回答のVB.NETバージョン。BeginInvoke
Private Sub ChkListBox1_ItemCheck(sender As Object, e As ItemCheckEventArgs) Handles ChkListBox1.ItemCheck
Debug.WriteLine($"checked item count {ChkListBox1.CheckedItems.Count}")
Debug.WriteLine($"{ChkListBox1.Items(e.Index)} - {e.Index} - {e.NewValue}")
BeginInvoke(Sub() HandleItemCheck(e))
End Sub
Private Sub HandleItemCheck(e As ItemCheckEventArgs)
Debug.WriteLine($"handle item {ChkListBox1.Items(e.Index)} - {e.Index} - {e.NewValue}")
Debug.WriteLine($"checked item count handle item - {ChkListBox1.CheckedItems.Count}")
End Sub
私のように、選択を1つのインジケーター(ユーザーが選択したアイテム)として使用しようとしていて、ユーザーがティックを変更したい場合は、卑劣な解決策を見つけました。
Form variables
Private IsTicked As Boolean = False
Private ListIndex = -1
ページ上にタイマーを作成します。たとえば、私のものはと呼ばれtmrBan
、私はとCheckBoxList
呼ばれていclbFTI
ます。
次に、のクリックイベントを作成しますCheckBoxList
。
Private Sub clbFTI_Click(sender As Object, e As EventArgs) Handles lbFTI.MouseClick
ListIndex = sender.SelectedIndex
IsTicked = clbFTI.SelectedIndices.Contains(ListIndex)
tmrBan.Interval = 10
tmrBan.Enabled = True
End Sub
次に、タイマーのティックイベントを作成します
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles tmrBan.Tick
clbFTI.SetItemChecked(ListIndex, IsTicked)
End Sub
ティックのちらつきが表示されますが、タイマーの間隔を試して、ケースに合わせてこれを改善してください。
私はこの問題を解決するためにタイマーを使用しています。ItemCheckイベントを介してタイマーを有効にします。タイマーのティックイベントでアクションを実行します。
これは、アイテムがマウスクリックでチェックされているか、スペースバーを押してチェックされているかに関係なく機能します。チェックしたばかりの(またはチェックしていない)アイテムが常に選択されたアイテムであるという事実を利用します。
タイマーの間隔は1まで低くすることができます。ティックイベントが発生するまでに、新しいチェック済みステータスが設定されます。
この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());}