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

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

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

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

MVVM

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

WPF

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

Q&A

解決済

2回答

4019閲覧

日付入力UserControlの実装方法について(西暦・和暦表示切替あり)

45_Shingo

総合スコア177

C#

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

MVVM

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

WPF

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

0グッド

1クリップ

投稿2019/06/21 08:09

前提・実現したいこと

MVVMモデルのWPFでアプリ開発を始めたばかりの初心者なのですが、日付入力のUserControl作成の仕様でどう実現したらいいか悩んでいることがあります。
具体的には、

元号(西暦、和暦)ComBobox
年入力TextBox
月入力TextBox
日入力TextBox
現時点での年齢を表示するLabel

があるUserControlを作成するうえで

1.すべての項目に入力された際、内部的に持っているDateTimeデータを更新し、年齢計算をしてViewに反映
2.VM側とBindingしているのはそのDateTimeのプロパティーのみ
3.未入力状態では元号ComboBoxは西暦をデフォルトで選択
4.VM側から初期値としてDateTimeをセットした場合は和暦としてViewに表示する

といった要件です。ソースは一部ですが、現在の状態を張りました

こういった仕様の場合どういう実装方法が考えられるか教えていただけないでしょうか。
よろしくお願いします。

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

VM側から初期値としてデータ更新された場合と、ユーザーがViewから入力してデータ更新された場合の判別方法がわからず、Viewで入力された場合に西暦で入力をしても自動的に和暦表示になってしまう

該当のソースコード

C#

1// Dateデータソース 2public static readonly DependencyProperty DateProperty = 3 DependencyProperty.Register( 4 "Date", 5 typeof(DateTime), 6 typeof(UC_Hoge), 7 new FrameworkPropertyMetadata(DateTime.MinValue, SetDateToView) { BindsTwoWayByDefault=true } 8 ); 9public DateTime Date 10{ 11 get { return (DateTime)GetValue(DateProperty); } 12 set { SetValue(DateProperty, value); } 13} 14 15protected static void SetDateToView(DependencyObject d, DependencyPropertyChangedEventArgs e) 16{ 17 if (e.OldValue != e.NewValue) 18 { 19 DateTime datetime = (DateTime)e.NewValue; 20 UC_Hoge view = d as UC_Hoge; 21 22 if (view != null) 23 { 24 string nYearEra = GetJEraName(datetime);// 和暦元号を取得 25 string nYearString = GetJEraYear(datetime);// 和暦年を取得 26 27 if (string.IsNullOrEmpty(nYearEra) == false && string.IsNullOrEmpty(nYearString) == false) 28 { 29 SetSelectedEraItem(view, nYearEra); // 和暦元号をセット 30 view.Year = nYearString; // 和暦年をセット 31 } 32 else 33 { 34 view.Year = (datetime.Year).ToString(); // 西暦年をセット 35 } 36 view.Month = (datetime.Month).ToString(); 37 view.Day = (datetime.Day).ToString(); 38 DateUtil birth = new DateUtil(datetime); 39 view.Age = birth.GetAgeString(); 40 } 41 } 42} 43// 年(月、日も同様の実装) 44public static readonly DependencyProperty YearProperty = 45 DependencyProperty.Register( 46 "Year", 47 typeof(string), 48 typeof(UC_Hoge), 49 new FrameworkPropertyMetadata(null, SetDateFromView) { BindsTwoWayByDefault = true } 50 ); 51public string Year 52{ 53 get { return (string)GetValue(YearProperty); } 54 set { SetValue(YearProperty, value); } 55} 56 57// 内容変更時の内部DateTime変更、年齢計算 58protected static void SetDateFromView(DependencyObject d, DependencyPropertyChangedEventArgs e) 59{ 60 UC_Hoge view = d as UC_Hoge; 61 62 if (view != null) 63 { 64 view.Age = null; 65 66 // 全ての値がセットされている場合年齢計算とDateの更新を行う 67 if ( 68 view.SelectedEraItem != null && 69 string.IsNullOrEmpty(view.Year) == false && 70 string.IsNullOrEmpty(view.Month) == false && 71 string.IsNullOrEmpty(view.Day) == false 72 ) 73 { 74 view.Date = view.MakeDate(view.SelectedEraItem.EraName, view.Year, view.Month, view.Day); // Viewの入力データからDateTimeを生成、Dateにセット 75 DateTime today = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1); // 当月1日時点でのDateTimeを取得 76 view.Age = new DateUtil(view.Date).GetAgeString(today); // 当月1日時点での年齢をセット 77 } 78 } 79} 80 81

試したこと

Viewの変更によるDateの更新の場合、和暦に強制変換しないようなフラグを用意してセットしてみるという方法を試しましたがうまくいきませんでした。

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

Microsoft Visual Studio 2017 v15.9.4
Prism.wpf v6.3.0

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

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

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

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

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

k.matsuda

2019/06/21 14:33

ComboBoxは参照しないのですか?
45_Shingo

2019/06/21 17:34

ComboBoxは参照しています。 Dateを更新する際に、ComboBoxの選択肢+年月日からDateTimeを作成しそれをDateにセットしています。 これ、ComboBoxを参照して判断すればVMからのセットなのかViewからの入力で更新されたのかって判断つきますか?ちょっとイメージがよくわからないのですが
guest

回答2

0

私だったら、和暦用の入力部分と西暦用の入力部分の両方を作っておいて
ComboBoxの選択によってどちらか片方を表示するようにします。
そのやり方ならView+コンバータぐらいで解決することもできそうですから。

投稿2019/06/21 08:25

編集2019/06/21 08:26
hihijiji

総合スコア4150

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

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

45_Shingo

2019/06/21 08:55

回答ありがとうございます。 質問がわかりにくくてすみません… 今の実装だと実際日付データとして持ってるのはDataプロパティーのみで、VMとのやりとりもこれのみで行っているため、VM側でセットされて値が変更されたのか、View側で値が変更されたのかに関わらず同じロジックが走ってしまうため、DateTimeを和暦表示させるという動作がView入力でも発生してしまう…という状態を回避する方法がないかという事です。 VMからの更新時はよいのですが Viewからの更新の場合選択肢は西暦になっているのに、入力が終わったとたんDateの更新が発生してしまうため、勝手に和暦に変わってしまう…という状態でして
hihijiji

2019/06/21 09:01

質問の趣旨は大体わかっているつもりですが、そんな複雑なことをしなくても簡単に済ませたらどうでしょうか? っていうレスです。 余計なお世話でしたらすみません。
45_Shingo

2019/06/21 09:09

余計なお世話とかは全くないです。ありがとうございます。 入力枠と動作については指定されていて変更できないのと、本当に始めたばかりで全くイメージがわいていないのですが、2つ入力枠を設けて、それを選択肢によって表示切り替えする…という事でしょうか。
hihijiji

2019/06/21 09:48

そんな感じです。
45_Shingo

2019/06/22 01:00

なるほど。ありがとございます。 切り替えをユーザー主導だけでなくVMからの値変更でも行う必要があって、VMからの場合は強制的に和暦で表示するという要件なので、この方法でどういう風にすればVMからの値セットなのかViewからの値セットなのかを判定する方法がわからなくて… 和暦、西暦2つの表示が同時にあって、両方を同時に表示したままにする…というのであれば切り替えの必要はないのですが、それはNGという話でして。
hihijiji

2019/06/22 01:19

和暦を強制するDependencyPropertyを作ってComboBoxのIsEnabledとでもBindすればいいのでは?
45_Shingo

2019/06/22 02:13

なるほど、VM側から和暦であるという情報を渡すという事ですね。 ありがとございます。相談してみます。
guest

0

自己解決

回答をいただき、実装方法について結局発注元と話し合った結果

1.VM側からはDateTimeの値のセット及びゲットのみで済ませたい
2.表示部分の制御はView側で実装したい

ということで、VM側から値のセット・ゲットを行うプロパティー以外に内部的にDateTimeを管理するプロパティーをもう1つ作ることで、値の流れをチェックする(VM→V、V→VM)という事になりました。

実際には

C#

1// VM受け渡し用DateTimeプロパティー 2public static readonly DependencyProperty DateProperty = 3 DependencyProperty.Register( 4 "Date", 5 typeof(DateTime?), 6 typeof(UC_Hoge), 7 new FrameworkPropertyMetadata(null, ChangeDate) { BindsTwoWayByDefault=true } 8 ); 9public DateTime? Date 10{ 11 get { return (DateTime?)GetValue(DateProperty); } 12 set { SetValue(DateProperty, value); } 13} 14 15protected static void ChangeDate(DependencyObject d, DependencyPropertyChangedEventArgs e) 16{ 17 if (e.OldValue != e.NewValue) 18 { 19 DateTime? newDate = (DateTime?)e.NewValue; 20 DateTime? oldDate = (DateTime?)e.OldValue; 21 UC_Hoge view = d as UC_Hoge; 22 23 if (view.InnerDate == oldDate) 24 { 25 view.InnerDate = newDate; 26 } 27 } 28} 29 30// 内部DateTime管理用プロパティー 31private static readonly DependencyProperty InnerDateProperty = 32 DependencyProperty.Register( 33 "InnerDate", 34 typeof(DateTime?), 35 typeof(UC_Hoge), 36 new FrameworkPropertyMetadata(null, ChangeInnerDate) { BindsTwoWayByDefault = true } 37 ); 38private DateTime? InnerDate 39{ 40 get { return (DateTime?)GetValue(InnerDateProperty); } 41 set { SetValue(InnerDateProperty, value); } 42} 43 44protected static void ChangeInnerDate(DependencyObject d, DependencyPropertyChangedEventArgs e) 45{ 46 DateTime? newDate = (DateTime?)e.NewValue; 47 UC_Hoge view = d as UC_Hoge; 48 49 if (view.Date == newDate) 50 { 51 // TextBox等への値のセット処理 52 } 53 else 54 { 55 // VM受け渡しプロパティーの更新 56 if ( 57 string.IsNullOrEmpty(view.Year) == true && 58 string.IsNullOrEmpty(view.Month) == true && 59 string.IsNullOrEmpty(view.Day) == true 60 ) 61 { 62 view.Date = null; 63 } 64 else 65 { 66 view.Date = newDate; 67 } 68 } 69}

という具合に、Date、InnerDateの更新時に値の比較を行い、どちら側からの流れなのかで挙動を変える方法にしました。
いかんせん、まだ始めたばかりで、クライアント側からの要求を満たすにはこれくらいしか思いつかず…もっとシンプルで良い方法があったら知りたいです。

投稿2019/06/25 02:00

45_Shingo

総合スコア177

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問