DynamicResource для стиля на основе - PullRequest
12 голосов
/ 29 февраля 2012

Создание приложения с настраиваемой темой «Высокая контрастность» для наружного использования, которую можно включать и выключать во время выполнения. Это отлично работает, объединяя и отменяя объединение словаря ресурсов, который содержит стили, как показано ниже ...

<Style x:Key="{x:Type MenuItem}" TargetType="{x:Type MenuItem}">
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
    <Setter Property="Template" Value="{StaticResource Theme_MenuItemTemplate}"/>
</Style>

Это прекрасно работает, когда использование меню не определяет стиль. Однако это нереально для многих ситуаций, поскольку невозможно связать сгенерированные дочерние элементы ItemsSource без стилей. Например:

<ContextMenu.ItemContainerStyle>
    <Style TargetType="MenuItem">
        <Setter Property="Header" Value="{Binding Path=Name}"/>
        <Setter Property="IsCheckable" Value="True"/>
        <Setter Property="IsChecked" Value="{Binding Path=Checked}"/>
        <EventSetter Event="Checked" Handler="HistoryItem_Checked"/>
    </Style>
</ContextMenu.ItemContainerStyle>

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

<Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
    <!-- Your overrides -->
</Style>

Но это не работает для моей ситуации, потому что мой BasedOn может и будет меняться во время выполнения (и, конечно, вы не можете использовать расширение DynamicResource для свойства BasedOn). Выполнение этого в моем приложении в настоящее время приводит к тому, что элементы управления, которые отменяют застревание в их стиле при загрузке элемента управления, в то время как каждый другой элемент управления правильно переключается без перезагрузки.

Так что мой вопрос ...

Есть ли способ заставить расширение DynamicResource работать для BasedOn или есть другой метод / хак, который я могу реализовать, чтобы заставить это работать?

Ответы [ 3 ]

7 голосов
/ 01 марта 2012

Наконец-то нашли решение для DynamicResouce для Style.BasedOn, используя AttachedDependencyProperty.

Вот исправление для ItemsControl.ItemContainerStyle (может быть легко изменено для изменения FrameworkElement.Style)

public class DynamicContainerStyle
{
    public static Style GetBaseStyle(DependencyObject obj)
    {
        return (Style)obj.GetValue(BaseStyleProperty);
    }

    public static void SetBaseStyle(DependencyObject obj, Style value)
    {
        obj.SetValue(BaseStyleProperty, value);
    }

    // Using a DependencyProperty as the backing store for BaseStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty BaseStyleProperty =
        DependencyProperty.RegisterAttached("BaseStyle", typeof(Style), typeof(DynamicContainerStyle), new UIPropertyMetadata(DynamicContainerStyle.StylesChanged));

    public static Style GetDerivedStyle(DependencyObject obj)
    {
        return (Style)obj.GetValue(DerivedStyleProperty);
    }

    public static void SetDerivedStyle(DependencyObject obj, Style value)
    {
        obj.SetValue(DerivedStyleProperty, value);
    }

    // Using a DependencyProperty as the backing store for DerivedStyle.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DerivedStyleProperty =
        DependencyProperty.RegisterAttached("DerivedStyle", typeof(Style), typeof(DynamicContainerStyle), new UIPropertyMetadata(DynamicContainerStyle.StylesChanged));

    private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        if (!typeof(System.Windows.Controls.ItemsControl).IsAssignableFrom(target.GetType()))
            throw new InvalidCastException("Target must be ItemsControl");

        var Element = (System.Windows.Controls.ItemsControl)target;

        var Styles = new List<Style>();

        var BaseStyle = GetBaseStyle(target);

        if (BaseStyle != null)
            Styles.Add(BaseStyle);

        var DerivedStyle = GetDerivedStyle(target);

        if (DerivedStyle != null)
            Styles.Add(DerivedStyle);

        Element.ItemContainerStyle = MergeStyles(Styles);
    }

    private static Style MergeStyles(ICollection<Style> Styles)
    {
        var NewStyle = new Style();

        foreach (var Style in Styles)
        {
            foreach (var Setter in Style.Setters)
                NewStyle.Setters.Add(Setter);

            foreach (var Trigger in Style.Triggers)
                NewStyle.Triggers.Add(Trigger);
        }

        return NewStyle;
    }
}

А вот пример ...

<!-- xmlns:ap points to the namespace where DynamicContainerStyle class lives -->
<MenuItem Header="Recent" 
    ItemsSource="{Binding Path=RecentFiles}"
    IsEnabled="{Binding RelativeSource={RelativeSource Self}, Path=HasItems}"
    ap:DynamicContainerStyle.BaseStyle="{DynamicResource {x:Type MenuItem}}">
    <ap:DynamicContainerStyle.DerivedStyle>
        <Style TargetType="MenuItem">
            <EventSetter Event="Click"  Handler="RecentFile_Clicked"/>
        </Style>
    </ap:DynamicContainerStyle.DerivedStyle>
    <MenuItem.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}"/>
        </DataTemplate>
    </MenuItem.ItemTemplate>
</MenuItem>

Вот измененная версия, которая устанавливает FrameworkElement.Style вместо этого в моем ответе на другой пост: Установка локального неявного стиля, отличного от стиля темы / альтернативы BasedOn DynamicResource

3 голосов
/ 15 ноября 2013

У меня небольшое улучшение для ответа NtscCobalts:

    #region Type-specific function (FrameworkElement)
    private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
    {
        var mergedStyles = GetMergedStyles<FrameworkElement>(target, GetBaseStyle(target), GetDerivedStyle(target)); // NOTE: change type on copy

        var element = (FrameworkElement)target; // NOTE: change type on copy

        element.Style = mergedStyles;
    }
    #endregion Type-specific function (FrameworkElement)


    #region Reused-function 
    public static Style GetMergedStyles<T>(DependencyObject target, Style baseStyle, Style derivedStyle) where T : DependencyObject
    {
        if (!(target is T)) throw new InvalidCastException("Target must be " + typeof(T));

        if (derivedStyle == null) return baseStyle;
        if (baseStyle == null) return derivedStyle;

        var newStyle = new Style { BasedOn = baseStyle, TargetType = derivedStyle.TargetType };
        foreach (var setter in derivedStyle.Setters) newStyle.Setters.Add(setter);
        foreach (var trigger in derivedStyle.Triggers) newStyle.Triggers.Add(trigger);
        return newStyle;

    }
    #endregion Reused-function

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

1 голос
/ 29 февраля 2012

Ваши стили должны быть в теге UIElement.Resources.Это может быть динамически очищено и повторно заполнено.

Я делаю что-то похожее, но не такое сложное, как это:

MobileApp.Get().Resources.MergedDictionaries.Clear();

Uri uri = new Uri("/Resources/DayModeButton.xaml", UriKind.Relative);
ResourceDictionary resDict = Application.LoadComponent(uri) as ResourceDictionary;

resDict["SelectedColor"] = selectedColor; //change an attribute of resdict

MobileApp.Get().Resources.MergedDictionaries.Add(resDict);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...