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

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

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

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

C#

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

WPF

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

Q&A

解決済

1回答

1433閲覧

データグリッドでラジオボタンの値を取得したい

Wind

総合スコア442

DataGrid

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

C#

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

WPF

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

1グッド

0クリップ

投稿2023/03/07 01:11

実現したいこと

  • データグリッドからラジオボタンの値を取得したい。

前提

データグリッドの値をラジオボタンも含め、全て取得したい。

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

System.NullReferenceException: 'オブジェクト参照がオブジェクト インスタンスに設定されていません。' radioButton が null でした。

該当のソースコード

XAML

1<Window x:Class="WpfRadio.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 6 xmlns:local="clr-namespace:WpfRadio" 7 mc:Ignorable="d" 8 Title="MainWindow" Height="450" Width="800"> 9 <Grid> 10 <StackPanel Orientation="Vertical"> 11 <StackPanel Orientation="Horizontal"> 12 <Button Width="100" x:Name="Test" Content="Test" Margin="10" Click="Test_Click"/> 13 <Label x:Name="LabelTest" Content="test"/> 14 </StackPanel> 15 16 <DataGrid ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Visible" Margin="5" 17 Name="DG" IsReadOnly="false" AutoGenerateColumns="False" FontSize="16" 18 CanUserSortColumns="False" 19 CanUserAddRows="False" 20 CanUserDeleteRows="False" 21 CanUserResizeRows="False" 22 SelectionUnit="Cell" 23 > 24 <DataGrid.Columns> 25 <DataGridTextColumn x:Name="No" Header="No." Binding="{Binding No}"/> 26 <DataGridTextColumn x:Name="Name" Header="名前" Binding="{Binding Name}"/> 27 <!-- DataGridCheckBoxColumn x:Name="Select" Header="選択" Binding="{Binding Select}" /--> 28 <DataGridTemplateColumn Header="選択" x:Name="Temp"> 29 <DataGridTemplateColumn.CellTemplate> 30 <DataTemplate> 31 <RadioButton x:Name="Select" GroupName="sel" IsChecked="False"/> 32 </DataTemplate> 33 </DataGridTemplateColumn.CellTemplate> 34 </DataGridTemplateColumn> 35 </DataGrid.Columns> 36 </DataGrid> 37 </StackPanel> 38 39 </Grid> 40</Window> 41

C#

1using System; 2using System.Collections.ObjectModel; 3using System.Runtime.InteropServices; 4using System.Threading.Tasks; 5using System.Windows; 6using System.Windows.Controls; 7 8namespace WpfRadio 9{ 10 // データグリッド用クラス 11 public class ClsDG 12 { 13 public string No { get; set; } = ""; 14 public string Name { get; set; } = ""; 15 public string Select { get; set; } = ""; 16 17 public ClsDG(int no, string name, bool select) 18 { 19 No = no.ToString(); 20 Name = name; 21 Select = select.ToString(); 22 } 23 } 24 25 //[StructLayout(LayoutKind.Sequential, Pack = 1)] 26 struct StructBase 27 { 28 public int No; 29 public string Name; 30 public bool Select; 31 } 32 33 public partial class MainWindow : Window 34 { 35 ObservableCollection<ClsDG> PDG = new ObservableCollection<ClsDG>(); // グリッド情報 36 StructBase[] st = new StructBase[5]; // グリッドを格納する構造体の配列 37 38 public MainWindow() 39 { 40 InitializeComponent(); 41 DataContext = this; 42 43 DG_5(); 44 } 45 46 // データグリッドの行を表示する 47 private async void DG_5() 48 { 49 for(int i = 0; i < 5; i++) 50 { 51 PDG.Add(new ClsDG(i+1, $"名前{i + 1}", false)); 52 } 53 await Task.Run(() => TaskViewGrid()); 54 } 55 56 private void TaskViewGrid() 57 { 58 Dispatcher.Invoke((Action)(() => 59 { 60 DG.ItemsSource = PDG; 61 })); 62 } 63 64 private void Test_Click(object sender, RoutedEventArgs e) 65 { 66 for (int i = 0; i < 5; i++) 67 { 68 var row = DG.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow; // 1列取得 69 70 // データグリッドの列数を取得 71 int columunCount = DG.Columns.Count; 72 73 string[] strCol = new string[columunCount]; 74 // データグリッドの中身を取得 75 for (int j = 0; j < columunCount; j++) 76 { 77 // セルを取得 78 var cell = DG.Columns[j].GetCellContent(row); 79 80 //var textBlock = cell as TextBlock; 81 //strCol[j] = textBlock.Text; // RadioButtonの値が常にnullになってしまう 82 83 if(j != 2) 84 { 85 var textBlock = cell as TextBlock; 86 strCol[j] = textBlock.Text; 87 } 88 else 89 { // ラジオボタンにしてみても、nullになってしまう 90 var radioButton = cell as RadioButton; 91 strCol[j] = radioButton.IsChecked.ToString(); 92 } 93 } 94 // 格納 95 st[i].No = int.Parse(strCol[0]); 96 st[i].Name = strCol[1]; 97 st[i].Select = bool.Parse(strCol[2]); 98 } 99 } 100 } 101}

試したこと

受け取る型をTextBlockRadioButtonで試してみましたが、そもそもセルの中身がnullなのでダメでした。

補足情報(FW/ツールのバージョンなど)

.NET Framework 4.7.2

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

受け取る型をTextBlockとRadioButtonで試してみましたが、そもそもセルの中身がnullなのでダメでした。

asは変換できなかった時にはnullになります。
as 演算子 | Microsoft Learn

ブレークポイントを設定して、実際は何になっているかを確認してください。
デバッガーでのはじめに - Visual Studio (Windows) | Microsoft Learn
超初心者のためのコードのデバッグ - Visual Studio (Windows) | Microsoft Learn

(この方法を使う必要はないので)この先は特に説明しませんが、さらに子要素を探すことになります。
方法: DataTemplate によって生成された要素を検索する - WPF .NET Framework | Microsoft Learn


データグリッドの値をラジオボタンも含め、全て取得したい。

(仮想化されていると)ContainerFromIndexはすぐnullを返すので、提示コードは実際のところ使い物になりません(やりようはありますが)

WPFでコントロールを取得してあれこれするのは大概悪手です。
ちゃんとバインドを使っていれば、そういうことが必要な場面はまずありません。

そもそもNoNamePDGを見ればわかりますし、選択は前回取得したのではありませんか?
データグリッドでラジオボタンが選択した行を取得したい

cs

1for (int i = 0; i < 5; i++) 2{ 3 st[i].No = int.Parse(PDG[i].No); 4 st[i].Name = PDG[i].Name; 5 st[i].Select = PDG[i] == SelectedClsDG; 6}

Selectプロパティを用意しているならバインドするのが正攻法だとは思うのですが、無駄もあるし罠も多いので前回はスルーしましたがこの際ちゃんと説明します。

WPFではListViewDataGridは、「UIの仮想化」によってパフォーマンスを稼いでいます。
仮想化されていると「見えていないコントロール」は、消えたり(別のデータを表示するために)再利用されたりすることがあります。
コントロールのパフォーマンスを最適化する - WPF .NET Framework | Microsoft Learn

仮想化を切れば以下のような注意点もなくなるのですが、データが多いと大幅にパフォーマンスが低下します。

今回の例でいうとRadioButtonは「見えている範囲+α」個しかなく、チェックを付けるのは見えている(見えないものをチェックする方法ある?)のでいいのですが、GroupNameでチェックを外される(と期待する)RadioButtonは常にあるとは限りません。
ないものはチェックを外しようがないので、当然バインドした値も変わりません。

GroupNameが信用できないとなると自分で変えるよりないですが、C#コード側からの変更はViewに変更通知が必要になります。
方法: プロパティの変更通知を実装する - WPF .NET Framework | Microsoft Learn

回答はこちらを使いました(別に自前実装でも他のでも何でもいいです)
NuGet Gallery | CommunityToolkit.Mvvm 8.1.0
ObservableObject - .NET Community Toolkit | Microsoft Learn

xml

1<Window 2 x:Class="Qj9if4cc6q4isqm.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Width="800" 6 Height="450"> 7 <DockPanel> 8 <StackPanel DockPanel.Dock="Top" Orientation="Horizontal"> 9 <Button 10 MinWidth="100" 11 Margin="10" 12 Click="Button_Click" 13 Content="Test" /> 14 <Label x:Name="label" VerticalAlignment="Center" /> 15 </StackPanel> 16 <DataGrid 17 Margin="5" 18 AutoGenerateColumns="False" 19 CanUserAddRows="False" 20 CanUserDeleteRows="False" 21 CanUserResizeRows="False" 22 CanUserSortColumns="False" 23 ItemsSource="{Binding Items}" 24 ScrollViewer.VerticalScrollBarVisibility="Visible" 25 SelectionUnit="Cell"> 26 <DataGrid.Columns> 27 <DataGridTextColumn Binding="{Binding No}" Header="No." /> 28 <DataGridTextColumn Binding="{Binding Name}" Header="名前" /> 29 <DataGridTemplateColumn Header="選択"> 30 <DataGridTemplateColumn.CellTemplate> 31 <DataTemplate> 32 <RadioButton 33 VerticalContentAlignment="Center" 34 Checked="RadioButton_Checked" 35 IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" /> 36 </DataTemplate> 37 </DataGridTemplateColumn.CellTemplate> 38 </DataGridTemplateColumn> 39 </DataGrid.Columns> 40 </DataGrid> 41 </DockPanel> 42</Window>

cs

1using System.Collections.ObjectModel; 2using System.Windows; 3using System.Windows.Controls; 4using CommunityToolkit.Mvvm.ComponentModel; 5 6 7namespace Qj9if4cc6q4isqm 8{ 9 // [ObservableObject - .NET Community Toolkit | Microsoft Learn](https://learn.microsoft.com/ja-jp/dotnet/communitytoolkit/mvvm/observableobject) 10 public class Item : ObservableObject 11 { 12 // ここはintがいいかstringがいいかは場合によるが 13 public int No { get; set; } 14 15 public string Name { get; set; } 16 17 // ここはboolがいいに決まってる 18 // C#コード側からも変更するので「変更通知」が必要 19 public bool IsSelected { get => _IsSelected; set => SetProperty(ref _IsSelected, value); } 20 private bool _IsSelected; 21 22 23 public Item(int no, string name, bool isSelected) => (No, Name, IsSelected) = (no, name, isSelected); 24 25 public override string ToString() => $"No:{No}, Name:{Name}, IsSelected:{IsSelected}"; 26 } 27 28 public partial class MainWindow : Window 29 { 30 public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>(); 31 32 private Item selectedItem; 33 34 public MainWindow() 35 { 36 InitializeComponent(); 37 DataContext = this; 38 39 for (var i = 1; i < 100; i++) 40 { 41 Items.Add(new Item(i, $"名前{i}", false)); 42 } 43 } 44 45 private void Button_Click(object sender, RoutedEventArgs e) 46 { 47 // Itemsを見てお好きなように処理 48 foreach (var item in Items) 49 { 50 if (item.IsSelected) label.Content = item; 51 } 52 } 53 54 private void RadioButton_Checked(object sender, RoutedEventArgs e) 55 { 56 var item = (Item)((RadioButton)sender).DataContext; 57 58 // チェック済が再チェックされることはないから selectedItem != item は無駄に見えるが 59 // (UI仮想化で)消えていたものが再出現したときに(バインドによって)チェックが復元するので 60 // スクロールしているだけでもCheckedが呼ばれる 61 if (selectedItem != null && selectedItem != item) 62 { 63 // 旧selectedItem.IsSelectedを自分で変える 64 selectedItem.IsSelected = false; 65 } 66 67 // 新selectedItemはバインドによってtrueになってる 68 selectedItem = item; 69 } 70 } 71}

投稿2023/03/07 10:14

TN8001

総合スコア9455

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

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

Wind

2023/03/08 07:45

細かい解説ありがとうございます。 見えているコントロールを小手先で直接操作したくなりますが、全てをバインドして操作した方が良いのですね。 お陰でRadioButtonの値も、DataGridTextColumnの値と同じ様に扱えるようになりました。 前回はRadioButtonを押す毎に記憶していましたが、その後に他の項目が変化するのに対応出来ないかなと思って悩んでいたところでした。 RadioButton IsCheckedへのバインドも、`Binding 変数`だけでは動かずに悩んでいました。 一度に色々と教えて頂き、本当にありがとうございます!
TN8001

2023/03/08 08:46

> 見えているコントロールを小手先で直接操作したくなりますが、全てをバインドして操作した方が良いのですね。 はい。WPFはバインドしてこそ真価が発揮されます。 「データを変更すれば表示も自動で追従する」のが気持ちいいですし、ロジックに集中できます。 > RadioButton IsCheckedへのバインドも、`Binding 変数`だけでは動かずに悩んでいました。 DataGrid中のRadioButtonというのがちょっと特殊でしたね^^; > 一度に色々と教えて頂き、本当にありがとうございます! 解決したということでいいんですかね? 回答について不明点があればお気軽にコメントください。
Wind

2023/03/16 00:46

>はい。WPFはバインドしてこそ真価が発揮されます。 >「データを変更すれば表示も自動で追従する」のが気持ちいいですし、ロジックに集中できます。 まだ感覚的な物ですが、バインドすればUIスレッドを意識しなくて済むのでしょうか? InvokeやDelegateを使わなくても安全に値を表示出来るのでしょうか?
TN8001

2023/03/16 03:45

> バインドすればUIスレッドを意識しなくて済むのでしょうか? > InvokeやDelegateを使わなくても安全に値を表示出来るのでしょうか? はい。 INotifyPropertyChangedは、自動ディスパッチされるので気にしないで大丈夫です。 INotifyCollectionChangedは、最初にBindingOperations.EnableCollectionSynchronizationをしておけば大丈夫です。 [c# - 非UIスレッドからReadOnlyReactivePorpertySlimにバインドされたTextBlockの更新が出来るのはなぜか - スタック・オーバーフロー](https://ja.stackoverflow.com/questions/74659) [c# - WPF and NotifyPropertyChanged from a different thread - Stack Overflow](https://stackoverflow.com/questions/38452688/wpf-and-notifypropertychanged-from-a-different-thread)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.44%

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

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

質問する

関連した質問