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

4
如果我有以下视图模型:

class Foo : INotifyPropertyChanged {

    ISubject<string> Name { ... }

} 

还有一些想象中的XAML代码

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

我希望双向绑定的行为是这样的

  • 当UI中的文本框被更新时,Subject.onNext被调用
  • 通过订阅Subject.Subscribe来更新文本框

由于WPF只直接支持INPC,我的想法是通过标记扩展在中创建一个代理INPC对象

class WPFSubjectProxy : INotifyPropertyChanged{

    string Value { ... }

}

代理将与主题连接起来,如下所示。
subject.Subscribe(v=>proxy.Value=v);

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

注意,WhenAny 是一个用于订阅 INPC 事件的 ReactiveUI 助手。但是,接下来我需要生成一个绑定并通过标记扩展返回它。我知道我想做什么,但无法弄清楚标记扩展魔法如何将所有内容组合在一起。
1个回答

2
很难没有具体问题的情况下做出判断,但或许这个链接可以帮到您? 编辑 我(bradgonesurfing)根据正确答案中的指针提供了以下解决方案:
节点
以及实现代码。它依赖于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;
        }

    }
}

我添加了一个解决方案来展示我想出的答案。目前它可以正常工作 :) - bradgonesurfing
嗨,我真的很喜欢你的解决方案,但我有一个问题:在运行时,我始终得到一个空的DataContext,因为使用Caliburn.Micro的WindowManager首先创建View,然后附加ViewModel,因此在View获取其DataContext之前解析MarkupExt。你知道有什么解决方法吗?谢谢! - Sergio
是的,我有一个解决方案。现在我使用了一种变体来监听数据上下文的更改。我会在周一寻找它。如果我没有发布,请再次在这里提醒我。 - bradgonesurfing

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接