I've created my first ReactiveUI app in WPF.

Simply, the user enters their full name in one textbox, and then the name gets split into its parts and displayed in the other textboxes. This is just an example ... I realise parsing names is often futile!

**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 Title, Mode=OneWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="First" />
                <TextBox Width="100" Text="{Binding First, Mode=OneWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="Middle" />
                <TextBox Width="100" Text="{Binding Middle, Mode=OneWay}"/>
            </StackPanel>
            <StackPanel Orientation="Horizontal">
                <Label Content="Last" />
                <TextBox Width="100" Text="{Binding 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:** (ignore the name parsing bit!)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    //
    using ReactiveUI;
    using System.Reactive.Linq;
    using System.Text.RegularExpressions;

    namespace TestHumanName.ViewModel
    {
        public class MainViewModel : ReactiveObject
        {

            public MainViewModel()
            {
                this.WhenAnyValue(x => x.Full).Where(x => x != null).Select(x => ParseName(x).Title)
                    .ToProperty(this, x => x.Title, out __oapTitle);
                this.WhenAnyValue(x => x.Full).Where(x => x != null).Select(x => ParseName(x).First)
                    .ToProperty(this, x => x.First, out __oapFirst);
                this.WhenAnyValue(x => x.Full).Where(x => x != null).Select(x => ParseName(x).Middle)
                    .ToProperty(this, x => x.Middle, out __oapMiddle);
                this.WhenAnyValue(x => x.Full).Where(x => x != null).Select(x => ParseName(x).Last)
                    .ToProperty(this, x => x.Last, out __oapLast);
            }

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

            readonly ObservableAsPropertyHelper<string> __oapTitle;
            public string Title { get { return __oapTitle.Value; } }

            readonly ObservableAsPropertyHelper<string> __oapFirst;
            public string First { get { return __oapFirst.Value; } }

            readonly ObservableAsPropertyHelper<string> __oapMiddle;
            public string Middle { get { return __oapMiddle.Value; } }

            readonly ObservableAsPropertyHelper<string> __oapLast;
            public string Last { get { return __oapLast.Value; } }


            //NAME PARSING FUNCTIONALITY 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;
            }

        }
    }

I have to call `ParseName()` 4 times each time "Full" changes. It feels like I should be able to do something like put the return value of `ParseName()` into a stream/observable, and then have all the dependent properties use the same return value, rather than calling `ParseName()` for each property.

I think I'm just lacking some essential terminology to guide me in the right direction!