Расширение разметки «StaticResourceExtension» требует, чтобы «IXamlSchemaContextProvider» был реализован в IServiceProvider для ProvideValue - PullRequest
4 голосов
/ 04 января 2012

Расширение ресурса, которое я использовал в течение нескольких лет, перестало работать во время разработки в новом проекте .Net 4 со следующей ошибкой:

Расширение разметки 'StaticResourceExtension' требует 'IXamlSchemaContextProvider'быть реализованным в 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);, сервис-провайдер, которого я передаю, пропускает информацию 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];
        }
    }
}

Ответы [ 4 ]

2 голосов
/ 22 июня 2016

Вместо использования StaticResourceExtension для получения стиля из статических ресурсов вы можете вместо этого получить стиль из статических ресурсов текущего приложения, что делает то же самое.

Заменить проблемную строку:

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

с:

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

(Примечание: см. мой другой ответ о том, почему использовать TryFindResource(key) вместо Resource[key])

2 голосов
/ 30 марта 2012

Я сталкиваюсь с той же проблемой. Некоторая дополнительная информация:

Во время выполнения значение IServiceProvider имеет тип "MS.Internal.Xaml.ServiceProviderContext".

В Visual Studio Xaml Designer значение IServiceProvider имеет тип "Microsoft.Expression.DesignModel.Core.InstanceBuilderOperations.InstanceBuilderServiceProvider".

Очевидно, что VS2010 использует классы из Expression Blend для обеспечения лучшего поведения во время разработки, чем VS2008 - но по цене, потому что классы Expression Blend не имеют всю ту же информацию, что и реальная система времени выполнения.

ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ: Я попытался вставить свой собственный класс, который реализует как IServiceProvider, так и IXamlSchemaContextProvider. Некоторые вызовы передаются исходному IServiceProvider, и я предоставляю фиктивный (пустой) XamlSchemaContext по запросу. Но я все еще получаю ту же ошибку.

Что-то внутри WPF инкапсулирует мой IServiceProvider с другим типом IServiceProvider - но тот, который не может реализовать IXamlSchemaContextProvider. У меня нет дальнейших идей о том, как решить эту проблему.

1 голос
/ 29 ноября 2012

У меня была такая же проблема с VS2010 Designer, чтобы решить ее, нужно изменить этот метод:

public override object ProvideValue(IServiceProvider serviceProvider)
{
    /* if in design-mode return null */
    if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(new DependencyObject()))
        return null;

    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;
}
0 голосов
/ 08 февраля 2012

Это происходит потому, что ProvideValue генерирует исключение во время разработки, но не делает во время выполнения. Поэтому дизайнер Visual Studio не может получить значение из расширения.

Строка проблемы

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

Visual Studio не предоставляет правильное значение для параметра serviceProvider, поэтому выдается исключение.

Используйте свойство NativeApi.IsInDesignMode, чтобы отключить часть кода во время разработки.

...