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

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

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

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

Q&A

解決済

2回答

4050閲覧

WPFのPropertyはふたつ必要

picko

総合スコア52

MVVM

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

XAML

XAML(Extensible Application Markup Language)はWPF、Silverlight、Windows PhoneそしてWindows Store appsでユーザーインターフェースを定義するために使われるXML言語です。

WPF

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

1グッド

1クリップ

投稿2016/07/12 02:32

こんにちは。いつもお世話になります。

WPF + Livet + Livet Extensionで、Propertyの処理を共通化できるのでしょうか?
環境は、Window10, Visual Studio2015, WPF4.5, Livet1.3.0.0, LivetExtensions1.0.3.0)の組み合わせです。

下記のとおり、X, Yを入力する二つのtextboxがあります。
値がdoubleの数値か判定して値を入れようとしています。
XとYでPropertyの処理は共通ですが、ふたつにしないとだめですか?

Mothodならたとえば、
private bool Validation(TextBox textbox){
double v;
try{
v = double.Parse(textbox.Text);
return true;
}
catch {
return false;
}
}
とかの処理で、TextBox.TextChangedeventから呼べば、Validationはひとつのコードを使いまわせると思います。

Propertyだと、

C#

1 private double x; 2 3 public string XTextBoxText 4 { 5 get 6 { 7 return this.x.ToString(CultureInfo.InvariantCulture); 8 } 9 10 set 11 { 12 if (this.XTextBoxText == value) 13 { 14 return; 15 } 16 17 double v; 18 19 try 20 { 21 v = double.Parse(value); 22 } 23 catch 24 { 25 this.RaisePropertyChanged(() => this.XTextBoxText); 26 27 return; 28 } 29 30 this.x = v; 31 32 this.RaisePropertyChanged(() => this.XTextBoxText); 33 } 34 } 35 36```のコードのXをYに変えてもうひとつ作らないとだめなのでしょうか? 37 38 39```XAML 40<Window x:Class="Livet.Window" 41 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 42 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 43 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 44 xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" 45 Height="250" Width="525" ResizeMode="NoResize"> 46 47 <i:Interaction.Triggers> 48 <i:EventTrigger EventName="ContentRendered"> 49 <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="Initialize"/> 50 </i:EventTrigger> 51 <i:EventTrigger EventName="Closed"> 52 <l:DataContextDisposeAction/> 53 </i:EventTrigger> 54 </i:Interaction.Triggers> 55 56 <Grid Margin="10"> 57 <Grid.ColumnDefinitions> 58 <ColumnDefinition Width="150"/> 59 <ColumnDefinition Width="*"/> 60 </Grid.ColumnDefinitions> 61 <Label Grid.Row="0" Grid.Column="0" Content="X" HorizontalAlignment="Left" FontFamily="Meiryo"/> 62 <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding XTextBoxText}" x:Name="LocationXTextBox" TextWrapping="NoWrap" FontFamily="Meiryo" FontSize="11"/> 63 <Label Grid.Row="1" Grid.Column="0" Content="Y" HorizontalAlignment="Left" FontFamily="Meiryo"/> 64 <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding YTextBoxText}" x:Name="LocationYTextBox" TextWrapping="NoWrap" FontFamily="Meiryo" FontSize="11"/> 65 </Grid> 66</Window>
ozwk👍を押しています

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

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

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

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

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

guest

回答2

0

ベストアンサー

C#

1public class HogeVM : ... //いい名前が思いつかなかった 2// 何かしら継承する 3{ 4 /* 5 何かあるとは思うが省略 6 */ 7 private double x; 8 public HogeVM(double initial){/*省略*/} 9 10 public string Text 11 { 12 get 13 { 14 return this.x.ToString(CultureInfo.InvariantCulture); 15 } 16 17 set 18 { 19 /* 20 長いので省略 21 */ 22 } 23 } 24}

のような「バリデーションして入力戻す機能付きテキストボックス」1つに対応するクラスを
元のVMから抜き出して作り、

C#

1public HogeVM XTextBox {get;} = new HogeVM(initial : 0); 2public HogeVM YTextBox {get;} = new HogeVM(initial : 0);

のように元のVMにプロパティで持たせ、

xaml

1<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding XTextBox.Text}" ...

とバインドしてはどうでしょうか。


こんな感じです。

C#

1class MainVM : ViewModel 2{ 3 public HogeVM TextBoxX { get; set; } = new HogeVM(initial: 2); 4} 5 6class HogeVM : ViewModel 7{ 8 9 10 private double x ; 11 public HogeVM(double initial) 12 { 13 x = initial; 14 } 15 public string Text 16 { 17 get 18 { 19 return this.x.ToString(); 20 } 21 22 set 23 { 24 Console.WriteLine("set"); 25 if (this.Text == value) 26 { 27 return; 28 } 29 30 double v; 31 32 try 33 { 34 v = double.Parse(value); 35 } 36 catch 37 { 38 this.RaisePropertyChanged(() => this.Text); 39 40 return; 41 } 42 43 this.x = v; 44 45 this.RaisePropertyChanged(() => this.Text); 46 } 47 } 48}

xml

1<Window ... 2 > 3 <Window.DataContext> 4 <local:MainVM/> 5 </Window.DataContext> 6 <StackPanel> 7 <TextBox Text = "{Binding TextBoxX.Text}"/> 8 </StackPanel> 9</Window>

投稿2016/07/12 04:57

編集2016/07/14 03:58
ozwk

総合スコア13521

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

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

picko

2016/07/14 01:55

ありがとうございます。 試してみました。errorが二つあります。 ひとつは、 public HogeVM XTextBox { get; } = new HogeVM(initial = 0); でinitialがないというもの。 //private static int initial = 0;//new HogeVM(initial = 0);でinitialがないので追加。 でOKでしょうか。 もうひとつは、 'HogeVM' に 'RaisePropertyChanged' の定義が含まれていないというもの。 こちらの対応方法に苦慮しております。 アドバイスほしいです。 using System; using System.Globalization; namespace LivetWPFApplication1.ViewModels { using Livet; public class HogeVM { private int v; private double x; public string XTextBoxText { get { return this.x.ToString(CultureInfo.InvariantCulture); } set { if (this.XTextBoxText == value) { return; } double v; if (!double.TryParse(value, out v)) { this.RaisePropertyChanged(() => this.XTextBoxText);//エラー CS1061 'HogeVM' に 'RaisePropertyChanged' の定義が含まれておらず、型 'HogeVM' の最初の引数を受け付ける拡張メソッド 'RaisePropertyChanged' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足していないことを確認してください。 LivetWPFApplication1 LivetWPFApplication1\LivetWPFApplication1\ViewModels\HogeVM.cs return; } this.x = v; this.RaisePropertyChanged(() => this.XTextBoxText);//上と同じエラー } } } }
ozwk

2016/07/14 02:11 編集

> RaisePropertyChanged 定義されてないんですから当然エラー出ます。 MVVMフレームワーク使っているなら当然VMの基底クラスがあるはずなので 継承してください。 > initial 名前付き引数の書き方間違えてました。 xの初期値をコンストラクタで設定したいなってことで 別に本題じゃないです。 (当然のようにstaticで定義しようとしたところに何か闇を感じました。)
picko

2016/07/14 02:33

あ。なるほど。継承するのですね。これは解決しました。ありがとうございます。 闇。。ですか。 staticなしで、 private int initial=0; エラー CS0236 フィールド初期化子は、静的でないフィールド、メソッド、またはプロパティ 'MainWindowViewModel.initial' を参照できません LivetWPFApplication とエラーなのです。 仮にinitialをなくしてみました。 C# public HogeVM XTextBox { get; } = new HogeVM(); public HogeVM YTextBox { get; } = new HogeVM(); xaml <TextBox Grid.Row="0" Grid.Column="1" Text="{Binding XTextBox}" x:Name="LocationXTextBox" TextWrapping="NoWrap" FontFamily="Meiryo" FontSize="11"/> //TwoWayまたはOneWayToSourceバインドは、型'LivetWPFApplication1.ViewModels.MainWindowViewModel'の読み取り専用プロパティ’XTextBox’では動作できません。とerror. <TextBox Grid.Row="1" Grid.Column="1" Text="{Binding YTextBox.Text}" x:Name="LocationYTextBox" TextWrapping="NoWrap" FontFamily="Meiryo" FontSize="11"/> //PropertyPath Cannot resolve property 'Text' in data content of type 'LivetWPFApplication1.ViewModels.HogeVM' とerror。
picko

2016/07/14 02:39

下記のとおりで起動はできますが、肝心の数値チェックをしなくなってしまいました。 C# private static int initial = 0; public HogeVM XTextBox { get; set; } = new HogeVM(initial = 0);//setしないと起動できない。 HogeVM.cs using System; using System.Globalization; namespace LivetWPFApplication1.ViewModels { using Livet; public class HogeVM : ViewModel//継承した。 { private int v; private double x; public HogeVM(int v)//コンストラクタ作った。 { this.v = v; } 以下同じ。
ozwk

2016/07/14 03:39

setterが呼ばれるタイミングは、テキストボックスからフォーカス外れたときですが、 大丈夫ですか? 私はこれに気づかず30分悩みました
picko

2016/07/14 04:08

それは大丈夫です。keyboardのtabキーで飛ばしてます。
ozwk

2016/07/14 04:12

xamlのバインディングパス間違えてません? コメントのコードだとText="{Binding XTextBox}"になってますが。 Text="{Binding XTextBox.Text}"です。
picko

2016/07/14 04:33

それでした。ありがとうございました。できました。
guest

0

・文字列を double 型に変換する
・戻り値はbooleanで成功or失敗を返す
・変換した値は参照渡しで返す
のようなメソッドを作って、それを setter で呼ぶのでは駄目でしょうか?

というか、本当にただ string を double に変換するだけだったら、TryParse とかはどうでしょうか。

if (this.XTextBoxText == value) return; double v; if(double.TryParse(value, v)) this.x = v; this.RaisePropertyChanged(() => this.XTextBoxText);

↑ ざくっと書いたのでコンパイル通るかわからないですが、こんな感じ?
これくらいだったらメソッド化しなくても良いような気もします。
(他にも色々チェックがあるならしても良いですが)


あ、TextBox の値と value が一致したら処理しないとか、
最後に RaiseProprtyChanged() するところまで共通化したいとかでしょうか。

だとすると TextBox とかプロパティ名とかも引数で渡すようなメソッドを作る感じでしょうか…?

同じような項目がすごい沢山あるならちょっと考えますが、
X, Y くらいで、処理も 文字列 → double に変換するくらいだったら、私なら setter の中にそのまま書いちゃいますかね。

まあでも setter の中でメソッドを呼んでも良いのではないでしょうか。

投稿2016/07/12 02:54

編集2016/07/12 02:57
sk_3122

総合スコア1126

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

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

picko

2016/07/12 04:21

なるほどねー。 setterのなかをそっくりmethodにして、XTextBoxTextとYTextBoxTextを引数で渡すのですね。 stringをdoubleに変換して、失敗したら元に戻す、のだそうなのです。 (103→103a→103みたいに) なので、TryParseではダメかなと。 これでざっくり40行くらいなのです。 setterをmethodにしてもあまり行数が減らない。 おっしゃるとおり、、RaisePropertyChanged()までまるっと共通化したいです。だって完全に同じなんですもん。 さらにこれを、同じprojectの別のwindowでもやるらしいのです。 ざっくり4か所以上あります。いや、まてまて。数えました。42か所あります。 解決方法はWPF UserControlなのでしょうか。
sk_3122

2016/07/12 04:35

- - - - - - - - - - 1) もともと「103」が入っている 2) TextBox に "103a" が入力された 3) 入力値を double にしてみて、変換できればセットする、できなければセットしない(103のまま) - - - - - - - - - - という要件であれば、TryParse で良さそうな気もしますけど、どうでしょうか? # 元々提示されていたコードも、私のコードも「変換に失敗したらセットしない」となっています。 どちらにしても 42 箇所ですか。それはちょっとメソッド化したくなりますかねー。 引数として渡すのは ・value と入力値が一致するかチェックしたいから TextBox ・RaisePropertyChanged を呼ぶ為のプロパティ名 ・value とかですかね… UserControl ... 今回のは ViewModel に突っ込む側の処理だから ... どうだろう 私ならメソッドですかね ...? 何か Common ぽいやつ ...
picko

2016/07/12 08:36

おっしゃるとおり、tryparseでokでした。 42か所なんですよー。 usercontrolは作ってみましたが、this.RaisePropertyChangedで頓挫しました。 methodなどを迷走中。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問