2016-04-03 22 views
1

codereview(https://codereview.stackexchange.com/questions/124300/reactiveui-and-wpf-reusing-a-value-to-update-multiple-properties)に質問を掲載しました。その質問に答える私の最近の努力は、うまくいくと思われる次のコードに私を導いてきましたが、実際にどのような仕組みが機能を提供しているのかよくわかりません!ReactiveUI - これらの{バインディング}はなぜ機能しますか?

MainWindow.xaml

<Window x:Class="TestHumanName.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="392" Width="391"> 
    <StackPanel Orientation="Vertical"> 
     <StackPanel Orientation="Horizontal"> 
      <Label Content="Full" /> 
      <TextBox Width="100" Text="{Binding Full, UpdateSourceTrigger=PropertyChanged}"/> 
      <Button Content="Go"/> 
     </StackPanel> 
     <StackPanel Orientation="Horizontal"> 
      <Label Content="Title" /> 
      <TextBox Width="100" Text="{Binding NameObject.Title, Mode=OneWay}"/> 
     </StackPanel> 
     <StackPanel Orientation="Horizontal"> 
      <Label Content="First" /> 
     <TextBox Width="100" Text="{Binding NameObject.First, Mode=OneWay}"/> 
     </StackPanel> 
     <StackPanel Orientation="Horizontal"> 
      <Label Content="Middle" /> 
     <TextBox Width="100" Text="{Binding NameObject.Middle, Mode=OneWay}"/> 
     </StackPanel> 
     <StackPanel Orientation="Horizontal"> 
      <Label Content="Last" /> 
     <TextBox Width="100" Text="{Binding NameObject.Last, Mode=OneWay}"/> 
     </StackPanel> 
    </StackPanel> 
</Window> 

MainWindow.xaml.cs:

using System.Windows; 
using TestHumanName.ViewModel; 

namespace TestHumanName 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = new MainViewModel(); 
     } 
    } 
} 

MainViewModel.cs

public class MainViewModel : ReactiveObject 
{ 
    public MainViewModel() 
    { 
     this.WhenAnyValue(x => x.Full).Where(x => x != null).Select(x => ParseName(x)) 
      .ToProperty(this, x => x.NameObject, out __oapName); 
    } 

    private string __sFull; 
    public string Full 
    { 
     get { return __sFull; } 
     set { this.RaiseAndSetIfChanged(ref __sFull, value); } 
    } 

    readonly ObservableAsPropertyHelper<Name> __oapName; 
    public Name NameObject { get { return __oapName.Value; } } 

    //NAME PARSING CODE BELOW THIS LINE 

     public class Name 
     { 
      public string Title { get; set; } 
      public string First { get; set; } 
      public string Middle { get; set; } 
      public string Last { get; set; } 
      public string Suffix { get; set; } 
     } 

     public Name ParseName(string s) 
     { 
      Name n = new Name(); 

      // Split on period, commas or spaces, but don't remove from results. 
      List<string> parts = Regex.Split(s, @"(?<=[., ])").ToList(); 

      // Remove any empty parts 
      for (int x = parts.Count - 1; x >= 0; x--) 
       if (parts[x].Trim() == "") 
        parts.RemoveAt(x); 

      if (parts.Count > 0) 
      { 
       // Might want to add more to this list 
       string[] prefixes = { "mr", "mrs", "ms", "dr", "miss", "sir", "madam", "mayor", "president" }; 

       // If first part is a prefix, set prefix and remove part 
       string normalizedPart = parts.First().Replace(".", "").Replace(",", "").Trim().ToLower(); 
       if (prefixes.Contains(normalizedPart)) 
       { 
        n.Title = parts[0].Trim(); 
        parts.RemoveAt(0); 
       } 
      } 

      if (parts.Count > 0) 
      { 
       // Might want to add more to this list, or use code/regex for roman-numeral detection 
       string[] suffixes = { "jr", "sr", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x", "xi", "xii", "xiii", "xiv", "xv" }; 

       // If last part is a suffix, set suffix and remove part 
       string normalizedPart = parts.Last().Replace(".", "").Replace(",", "").Trim().ToLower(); 
       if (suffixes.Contains(normalizedPart)) 
       { 
        n.Suffix = parts.Last().Replace(",", "").Trim(); 
        parts.RemoveAt(parts.Count - 1); 
       } 
      } 

      // Done, if no more parts 
      if (parts.Count == 0) 
       return n; 

      // If only one part left... 
      if (parts.Count == 1) 
      { 
       // If no prefix, assume first name, otherwise last 
       // i.e.- "Dr Jones", "Ms Jones" -- likely to be last 
       if (n.Title == "") 
        n.First = parts.First().Replace(",", "").Trim(); 
       else 
        n.Last = parts.First().Replace(",", "").Trim(); 
      } 

      // If first part ends with a comma, assume format: 
      // Last, First [...First...] 
      else if (parts.First().EndsWith(",")) 
      { 
       n.Last = parts.First().Replace(",", "").Trim(); 
       for (int x = 1; x < parts.Count; x++) 
        n.First += parts[x].Replace(",", "").Trim() + " "; 
       n.First = n.First.Trim(); 
      } 

      // Otherwise assume format: 
      // First [...Middle...] Last 

      else 
      { 
       n.First = parts.First().Replace(",", "").Trim(); 
       n.Last = parts.Last().Replace(",", "").Trim(); 
       for (int x = 1; x < parts.Count - 1; x++) 
        n.Middle += parts[x].Replace(",", "").Trim() + " "; 
       if (n.Middle != null) n.Middle = n.Middle.Trim(); 
      } 

      return n; 
     } 
    } 
} 

私が理解していないのは、NameObjectプロパティの値をどのように置き換えることができるかであり、{Binding ...}sは魔法のように更新する必要があることを知っています。確かに、NameObjectプロパティの内容を置換しても、その子プロパティでOnPropertyChangedは呼び出されません。Nameクラスでは、INotifyPropertyChangedも実装されません。

どうしたの?

ありがとうございました。

+0

ウィンドウはウィンドウが更新されますときに制御Paintイベントがあります。標準ネットライブラリクラスでデータが変更されると、ペイントイベントが呼び出されます。独自のクラスを作成する場合は、ウィンドウペイントイベントを呼び出す必要があります。 – jdweng

+0

ペイントイベントが{バインディング}とどのように関連しているのかわかりません。 –

+0

それは動作しないバインディングではありません。それは発生していない更新です。バインドを変更しても、ウィンドウに表示されている内容は自動的に変更されません。ウィンドウを更新するようにペイントを呼び出す必要があります。 – jdweng

答えて

2

一般的な概念

あなたの誤解がどこから来たのかと思います。あなたがここに何 :

this.WhenAnyValue(x => x.Full).Where(x => x != null).Select(x => ParseName(x)) 
     .ToProperty(this, x => x.NameObject, out __oapName); 

はいつでもFullプロパティの変更、火災ParseName方法、それは財産_oapNameを更新するようにすることを意味します。 _oapNameです。ObservableAsPropertyHelperタイプは、Rxの心臓部であり、UI自体に通知します。ドキュメントの状態として

これは 現在の検索テキストの長さと、それが変わるたびに更新されるプロパティとして「イメージを置く」プロパティ( ObservableAsPropertyHelperプロパティ)を初期化します。 プロパティは他の方法で設定することはできません。は変更 通知を生成します。したがって、それ自体はWhenAny式または バインドで使用できます。

出典:舞台裏 http://docs.reactiveui.net/en/user-guide/when-any/index.html

我々はToPropertyの署名の表情取る場合:

public static ObservableAsPropertyHelper<TRet> ToProperty<TObj, TRet>(
    this IObservable<TRet> This, 
    TObj source, 
    Expression<Func<TObj, TRet>> property, 
    out ObservableAsPropertyHelper<TRet> result, 
    TRet initialValue = default(TRet), 
    IScheduler scheduler = null) 
    where TObj : IReactiveObject 
    { 
     var ret = source.observableToProperty(This, property, initialValue, scheduler); 

     result = ret; 
     return ret; 
    } 

を我々はそれが実際に何をしていることがわかります、拡張メソッドobservableToPropertyを呼び出して、を生成するだけです。Observ ableAsPropertyHelper

var ret = new ObservableAsPropertyHelper<TRet>(observable, 
      _ => This.raisePropertyChanged(name), 
      _ => This.raisePropertyChanging(name), 
      initialValue, scheduler); 

とすることを念頭に置いて:

  • ThisobservableToProperty中(変数名の名声が)とタイプTObjである はobservableToPropertyの内側に、我々はこのような数行を見ることができ、さらに正確には制約のwhere TObj : IReactiveObject
  • nameは、あなたがExpression<Func<TObj, TRet>> propertyに渡したプロパティの名前です。 x => x.NameObjectからあなたのケースnameには、それはを上げることになりますNameObject

なりますMainViewModelあるNameObjectの親ReactiveObjectをRaisePropertyChanged。

ソースコードObservableAsPropertyHelper.cs @ ReactiveUI on Github

+0

あなたの答えをありがとう。それは意味があります、そして、私は参照から入れ子になったプロパティを見ることができますが、参照で与えられた例では、ネストされたプロパティは明示的に 'this.WhenAnyValue(x => x.Foo.Bar.Baz); '。私のxamlでは、例えば 'NameObject.Title'にバインドします。これはReactiveObjectではない' Name'クラスのインスタンスです。そして、コード内にネストされたプロパティを明示的に記述していないので、これらのプロパティがどのように変更を通知しているのか見苦しくなっています。 –

+0

'WhenAny'は' out'を使用しているので、 'ParseName'の出力はプロパティ' _oapName'の新しい値(正確には正確に参照)として割り当てられたことを意味します。あなたは、プロパティ 'NameObject'のプロパティが通知を発行しないということを正しく感じました。 '_oapName'全体が、​​ObservableAsPropertyHelperによってラップされたオブジェクトが変更されたとき(つまり、OAPHのプロパティが通知を発生させたとき)、通知を発生させます。 – mwilczynski

+0

ok。クール。だから私は今質問しなければならない、入れ子になったプロパティのツリーがどれくらい遠くまで動くのだろうか?私は 'NameObject.Title.Length'を持っていた場合、それでも動作しますか? {Name}に属しているネストされたプロパティが変更されている必要があるので、{Name}が変更されたことを知るのに十分な{Binding}はスマートですか?私はドキュメントで決定的な答えを見つけることができません... –

関連する問題