想象一下我可以动态更改主题的wpf应用程序。我这样做是通过在Application-resource级别交换ResourceDictionaries。主题资源为TextBox等定义了隐式样式。
现在我在我的应用程序中有一个部分,其中文本框应该具有此特定样式“NonDefaultTextBoxStyle”而不是应用程序范围的隐式文本。
我很乐意这样做(使用DynamicResource,因为主题可以在运行时更改):
<StackPanel>
<StackPanel.Resources>
<Style TargetType="TextBox" BasedOn="{DynamicResource NonDefaultTextBoxStyle}"/>
</StackPanel.Resources>
<TextBox .../>
<TextBox .../>
<TextBox .../>
</StackPanel>
而不是必须这样做:
<StackPanel>
<TextBox Style="{DynamicResource NonDefaultTextBoxStyle}" .../>
<TextBox Style="{DynamicResource NonDefaultTextBoxStyle}" .../>
<TextBox Style="{DynamicResource NonDefaultTextBoxStyle}" .../>
</StackPanel>
现在为了简化这一点,我想到了在StackPanel上设置一个可继承的附加属性,它将在每个后代文本框上设置一个指定的样式。
这是个好主意吗?有更简单的方法吗?我错过了什么吗?
这很大程度上归结为:什么是样式中的BasedOn =“{DynamicResource ...}”的替代方案?
答案 0 :(得分:4)
我有完全相同的问题但是对于ItemsContainerStyle ...所以我做了很多你所说的并写了一个AttachedProperty,它将允许为BasedOn提供动态资源。
DynamicResource for Style BasedOn
以下是针对您的情况修改的解决方案:
public class DynamicStyle
{
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(DynamicStyle), new UIPropertyMetadata(DynamicStyle.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(DynamicStyle), new UIPropertyMetadata(DynamicStyle.StylesChanged));
private static void StylesChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
if (!typeof(FrameworkElement).IsAssignableFrom(target.GetType()))
throw new InvalidCastException("Target must be FrameworkElement");
var Element = (FrameworkElement)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.Style = 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 DynamicStyle class lives -->
<Button ap:DynamicStyle.BaseStyle="{DynamicResource {x:Type Button}}">
<ap:DynamicStyle.DerivedStyle>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=FirstButtonWarning}" Value="True"/>
<Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsEnabled}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Background" Value="{DynamicResource WarningBackground}"/>
<Setter Property="Foreground" Value="{DynamicResource WarningForeground}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</ap:DynamicStyle.DerivedStyle>
<TextBlock Text="Button that changes background and foreground when warning is active"/>
</Button>
答案 1 :(得分:3)
真的需要成为DynamicResource吗? 所以也许this会帮助你
如果不是这里是StaticResource的简单示例
App.xaml
<Application.Resources>
<Style x:Key="myResource" TargetType="Button">
<Setter Property="Background" Value="Black"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</Application.Resources>
<强> MainWindow.xaml 强>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button" BasedOn="{StaticResource myResource}"/>
</StackPanel.Resources>
<Button Content="blubb" Height="23" Width="75" />
</StackPanel>
如果两者都无济于事,您可以提供添加资源的方式
好的,现在应该回答你的问题
App.xaml
<Application.Resources>
<SolidColorBrush x:Key="background" Color="Red" />
<SolidColorBrush x:Key="foreground" Color="Blue" />
<Style x:Key="NonDefaultTextBoxStyle" >
<Setter Property="TextBox.Background" Value="{DynamicResource background}"/>
<Setter Property="TextBox.Foreground" Value="{DynamicResource foreground}"/>
</Style>
</Application.Resources>
<强> MainWindow.xaml 强>
<StackPanel>
<Button Content="Button" Height="23" Width="75" Click="Button_Click" />
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource NonDefaultTextBoxStyle}">
</Style>
</StackPanel.Resources>
<TextBox Text="bliii" Height="23" Width="75" />
<TextBox Text="blaaa" Height="23" Width="75" />
<TextBox Text="blubb" Height="23" Width="75" />
</StackPanel>
</StackPanel>
<强> MainWindow.cs 强>
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Resources["background"] = Brushes.Black;
this.Resources["foreground"] = Brushes.Yellow;
}