简体   繁体   English

XAML 中用于绑定到 ISubject 的标记扩展<string>

[英]Markup extension in XAML for binding to ISubject<string>

If I have the following view model如果我有以下视图模型

class Foo : INotifyPropertyChanged {

    ISubject<string> Name { ... }

} 

and some imagined XAML code和一些想象的 XAML 代码

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

I wish the two way binding to behave that我希望这两种方式绑定的行为

  • Subject.onNext is called when the text box is updated in the UI当 UI 中的文本框更新时会调用 Subject.onNext
  • the text box is updated by subscribing to the Subject.Subscribe通过订阅 Subject.Subscribe 来更新文本框

As WPF only supports INPC directly my idea is to create a proxy INPC object in via a markup extension由于 WPF 仅直接支持 INPC,我的想法是通过标记扩展创建一个代理 INPC 对象

class WPFSubjectProxy : INotifyPropertyChanged{

    string Value { ... }

}

The proxy would be wired up to the subject as so代理将如此连接到主题

subject.Subscribe(v=>proxy.Value=v);

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

Note WhenAny is a ReactiveUI helper for subscribing to INPC events.注意 WhenAny 是用于订阅 INPC 事件的ReactiveUI助手。

But then I would need to generate a binding and return that via the markup extension.但是我需要生成一个绑定并通过标记扩展返回它。

I know what I want to do but can't figure out the Markup extension magic to put it all together.我知道我想做什么,但无法弄清楚将它们组合在一起的标记扩展魔法。

It's hard to say without seeing specifically what you're struggling with, but perhaps this helps?很难没有看到具体情况,你正在与挣扎说什么,但或许可以帮助?

EDIT编辑

The solution I (bradgonesurfing) came up with is below thanks to the pointer in the assigned correct answer.由于指定的正确答案中的指针,我(bradgonesurfing)提出的解决方案如下。

Nodes节点

and the implementing code.和实现代码。 It has a dependency on ReactiveUI and a helper function in a private library for binding ISubject to a mutable property on an INPC supporting object它依赖于 ReactiveUI 和私有库中的辅助函数,用于将 ISubject 绑定到 INPC 支持对象上的可变属性

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;
        }

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM