Подтвердить что ты не робот

DynamicResource для Style BasedOn

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

<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, или есть другой метод/взлома, который я могу реализовать, чтобы заставить это работать?

4b9b3361

Ответ 1

Наконец, выяснилось решение для 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 устанавливает в моем ответе на другой пост: Настройка локального неявного стиля, отличного от стиля темы/альтернативы DynamicResource BasedOnn

Ответ 2

У меня есть небольшое улучшение для ответа 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

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

Ответ 3

Ваши стили должны быть в теге 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);