アイテムがチェックされた後にトリガーされるCheckedListBoxイベントはどれですか?


96

新しい状態でCheckedItemsを使用できるよう、アイテムがチェックされた後にイベントが必要なCheckedListBoxがあります。

ItemCheckedはCheckedItemsが更新される前に発生するため、そのままでは機能しません。

CheckedItemsが更新されたときに通知を受けるには、どのようなメソッドまたはイベントを使用できますか?

回答:


88

ItemCheckクリックされているアイテムの新しい状態も確認する場合は、イベントを使用できます。これは、イベント引数で使用できますe.NewValueNewValueがチェックされている場合、現在のアイテムと適切なコレクションをロジックに含めます。

    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
        ...
}

3
それぞれの第一に、私たちは...条件場合は、1つを追加する必要があるかもしれませんif not item = checkedListBox1.Items[e.Index].ToString()
レーニンラジRajasekaran

8
問題は、チェックが処理される前にItemCheckイベントが発生することです。あなたの解決策はあなた自身のリストを維持することを含み、本質的に標準コードを複製します。Duncの最初の提案(ItemCheckでの遅延実行)は、追加の処理を必要としないため、phqの質問に対する最も明確な答えです。
Berend Engelbrecht 2014

34

これには、関連するStackOverflowの投稿がたくさんあります... Branimirのソリューションと同様に、さらに2つの簡単なものがあります。

ItemCheckの実行の遅延ここにもあります):

    void checkedListBox1_ItemCheck(object sender, ItemCheckEventArgs e)
    {
        this.BeginInvoke((MethodInvoker) (
            () => Console.WriteLine(checkedListBox1.SelectedItems.Count)));
    }

MouseUpイベントの使用

    void checkedListBox1_MouseUp(object sender, MouseEventArgs e)
    {
        Console.WriteLine(checkedListBox1.SelectedItems.Count);
    }

2番目のオプションは誤検知(つまり、頻繁に発火する)になるため、最初のオプションを選択します。


13
2番目の方法も、キーボードでチェックまたはチェック解除されている項目を見逃します。

1
BeginInvokeは、自分のイベントが実際にインターフェイスを呼び出すときに必要だったものであり、どのような種類の制御を処理しているかがわかりませんでした。受け入れられた回答は、イベントハンドラー内でロジックを実行できる場合、またはイベントハンドラーから直接呼び出された場合にのみ機能します。これは私には当てはまりませんでした。この素晴らしくてシンプルな解決策をありがとう。
ジェシー

Thx、BeginInvokeの最初のオプションは私のために動作します。多分ばかげたコメントの人たちかもしれませんが、なぜ2010年に始まったトピックで報告されているこのバグが2018年に解決されないのですか?
グッズ

1
@Goodiesは同意しましたが、Microsoftが動作を変更した場合、多くのコードが壊れる可能性があると思います。ドキュメントは、明示的に述べますThe check state is not updated until after the ItemCheck event occurs。別のイベントまたは任意ではない回避策がIMOに適しています。
2018年

24

私はこれを試してみましたがうまくいきました:

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

8
この!...正解である必要がありますが、これは最も残念です。これは、M $の誰かがItemCheckedイベントを実装するのを忘れたために動作するばかげたハッキン​​グであり、誰もそれが存在しないことについて対処したことがありません。
RLH 2014

定義上はバグではありませんが、実装する必要があると思いますが、+ 1をクリックしてこのバグレポートをサポートすることを同意する場合は、connect.microsoft.com
VisualStudio

@Sebastian –ここで修正を要求しないでください。これを「修正」すると、既存のソリューションが機能しなくなります。2つのイベントがあった場合:ItemCheckingItemChecked、あなたは後者を使用することができます。しかし、1つだけが実装されている場合(ItemCheck)は、物事を正しく実行しています。つまり、パラメーターとして指定された新しい値とインデックスで値がチェックされるにイベントを発生させます。「変更後」のイベントを希望する人は誰でも、簡単に上記を使用できます。Microsoftに提案する場合は ItemChecked、既存のイベントを変更せずに、新しいイベントを提案します。diimdeepの回答を
miroxlav

このように、しかし私がいつも使用する1つのわずかな代替手段は、ある種の「スキップ」フラグを設定して、SetItemCheckStateが同じイベントを再トリガーしないようにすることです。単純なグローバル、または私がやりたいことは、タグを確認することです。たとえば、アクションをIf myCheckListBox.Tag!= nullでラップし、イベントのDelete \ Addの代わりに、タグを何か(空の文字列でも)に設定し、次にnullに戻してオンに戻します。
da_jokker 2017

10

から派生し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);
}

4

理想的ではありませんが、ItemCheckイベントに渡される引数を使用してCheckedItemsを計算できます。MSDNでこのを見ると、新しく変更されたアイテムがチェックされているかチェックされていないかがわかります。これにより、アイテムを操作するのに適した位置に残ります。

アイテムがチェックされた後に発生する新しいイベントを作成することもできます。これにより、必要に応じて、まさにあなたが望むものを得ることができます。


1
この新しいイベントの作成方法について具体的なアイデアはありますか?ItemCheckeイベントの後にCheckedItemsが更新されたことを確認するにはどうすればよいですか?
hultqvist

4

いくつかのテストの後、SelectedIndexChangedイベントがItemCheckイベントの後にトリガーされることがわかりました。プロパティCheckOnClick Trueを維持する

最高のコーディング


そうです、これが最も簡単な方法です。しかし、それは文書化されておらず、予想外の動作であるため、依然としてハックのようなものです。Microsoftの新入生なら誰もが思うかもしれません。まあ、Checkstateだけが変わるときにSelectedIndexChangedを起動するのはなぜでしょう。それを最適化しましょう。そしてBangがあなたのコードを書きます:(
Rolf

また、プログラムでチェック状態を変更してもSelectedIndexChangedは発生しません。
Rolf

1
また、スペースキーを使用してチェック状態を変更しても起動しません。これを使うのは間違っています。
Elmue

2

これは機能しますが、どれほどエレガントかはわかりません!

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

1

これが当てはまるかどうかはわかりませんが、チェックリストボックスを使用して結果をフィルタリングしたいと思いました。そのため、ユーザーがアイテムをオンまたはオフにしたときに、リストにアイテムを表示/非表示にしたいと考えました。

この投稿につながったいくつかの問題がありました。特別なことをせずに、私がどのようにそれをしたかを共有したかっただけです。

注: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
        }
   }

}

SelectedIndexChangedは、ユーザーがスペースキーを使用してチェック状態を変更しても発生しません。
Elmue

コード内のアイテムをチェックまたはチェック解除するためにSetItemCheckedを呼び出すときにSelectedIndexChangedは発生しません。
bkqc

1

からの引数を保持したいItemCheckが、モデルが変更された後に通知を受けたいとすると、次のようになります。

CheckedListBox ctrl = new CheckedListBox();
ctrl.ItemCheck += (s, e) => BeginInvoke((MethodInvoker)(() => CheckedItemsChanged(s, e)));

どこにありCheckedItemsChangedますか:

private void CheckedItemsChanged(object sender, EventArgs e)
{
    DoYourThing();
}

0

タイマーを使用してこの問題を解決しています。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系統的解決のために。オンライン変換ツールの1つを使用して、C#からVBに変換します。
miroxlav

0

通常の動作では、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());}

0

私はこれを試してみましたがうまくいきました:

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

リストのインデックスによって決定します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.