質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.22%

【WPF】DataGridの一括選択の実装

解決済

回答 2

投稿

  • 評価
  • クリップ 0
  • VIEW 6,826
退会済みユーザー

退会済みユーザー

現在DataGridのアイテムの一括選択を右クリックで実装しようとしています。
DataGridのMouseRightButtonUpイベントで処理を行っており、
始めに選択されていたRowの場所とShiftを押されながら右クリックされた場所の
インデックスを使用しforで回して間のアイテムのIsSelectedをTrueにしていくという処理なのですが、

DataGridのインスタンスからRowを取得するときの
ItemContainerGenerator.ContainerFromIndex()でnullを返してくる場合がありうまくいきません。

nullを返してくる条件なのですが、Item数が多くScrollBarが出ている状態で画面外になっているアイテムが
nullを返してきています。

いろいろ調べて、UpdateLayout()やScrollIntoView()を所得前に行うようにしてみたのですが解決できませんでした。

なにか解決策があればぜひ教えていただきたいです。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

原因

ほとんどのWPFのリストコントロール(DataGridやListView等)は既定で仮想化されており、描画範囲外のコントロールが存在しません。
このため、コントロールベースでこの手の操作を実装をしようとすると失敗します。

対策

対策は2つあります。1つはアイテムコンテナの仮想化を止めること、もう1つはデータバインディングによって選択済みのアイテムを取得することです。基本的には後者の方がパフォーマンス上ベターです。

サンプル

データバインディングを使用した選択行を取得する方法を紹介します。どこまでMVVMに準拠するのかによってコーディングは変わってきますが、今回ご紹介するのは最も簡単と思われる方法です。必要であればこちらをBehavior等にすると良いでしょう。

CommandParameterにDataGridのSelectedItemsをバインドすると、選択されたItemのコレクションを取得できます。

<DataGrid x:Name="MyDataGrid" ItemsSource="{Binding Items}">
    <i:Interaction.Triggers>
        <!-- 下記のようにすればMouseRightButtonUpイベントとバインドできます -->
        <i:EventTrigger EventName="MouseRightButtonUp">
            <i:InvokeCommandAction 
                Command="{Binding ShowSelectedCommand}" 
                CommandParameter="{Binding ElementName=MyDataGrid, Path=SelectedItems}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</DataGrid>

Presenter/ViewModelクラス上の実装

public ICommand ShowSelectedCommand
{
    get
    {
        // DelegateCommandは一般的なICommand実装を想定していますが、分からなければ再度聞いてください
        return _ShowSelectedCommand = _ShowSelectedCommand ?? 
            new DelegateCommand(showSelected);
    }
}
private ICommand _ShowSelectedCommand;

private void showSelected(object parameter)
{
    // SelectedItemsの型はSelectedItemCollectionという型ですが非公開です
    // IList型に一旦キャストした後、Castメソッドで目的のコレクションに変換します。
    var items = (parameter as IList)?.Cast<MyItem>();
    if (items != null)
    {
        // このようにすれば、MyItemsから選択されたMyItemの一覧を取得できます。
        // ここで目的のItemのIsSelectedプロパティをtrueに設定すれば良いでしょう。
        MessageBox.Show(string.Join(",", items.Select(item => item.Index)));
    }
}

サンプルはSystem.Windows.Interactivityを使用しています。
プロジェクトの参照にSystem.Windows.Interactivityを追加し、XAMLのWindow/UserControlには以下の名前空間を定義してください。

<Window ...
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" 
/>

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/15 11:26

    んん…やりたいことを勘違いしていたかもしれません。
    選択すること自体を実行したいってことですね。
    すみません、これについては仮想化を止める以外の方法を知らないので即答できません。
     
    DataGridの仮想化を止める方法についても書いておきます。
    これをすると行数が大量にある場合は動作が遅くなることは留意しておいてください。
    DataGridのプロパティに下記を追加してください。
     
    VirtualizingStackPanel.IsVirtualizing="False"
     
    これでItemContainerGenerator.ContainerFromIndex()はnullを返してこなくなるはずです。
    この場合描画範囲外の部分についてもDependencyObjectを取得できますので、IsSelectedプロパティをtrueにしてあげてください。
     
    通常範囲選択は標準で実行できるので質問の意味を取り違えていました。申し訳ないです。

    キャンセル

  • 2016/12/15 14:06

    ありがとうございました。
    少し勘違いされていたみたいですが回答としてはとても参考になりました。

    やはり仮想化をoffにする方法はパフォーマンス的に厳しかったのですが、無事仮想化のままでやりたいことを行えました!
    ありがとうございます!

    キャンセル

  • 2016/12/15 14:11

    やりたいことができたのならよかったです:-)

    キャンセル

0

これを呼ぶのではダメなのでしょうか?

DataGrid全選択

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/12/12 15:22

    全選択ではなく複数選択なので指定範囲内の選択が行いたいのです。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る