Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何绑定到DynamicSource以便使用转换器或StringFormat等。?(第4次修订)_C#_Wpf_Ivalueconverter_Dynamicresource - Fatal编程技术网

C# 如何绑定到DynamicSource以便使用转换器或StringFormat等。?(第4次修订)

C# 如何绑定到DynamicSource以便使用转换器或StringFormat等。?(第4次修订),c#,wpf,ivalueconverter,dynamicresource,C#,Wpf,Ivalueconverter,Dynamicresource,注意:这是对早期设计的修订,该设计的局限性是不能在某个样式中使用,这相当程度上否定了它的有效性。但是,这个新版本现在可以与样式一起使用,本质上让您可以在任何可以使用绑定或动态资源的地方使用它,并获得预期的结果,从而使它变得非常有用 从技术上讲,这不是一个问题。这篇文章展示了我发现的一种方法,可以轻松地使用转换器,并将DynamicResource作为源代码,但为了遵循s/o的最佳实践,我将其作为问答对发布。所以,看看我下面的答案,看看我是如何做到这一点的。希望有帮助 我一直觉得WPF中缺少了一些

注意:这是对早期设计的修订,该设计的局限性是不能在某个样式中使用,这相当程度上否定了它的有效性。但是,这个新版本现在可以与样式一起使用,本质上让您可以在任何可以使用绑定或动态资源的地方使用它,并获得预期的结果,从而使它变得非常有用


从技术上讲,这不是一个问题。这篇文章展示了我发现的一种方法,可以轻松地使用转换器,并将
DynamicResource
作为源代码,但为了遵循s/o的最佳实践,我将其作为问答对发布。所以,看看我下面的答案,看看我是如何做到这一点的。希望有帮助

我一直觉得WPF中缺少了一些功能:使用动态资源作为绑定源的能力。我从技术上理解了这一点——为了检测更改,绑定的源必须是
DependencyObject
或支持
INotifyPropertyChanged
的对象上的属性,动态资源实际上是一个Microsoft internal
ResourceReferenceExpression
,它等同于资源的值(即,它不是一个具有要绑定的属性的对象,更不用说具有更改通知的对象了)——但是,它始终困扰着我,因为在运行时可以更改,它应该能够在需要时通过转换器推动

嗯,我相信我终于纠正了这个限制

进入动态资源绑定

注意:我称之为“绑定”,但从技术上讲,它是一个
标记扩展
,我在其上定义了属性,如
转换器
转换器参数
转换器文化
,等等,但它最终会在内部使用绑定(实际上有几个!),因此,我根据其用法对其进行了命名,不是它的实际类型

但是为什么呢? 那你为什么还要这么做呢?如何根据用户偏好全局缩放字体大小,同时借助
MultiplyByConverter
,仍能使用相对字体大小?或者简单地基于
double
资源定义应用程序范围的边距,使用
doubletothicknesconverter
不仅可以将边距转换为厚度,还可以根据需要在布局中遮住边距?或者在一个资源中定义一个基本的颜色,然后使用一个转换器使其变亮或变暗,或者使用一个颜色着色转换器改变其不透明度

更好的是,将上述实现为
MarkupExtension
s,您的XAML也会简化


简言之,这有助于整合主资源中的所有“基本值”,但能够在使用它们的时间和地点对它们进行调整,而不必在资源集合中为它们添加“x”个变体

神奇的酱汁
DynamicResourceBinding
的实现得益于
Freezable
数据类型的巧妙技巧。具体地说

如果向FrameworkElement的资源集合添加Freezable,则该Freezable对象上设置为动态资源的任何依赖项属性都将解析相对于该FrameworkElement在可视树中的位置的资源

使用这一点“魔力酱”,诀窍是在代理
可自由化的
对象的
依赖属性
上设置
DynamicResource
,将该
可自由化的
添加到目标
框架元素的资源集合中,然后在这两者之间设置一个绑定,现在这是允许的,因为源现在是一个
DependencyObject
(即
Freezable

复杂性是在
样式中使用时获得目标
框架元素
,因为
标记扩展
在定义的地方提供其值,而不是最终应用其结果的地方。这意味着当您直接在
框架元素
上使用
标记扩展时,它的目标是您所期望的
框架元素
。但是,如果在样式中使用
标记扩展名
,则
样式
对象是
标记扩展名
的目标,而不是应用它的
框架元素
。由于使用了第二个内部绑定,我也设法绕过了这个限制

也就是说,下面是一个内嵌注释的解决方案:

动态资源绑定 “神奇酱汁!”阅读内联评论了解发生了什么

绑定代理 这是上面提到的
Freezable
,但对于其他需要跨越可视树边界的绑定代理相关模式也很有帮助。在这里或谷歌上搜索“BindingProxy”,以获取有关其他用法的更多信息。太棒了

public class BindingProxy : Freezable {

    public BindingProxy(){}
    public BindingProxy(object value)
        => Value = value;

    protected override Freezable CreateInstanceCore()
        => new BindingProxy();

    #region Value Property

        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
            nameof(Value),
            typeof(object),
            typeof(BindingProxy),
            new FrameworkPropertyMetadata(default));

        public object Value {
            get => GetValue(ValueProperty);
            set => SetValue(ValueProperty, value);
        }

    #endregion Value Property
}
注意:同样,您必须使用Freezable才能工作。将任何其他类型的DependencyObject插入目标FrameworkElement的资源(讽刺的是,甚至是另一个FrameworkElement)将解析相对于应用程序而不是关联FrameworkElement的动态资源,因为资源集合中的非Freezable不参与本地化资源查找。因此,您将丢失可视树中定义的所有资源

绑定触发器 此类用于强制
多重绑定
刷新,因为我们无法访问最终的
绑定表达式
。(从技术上讲,您可以使用任何支持更改通知的类,但我个人喜欢我的设计明确说明它们的用法。)

内联多转换器 这允许您通过简单地提供用于转换的方法,轻松地在代码隐藏中设置转换器。(对于InlineConverter,我有一个类似的示例)public class BindingProxy : Freezable { public BindingProxy(){} public BindingProxy(object value) => Value = value; protected override Freezable CreateInstanceCore() => new BindingProxy(); #region Value Property public static readonly DependencyProperty ValueProperty = DependencyProperty.Register( nameof(Value), typeof(object), typeof(BindingProxy), new FrameworkPropertyMetadata(default)); public object Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } #endregion Value Property }
public class BindingTrigger : INotifyPropertyChanged {

    public BindingTrigger()
        => Binding = new Binding(){
            Source = this,
            Path   = new PropertyPath(nameof(Value))};

    public event PropertyChangedEventHandler PropertyChanged;

    public Binding Binding { get; }

    public void Refresh()
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Value)));

    public object Value { get; }
}
public class InlineMultiConverter : IMultiValueConverter {

    public delegate object   ConvertDelegate    (object[] values, Type   targetType,  object parameter, CultureInfo culture);
    public delegate object[] ConvertBackDelegate(object   value,  Type[] targetTypes, object parameter, CultureInfo culture);

    public InlineMultiConverter(ConvertDelegate convert, ConvertBackDelegate convertBack = null){
        _convert     = convert ?? throw new ArgumentNullException(nameof(convert));
        _convertBack = convertBack;
    }

    private ConvertDelegate     _convert     { get; }
    private ConvertBackDelegate _convertBack { get; }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        => _convert(values, targetType, parameter, culture);

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        => (_convertBack != null)
            ? _convertBack(value, targetTypes, parameter, culture)
            : throw new NotImplementedException();
}