如何将样式设置器添加为静态资源的附加行为
How to add style setters as an attached behaviour from a static resource
我使用附加的依赖项 属性 创建了一个行为,以将常见的 SetterBaseCollection
提供给不同的相关样式。
该行为只是管理附加主机上的集合 属性 以使其与视图中的静态资源对齐。我只是将其连接到 AP PropertyChangedCallback,这对于我拥有的另一种行为来说效果很好,即填充 CommandBindings,但是当我将其应用于样式设置器时抛出。
错误是
System.Windows.Markup.XamlParseException occurred
_HResult=-2146233087
_message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '12' and line position '26'.
HResult=-2146233087
IsTransient=false
Message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '12' and line position '26'.
Source=PresentationFramework
LineNumber=12
LinePosition=26
StackTrace:
at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
InnerException: System.ArgumentNullException
_HResult=-2147467261
_message=Value cannot be null.
HResult=-2147467261
IsTransient=false
Message=Value cannot be null.
Parameter name: value
Source=PresentationFramework
ParamName=value
StackTrace:
at System.Windows.EventSetter.set_Event(RoutedEvent value)
InnerException:
在错误发生之前不会调用行为代码,因此它在 xaml.
中
风景
<Window.Resources>
<Thickness x:Key="ButtonMargin">6</Thickness>
<SetterBaseCollection x:Key="ButtonStyleSetters">
<EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
<Setter Property="FrameworkElement.Height" Value="30" />
<Setter Property="FrameworkElement.Margin" Value="6" />
</SetterBaseCollection>
<Style TargetType="{x:Type Button}"
b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
<Style TargetType="{x:Type ToggleButton}"
b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
<Style TargetType="{x:Type CheckBox}">
<EventSetter Event="Click" Handler="StyleClick" />
</Style>
<Style TargetType="{x:Type b:MultiCommandButton}"
b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
<Style TargetType="UniformGrid">
<Setter Property="Columns" Value="2" />
</Style>
<CommandBindingCollection x:Key="OutputToggleReceiveAll">
<b:OutputToggleBind />
<b:OutputToggleEnabledBind />
</CommandBindingCollection>
</Window.Resources>
行为
public static class Behaviours
{
#region AP CommandReceivers
public static readonly DependencyProperty CommandReceiversProperty =
DependencyProperty.RegisterAttached(
"CommandReceivers", typeof(CommandBindingCollection),
typeof(Behaviours),
new PropertyMetadata(default(CommandBindingCollection),
(t, a) => UpdateCollection<CommandBindingCollection, UIElement>
(t, a, "CommandBindings")));
public static void SetCommandReceivers(DependencyObject element,
CommandBindingCollection value)
{
element.SetValue(CommandReceiversProperty, value);
}
public static CommandBindingCollection GetCommandReceivers(
DependencyObject element)
{
return (CommandBindingCollection) element
.GetValue(CommandReceiversProperty);
}
#endregion
#region AP StyleSetters
public static readonly DependencyProperty StyleSettersProperty =
DependencyProperty.RegisterAttached(
"StyleSetters", typeof(SetterBaseCollection),
typeof(Behaviours),
new PropertyMetadata(default(SetterBaseCollection),
(t, a) => UpdateCollection<SetterBaseCollection, UIElement>
(t, a, "Setters")));
public static void SetStyleSetters (DependencyObject element,
SetterBaseCollection value)
{
element.SetValue(StyleSettersProperty, value);
}
public static SetterBaseCollection GetStyleSetters (
DependencyObject element)
{
return (SetterBaseCollection)element
.GetValue(StyleSettersProperty);
}
#endregion
private static void UpdateCollection<TCollection, THost> (
DependencyObject target, DependencyPropertyChangedEventArgs args,
string targetCollection)
where TCollection : IList
where THost : class
{
var host = target as THost;
if (host == null) return;
var collection = typeof(THost).GetProperty(targetCollection)
.GetValue(host) as IList;
if (collection == null) return;
if (args.OldValue != null)
{
foreach (var member in
(TCollection) args.OldValue)
{
collection.Remove(member);
}
}
if (args.NewValue != null)
{
foreach (var member in
(TCollection) args.NewValue)
{
collection.Add(member);
}
}
}
}
问题
我想我可能还必须添加一个 OnAttached
处理程序,但是我在 xaml 中做错了什么吗?令人讨厌的是找不到附加的 属性 但这通常不是问题,我希望它会在程序集成功编译后消失。
编辑
根据@mm8 和 this answer 的反馈,我尝试了以下方法,但也抛出错误。
查看
<Window.Resources>
<SetterBaseCollection x:Key="ButtonStyleSetters">
<EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
<Setter Property="FrameworkElement.Height" Value="30" />
<Setter Property="FrameworkElement.Margin" Value="6" />
</SetterBaseCollection>
<Style TargetType="{x:Type Button}" >
<Setter Property="local:Behaviours.StyleSetters"
Value="{StaticResource ButtonStyleSetters}"/>
</Style>
</Window.Resources>
在行为中class
public static readonly DependencyProperty StyleSettersProperty =
DependencyProperty.RegisterAttached(
"StyleSetters", typeof(SetterBaseCollection),
typeof(Behaviours),
new PropertyMetadata(default(SetterBaseCollection),
(t, a) => UpdateCollection<SetterBaseCollection, Style>
(((FrameworkElement)t).Style, a, "Setters")));
public static void SetStyleSetters (DependencyObject element,
SetterBaseCollection value)
{
element.SetValue(StyleSettersProperty, value);
}
public static SetterBaseCollection GetStyleSetters (
DependencyObject element)
{
return (SetterBaseCollection)element
.GetValue(StyleSettersProperty);
}
#endregion
private static void UpdateCollection<TCollection, THost> (
object target, DependencyPropertyChangedEventArgs args,
string targetCollection)
where TCollection : class, IList
where THost : class
{
var host = target as THost;
if (host == null) return;
var collection = typeof(THost).GetProperty(targetCollection)
.GetValue(host) as IList;
if (collection == null) return;
var oldValue = args.OldValue as TCollection;
if (oldValue != null)
{
foreach (var member in oldValue)
{
collection.Remove(member);
}
}
try
{
var newValue = args.NewValue as TCollection;
if (newValue != null)
{
foreach (var member in newValue)
{
collection.Add(member);
}
}
}
catch (Exception e)
{
Debug.WriteLine("{0} in UpdateCollection<TCollection, THost>");
}
}
StaticResource 的值始终为 null。
每次在按钮上设置 属性 并将按钮作为第一个参数传递时,都会触发 OnPropertyChanged 回调。
Style 不是 DependencyObject,因此您将无法在 Style 上设置附加 属性 的 StyleSetter。
我使用附加的依赖项 属性 创建了一个行为,以将常见的 SetterBaseCollection
提供给不同的相关样式。
该行为只是管理附加主机上的集合 属性 以使其与视图中的静态资源对齐。我只是将其连接到 AP PropertyChangedCallback,这对于我拥有的另一种行为来说效果很好,即填充 CommandBindings,但是当我将其应用于样式设置器时抛出。
错误是
System.Windows.Markup.XamlParseException occurred
_HResult=-2146233087
_message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '12' and line position '26'.
HResult=-2146233087
IsTransient=false
Message='Set property 'System.Windows.EventSetter.Event' threw an exception.' Line number '12' and line position '26'.
Source=PresentationFramework
LineNumber=12
LinePosition=26
StackTrace:
at System.Windows.Markup.XamlReader.RewrapException(Exception e, IXamlLineInfo lineInfo, Uri baseUri)
InnerException: System.ArgumentNullException
_HResult=-2147467261
_message=Value cannot be null.
HResult=-2147467261
IsTransient=false
Message=Value cannot be null.
Parameter name: value
Source=PresentationFramework
ParamName=value
StackTrace:
at System.Windows.EventSetter.set_Event(RoutedEvent value)
InnerException:
在错误发生之前不会调用行为代码,因此它在 xaml.
中风景
<Window.Resources>
<Thickness x:Key="ButtonMargin">6</Thickness>
<SetterBaseCollection x:Key="ButtonStyleSetters">
<EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
<Setter Property="FrameworkElement.Height" Value="30" />
<Setter Property="FrameworkElement.Margin" Value="6" />
</SetterBaseCollection>
<Style TargetType="{x:Type Button}"
b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
<Style TargetType="{x:Type ToggleButton}"
b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
<Style TargetType="{x:Type CheckBox}">
<EventSetter Event="Click" Handler="StyleClick" />
</Style>
<Style TargetType="{x:Type b:MultiCommandButton}"
b:Behaviours.StyleSetters="{StaticResource ButtonStyleSetters}" />
<Style TargetType="UniformGrid">
<Setter Property="Columns" Value="2" />
</Style>
<CommandBindingCollection x:Key="OutputToggleReceiveAll">
<b:OutputToggleBind />
<b:OutputToggleEnabledBind />
</CommandBindingCollection>
</Window.Resources>
行为
public static class Behaviours
{
#region AP CommandReceivers
public static readonly DependencyProperty CommandReceiversProperty =
DependencyProperty.RegisterAttached(
"CommandReceivers", typeof(CommandBindingCollection),
typeof(Behaviours),
new PropertyMetadata(default(CommandBindingCollection),
(t, a) => UpdateCollection<CommandBindingCollection, UIElement>
(t, a, "CommandBindings")));
public static void SetCommandReceivers(DependencyObject element,
CommandBindingCollection value)
{
element.SetValue(CommandReceiversProperty, value);
}
public static CommandBindingCollection GetCommandReceivers(
DependencyObject element)
{
return (CommandBindingCollection) element
.GetValue(CommandReceiversProperty);
}
#endregion
#region AP StyleSetters
public static readonly DependencyProperty StyleSettersProperty =
DependencyProperty.RegisterAttached(
"StyleSetters", typeof(SetterBaseCollection),
typeof(Behaviours),
new PropertyMetadata(default(SetterBaseCollection),
(t, a) => UpdateCollection<SetterBaseCollection, UIElement>
(t, a, "Setters")));
public static void SetStyleSetters (DependencyObject element,
SetterBaseCollection value)
{
element.SetValue(StyleSettersProperty, value);
}
public static SetterBaseCollection GetStyleSetters (
DependencyObject element)
{
return (SetterBaseCollection)element
.GetValue(StyleSettersProperty);
}
#endregion
private static void UpdateCollection<TCollection, THost> (
DependencyObject target, DependencyPropertyChangedEventArgs args,
string targetCollection)
where TCollection : IList
where THost : class
{
var host = target as THost;
if (host == null) return;
var collection = typeof(THost).GetProperty(targetCollection)
.GetValue(host) as IList;
if (collection == null) return;
if (args.OldValue != null)
{
foreach (var member in
(TCollection) args.OldValue)
{
collection.Remove(member);
}
}
if (args.NewValue != null)
{
foreach (var member in
(TCollection) args.NewValue)
{
collection.Add(member);
}
}
}
}
问题
我想我可能还必须添加一个 OnAttached
处理程序,但是我在 xaml 中做错了什么吗?令人讨厌的是找不到附加的 属性 但这通常不是问题,我希望它会在程序集成功编译后消失。
编辑
根据@mm8 和 this answer 的反馈,我尝试了以下方法,但也抛出错误。
查看
<Window.Resources>
<SetterBaseCollection x:Key="ButtonStyleSetters">
<EventSetter Event="ButtonBase.Click" Handler="StyleClick" />
<Setter Property="FrameworkElement.Height" Value="30" />
<Setter Property="FrameworkElement.Margin" Value="6" />
</SetterBaseCollection>
<Style TargetType="{x:Type Button}" >
<Setter Property="local:Behaviours.StyleSetters"
Value="{StaticResource ButtonStyleSetters}"/>
</Style>
</Window.Resources>
在行为中class
public static readonly DependencyProperty StyleSettersProperty =
DependencyProperty.RegisterAttached(
"StyleSetters", typeof(SetterBaseCollection),
typeof(Behaviours),
new PropertyMetadata(default(SetterBaseCollection),
(t, a) => UpdateCollection<SetterBaseCollection, Style>
(((FrameworkElement)t).Style, a, "Setters")));
public static void SetStyleSetters (DependencyObject element,
SetterBaseCollection value)
{
element.SetValue(StyleSettersProperty, value);
}
public static SetterBaseCollection GetStyleSetters (
DependencyObject element)
{
return (SetterBaseCollection)element
.GetValue(StyleSettersProperty);
}
#endregion
private static void UpdateCollection<TCollection, THost> (
object target, DependencyPropertyChangedEventArgs args,
string targetCollection)
where TCollection : class, IList
where THost : class
{
var host = target as THost;
if (host == null) return;
var collection = typeof(THost).GetProperty(targetCollection)
.GetValue(host) as IList;
if (collection == null) return;
var oldValue = args.OldValue as TCollection;
if (oldValue != null)
{
foreach (var member in oldValue)
{
collection.Remove(member);
}
}
try
{
var newValue = args.NewValue as TCollection;
if (newValue != null)
{
foreach (var member in newValue)
{
collection.Add(member);
}
}
}
catch (Exception e)
{
Debug.WriteLine("{0} in UpdateCollection<TCollection, THost>");
}
}
StaticResource 的值始终为 null。
每次在按钮上设置 属性 并将按钮作为第一个参数传递时,都会触发 OnPropertyChanged 回调。
Style 不是 DependencyObject,因此您将无法在 Style 上设置附加 属性 的 StyleSetter。