none
(C#)WPFでListBox.Items.Clear();のコードを入れると例外が起きる RRS feed

  • 質問

  • WPF(C#)で、ListBox.Items.Clear();のコードを記述して、実行すると例外が発生していしまいます。

    <例外>

    System.NullReferenceException: 'Object reference not set to an instance of an object.'

    調べてみると、

    https://social.msdn.microsoft.com/Forums/security/ja-JP/5f9b5bf8-115f-4f6e-ac6d-5ec873e02761/wpf-listboxitemsclear123912098612427203632280612398352992777027861?forum=wpfja

    ListBox1.ClearValue(ListBox.ItemsSourceProperty);

    で、できるとのことだったのでやってみましたが駄目でした...

    どなたかわかる方ご教授くださると幸いです。

    2018年6月9日 8:59

回答

  • NullReferenceException のスタックトレースは見覚えのない、WPF のクラス群という感じになっていますか?
    そうであれば、ListBox1 に対するスタイルとか、項目テンプレートとか、バインディングとか、その辺の情報も開示していただかないと助言は難しいかもしれません。

    ところで、参照されたスレッドには Tak1wa さんも助言を書いていますが、そちらのコードも試された後ですか?
    C# に翻訳すると以下のようなものです。

     var list = listBox1.ItemsSource as IList;
     if (list != null) list.Clear();

    • 回答としてマーク WPF開発 2018年6月9日 13:45
    2018年6月9日 10:45
  • private void listBox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
         player.URL = listBox2.SelectedItem.ToString();
    }

    listBox1をクリアするとlistBox1_SelectionChangedが発生し、listBox1hは何も選択できないのでSelectedIndexは-1になっており、そのときlistBox2.SelectedIndexが-1以外になっていたらlistBox2.SelectedIndexは-1に変更されるためlistBox2_SelectionChangedイベントが連鎖して発生します。
    listBox2で何か選択されている状態でlistBox2をクリアするとlistBox2.SelectedIndexは-1になり、listBox2_SelectionChangedが発生します。

    これらにより呼ばれることになるlistBox2_SelectionChangedで、listBox2のSelectedIndexが-1であればSelectedItemはnullになっています。
    この状態で、SelectedItem.ToString()を実行しようとすれば当然NullReferenceExceptionが発生します。

    SelectedItemがnullで無い事を確認してからToString()を実行するように変更しましょう。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 AzuleanMVP 2018年6月9日 21:52
    • 回答としてマーク WPF開発 2018年6月9日 23:04
    2018年6月9日 16:21
  • SelectionIndexChangedはありませんが、一般的にはSelectionChangedで十分であるように思います。SelectionChangedのイベントハンドラで、当該コントロールのSelectedIndexを見れば良いからです。
    もし、これでも不便という場合には、どのようなことがやりたくて具体的にどの辺りが不便かを書いていただくと、具体的なアドバイスが得やすくなると思います。
    なお、その場合には新しいスレッドを起こすようにして下さい。
    お手数をおかけしますが、1つのスレッドには1つの質問でお願いいたします。
    ご協力のほど、よろしくお願いいたします。

    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    • 回答としてマーク WPF開発 2018年6月11日 8:20
    2018年6月11日 1:27
    モデレータ

すべての返信

  • NullReferenceException のスタックトレースは見覚えのない、WPF のクラス群という感じになっていますか?
    そうであれば、ListBox1 に対するスタイルとか、項目テンプレートとか、バインディングとか、その辺の情報も開示していただかないと助言は難しいかもしれません。

    ところで、参照されたスレッドには Tak1wa さんも助言を書いていますが、そちらのコードも試された後ですか?
    C# に翻訳すると以下のようなものです。

     var list = listBox1.ItemsSource as IList;
     if (list != null) list.Clear();

    • 回答としてマーク WPF開発 2018年6月9日 13:45
    2018年6月9日 10:45
  • WPFの特徴であるBindingを使用されているのでしょうか? もし、そうであれば、Bindingの元となっているソースにおける要素を全てクリアし、空になった状態をバインディング先のListBoxに伝え、ListBoxに空の表示をさせるというのが一般的な方法となります。
    ListBoxに限らず、バインドしている場合は、あくまでバインド元であるソースの要素を変更し、その結果をバインド先(今回の場合はListBox)に伝え、バインド先でその結果を表示するという考えでプログラムを組んでみて下さい。

    全ての要素を削除するのではなく、例えば1つの要素を削除する場合も同じように行います。どの要素が削除されるかをバインド元に伝え、バインド元でそのデータソースからその要素を削除し、その結果をバインド先に伝えます。バインド先は再表示を行い、その要素が削除されて表示されます。この際、バインド先では画面が先頭から再表示されてしまうのではなく、スクロール位置はそのままで、その要素のみが消えるだけですので安心して下さい。この辺りの制御は自動的に行われます。


    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    2018年6月9日 12:44
    モデレータ
  • ご回答ありがとうございます。

    試しましたが駄目でした...

    ↓下にわかりにくいかもしれませんが再現方法(?)を書いておきます。(今作っているのは音楽プレイヤーです。)

    1.音楽ファイルを選択して再生。

    private void SelectMusic_Click(object sender, RoutedEventArgs e)
            {
                OpenFileDialog openFileDialog1 = new OpenFileDialog();
                openFileDialog1.Filter="Audio Files|*.mp3;*.wav;*.flac;*.m4a;*.wma;*.aac;*.aif;*.ogg;*.asf;*.3g2;*.3gp";
                openFileDialog1.Title = "Select Song";

                if(openFileDialog1.ShowDialog() == true)
                {
                    textBox1.Clear();

                    listBox1.Items.Clear();

                    listBox2.Items.Clear();

                    textBox1.Text = openFileDialog1.FileName;
                    player.URL = textBox1.Text;
                    listBox1.Items.Add(Path.GetFileNameWithoutExtension(textBox1.Text));
                    listBox2.Items.Add(openFileDialog1.FileName);
                }
            }

    2.listBox1とlistBox2を紐づけ(?)してlistBox1をクリック(listBox2はHiddenにしています)

    private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                listBox2.SelectedIndex = listBox1.SelectedIndex;
            }

            private void listBox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                player.URL = listBox2.SelectedItem.ToString();
            }

    3.そしてもう1度1.を繰り返すと

    private void SelectMusic_Click(object sender, RoutedEventArgs e)
            {
                OpenFileDialog openFileDialog1 = new OpenFileDialog();
                openFileDialog1.Filter="Audio Files|*.mp3;*.wav;*.flac;*.m4a;*.wma;*.aac;*.aif;*.ogg;*.asf;*.3g2;*.3gp";
                openFileDialog1.Title = "Select Song";

                if(openFileDialog1.ShowDialog() == true)
                {
                    textBox1.Clear();

                    listBox1.Items.Clear();  ←ここで例外が発生する

                    listBox2.Items.Clear();  ←ここで例外が発生する

                    textBox1.Text = openFileDialog1.FileName;
                    player.URL = textBox1.Text;
                    listBox1.Items.Add(Path.GetFileNameWithoutExtension(textBox1.Text));
                    listBox2.Items.Add(openFileDialog1.FileName);
                }
            }

    2018年6月9日 13:45
  • private void listBox2_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
         player.URL = listBox2.SelectedItem.ToString();
    }

    listBox1をクリアするとlistBox1_SelectionChangedが発生し、listBox1hは何も選択できないのでSelectedIndexは-1になっており、そのときlistBox2.SelectedIndexが-1以外になっていたらlistBox2.SelectedIndexは-1に変更されるためlistBox2_SelectionChangedイベントが連鎖して発生します。
    listBox2で何か選択されている状態でlistBox2をクリアするとlistBox2.SelectedIndexは-1になり、listBox2_SelectionChangedが発生します。

    これらにより呼ばれることになるlistBox2_SelectionChangedで、listBox2のSelectedIndexが-1であればSelectedItemはnullになっています。
    この状態で、SelectedItem.ToString()を実行しようとすれば当然NullReferenceExceptionが発生します。

    SelectedItemがnullで無い事を確認してからToString()を実行するように変更しましょう。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 回答の候補に設定 AzuleanMVP 2018年6月9日 21:52
    • 回答としてマーク WPF開発 2018年6月9日 23:04
    2018年6月9日 16:21
  • ありがとうございます。

    早速変更してみました。

    しかし、多分これとは関係ないと思いますが例外が出てしまいました。

    <例外>

    System.Windows.Markup.XamlParseException: 'Provide value on 'System.Windows.Baml2006.TypeConverterMarkupExtension' threw an exception.'

    内部例外
    IOException: Cannot locate resource 'image/%e3%83%a1%e3%83%8b%e3%83%a5%e3%83%bc%e3%81%ae%e7%84%a1%e6%96%99%e3%82%a2%e3%82%a4%e3%82%b3%e3%83%b39.png'.

    画像のresourceが見つからないというエラーだと思うのですが、今コードに入れている画像のパスを変更してみても変わらず出続けます。

    画像のコピーのとこは新しい場合はコピーにしています。ビルドアクションはなしです。

    ただ、この例外、よくわからないとこで消えたりします…

    関係ないであろうコードを変更したり、ビルドしたり、リビルドしたり、画像のコピーのところを変更したりするとたまに消えます…

    2018年6月10日 0:18
  • 上の例外のやつは解決しました。

    あと、同じ質問をしてしまいましたが、WPFでWindowsFormでいうSelectionIndexChangedのようなものを実行することはできるでしょうか?

    先ほどのコードにもあるように、SelectionChangedだといろいろ使いにくいので...

    2018年6月10日 4:58
  • SelectionIndexChangedはありませんが、一般的にはSelectionChangedで十分であるように思います。SelectionChangedのイベントハンドラで、当該コントロールのSelectedIndexを見れば良いからです。
    もし、これでも不便という場合には、どのようなことがやりたくて具体的にどの辺りが不便かを書いていただくと、具体的なアドバイスが得やすくなると思います。
    なお、その場合には新しいスレッドを起こすようにして下さい。
    お手数をおかけしますが、1つのスレッドには1つの質問でお願いいたします。
    ご協力のほど、よろしくお願いいたします。

    ★良い回答には質問者は回答済みマークを、閲覧者は投票を!

    • 回答としてマーク WPF開発 2018年6月11日 8:20
    2018年6月11日 1:27
    モデレータ
  • そうなのですね。例外が発生する件はnullかどうか確認するコードを入れることによって、解決しました! すみません… 以後このことに気をつけて質問します。
    2018年6月11日 8:54