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も実装されません。
どうしたの?
ありがとうございました。
ウィンドウはウィンドウが更新されますときに制御Paintイベントがあります。標準ネットライブラリクラスでデータが変更されると、ペイントイベントが呼び出されます。独自のクラスを作成する場合は、ウィンドウペイントイベントを呼び出す必要があります。 – jdweng
ペイントイベントが{バインディング}とどのように関連しているのかわかりません。 –
それは動作しないバインディングではありません。それは発生していない更新です。バインドを変更しても、ウィンドウに表示されている内容は自動的に変更されません。ウィンドウを更新するようにペイントを呼び出す必要があります。 – jdweng