c# - online - xaml visual studio 2015




Проверка времени разработки аргументов расширения разметки в конструкторе WPF (2)

Я написал расширение 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);
        }

    }
}

Нет, к сожалению, мое понимание проблемы заключается в следующем,

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

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

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


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

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

Дело в том, что 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>

Наслаждаться ;)







visual-studio-designer