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

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

ただいまの
回答率

87.90%

WPFのPropertyはふたつ必要

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 3,273

score 52

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

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だと、

private double x;

        public string XTextBoxText
        {
            get
            {
                return this.x.ToString(CultureInfo.InvariantCulture);
            }

            set
            {
                if (this.XTextBoxText == value)
                {
                    return;
                }

                double v;

                try
                {
                    v = double.Parse(value);
                }
                catch
                {
                    this.RaisePropertyChanged(() => this.XTextBoxText);

                    return;
                }

                this.x = v;

                this.RaisePropertyChanged(() => this.XTextBoxText);
            }
        }

のコードのXをYに変えてもうひとつ作らないとだめなのでしょうか?

<Window x:Class="Livet.Window"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
        Height="250" Width="525" ResizeMode="NoResize">

    <i:Interaction.Triggers>
        <i:EventTrigger EventName="ContentRendered">
            <l:LivetCallMethodAction MethodTarget="{Binding}" MethodName="Initialize"/>
        </i:EventTrigger>
        <i:EventTrigger EventName="Closed">
            <l:DataContextDisposeAction/>
        </i:EventTrigger>
    </i:Interaction.Triggers>

    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Content="X" HorizontalAlignment="Left" FontFamily="Meiryo"/>
        <TextBox Grid.Row="0" Grid.Column="1"  Text="{Binding XTextBoxText}" x:Name="LocationXTextBox" TextWrapping="NoWrap" FontFamily="Meiryo" FontSize="11"/>
        <Label Grid.Row="1" Grid.Column="0" Content="Y" HorizontalAlignment="Left" FontFamily="Meiryo"/>
        <TextBox Grid.Row="1" Grid.Column="1"  Text="{Binding YTextBoxText}" x:Name="LocationYTextBox" TextWrapping="NoWrap" FontFamily="Meiryo" FontSize="11"/>
    </Grid>
</Window>
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

public class HogeVM : ... //いい名前が思いつかなかった
// 何かしら継承する
{
    /*
    何かあるとは思うが省略
    */
    private double x;
    public HogeVM(double initial){/*省略*/}

    public string Text
    {
        get
        {
            return this.x.ToString(CultureInfo.InvariantCulture);
        }

        set
        {
                /*
                長いので省略
                */
        }
    }
}

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

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

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

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

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


こんな感じです。

class MainVM : ViewModel
{
    public HogeVM TextBoxX { get; set; } = new HogeVM(initial: 2);
}

class HogeVM : ViewModel
{


    private double x ;
    public HogeVM(double initial)
    {
        x = initial;
    }
    public string Text
    {
        get
        {
            return this.x.ToString();
        }

        set
        {
            Console.WriteLine("set");
            if (this.Text == value)
            {
                return;
            }

            double v;

            try
            {
                v = double.Parse(value);
            }
            catch
            {
                this.RaisePropertyChanged(() => this.Text);

                return;
            }

            this.x = v;

            this.RaisePropertyChanged(() => this.Text);
        }
    }
}
<Window ...
        >
    <Window.DataContext>
        <local:MainVM/>
    </Window.DataContext>
    <StackPanel>
        <TextBox Text = "{Binding TextBoxX.Text}"/>
    </StackPanel>
</Window>

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/07/14 13:08

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

    キャンセル

  • 2016/07/14 13:12

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

    キャンセル

  • 2016/07/14 13:33

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

    キャンセル

+1

・文字列を 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 13:21

    なるほどねー。
    setterのなかをそっくりmethodにして、XTextBoxTextとYTextBoxTextを引数で渡すのですね。

    stringをdoubleに変換して、失敗したら元に戻す、のだそうなのです。
    (103→103a→103みたいに)
    なので、TryParseではダメかなと。

    これでざっくり40行くらいなのです。
    setterをmethodにしてもあまり行数が減らない。
    おっしゃるとおり、、RaisePropertyChanged()までまるっと共通化したいです。だって完全に同じなんですもん。
    さらにこれを、同じprojectの別のwindowでもやるらしいのです。
    ざっくり4か所以上あります。いや、まてまて。数えました。42か所あります。

    解決方法はWPF UserControlなのでしょうか。

    キャンセル

  • 2016/07/12 13:35

    - - - - - - - - - -
    1) もともと「103」が入っている
    2) TextBox に "103a" が入力された
    3) 入力値を double にしてみて、変換できればセットする、できなければセットしない(103のまま)
    - - - - - - - - - -
    という要件であれば、TryParse で良さそうな気もしますけど、どうでしょうか?

    # 元々提示されていたコードも、私のコードも「変換に失敗したらセットしない」となっています。

    どちらにしても 42 箇所ですか。それはちょっとメソッド化したくなりますかねー。
    引数として渡すのは
    ・value と入力値が一致するかチェックしたいから TextBox
    ・RaisePropertyChanged を呼ぶ為のプロパティ名
    ・value
    とかですかね…

    UserControl ... 今回のは ViewModel に突っ込む側の処理だから ... どうだろう
    私ならメソッドですかね ...? 何か Common ぽいやつ ...

    キャンセル

  • 2016/07/12 17:36

    おっしゃるとおり、tryparseでokでした。

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

    キャンセル

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

  • ただいまの回答率 87.90%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る