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

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

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

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

MVVM

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

WPF

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

Q&A

解決済

1回答

2449閲覧

WPF[Livet] DataGridで発生するエラーが取得できない

Base

総合スコア28

DataGrid

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

MVVM

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

WPF

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

1グッド

0クリップ

投稿2021/10/13 07:25

編集2023/07/29 16:03

前提・実現したいこと

WPF/Livetを使ってDataGridを使った顧客情報と関連設定を操作するアプリを作っています。
MVVMの設計思想にて進めている初心者です。

今回、「対象No」という値に関してのみ、ユーザーに変更可能な項目として設定。
入力されたときにエラーの場合、データ更新ボタンを押せないようにしようと考えています。

ReactivePropertyを導入するとエラーチェックが簡単にできると調べて分かり導入しましたが、エラー状態を取得できずに困っています。
ReactivePropertyを使用しなくても、エラー状態を監視し、ボタンをOFFに出来る簡単な方法があるなら、それも知りたいです。

よろしくお願いいたします。

発生している問題

Numの入力エラー状態を取得できずに、ButoonをOFFに出来ない

適用ボタンを押した際でも良いので、エラー時にはメッセージを出すようにしたいがエラー自体がキャッチできないので、それも出来ない。

C#

1 var ret = val.Num.HasErrors;//エラーがある場合、Trueになることが期待値

該当のソースコード

App.xaml

xaml

1 <Application.Resources> 2 <Style x:Key="errorStyleLostFocus" TargetType="{x:Type TextBlock}" BasedOn="{StaticResource {x:Type FrameworkElement}}" > 3 <Style.Triggers> 4 <Trigger Property="Validation.HasError" Value="True"> 5 <Setter Property="Background" Value="Red"/> 6 </Trigger> 7 </Style.Triggers> 8 </Style> 9 <Style x:Key="errorStyleGotFocus" TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type FrameworkElement}}" > 10 <Style.Triggers> 11 <Trigger Property="Validation.HasError" Value="True"> 12 <Setter Property="Background" Value="Red"/> 13 </Trigger> 14 </Style.Triggers> 15 </Style> 16 </Application.Resources>

View

xaml

1 <Window.DataContext> 2 <vm:MainWindowVM/> 3 </Window.DataContext> 4 <Grid> 5 <Grid.RowDefinitions> 6 <RowDefinition Height="9.5*"/> 7 <RowDefinition Height="1*"/> 8 </Grid.RowDefinitions> 9 <TabControl Height="410" VerticalAlignment="Top"> 10 <TabItem Header="顧客"> 11 <DataGrid x:Name="clientData" ItemsSource="{Binding ClientData}" IsReadOnly="False" AutoGenerateColumns="False" CanUserAddRows="False"> 12 <DataGrid.Columns> 13 <DataGridTextColumn Header="ID" Binding="{Binding ID}" IsReadOnly="True" /> 14 <DataGridTextColumn Header="名前" Binding="{Binding Name}" IsReadOnly="True" /> 15 <DataGridTextColumn Header="対象No" Binding="{Binding Num.Value}" IsReadOnly="False" /> 16 <DataGridTextColumn Header="備考" Binding="{Binding Other}" IsReadOnly="True" EditingElementStyle="{StaticResource errorStyleGotFocus}"/> 17 </DataGrid.Columns> 18 </DataGrid> 19 </TabItem> 20 </TabControl> 21 <Button Grid.Row="1" x:Name="DataUpdata" Content="データ更新" HorizontalAlignment="Left" IsEnabled="{Binding IsEnable}" VerticalAlignment="Center" Width="75" RenderTransformOrigin="1.202,0.058" Height="29"> 22 <i:Interaction.Triggers> 23 <i:EventTrigger EventName="Click"> 24 <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="UpdataButton"/> 25 </i:EventTrigger> 26 </i:Interaction.Triggers> 27 </Button> 28 </Grid>

ViewModel

C#

1 class MainWindowVM : ViewModel 2 { 3 private ObservableCollection<ClientInfo> _clientData; 4 public ObservableCollection<ClientInfo> ClientData 5 { 6 get 7 { 8 return _clientData; 9 } 10 set 11 { 12 _clientData = value; 13 RaisePropertyChanged(nameof(ClientInfo)); 14 } 15 } 16 17 private ObservableCollection<bool> _IsEnable; 18 public ObservableCollection<bool> IsEnable 19 { 20 get 21 { 22 return _IsEnable; 23 } 24 set 25 { 26 _IsEnable = value; 27 RaisePropertyChanged(nameof(IsEnable)); 28 } 29 } 30 31 /// <summary> 32 /// コンストラクタ 33 /// </summary> 34 public MainWindowVM() 35 { 36 ClientData = new ObservableCollection<ClientInfo>(); 37 38 ReactiveProperty<double> reactive = new ReactiveProperty<double>(); 39 reactive.Value = 1.1; 40 ClientData.Add(new ClientInfo { ID = 1, Name = "クライアント1", Num = reactive, Other = "" }); 41 ClientData.Add(new ClientInfo { ID = 50, Name = "クライアント2", Num = reactive, Other = "" }); 42 ClientData.Add(new ClientInfo { ID = 100, Name = "クライアント3", Num = reactive, Other = "" }); 43 ClientData.Add(new ClientInfo { ID = 999, Name = "クライアント4", Num = reactive, Other = "" }); 44 } 45 46 /// <summary> 47 /// 更新ボタン押下 48 /// </summary> 49 public void UpdataButton() 50 { 51 foreach(var val in ClientData) 52 { 53 var ret = val.Num.HasErrors;//エラーがある場合、Trueになることが期待値 54 } 55 } 56 }

Model

C#

1 class ClientInfo:ViewModel 2 { 3 private ReactiveProperty<double> _num; 4 5 public int ID { get; set; } 6 public string Name { get; set; } 7 public ReactiveProperty<double> Num 8 { 9 get 10 { 11 return _num; 12 } 13 set 14 { 15 _num = value; 16 RaisePropertyChanged(nameof(Num)); 17 } 18 } 19 public string Other { get; set; } 20 }

試したこと

  1. Viewのエラー時の表示

https://thinkami.hatenablog.com/entry/2014/07/17/060723

  1. ReactivePropertyを使ったエラーチェック

https://teratail.com/questions/346500

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

VisualStudio2019

利用フレームワーク
.Net4.7.2
Livet3.0
ReactiveProperty

TN8001👍を押しています

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

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

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

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

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

guest

回答1

0

ベストアンサー

ReactivePropertyを導入するとエラーチェックが簡単にできると調べて分かり導入しましたが、エラー状態を取得できずに困っています。

現状SetValidateNotifyErrorSetValidateAttributeしていないので、なんの検証(バリデーション)もしていません。
MVVM をリアクティブプログラミングで快適に ReactiveProperty オーバービュー 2020 年版 前編 - Qiita

例えばdoubleにバインドしているTextBoxにアルファベットを入力してエラーになるのは、バリデーション以前の話です(このあたりかなり悩ましいのですが^^;
C# - ReactivePropertyでTextBoxが空欄時にButtonをDisableにしたい。|teratail

ReactivePropertyを使用しなくても、エラー状態を監視し、ボタンをOFFに出来る簡単な方法があるなら、それも知りたいです。

要はdouble値以外の入力時にボタンを押せなくしたいということですかね?
下記リンクで言及されているGu.Wpf.ValidationScopeが、楽そうでした(使い方があっているかあまり自信はないです^^;
Detecting WPF Validation Errors - Stack Overflow
NuGet Gallery | Gu.Wpf.ValidationScope 0.2.4

もしバリデーション(例えばNumは0以上とか)も必要な場合は、組み合わせないほうがよさそうです(なんか動作がおかしくなる)

xml

1<Window 2 x:Class="Questions364210.Views.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:v="clr-namespace:Questions364210.Views" 6 xmlns:validation="https://github.com/JohanLarsson/Gu.Wpf.ValidationScope" 7 xmlns:vm="clr-namespace:Questions364210.ViewModels" 8 Width="525" 9 Height="350"> 10 11 <Window.DataContext> 12 <vm:MainWindowVM /> 13 </Window.DataContext> 14 15 <Window.Resources> 16 <Style x:Key="errorStyleGotFocus" TargetType="{x:Type TextBox}"> 17 <Style.Triggers> 18 <Trigger Property="Validation.HasError" Value="True"> 19 <Setter Property="Background" Value="Red" /> 20 </Trigger> 21 </Style.Triggers> 22 </Style> 23 </Window.Resources> 24 25 <DockPanel> 26 <Button 27 HorizontalAlignment="Left" 28 Command="{Binding UpdateCommand}" 29 Content="データ更新" 30 DockPanel.Dock="Bottom" 31 IsEnabled="{Binding (validation:Scope.HasError), Converter={v:InverseBooleanConverter}, ElementName=Form}" /> 32 33 <TabControl x:Name="Form" validation:Scope.ForInputTypes="{x:Static validation:InputTypeCollection.Default}"> 34 <TabItem Header="顧客"> 35 <DataGrid 36 AutoGenerateColumns="False" 37 CanUserAddRows="False" 38 ItemsSource="{Binding ClientData}"> 39 <DataGrid.Columns> 40 <DataGridTextColumn 41 Binding="{Binding ID}" 42 Header="ID" 43 IsReadOnly="True" /> 44 <DataGridTextColumn 45 Binding="{Binding Name}" 46 Header="名前" 47 IsReadOnly="True" /> 48 <DataGridTextColumn 49 Binding="{Binding Num}" 50 EditingElementStyle="{StaticResource errorStyleGotFocus}" 51 Header="対象No" /> 52 <DataGridTextColumn 53 Binding="{Binding Other}" 54 Header="備考" 55 IsReadOnly="True" /> 56 </DataGrid.Columns> 57 </DataGrid> 58 </TabItem> 59 </TabControl> 60 </DockPanel> 61</Window>

cs

1using Livet; 2using Livet.Commands; 3using System.Collections.ObjectModel; 4using System.Diagnostics; 5 6namespace Questions364210.ViewModels 7{ 8 class MainWindowVM : ViewModel 9 { 10 public ObservableCollection<ClientInfo> ClientData { get; } 11 public ViewModelCommand UpdateCommand { get; } 12 13 public MainWindowVM() 14 { 15 ClientData = new ObservableCollection<ClientInfo> 16 { 17 new ClientInfo { ID = 1, Name = "クライアント1", Num = 1.1, Other = "" }, 18 new ClientInfo { ID = 50, Name = "クライアント2", Num = 1.1, Other = "" }, 19 new ClientInfo { ID = 100, Name = "クライアント3", Num = 1.1, Other = "" }, 20 new ClientInfo { ID = 999, Name = "クライアント4", Num = 1.1, Other = "" }, 21 }; 22 23 UpdateCommand = new ViewModelCommand(Update); 24 } 25 26 private void Update() 27 { 28 foreach (var i in ClientData) 29 { 30 Debug.WriteLine($"ID: {i.ID}, Name: {i.Name}, Num: {i.Num}, Other: {i.Other}"); 31 } 32 } 33 } 34 35 class ClientInfo : ViewModel 36 { 37 public int ID { get; set; } 38 public string Name { get; set; } 39 40 private double _Num; 41 public double Num { get => _Num; set => RaisePropertyChangedIfSet(ref _Num, value); } 42 43 public string Other { get; set; } 44 } 45}

cs

1using System; 2using System.Globalization; 3using System.Windows; 4using System.Windows.Data; 5using System.Windows.Markup; 6 7namespace Questions364210.Views 8{ 9 public class InverseBooleanConverter : MarkupExtension, IValueConverter 10 { 11 public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 12 => !(bool)value; 13 public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 14 => throw new NotImplementedException(); 15 public override object ProvideValue(IServiceProvider serviceProvider) => this; 16 } 17}

投稿2021/10/13 12:03

編集2023/07/29 07:03
TN8001

総合スコア9242

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

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

Base

2021/10/14 08:23

ありがとうございます。 やりたい事が出来ましたので、ベストアンサーとさせて頂きます。 下記のみ、セッターが必要でしたので、変更させて頂きました。 public ViewModelCommand UpdateCommand { get; }   ↓ public ViewModelCommand UpdateCommand { get; set;} また、下記の書き方が分からなかった(なくても動いた)ので、そこはCoverterに反映していません。 public partial class MainWindow : Window { public MainWindow() => InitializeComponent(); }
TN8001

2021/10/14 08:35

> また、下記の書き方が分からなかった(なくても動いた)ので、そこはCoverterに反映していません。 public MainWindow() { InitializeComponent(); } を短縮した書き方です。 MainWindowクラスのコンストラクタで、InverseBooleanConverterクラスとは無関係です(わかりにくくて申し訳ない^^; おそらくもうあるはずですので気にしないでいいでしょう^^
Base

2021/10/14 08:47 編集

なるほど。 コンストラクタをコンバーターにも記載することが可能なんですね。 気になっているところなど、詳しく説明していただけて、理解が深まったと思います。 ありがとうございます。
TN8001

2021/10/14 09:03

> コンストラクタをコンバーターにも記載することが可能なんですね。 まあ可能ですが今回の話はそういうことではなくって 本来InverseBooleanConverter.csと、MainWindow.xaml.csの別ファイルにすべき2つのクラスをひとつのファイルに書いてしまった ということです^^; MainWindowクラスを載せる必要もないのですが、「コードビハインドはないですよ」というのの強調と、「ConverterはViews namespaceにあるよ」という意図を示したかったのですが混乱させてしまったようで申し訳ないです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問