Проверка времени разработки аргументов расширения разметки в дизайнере WPF

Я написал расширение Markup для WPF, которое позволяет мне делать

<!-- Generic Styles -->
<Style x:Key="bold" TargetType="Label">
    <Setter Property="FontWeight" Value="ExtraBold" />
</Style>

<Style x:Key="italic" TargetType="Label">
    <Setter Property="FontStyle" Value="Italic" />
</Style>

<Style x:Key="gridHeader" TargetType="Label" 
    BasedOn="{WPF:CombiStyle bold italic }" >

Это очень полезное расширение, и оно отлично работает во время выполнения. Однако во время разработки я не могу видеть применяемые стили или что если Я ошибаюсь, жирный и курсивный, они не могут быть найдены как StaticResources.

Любые хаки, которые я могу сделать, чтобы заставить это работать?

Исходный код расширения

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;

namespace MarkupExtensions
{
    [MarkupExtensionReturnType(typeof(Style))]
    public class CombiStyleExtension : MarkupExtension
    {

        private string[] MergeStyleProviders { get; set; }

        public CombiStyleExtension(string s0)
        { 
            MergeStyleProviders = s0.Split(new[]{' '});
        }

        public override object ProvideValue(IServiceProvider
                                            serviceProvider)
        {
            return MergeStyleProviders
                .Select(x => StringToStyle(serviceProvider, x))
                .Aggregate(new Style(), RecursivelyMergeStyles);
        }

        private static Style StringToStyle(IServiceProvider serviceProvider, string x)
        {
            var style = new StaticResourceExtension() { ResourceKey = x }.ProvideValue(serviceProvider) as Style;
            if (style==null)
            {
                throw new ArgumentException("Argument could not be converted to a style");
            }
            return style;
        }

        private static Style RecursivelyMergeStyles(Style accumulator,
                                           Style next)
        {
            if (next.BasedOn != null)
            {
                RecursivelyMergeStyles(accumulator, next.BasedOn);
            }

            MergeStyle(accumulator, next);

            return accumulator;
        }

        private static void MergeStyle(Style targetStyle, Style sourceStyle)
        {
            targetStyle.TargetType = sourceStyle.TargetType;
            // Merge the Setters...
            foreach (var setter in sourceStyle.Setters)
                targetStyle.Setters.Add(setter);

            // Merge the Triggers...
            foreach (var trigger in sourceStyle.Triggers)
                targetStyle.Triggers.Add(trigger);
        }

    }
}

Ответы

Ответ 1

Обновление: добавлены скриншоты для VS2012 (отлично работает) и Blend для VS2012 (работает частично: базовые стили на основе BasedOn-стилей не правильно поднял по какой-то причине).

Также проверили его в VS2013 Preview и Blend for VS2013 Preview - там он работает частично и точно так же, как в Blend для VS2012. Надеюсь, они исправит это в выпуске.

Works in VS2012 as good as in VS2010

In Blend for VS2012 it works partionally

Дело в том, что дизайнеру Visual Studio очень нравится, когда объект, который вы пытаетесь описать в XAML, имеет встроенный конструктор по умолчанию, который он использует для создания экземпляра времени разработки этого объекта.

Я немного обновил ваш класс CombiStyleExtension.cs, чтобы учесть это, и конструктор Visual Studio 2010, как сейчас. Однако разработчику Blend 4 все равно не жаль.

Посмотрите:

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

namespace WpfApplication7
{
    [MarkupExtensionReturnType(typeof(Style))]
    public class CombiStyleExtension : MarkupExtension
    {
        /// <summary>
        /// Set space-separated style names i.e. "size16 grey verdana".
        /// </summary>
        public string Names { private get; set; }

        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return Names.Split(new[] { ' ' })
                        .Select(x => Application.Current.TryFindResource(x)
                            as Style)
                        .Aggregate(new Style(), RecursivelyMergeStyles);
        }

        private static Style RecursivelyMergeStyles(Style accumulator,
                                                    Style next)
        {
            if(accumulator == null || next == null)
                return accumulator;

            if(next.BasedOn != null)
                RecursivelyMergeStyles(accumulator, next.BasedOn);

            MergeStyle(accumulator, next);

            return accumulator;
        }

        private static void MergeStyle(Style targetStyle, Style sourceStyle)
        {
            if(targetStyle == null || sourceStyle == null)
            {
                return;
            }

            targetStyle.TargetType = sourceStyle.TargetType;

            // Merge the Setters...
            foreach(var setter in sourceStyle.Setters)
                targetStyle.Setters.Add(setter);

            // Merge the Triggers...
            foreach(var trigger in sourceStyle.Triggers)
                targetStyle.Triggers.Add(trigger);
        }
    }
}

Использование этого расширения разметки изменилось также немного. Как это было:

BasedOn="{WPF:CombiStyle bold italic }"

и как это теперь:

BasedOn="{WPF:CombiStyle Names='bold italic'}"

И просто для того, чтобы сэкономить некоторое время для вас, вам нужно немного xaml для копирования-вставки-запуска-просмотра:

MainWindow.xaml

<Window x:Class="WpfApplication7.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:WPF="clr-namespace:WpfApplication7"
        Title="MainWindow" Height="350" Width="569">
    <Window.Resources>
        <!-- Did not managed to make the type-level style work -->
        <!-- from app.xaml, so put it here. Just in case. -->
        <Style TargetType="{x:Type Label}"
                  BasedOn="{WPF:CombiStyle Names='size16 grey verdana'}" />
    </Window.Resources>
    <StackPanel>
        <Label Content="Type-level: size16 + grey + verdana" />
        <Label Content="'h1': size24 + royalBlue" Style="{DynamicResource h1}" />
        <Label Content="'warning': size24 + yellow + bold + shadow"
                   Style="{DynamicResource warning}" />
        <Label Content="Inline: size12 + italic"
                   Style="{WPF:CombiStyle Names='size12 italic'}" />
        <Label Content="Inline: size16 + bold + italic + red"
                   Style="{WPF:CombiStyle Names='size16 bold italic red'}" />
        <Label Content="Inline: size24 + green"
                   Style="{WPF:CombiStyle Names='size24 green'}" />
    </StackPanel>
</Window>

App.xaml

<Application x:Class="WpfApplication7.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:WPF="clr-namespace:WpfApplication7"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <!-- Sizes -->
        <Style x:Key="size12" TargetType="Label">
            <Setter Property="FontSize" Value="12" />
        </Style>
        <Style x:Key="size16" TargetType="Label">
            <Setter Property="FontSize" Value="16" />
        </Style>
        <Style x:Key="size24" TargetType="Label">
            <Setter Property="FontSize" Value="24" />
        </Style>
        <!-- Bold/Italic -->    
        <Style x:Key="bold" TargetType="Label">
            <Setter Property="FontWeight" Value="ExtraBold" />
        </Style>
        <Style x:Key="italic" TargetType="Label">
            <Setter Property="FontStyle" Value="Italic" />
        </Style>
        <!-- Colors --> 
        <Style x:Key="grey" TargetType="Label">
            <Setter Property="Foreground" Value="#333333" />
        </Style>
        <Style x:Key="royalBlue" TargetType="Label">
            <Setter Property="Foreground" Value="RoyalBlue" />
        </Style>
        <Style x:Key="green" TargetType="Label">
            <Setter Property="Foreground" Value="Green" />
        </Style>
        <Style x:Key="yellow" TargetType="Label">
            <Setter Property="Foreground" Value="Yellow" />
        </Style>
        <Style x:Key="red" TargetType="Label">
            <Setter Property="Foreground" Value="#D00000" />
        </Style>
        <!-- Fonts -->  
        <Style x:Key="verdana" TargetType="Label">
            <Setter Property="FontFamily" Value="Verdana" />
        </Style>
        <!-- Effects -->
        <Style x:Key="shadow" TargetType="Label">
            <Setter Property="Effect">
                <Setter.Value>
                    <DropShadowEffect ShadowDepth="0" />
                </Setter.Value>
            </Setter>
        </Style>
        <!-- Predefined Combinations -->
        <Style x:Key="h1" TargetType="{x:Type Label}"
               BasedOn="{WPF:CombiStyle Names='size24 royalBlue'}" />
        <Style x:Key="warning" TargetType="{x:Type Label}"
               BasedOn="{WPF:CombiStyle Names='size24 yellow bold shadow'}">
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="BorderBrush" Value="Yellow" />
        </Style>
    </Application.Resources>
</Application>

Наслаждайтесь;)

Ответ 2

Нет. К сожалению, мое понимание проблемы таково:

Временное разрешение дизайна, похоже, работает в WPF из-за свойств зависимостей. Поскольку MarkupExtension не выводится из объекта зависимостей, ваше расширение никогда не оценивается во время разработки. Что касается погоды, то это был недосмотр или преднамеренный спор.

Возможно, есть другой способ решить эту проблему, хотя это будет несколько иначе. Создайте новое прикрепленное свойство, называемое MergeStyles. В этом свойстве вы можете указать имена стилей, которые вы хотите объединить и применить. Когда значение изменяется, просто обновите стиль атташе, используя свой код выше. Вы можете использовать положение, в котором каждый стиль будет объединен, чтобы определить иерархию.

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