c# - 标记扩展 'StaticResourceExtension' 需要在 IServiceProvider 中为 ProvideValue 实现 'IXamlSchemaContextProvider'

标签 c# wpf xaml .net-4.0 extension-methods

我已经使用了几年的资源扩展现在在新的 .Net 4 项目的设计时停止工作,并出现以下错误:

Markup extension 'StaticResourceExtension' requires 'IXamlSchemaContextProvider' be implemented in the IServiceProvider for ProvideValue.

扩展中的相关方法如下:

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        Style resultStyle = new Style();
        foreach (string currentResourceKey in resourceKeys)
        {
            object key = currentResourceKey;
            if (currentResourceKey == ".")
            {
                IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
                key = service.TargetObject.GetType();
            }
            Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style;
            if (currentStyle == null)
                throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + ".");
            resultStyle.Merge(currentStyle);
        }
        return resultStyle;
    }

据推测,编译器给出了错误,因为当我调用 currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider); 时,我传递的 serviceProvider 缺少 IXamlSchemaContextProvider 信息。虽然不知道它是从哪里来的,我什至不知道标记扩展的服务提供商是如何首先设置的,我只是像这样使用它:

<Style x:Key="ReadOnlyTextCell" TargetType="{x:Type TextBlock}" BasedOn="{util:MultiStyle ReadOnlyCell TextCell}"/>


扩展的完整代码在这里:

using System;
using System.Windows;
using System.Windows.Markup;

/* MultiStyleExtension - used to merge multiple existing styles.
 *  
 * Example:
    <Window xmlns:local="clr-namespace:FlagstoneRe.WPF.Utilities.UI">
        <Window.Resources>
            <Style x:Key="ButtonStyle" TargetType="Button">
                <Setter Property="Width" Value="120" />
                <Setter Property="Height" Value="25" />
                <Setter Property="FontSize" Value="12" />
            </Style>
            <Style x:Key="GreenButtonStyle" TargetType="Button">
                <Setter Property="Foreground" Value="Green" />
            </Style>
            <Style x:Key="RedButtonStyle" TargetType="Button">
                <Setter Property="Foreground" Value="Red" />
            </Style>
            <Style x:Key="BoldButtonStyle" TargetType="Button">
                <Setter Property="FontWeight" Value="Bold" />
            </Style>
        </Window.Resources>

        <Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle}" Content="Green Button" />
        <Button Style="{local:MultiStyle ButtonStyle RedButtonStyle}" Content="Red Button" />
        <Button Style="{local:MultiStyle ButtonStyle GreenButtonStyle BoldButtonStyle}" Content="green, bold button" />
        <Button Style="{local:MultiStyle ButtonStyle RedButtonStyle BoldButtonStyle}" Content="red, bold button" />

 * Notice how the syntax is just like using multiple CSS classes.
 * The current default style for a type can be merged using the "." syntax:

        <Button Style="{local:MultiStyle . GreenButtonStyle BoldButtonStyle}" Content="Bold Green Button" />

 */

namespace FlagstoneRe.WPF.Utilities.UI
{
    [MarkupExtensionReturnType(typeof(Style))]
    public class MultiStyleExtension : MarkupExtension
    {
        private string[] resourceKeys;

        /// <summary>
        /// Public constructor.
        /// </summary>
        /// <param name="inputResourceKeys">The constructor input should be a string consisting of one or more style names separated by spaces.</param>
        public MultiStyleExtension(string inputResourceKeys)
        {
            if (inputResourceKeys == null)
                throw new ArgumentNullException("inputResourceKeys");
            this.resourceKeys = inputResourceKeys.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (this.resourceKeys.Length == 0)
                throw new ArgumentException("No input resource keys specified.");
        }

        /// <summary>
        /// Returns a style that merges all styles with the keys specified in the constructor.
        /// </summary>
        /// <param name="serviceProvider">The service provider for this markup extension.</param>
        /// <returns>A style that merges all styles with the keys specified in the constructor.</returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            Style resultStyle = new Style();
            foreach (string currentResourceKey in resourceKeys)
            {
                object key = currentResourceKey;
                if (currentResourceKey == ".")
                {
                    IProvideValueTarget service = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
                    key = service.TargetObject.GetType();
                }
                Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style;
                if (currentStyle == null)
                    throw new InvalidOperationException("Could not find style with resource key " + currentResourceKey + ".");
                resultStyle.Merge(currentStyle);
            }
            return resultStyle;
        }
    }

    public static class MultiStyleMethods
    {
        /// <summary>
        /// Merges the two styles passed as parameters. The first style will be modified to include any 
        /// information present in the second. If there are collisions, the second style takes priority.
        /// </summary>
        /// <param name="style1">First style to merge, which will be modified to include information from the second one.</param>
        /// <param name="style2">Second style to merge.</param>
        public static void Merge(this Style style1, Style style2)
        {
            if(style1 == null)
                throw new ArgumentNullException("style1");
            if(style2 == null)
                throw new ArgumentNullException("style2");
            if(style1.TargetType.IsAssignableFrom(style2.TargetType))
                style1.TargetType = style2.TargetType;
            if(style2.BasedOn != null)
                Merge(style1, style2.BasedOn);
            foreach(SetterBase currentSetter in style2.Setters)
                style1.Setters.Add(currentSetter);
            foreach(TriggerBase currentTrigger in style2.Triggers)
                style1.Triggers.Add(currentTrigger);
            // This code is only needed when using DynamicResources.
            foreach(object key in style2.Resources.Keys)
                style1.Resources[key] = style2.Resources[key];
        }
    }
}

最佳答案

除了使用 StaticResourceExtension 从静态资源中获取样式,您还可以从当前应用程序的静态资源中获取样式,它做同样的事情。

替换有问题的行:

Style currentStyle = new StaticResourceExtension(key).ProvideValue(serviceProvider) as Style;

与:

Style currentStyle = Application.Current.TryFindResource(key) as Style;

(旁注:请参阅 my other answer,了解为什么要使用 TryFindResource(key) 而不是 Resource[key])

关于c# - 标记扩展 'StaticResourceExtension' 需要在 IServiceProvider 中为 ProvideValue 实现 'IXamlSchemaContextProvider',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8731547/

相关文章:

c# - 如何在 VS 代码中检查 Debug.Writeline() 输出?

c# - 在 EF 的 DBSet 中设置返回对象的条件

c# - 有条件地绑定(bind)到 Xamarin XAML MVVM 中的两个不同命令

wpf - 确定 Selector.SelectionChanged 事件是否由用户发起

c# - 如何在XAML中将DataBinding设置为任意属性?

xml - 如何为 XAML 创建我自己的(非 CLR!)XML 命名空间?

xaml - 使用 VisualStateManager 对按钮的 ScaleTransform 进行动画处理

c# - 在应用 Mode-View-ViewModel 设计模式时包括部分 View

wpf - 在wpf中找到uielement的中心

wpf - 在WPF中的所有Windows中为按钮应用样式