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

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

ただいまの
回答率

90.35%

  • C#

    9667questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    893questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

【C#】【WPF】別のコントロールのInputBindingsを呼べるか

解決済

回答 1

投稿

  • 評価
  • クリップ 1
  • VIEW 79

OXamarin

score 29

前提

環境:VS2019
言語:C# WPF
フレームワーク:MVVM

最終的に達成したい事

TextBox上で[Shift + ↓]と入力した際に、データグリッド上にその入力を反映させたい

悩んでいる事

現状、テキストボックスにフォーカスがありながらも、[↓]ボタンを押せばデータグリッドで[↓]を押した事と同様の処理を KeyDownDownArrowCommand で行っています。
UserControlにInputBindingsを定義しておいて、そこで定義した入力を後述するInputBindingsBehavior
で優先的に処理させる事で解決しています。
優先させない場合、該当コントロールのInputBindingsが実行されてしまうので、InputBindingsを上書きをしているようなイメージです。
イメージ説明

このGIFの最後に複数行をマウスで選択していますが、
これと同じ動作をテキストボックス上で[Shift + ↓]と入力してデータグリッドを複数選択させる事は可能でしょうか。

以下のどちらかの方法で解決できるのではないのかと考えていますが、理解力不足で組めません。
① テキストボックスのInputBindingsを、指定したコントロール(ここではDataGrid)のInputBindingsに置き換える、もしくは呼ぶ
② 1列チェックボックスを増やし、VMから[Ctrl + ↓]で移動した際の行を選択させる。Xaml側ではGrid.RowStyleでその行のチェックボックスのチェックを見て行選択かどうかを切り替える

また他に、方法などはありますでしょうか。

コード

簡略化していますが、Xamlは以下のようにしています。

<UserControl x:Class="MegaClibor.Views.RecentFile"
             b:InputBindingsBehavior.TakesInputBindingPrecedence="True"
             >
    <!--キーイベントの割り当て-->
    <UserControl.InputBindings>
        <KeyBinding Key="Enter" Command="{Binding KeyDownFileOpenCommand}"/>
        <KeyBinding Key="Escape" Command="{Binding KeyDownEscapeCommand}"/>
        <KeyBinding Key="Up" Command="{Binding KeyDownUpArrowCommand}"/>
        <KeyBinding Key="Up" Modifiers="Shift" Command="{Binding KeyDownUpArrowAndShiftCommand}"/>
        <KeyBinding Key="Down" Command="{Binding KeyDownDownArrowCommand}"/>
        <KeyBinding Key="C" Modifiers="Control" Command="{Binding KeyDownFileCopyCommand}"/>
        <KeyBinding Key="D" Modifiers="Control" Command="{Binding KeyDownFileDeleteCommand}"/>
        <KeyBinding Key="O" Modifiers="Control" Command="{Binding KeyDownFileOpenCommand}"/>
        <KeyBinding Key="T" Modifiers="Control" Command="{Binding KeyDownFileCutOutCommand}"/>
    </UserControl.InputBindings>

    <Grid Margin="10">
        <materialDesign:PackIcon Kind="FolderSearch" Foreground="#eda600" Height="20" Width="20" VerticalAlignment="Bottom"/>
        <TextBox Grid.Column="1" Text="{Binding TextFullPathCondition, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}">
        </TextBox>
        <TextBlock Grid.Column="2" Text="{Binding FileCount}"/>

        <DataGrid
            Grid.Row="1" Grid.ColumnSpan="3"
            ItemsSource="{Binding FileList}"
            IsReadOnly="{Binding IsReadOnlyFileList}"
            IsSelected="{Binding IsSelectedGridRow}"
            SelectedItem="{Binding SelectedFile}"
            SelectedIndex="{Binding SelectedFileIndex}"
            b:MouseDoubleClick.Command="{Binding FileListDoubleClickCommand}"
            SelectionChanged="Selector_OnSelectionChanged"

            AutoGenerateColumns="False"
            CanUserAddRows="False"
            CanUserDeleteRows="False"
            CanUserReorderColumns="False"
            CanUserSortColumns="True"
            ClipboardCopyMode="ExcludeHeader"
            FontSize="10"
            Height="Auto"
            SelectionMode="Extended"
            SelectionUnit="FullRow"

            EnableRowVirtualization="True"
            EnableColumnVirtualization="true"
            ScrollViewer.CanContentScroll="True"
            ScrollViewer.HorizontalScrollBarVisibility="Disabled"
            ScrollViewer.IsDeferredScrollingEnabled="True"
            ScrollViewer.PanningMode="VerticalOnly"
            VirtualizingPanel.CacheLength="10"
            VirtualizingPanel.CacheLengthUnit="Item"
            VirtualizingPanel.ScrollUnit="Item"
            VirtualizingPanel.IsContainerVirtualizable="True"
            VirtualizingPanel.IsVirtualizing="True"
            VirtualizingPanel.IsVirtualizingWhenGrouping="True"
            VirtualizingPanel.VirtualizationMode="Recycling"
            >
            <!--キーイベントの割り当て-->
            <!--UserControlに以下のキーを割り当てるとテキストボックス上で意図した動作をしなくなる為ここで割り当てる-->
            <DataGrid.InputBindings>
                <KeyBinding Key="Delete" Command="{Binding KeyDownFileDeleteCommand}"/>
                <KeyBinding Key="Space" Command="{Binding KeyDownFileOpenCommand}"/>
            </DataGrid.InputBindings>

            <!--SelectedItemsをVM側で取得できるようにする-->
            <i:Interaction.Behaviors>
                <b:SelectedItemsBehavior SelectedItems="{Binding SelectedItems}" />
            </i:Interaction.Behaviors>

            <!--各カラムの設定-->
            <DataGrid.Columns>
                <DataGridTemplateColumn CanUserResize="False">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Image Source="{Binding ImageSource}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="ファイル名" SortMemberPath="FileName">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlockText="{Binding FileName}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="フルパス" >
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding FullPath}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridTemplateColumn Header="更新日時">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <TextBlock Text="{Binding LastWriteTime}" />
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</UserControl>

以下のビヘイビアを使用することで、UserControlで宣言したInputBindingsが優先して実行されます。

public class InputBindingsBehavior
{
    public static readonly DependencyProperty TakesInputBindingPrecedenceProperty =
        DependencyProperty.RegisterAttached("TakesInputBindingPrecedence",
            typeof(bool),
            typeof(InputBindingsBehavior),
            new UIPropertyMetadata(false, OnTakesInputBindingPrecedenceChanged));

    public static bool GetTakesInputBindingPrecedence(UIElement obj)
    {
        return (bool)obj.GetValue(TakesInputBindingPrecedenceProperty);
    }

    public static void SetTakesInputBindingPrecedence(UIElement obj, bool value)
    {
        obj.SetValue(TakesInputBindingPrecedenceProperty, value);
    }

    private static void OnTakesInputBindingPrecedenceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ((UIElement)d).PreviewKeyDown += new KeyEventHandler(InputBindingsBehavior_PreviewKeyDown);
    }

    private static void InputBindingsBehavior_PreviewKeyDown(object sender, KeyEventArgs e)
    {
        var uielement = (UIElement)sender;

        var foundBinding = uielement.InputBindings
            .OfType<KeyBinding>()
            .FirstOrDefault(kb => kb.Key == e.Key && kb.Modifiers == e.KeyboardDevice.Modifiers);

        if (foundBinding != null)
        {
            e.Handled = false;
            if (foundBinding.Command.CanExecute(foundBinding.CommandParameter))
            {
                foundBinding.Command.Execute(foundBinding.CommandParameter);
            }
        }
    }
}
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

以下のような方法を提案してみます。
(コードを作成して確認していないので参考までに。)

ViewModelに直前に選択したコレクション要素を保持するプロパティ(例 CurrentDataプロパティ)と
Shift+↓キーを押下したときに実行するコマンドを新たに設けます。
DataGridのInputBindingsでShift+↓のKeyBindingを設定してそのコマンドをバインドしておきます。

こちらの質問「【C#】【WPF】VM側からデータグリッドの行を複数選択を変更する事は可能か」の私の回答を参考に
DataGridのItemsSourceにバインドしているViewModelのコレクション要素のメンバーに
IsSelectedプロパティを設けてDataGridRowのIsSelectedプロパティとバインドしておいてください。

上記バインド設定により、行が選択されたら
ViewModelのコレクション要素のIsSelectedの値がTrueになるので
ViewModel側でそれを検出してそのコレクション要素をCurrentDataプロパティに設定します。

上記KeyBindingにより、Shift+↓キーが押されたタイミングでコマンドを実行されるので、
コマンドの処理としてCurrentDataプロパティを通して直前に選択したコレクション要素をもとに
DataGridのItemsSourceにバインドしているViewModelのコレクションから次に選択すべき要素を取得して
そのメンバーのIsSelectedプロパティをTrueに設定する感じでどうでしょうか?

場合によってはViewModelのコレクション要素のIsSelectedの実装は必須ですが
ViewModel側で処理する以外にItemsSourceとDataGrid.SelectedCellsChangedイベントなどを利用して
ビヘイビアとしても実装も可能かもしれません。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/08/15 18:12

    回答ありがとうございます。
    認識を合わせます。

    以下を実装しました。
    ・DataGridに設定しているコレクション(ObservableCollection) に IsSelectedプロパティを追加
    ・DataGridRowStyleのSetter に IsSelectedを追加
    ・DataGridのSelectionMode を Extended に変更
    ・Shift+↓キーを押下したときに実行するコマンドを追加
    ・ObservableCollection<T>型でCurrentDataプロパティを追加

    実装出来ていない事
    ・IsSelectedプロパティの変更通知
    ・(上記が実装出来ていないので)ViewModel側でそれを検出してそのコレクション要素をCurrentDataプロパティに設定
    ・(上記が実装出来ていないので)Shift + ↓ 押下時にCurrentDataのIsSelectedプロパティを変更

    殆どは実装済みでしたが、CurrentDataは思いついておらずなるほど!となりました。

    IsSelectedプロパティの変更通知が私の中でネックになっているのですが、実装の参考になるサイトはありますでしょうか。幾つか実装してはみたんですが、上手くいきませんでした…。
    ReactivePropertyも好きではあるのですが、モジュール数が多くなりすぎるので、プロパティの変更通知ぐらいでしたら、ReactivePropertyを使用せずに実装したいと考えています。

    キャンセル

  • 2019/08/15 18:35

    ・IsSelectedプロパティの変更通知
    に関して実装できました。
    もう少し頑張ってみます。

    キャンセル

  • 2019/08/15 18:36

    ググった感じだといくつか方法はあるみたいですね。
    https://ja.stackoverflow.com/questions/36864/observablecollectiont型のプロパティの値が変更した事を知りたい
    https://qiita.com/kengop/items/7be8d87c26c56630f2e5

    私はReactivePropertyを利用しているのでObservableCollectionの拡張メソッドの「ObserveElementProperty」を使ってそのあたりの実装はライブラリ任せで端折っています。
    https://blog.okazuki.jp/entry/2015/12/05/221154

    ReactivePropertyはMVVMパターンを強力にサポートしてくれるライブラリですので
    特に縛りがなければ積極的に使ったほうがいいと思います。
    私はもうReactivePropertyなしでMVVMパターンでの開発はできない体になってしまいました。

    キャンセル

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

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

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

  • C#

    9667questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

  • WPF

    893questions

    Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです