XAML markup extension for binding to ISubject <string>

If I have the following presentation model

class Foo : INotifyPropertyChanged { ISubject<string> Name { ... } } 

and some supposed XAML code

 <TextBox Text="{my:Subscribe Path=Name}/> 

I want the two-way binding to behave so that

  • Subject.onNext is called when the text field is updated in the user interface.
  • the text field is updated by subscribing to Subject.Subscribe

Since WPF only supports INPC, my idea is to create an INPC proxy object through a markup extension

 class WPFSubjectProxy : INotifyPropertyChanged{ string Value { ... } } 

The proxy server will connect to the object this way

 subject.Subscribe(v=>proxy.Value=v); proxy .WhenAny(p=>p.Value, p.Value) .Subscribe(v=>subject.OnNext(v)) 

Note. When Any is a ReactiveUI helper to subscribe to INPC Events.

But then I will need to create a binding and return that through the markup extension.

I know what I want, but I can’t understand the Magic of markup extension to bring it all together.

+4
source share
1 answer

It’s hard to say, without seeing specifically what you are struggling with, but maybe this helps?

EDIT

The solution I (bradgonesurfing) appeared below, thanks to the pointer in assigned the correct answer.

Knots

and implementing code. It has a dependency on ReactiveUI and a helper function in a private library to bind ISubject to a mutable property on a supporting INPC object

 using ReactiveUI.Subjects; using System; using System.Linq; using System.Reactive.Subjects; using System.Windows; using System.Windows.Data; using System.Windows.Markup; namespace ReactiveUI.Markup { [MarkupExtensionReturnType(typeof(BindingExpression))] public class SubscriptionExtension : MarkupExtension { [ConstructorArgument("path")] public PropertyPath Path { get; set; } public SubscriptionExtension() { } public SubscriptionExtension(PropertyPath path) { Path = Path; } class Proxy : ReactiveObject { string _Value; public string Value { get { return _Value; } set { this.RaiseAndSetIfChanged(value); } } } public override object ProvideValue(IServiceProvider serviceProvider) { var pvt = serviceProvider as IProvideValueTarget; if (pvt == null) { return null; } var frameworkElement = pvt.TargetObject as FrameworkElement; if (frameworkElement == null) { return this; } object propValue = GetProperty(frameworkElement.DataContext, Path.Path); var subject = propValue as ISubject<string>; var proxy = new Proxy(); Binding binding = new Binding() { Source = proxy, Path = new System.Windows.PropertyPath("Value") }; // Bind the subject to the property via a helper ( in private library ) var subscription = subject.ToMutableProperty(proxy, x => x.Value); // Make sure we don't leak subscriptions frameworkElement.Unloaded += (e,v) => subscription.Dispose(); return binding.ProvideValue(serviceProvider); } private static object GetProperty(object context, string propPath) { object propValue = propPath .Split('.') .Aggregate(context, (value, name) => value.GetType() .GetProperty(name) .GetValue(value, null)); return propValue; } } } 
+2
source

All Articles