【问题标题】:Markup extension in XAML for binding to ISubject<string>XAML 中用于绑定到 ISubject<string> 的标记扩展
【发布时间】:2013-04-16 07:44:26
【问题描述】:

如果我有以下视图模型

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 是ReactiveUI 订阅助手 INPC 事件。

但是我需要生成一个绑定并返回 通过标记扩展。

我知道我想做什么,但不知道 标记扩展魔法将所有内容组合在一起。

【问题讨论】:

    标签: c# wpf xaml system.reactive markup-extensions


    【解决方案1】:

    如果不具体了解您遇到的问题,很难说,但也许this 有帮助?

    编辑

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

    【讨论】:

    • 我添加了一个解决方案来显示我想出的答案。到目前为止它有效:)
    • 嗨,我真的很喜欢你的解决方案,但我有一个问题:在运行时我总是得到一个 null DataContext,因为使用 Caliburn.Micro 的 WindowManager 首先创建视图,然后附加 ViewModel,所以 MarkupExt 是在 View 获取其 DataContext 之前解析。您知道解决此问题的任何解决方法吗?谢谢你!
    • 是的,我有一个解决方案。我现在使用这个变量来监听数据上下文的变化。我会在星期一寻找它。如果我不在这里发布它再次ping我。
    猜你喜欢
    • 1970-01-01
    • 2016-08-13
    • 2011-03-10
    • 2017-04-22
    • 2012-02-01
    • 2011-03-11
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多