构建一个应用程序,它有一个自定义的“高对比度”主题用于户外使用,可以在运行时开关。通过合并和取消合并包含如下样式的资源字典,可以很好地工作。
<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可以并且将在运行时更改(当然,您不能在BasedOn属性上使用DynamicResource扩展)。目前,在我的应用程序中这样做会导致重写的控件在加载控件时停留在其样式上,而其他所有控件在不重新加载的情况下都会正确切换。
所以我的问题是。
有没有一种方法可以让DynamicResource扩展在BasedOn上工作,或者我可以实现另一种方法/黑客来让它工作?
发布于 2012-03-01 04:33:37
最后找出了一个使用AttachedDependencyProperty的Style.BasedOn的DynamicResouce的解决方案。
以下是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:Setting a local implicit style different from theme-style / alternative to BasedOn DynamicResource
发布于 2013-11-15 21:17:41
我对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您看,在创建新样式时,可以简单地设置基本样式。这样,来自基本样式的基本样式会自动出现在层次结构中。
发布于 2020-06-19 15:16:00
随着时间的推移,我变得越来越聪明,我想提供另一个选项来处理不同的外观,如果更改的主要是颜色(带有边框ext。应该也可以)。
创建您想要作为资源的颜色,当您为控件创建样式时,使用DynamicResources而不是StaticResource作为颜色,这就是整个技巧。
当你想改变“主题”时,只需加载/覆盖颜色资源即可。由于采用了动态绑定,样式将自动更新/使用最近的资源定义。
类似地,如果您希望某个区域看起来有所不同,只需通过在父控件的resources中设置新值来设置/覆盖颜色资源。
我希望这对其他人有用=0)
https://stackoverflow.com/questions/9490264
复制相似问题