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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

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

WPF

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

Q&A

解決済

1回答

4895閲覧

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

OXamarin

総合スコア59

C#

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

WPF

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

0グッド

0クリップ

投稿2019/08/12 12:17

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

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

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

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

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

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

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

XAML

1<UserControl x:Class="MegaClibor.Views.RecentFile" 2 b:InputBindingsBehavior.TakesInputBindingPrecedence="True" 3 > 4 <!--キーイベントの割り当て--> 5 <UserControl.InputBindings> 6 <KeyBinding Key="Enter" Command="{Binding KeyDownFileOpenCommand}"/> 7 <KeyBinding Key="Escape" Command="{Binding KeyDownEscapeCommand}"/> 8 <KeyBinding Key="Up" Command="{Binding KeyDownUpArrowCommand}"/> 9 <KeyBinding Key="Up" Modifiers="Shift" Command="{Binding KeyDownUpArrowAndShiftCommand}"/> 10 <KeyBinding Key="Down" Command="{Binding KeyDownDownArrowCommand}"/> 11 <KeyBinding Key="C" Modifiers="Control" Command="{Binding KeyDownFileCopyCommand}"/> 12 <KeyBinding Key="D" Modifiers="Control" Command="{Binding KeyDownFileDeleteCommand}"/> 13 <KeyBinding Key="O" Modifiers="Control" Command="{Binding KeyDownFileOpenCommand}"/> 14 <KeyBinding Key="T" Modifiers="Control" Command="{Binding KeyDownFileCutOutCommand}"/> 15 </UserControl.InputBindings> 16 17 <Grid Margin="10"> 18 <materialDesign:PackIcon Kind="FolderSearch" Foreground="#eda600" Height="20" Width="20" VerticalAlignment="Bottom"/> 19 <TextBox Grid.Column="1" Text="{Binding TextFullPathCondition, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"> 20 </TextBox> 21 <TextBlock Grid.Column="2" Text="{Binding FileCount}"/> 22 23 <DataGrid 24 Grid.Row="1" Grid.ColumnSpan="3" 25 ItemsSource="{Binding FileList}" 26 IsReadOnly="{Binding IsReadOnlyFileList}" 27 IsSelected="{Binding IsSelectedGridRow}" 28 SelectedItem="{Binding SelectedFile}" 29 SelectedIndex="{Binding SelectedFileIndex}" 30 b:MouseDoubleClick.Command="{Binding FileListDoubleClickCommand}" 31 SelectionChanged="Selector_OnSelectionChanged" 32 33 AutoGenerateColumns="False" 34 CanUserAddRows="False" 35 CanUserDeleteRows="False" 36 CanUserReorderColumns="False" 37 CanUserSortColumns="True" 38 ClipboardCopyMode="ExcludeHeader" 39 FontSize="10" 40 Height="Auto" 41 SelectionMode="Extended" 42 SelectionUnit="FullRow" 43 44 EnableRowVirtualization="True" 45 EnableColumnVirtualization="true" 46 ScrollViewer.CanContentScroll="True" 47 ScrollViewer.HorizontalScrollBarVisibility="Disabled" 48 ScrollViewer.IsDeferredScrollingEnabled="True" 49 ScrollViewer.PanningMode="VerticalOnly" 50 VirtualizingPanel.CacheLength="10" 51 VirtualizingPanel.CacheLengthUnit="Item" 52 VirtualizingPanel.ScrollUnit="Item" 53 VirtualizingPanel.IsContainerVirtualizable="True" 54 VirtualizingPanel.IsVirtualizing="True" 55 VirtualizingPanel.IsVirtualizingWhenGrouping="True" 56 VirtualizingPanel.VirtualizationMode="Recycling" 57 > 58 <!--キーイベントの割り当て--> 59 <!--UserControlに以下のキーを割り当てるとテキストボックス上で意図した動作をしなくなる為ここで割り当てる--> 60 <DataGrid.InputBindings> 61 <KeyBinding Key="Delete" Command="{Binding KeyDownFileDeleteCommand}"/> 62 <KeyBinding Key="Space" Command="{Binding KeyDownFileOpenCommand}"/> 63 </DataGrid.InputBindings> 64 65 <!--SelectedItemsをVM側で取得できるようにする--> 66 <i:Interaction.Behaviors> 67 <b:SelectedItemsBehavior SelectedItems="{Binding SelectedItems}" /> 68 </i:Interaction.Behaviors> 69 70 <!--各カラムの設定--> 71 <DataGrid.Columns> 72 <DataGridTemplateColumn CanUserResize="False"> 73 <DataGridTemplateColumn.CellTemplate> 74 <DataTemplate> 75 <Image Source="{Binding ImageSource}" /> 76 </DataTemplate> 77 </DataGridTemplateColumn.CellTemplate> 78 </DataGridTemplateColumn> 79 <DataGridTemplateColumn Header="ファイル名" SortMemberPath="FileName"> 80 <DataGridTemplateColumn.CellTemplate> 81 <DataTemplate> 82 <TextBlockText="{Binding FileName}" /> 83 </DataTemplate> 84 </DataGridTemplateColumn.CellTemplate> 85 </DataGridTemplateColumn> 86 <DataGridTemplateColumn Header="フルパス" > 87 <DataGridTemplateColumn.CellTemplate> 88 <DataTemplate> 89 <TextBlock Text="{Binding FullPath}" /> 90 </DataTemplate> 91 </DataGridTemplateColumn.CellTemplate> 92 </DataGridTemplateColumn> 93 <DataGridTemplateColumn Header="更新日時"> 94 <DataGridTemplateColumn.CellTemplate> 95 <DataTemplate> 96 <TextBlock Text="{Binding LastWriteTime}" /> 97 </DataTemplate> 98 </DataGridTemplateColumn.CellTemplate> 99 </DataGridTemplateColumn> 100 </DataGrid.Columns> 101 </DataGrid> 102 </Grid> 103</UserControl>

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

C#

1public class InputBindingsBehavior 2{ 3 public static readonly DependencyProperty TakesInputBindingPrecedenceProperty = 4 DependencyProperty.RegisterAttached("TakesInputBindingPrecedence", 5 typeof(bool), 6 typeof(InputBindingsBehavior), 7 new UIPropertyMetadata(false, OnTakesInputBindingPrecedenceChanged)); 8 9 public static bool GetTakesInputBindingPrecedence(UIElement obj) 10 { 11 return (bool)obj.GetValue(TakesInputBindingPrecedenceProperty); 12 } 13 14 public static void SetTakesInputBindingPrecedence(UIElement obj, bool value) 15 { 16 obj.SetValue(TakesInputBindingPrecedenceProperty, value); 17 } 18 19 private static void OnTakesInputBindingPrecedenceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 20 { 21 ((UIElement)d).PreviewKeyDown += new KeyEventHandler(InputBindingsBehavior_PreviewKeyDown); 22 } 23 24 private static void InputBindingsBehavior_PreviewKeyDown(object sender, KeyEventArgs e) 25 { 26 var uielement = (UIElement)sender; 27 28 var foundBinding = uielement.InputBindings 29 .OfType<KeyBinding>() 30 .FirstOrDefault(kb => kb.Key == e.Key && kb.Modifiers == e.KeyboardDevice.Modifiers); 31 32 if (foundBinding != null) 33 { 34 e.Handled = false; 35 if (foundBinding.Command.CanExecute(foundBinding.CommandParameter)) 36 { 37 foundBinding.Command.Execute(foundBinding.CommandParameter); 38 } 39 } 40 } 41}

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

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

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 08:03

mikupedia

総合スコア159

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OXamarin

2019/08/15 09:12

回答ありがとうございます。 認識を合わせます。 以下を実装しました。 ・DataGridに設定しているコレクション(ObservableCollection) に IsSelectedプロパティを追加 ・DataGridRowStyleのSetter に IsSelectedを追加 ・DataGridのSelectionMode を Extended に変更 ・Shift+↓キーを押下したときに実行するコマンドを追加 ・ObservableCollection<T>型でCurrentDataプロパティを追加 実装出来ていない事 ・IsSelectedプロパティの変更通知 ・(上記が実装出来ていないので)ViewModel側でそれを検出してそのコレクション要素をCurrentDataプロパティに設定 ・(上記が実装出来ていないので)Shift + ↓ 押下時にCurrentDataのIsSelectedプロパティを変更 殆どは実装済みでしたが、CurrentDataは思いついておらずなるほど!となりました。 IsSelectedプロパティの変更通知が私の中でネックになっているのですが、実装の参考になるサイトはありますでしょうか。幾つか実装してはみたんですが、上手くいきませんでした…。 ReactivePropertyも好きではあるのですが、モジュール数が多くなりすぎるので、プロパティの変更通知ぐらいでしたら、ReactivePropertyを使用せずに実装したいと考えています。
OXamarin

2019/08/15 09:35

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

2019/08/15 09: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パターンでの開発はできない体になってしまいました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問