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

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

新規登録して質問してみよう
ただいま回答率
85.47%
DataGrid

GUIの一種であり、データを表の形式でみることが可能です。

C#

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

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WinUI3

WinUI3は、Windowsデスクトップアプリ開発向けのネイティブUIフレームワークのバージョン3です。Windows10以降で採用されたFluentデザインに対応。直観的で使いやすい機能を備えています。

Q&A

解決済

1回答

1166閲覧

WinUI3でのDataGridでIsReadOnlyやCanUserSortColumnsなど値が反映されない

MomenToufu

総合スコア10

DataGrid

GUIの一種であり、データを表の形式でみることが可能です。

C#

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

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

WinUI3

WinUI3は、Windowsデスクトップアプリ開発向けのネイティブUIフレームワークのバージョン3です。Windows10以降で採用されたFluentデザインに対応。直観的で使いやすい機能を備えています。

1グッド

0クリップ

投稿2023/07/06 01:55

編集2023/07/07 04:15

実現したいこと

  • WinUI3で規格の決まっていないデータを入力しDataGridで表示させたい。
  • DataGridの機能(ソートやセルの編集など)を行えるようにしたい。
  • 基本的にはMVVMで考えています。

前提

  • WinUI3 / C# で実装しています。
  • CommunityToolkit.WinUI.UI.Controls (7.1.2)をNugetでインストール
  • Prism.Core (8.1.97) をNugetでインストール

また、DataGridにで表示させたいデータの規格は決まっていないため、WPFで可能であったItemsourceにDataTableをバインドさせたいとおもっていましたが、それが不可のため、次のWebサイトを参考に動的にデータを設定できるようにしました。

https://learn.microsoft.com/en-us/answers/questions/849470/win-ui-3-load-datagrid-from-datatable

さらに、MVVMで実装しているため、ViewModelでDataGridを操作できるように、参照渡しで渡しています(コードビハインドの18行目、21行目~24行目)。

発生している問題・エラーメッセージ

表示ボタンをクリックすると(図1)、データが生成され、DataGrid上に表示されることは確認できましたが、DatGridの機能であるソートやセルの編集などができません。(図2)
図1
図1

図2
図2

  • ExeDispDataGrid()での操作
  • DataGridを参照渡ししている事

あたりが原因かと思いますが、対処方法の検討がつきません。
何かヒントをいただけたらと思っております。

該当のソースコード

Viewのコード

xml

1<?xml version="1.0" encoding="utf-8"?> 2<Window 3 x:Class="TeraTail0001.MainWindow" 4 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 5 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 6 xmlns:local="using:TeraTail0001" 7 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 8 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9 xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" 10 mc:Ignorable="d"> 11 12 <Grid> 13 <StackPanel Orientation="Vertical"> 14 <Button Content="表示" Command="{x:Bind ViewModel.DispDataGrid}"/> 15 <controls:DataGrid 16 x:Name="MyDataGrid" 17 AutoGenerateColumns="False" 18 GridLinesVisibility="All" 19 SelectionMode="Extended" 20 IsReadOnly="False" 21 CanUserSortColumns="{x:Bind ViewModel.MyCanUserSortColumns, Mode=TwoWay}"/> 22 <TextBlock Text="{x:Bind ViewModel.Para, Mode=TwoWay}"/> 23 </StackPanel> 24 </Grid> 25</Window>

コードビハインド

C#

1using CommunityToolkit.WinUI.UI.Controls; 2using Microsoft.UI.Xaml; 3 4// To learn more about WinUI, the WinUI project structure, 5// and more about our project templates, see: http://aka.ms/winui-project-info. 6 7namespace TeraTail0001 8{ 9 /// <summary> 10 /// An empty window that can be used on its own or navigated to within a Frame. 11 /// </summary> 12 public sealed partial class MainWindow : Window 13 { 14 private MainWindowViewModel ViewModel { get; } = new(); 15 public MainWindow() 16 { 17 this.InitializeComponent(); 18 DataGridRef(ref MyDataGrid); 19 } 20 21 void DataGridRef(ref DataGrid tmp) 22 { 23 ViewModel.MyDataGridVM = tmp; 24 } 25 } 26} 27

ViewModel

C#

1using CommunityToolkit.WinUI.UI.Controls; 2using Microsoft.UI.Xaml;//PropertyPath 3using Microsoft.UI.Xaml.Data;//Binding 4using Prism.Commands;//DelegateCommand 5using Prism.Mvvm;//BindableBase(SetProperty) 6using System.Data;//DataTable 7 8namespace TeraTail0001 9{ 10 public class MainWindowViewModel : BindableBase 11 { 12 //データテーブルに表示する内容 13 public DataGrid MyDataGridVM { get; set; } = new(); 14 //パラメータ 15 public string Para { get => _Para; set => SetProperty(ref _Para, value); } 16 private string _Para; 17 //ソートOn・Off 18 public bool MyCanUserSortColumns { get => _MyCanUserSortColumns; set => SetProperty(ref _MyCanUserSortColumns, value); } 19 private bool _MyCanUserSortColumns; 20 21 //表示 22 public DelegateCommand DispDataGrid { get; private set; } 23 24 public MainWindowViewModel() 25 { 26 Para = ""; 27 DispDataGrid = new DelegateCommand(ExeDispDataGrid, CanExeDispDataGrid); 28 MyCanUserSortColumns = false; 29 } 30 31 private bool CanExeDispDataGrid() 32 { 33 return true; 34 } 35 private void ExeDispDataGrid() 36 { 37 DataTable MyDataTable = new(); 38 39 MyDataTable.Columns.Add("Name"); 40 MyDataTable.Columns.Add("Age"); 41 42 MyDataTable.Rows.Add("aaa", "11"); 43 MyDataTable.Rows.Add("bbb", "22"); 44 MyDataTable.Rows.Add("ccc", "33"); 45 46 for (int i = 0; i < MyDataTable.Columns.Count; i++) 47 { 48 MyDataGridVM.Columns.Add(new CommunityToolkit.WinUI.UI.Controls.DataGridTextColumn() 49 { 50 Header = MyDataTable.Columns[i].ColumnName, 51 Binding = new Binding { Path = new PropertyPath("[" + i.ToString() + "]") } 52 }); 53 } 54 55 var collectionObjects = new System.Collections.ObjectModel.ObservableCollection<object>(); 56 foreach (DataRow row in MyDataTable.Rows) 57 { 58 collectionObjects.Add(row.ItemArray); 59 } 60 MyDataGridVM.ItemsSource = collectionObjects; 61 62 MyCanUserSortColumns = true; 63 64 Para = "MyDataGridVM.CanUserSortColumns : " + MyDataGridVM.CanUserSortColumns + "\n"; 65 Para += "MyDataGridVM.IsReadOnly : " + MyDataGridVM.IsReadOnly+ "\n"; 66 67 } 68 69 } 70} 71

試したこと

View、ViewModelのコードにありますが、MyCanUserSortColumns の値をバインドしてみても意図した動作にはなりませんでした。

情報の過不足、表現がおかしいところなどあるかと思いますがよろしくお願いいたします。

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

DataGridの機能(ソートやセルの編集など)を行えるようにしたい。

ToolkitのDataGridはソートのガワだけ用意されていて、実際にソートするのは開発者の責任です。
サンプルアプリを見ていてそれを知ったときは愕然としました(そのうえ丸ごと入れ替える実装ってorz

How to - Group, Sort and Filter data in the DataGrid Control - Windows Community Toolkit | Microsoft Learn

WindowsCommunityToolkit/Microsoft.Toolkit.Uwp.SampleApp/SamplePages/DataGrid/DataGridPage.xaml.cs at rel/7.1.0 · CommunityToolkit/WindowsCommunityToolkit · GitHub

AdvancedCollectionViewというのができたようなんですが、DataGridは未対応です...
AdvancedCollectionView - Windows Community Toolkit | Microsoft Learn

DataGrid sorting: ObservableCollection vs AdvancedCollectionView · Issue #2961 · CommunityToolkit/WindowsCommunityToolkit

MVVMで実装しているため、ViewModelでDataGridを操作できるように、参照渡しで渡しています

ViewModelでコントロールを参照するのは一般的にNGだと思います。
参照型なのでrefで渡す必要はありません。

DataViewで何とかできそうな気もするんですが、うまくいかないのでDataRow[]でやりました。

xml

1<?xml version="1.0" encoding="utf-8" ?> 2<winex:WindowEx 3 x:Class="Qgv1ydqysalvc7l.MainWindow" 4 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 5 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 6 xmlns:controls="using:CommunityToolkit.WinUI.UI.Controls" 7 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 8 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 9 xmlns:winex="using:WinUIEx" 10 Width="800" 11 Height="450" 12 mc:Ignorable="d"> 13 <Grid> 14 <StackPanel Orientation="Vertical"> 15 <Button Command="{x:Bind viewModel.Command}" Content="表示" /> 16 <Button Click="{x:Bind viewModel.Dump}" Content="Dump" /> 17 <controls:DataGrid 18 x:Name="dataGrid" 19 AutoGenerateColumns="False" 20 GridLinesVisibility="All" 21 ItemsSource="{x:Bind viewModel.Rows, Mode=OneWay}" 22 SelectionMode="Extended" 23 Sorting="DataGrid_Sorting" /> 24 <TextBlock FontFamily="Consolas"> 25 <Run Text="CanUserSortColumns : " /> 26 <Run Text="{x:Bind dataGrid.CanUserSortColumns, Mode=OneWay}" /> 27 <LineBreak /> 28 <Run Text="IsReadOnly : " /> 29 <Run Text="{x:Bind dataGrid.IsReadOnly, Mode=OneWay}" /> 30 <LineBreak /><LineBreak /> 31 <Run Text="{x:Bind viewModel.Csv, Mode=OneWay}" /> 32 </TextBlock> 33 </StackPanel> 34 </Grid> 35</winex:WindowEx>

cs

1using System; 2using System.Data; 3using System.Linq; 4using CommunityToolkit.WinUI.UI.Controls; 5using Microsoft.UI.Xaml; 6using Microsoft.UI.Xaml.Data; 7 8namespace Qgv1ydqysalvc7l; 9 10 11public sealed partial class MainWindow //: Window 12{ 13 private readonly ViewModel viewModel = new(); 14 private readonly DependencyPropertyWatcher<DataRow[]> watcher; 15 16 public MainWindow() 17 { 18 InitializeComponent(); 19 20 // DataGridにItemsSourceChangedがないorz 21 watcher = new(dataGrid, "ItemsSource"); 22 watcher.PropertyChanged += ItemsSourceChanged; 23 } 24 25 private void ItemsSourceChanged(object sender, EventArgs e) 26 { 27 var rows = watcher.Value; 28 if (0 < rows.Length) 29 { 30 var columns = rows[0].Table.Columns.Cast<DataColumn>(); 31 32 // ソートでもItemsSourceが変更されてしまうクソ仕様なので、 33 // 新しいデータなのか・単なるソートなのかの判断がつかない! 34 // カラム名が同じならソートだとみなすw(ひどすぎるがほかに何か手があるか?? 35 var m = string.Join(",", columns.Select(x => x.ColumnName)); 36 var n = string.Join(",", dataGrid.Columns.Select(x => x.Header)); 37 if (m == n) return; 38 39 dataGrid.Columns.Clear(); 40 foreach (var (column, index) in columns.Select((x, i) => (x, i))) 41 { 42 dataGrid.Columns.Add(new DataGridTextColumn() 43 { 44 IsReadOnly = false, // 付けないと編集できない(なんでだろ?) 45 Header = column.ColumnName, 46 Binding = new() { Path = new($"[{index}]"), }, 47 }); 48 } 49 } 50 else 51 { 52 dataGrid.Columns.Clear(); 53 } 54 } 55 56 private string previousSortedColumn = ""; 57 private void DataGrid_Sorting(object sender, DataGridColumnEventArgs e) 58 { 59 foreach (var dataGridColumn in dataGrid.Columns) 60 { 61 if (dataGridColumn.Header?.ToString() == previousSortedColumn 62 && previousSortedColumn != e.Column.Header?.ToString()) 63 { 64 dataGridColumn.SortDirection = null; 65 } 66 } 67 68 if (e.Column.Header != null) 69 { 70 previousSortedColumn = e.Column.Header.ToString(); 71 if (e.Column.SortDirection != DataGridSortDirection.Ascending) 72 { 73 viewModel.SortData(e.Column.Header.ToString(), true); 74 e.Column.SortDirection = DataGridSortDirection.Ascending; 75 } 76 else 77 { 78 viewModel.SortData(e.Column.Header.ToString(), false); 79 e.Column.SortDirection = DataGridSortDirection.Descending; 80 } 81 } 82 } 83} 84 85 86// [Dependency Property の変更をイベントで通知する - しっぽを追いかけて](https://www.matatabi-ux.com/entry/2014/03/10/072240) 87public class DependencyPropertyWatcher<T> : DependencyObject, IDisposable 88{ 89 public T Value => (T)GetValue(ValueProperty); 90 public static readonly DependencyProperty ValueProperty 91 = DependencyProperty.Register(nameof(Value), typeof(object), typeof(DependencyPropertyWatcher<T>), 92 new PropertyMetadata(null, (s, e) => (s as DependencyPropertyWatcher<T>)?.PropertyChanged?.Invoke(s, EventArgs.Empty))); 93 94 public DependencyObject Target { get; private set; } 95 96 public event EventHandler PropertyChanged; 97 98 public DependencyPropertyWatcher(DependencyObject target, string propertyPath) 99 { 100 Target = target; 101 BindingOperations.SetBinding(this, ValueProperty, new Binding() { Source = target, Path = new(propertyPath), Mode = BindingMode.OneWay, }); 102 } 103 public void Dispose() => ClearValue(ValueProperty); 104}

cs

1using System.Data; 2using System.Linq; 3using Prism.Commands; 4using Prism.Mvvm; 5 6namespace Qgv1ydqysalvc7l; 7 8 9public class ViewModel : BindableBase 10{ 11 public DataRow[] Rows { get => _Rows; set => SetProperty(ref _Rows, value); } 12 private DataRow[] _Rows; 13 14 public string Csv { get => _Csv; set => SetProperty(ref _Csv, value); } 15 private string _Csv; 16 17 public DelegateCommand Command { get; } 18 19 private DataTable dataTable; 20 21 public ViewModel() => Command = new(ExecCommand); 22 23 private void ExecCommand() 24 { 25 dataTable = new(); 26 dataTable.Columns.Add("Name"); 27 dataTable.Columns.Add("Age"); 28 29 dataTable.Rows.Add("aaa", "11"); 30 dataTable.Rows.Add("bbb", "22"); 31 dataTable.Rows.Add("ccc", "33"); 32 33 Rows = dataTable.Select(); 34 } 35 36 public void SortData(string sortBy, bool ascending) 37 => Rows = dataTable.Select("", $"{sortBy} {(ascending ? "ASC" : "DESC")}"); 38 39 public void Dump() 40 { 41 Csv = $"{string.Join(",", dataTable.Columns.Cast<DataColumn>().Select(x => x.ColumnName))}\n"; 42 foreach (DataRow row in Rows) Csv += $"{string.Join(",", row.ItemArray)}\n"; 43 } 44}

NuGet Gallery | WinUIEx 2.2.0

アプリ画像

投稿2023/07/06 12:08

TN8001

総合スコア9326

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

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

TN8001

2023/07/06 12:09

コードビハインドをビヘイビア等に出すことは可能ですが、ソートで萎えてそんな気にもなれませんでした。 WinUI3はやればやるほどヘイトがたまっていくぅw
MomenToufu

2023/07/07 04:14

毎度毎度回答いただきありがとうございます!! ”ViewModelでコントロールを参照するのは一般的にNGだと思います。 参照型なのでrefで渡す必要はありません。” については、コードビハインドでデータの移し替えを行っているということですね。 Itemsourceの値が変わったことを受けて、Itemsourceのデータを name=datagrid の値へ入れる。 そのため、Itemsourceの変更通知(watcher)を設けているという感じかと解釈しました。 (WPFだとDataTableをItemsourceにバインドできていたのに...なんて楽だっただろうか。) セルのIsReadOnlyについてはカラムにパラメータがあるのですね。 class DataGridColumn を探っていると、Cellに対しても IsReadOnly を設定できるようです。 確かに、表レベルより、表のここは編集可、ここは不可のような場合の方が多そうに思いますので なんとなく直感的かなと感じました。 次にソートについてですが、ソートのロジックは各々でってことですが、 確かに”俺のソート”ってのがあるかもしれないので自由度を確保?しているのでしょうか。 コードについて完全に理解できていませんが、求めていたことが実現できましたのでベストアンサーとさせていただきました。 (これから内容についても理解していきたいと思います。) 完全に別件ですが、xaml のコードを記載するときは、xml 指定だと色付きになるのですね。 そりゃそうだよねっていえばそうですが。
TN8001

2023/11/24 08:35 編集

> については、 指摘したかった2点は、 >> ViewModelでコントロールを参照するのは一般的にNGだと思います。 「ViewとVMが疎結合にならないのであまりよろしくないですね」ってことです。 わたし個人は厳格に守る派ではありませんが^^; >> 参照型なのでrefで渡す必要はありません。 参照型の参照渡しが必要なのは、tmpに代入がある場合です。 提示コードの使い方であれば、refを付けなくても同じ動作です。 [値渡しと参照渡しの違いと使い分け - .NET Tips (VB.NET,C#...)](https://dobon.net/vb/dotnet/beginner/byvalbyref.html) > コードビハインドでデータの移し替えを行っているということですね。 「データの移し替え」と言うとちょっと違和感があります。 データ(DataRow)自体はDataTableが持っている値そのものです(編集すればDataTableも変わります) 「配列に移し替えた」といえばそうですね。 > Itemsourceの値が変わったことを受けて、Itemsourceのデータを name=datagrid の値へ入れる。 (わかっているでしょうが、何でこんなことになってしまったかのグチw DataGridはItemsSourceにバインドしています。 VMがいろいろなカラム数のデータを次々入れてくるかもしれません。 ItemsSourceが変わったら、(今回の話では)カラムを作り直さないといけません。 そのためItemsSourceChangedを監視する必要がありますが、ないのでWatcherが必要です。 WPFのソートはCollectionView(CollectionViewSource)でやるのでItemsSourceは変更しません。 しかしToolkitはそれがないため、ItemsSource自体を入れ替えるという暴挙!にでていますw(パフォーマンス差がどうかとかは調べていませんが) ソートでもカラムを作り直してしまうと、↑↓の印も忘れてしまうのでソートでは作り直さないようにしなければなりません。 データの変更なのかソートなのかどう判断しましょう?w VMはデータの変更なのを知っていますし、DataGridはソートが始まった?ことも知っています。 しかし受け口がItemsSourceだけだと、どうにもならない気がします。 もちろん何らかの仕組み?を作ればいいんでしょうけど(特に考えてはいませんが) > (WPFだとDataTableをItemsourceにバインドできていたのに...なんて楽だっただろうか。) まったくですね^^ Toolkitを擁護するなら↓あたり? * 楽をするためWPFでは裏で大変なこと(かつ結構汚い手を使ってたりしそう)をしている可能性 * DataGridがないよりはとりあえずでもあったほうがいい * 単に貢献者不足?(欲しい欲しい言われる割に複雑すぎて誰も手伝ってくれない) > セルのIsReadOnlyについてはカラムにパラメータがあるのですね。 はい。しかしWPFではfalseが規定なんですよね。 [DataGridColumn.IsReadOnly プロパティ (System.Windows.Controls) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.controls.datagridcolumn.isreadonly) Toolkitは書いてません^^; [DataGridColumn.IsReadOnly Property (CommunityToolkit.WinUI.UI.Controls) | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/api/communitytoolkit.winui.ui.controls.datagridcolumn.isreadonly) > 次にソートについてですが、ソートのロジックは各々でってことですが、 > 確かに”俺のソート”ってのがあるかもしれないので自由度を確保?しているのでしょうか。 いえ、「単に実装できていない」が実情だと思います。 ほんとはなにもしなくてもソートしたいはずです。 > (これから内容についても理解していきたいと思います。) 不明点はお気軽にコメントください^^ > xaml のコードを記載するときは、xml 指定だと色付きになるのですね。 > そりゃそうだよねっていえばそうですが。 いえ以前(2022/01のリニューアル前)は、「xaml」としても色がついてました。 「C#」と「cs」でも色の付き方が違います。 ハイライターを変更したんでしょうね(過去回答で使いまくってるのを直すかどうか悩んでる^^; その代わりといってはなんですが、「xml:MainWindow.xaml」とファイル名が書けるようになっています^^
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問