如何在样式设置器中添加混合行为

我已经为Buttondevise了Blend行为。 我怎样才能将其设置为应用程序中的所有button。

<Button ...> <i:Interaction.Behaviors> <local:MyBehavior /> </i:Interaction.Behaviors> </Button> 

但是,当我尝试:

 <Style> <Setter Property="i:Interaction.Behaviors"> <Setter.Value> <local:MyBehavior /> </Setter.Value> </Setter> </Style> 

我得到错误

属性“行为”没有可访问的setter。

我有同样的问题,我想出了一个解决scheme。 我解决了这个问题之后,我发现了这个问题,我发现我的解决scheme和Mark的很多共同之处。 但是,这种方法有点不同。

主要的问题是行为和触发器与一个特定的对象相关联,所以你不能将同一个行为的实例用于多个不同的关联对象。 当你定义你的行为时,内联XAML强制执行这种一对一的关系。 但是,当您尝试在样式中设置行为时,可以将样式重用于所有适用的对象,这会在基本行为类中引发exception。 实际上,作者做了大量的努力来阻止我们甚至试图做到这一点,知道这是行不通的。

第一个问题是,我们甚至不能构造一个行为setter的值,因为构造函数是内部的。 所以我们需要自己的行为并触发集合类。

接下来的问题是行为和触发器附加属性没有setter,所以只能通过内联XAML添加。 这个问题我们解决了我们自己的附加属性,操纵主要行为和触发属性。

第三个问题是我们的行为收集只适合单一的风格目标。 这个我们通过利用less量使用的XAML特性x:Shared="False" ,每次被引用时都会创build一个新的资源副本。

最后的问题是,行为和触发器不像其他的风格制定者; 我们不想用新的行为取代旧的行为,因为他们可以做很大的不同的事情。 所以如果我们接受一旦你添加一个行为,你不能把它拿走(这是目前的行为方式),我们可以得出结论,行为和触发器应该是叠加的,这可以通过我们附加的属性来处理。

这里是一个使用这种方法的示例:

 <Grid> <Grid.Resources> <sys:String x:Key="stringResource1">stringResource1</sys:String> <local:Triggers x:Key="debugTriggers" x:Shared="False"> <i:EventTrigger EventName="MouseLeftButtonDown"> <local:DebugAction Message="DataContext: {0}" MessageParameter="{Binding}"/> <local:DebugAction Message="ElementName: {0}" MessageParameter="{Binding Text, ElementName=textBlock2}"/> <local:DebugAction Message="Mentor: {0}" MessageParameter="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type FrameworkElement}}}"/> </i:EventTrigger> </local:Triggers> <Style x:Key="debugBehavior" TargetType="FrameworkElement"> <Setter Property="local:SupplementaryInteraction.Triggers" Value="{StaticResource debugTriggers}"/> </Style> </Grid.Resources> <StackPanel DataContext="{StaticResource stringResource1}"> <TextBlock Name="textBlock1" Text="textBlock1" Style="{StaticResource debugBehavior}"/> <TextBlock Name="textBlock2" Text="textBlock2" Style="{StaticResource debugBehavior}"/> <TextBlock Name="textBlock3" Text="textBlock3" Style="{StaticResource debugBehavior}"/> </StackPanel> </Grid> 

该示例使用触发器,但行为的工作方式相同。 在这个例子中,我们显示:

  • 该样式可以应用于多个文本块
  • 几种types的数据绑定都可以正常工作
  • 在输出窗口中生成文本的debugging操作

这是一个示例行为,我们的DebugAction 。 更恰当地说,这是一个行动,但通过滥用语言,我们称之为行为,触发器和行为“行为”。

 public class DebugAction : TriggerAction<DependencyObject> { public string Message { get { return (string)GetValue(MessageProperty); } set { SetValue(MessageProperty, value); } } public static readonly DependencyProperty MessageProperty = DependencyProperty.Register("Message", typeof(string), typeof(DebugAction), new UIPropertyMetadata("")); public object MessageParameter { get { return (object)GetValue(MessageParameterProperty); } set { SetValue(MessageParameterProperty, value); } } public static readonly DependencyProperty MessageParameterProperty = DependencyProperty.Register("MessageParameter", typeof(object), typeof(DebugAction), new UIPropertyMetadata(null)); protected override void Invoke(object parameter) { Debug.WriteLine(Message, MessageParameter, AssociatedObject, parameter); } } 

最后,我们的集合和附加属性,使这一切工作。 通过类比Interaction.Behaviors ,您定位的属性被称为SupplementaryInteraction.Behaviors因为通过设置此属性,您将添加行为到Interaction.Behaviors和类似的触发器。

 public class Behaviors : List<Behavior> { } public class Triggers : List<TriggerBase> { } public static class SupplementaryInteraction { public static Behaviors GetBehaviors(DependencyObject obj) { return (Behaviors)obj.GetValue(BehaviorsProperty); } public static void SetBehaviors(DependencyObject obj, Behaviors value) { obj.SetValue(BehaviorsProperty, value); } public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached("Behaviors", typeof(Behaviors), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyBehaviorsChanged)); private static void OnPropertyBehaviorsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behaviors = Interaction.GetBehaviors(d); foreach (var behavior in e.NewValue as Behaviors) behaviors.Add(behavior); } public static Triggers GetTriggers(DependencyObject obj) { return (Triggers)obj.GetValue(TriggersProperty); } public static void SetTriggers(DependencyObject obj, Triggers value) { obj.SetValue(TriggersProperty, value); } public static readonly DependencyProperty TriggersProperty = DependencyProperty.RegisterAttached("Triggers", typeof(Triggers), typeof(SupplementaryInteraction), new UIPropertyMetadata(null, OnPropertyTriggersChanged)); private static void OnPropertyTriggersChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var triggers = Interaction.GetTriggers(d); foreach (var trigger in e.NewValue as Triggers) triggers.Add(trigger); } } 

在那里,通过样式应用function齐全的行为和触发器。

1.创build附加属性

 public static class DataGridCellAttachedProperties { //Register new attached property public static readonly DependencyProperty IsSingleClickEditModeProperty = DependencyProperty.RegisterAttached("IsSingleClickEditMode", typeof(bool), typeof(DataGridCellAttachedProperties), new UIPropertyMetadata(false, OnPropertyIsSingleClickEditModeChanged)); private static void OnPropertyIsSingleClickEditModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var dataGridCell = d as DataGridCell; if (dataGridCell == null) return; var isSingleEditMode = GetIsSingleClickEditMode(d); var behaviors = Interaction.GetBehaviors(d); var singleClickEditBehavior = behaviors.SingleOrDefault(x => x is SingleClickEditDataGridCellBehavior); if (singleClickEditBehavior != null && !isSingleEditMode) behaviors.Remove(singleClickEditBehavior); else if (singleClickEditBehavior == null && isSingleEditMode) { singleClickEditBehavior = new SingleClickEditDataGridCellBehavior(); behaviors.Add(singleClickEditBehavior); } } public static bool GetIsSingleClickEditMode(DependencyObject obj) { return (bool) obj.GetValue(IsSingleClickEditModeProperty); } public static void SetIsSingleClickEditMode(DependencyObject obj, bool value) { obj.SetValue(IsSingleClickEditModeProperty, value); } } 

2.创build一个行为

 public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell> { protected override void OnAttached() { base.OnAttached(); AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown; } protected override void OnDetaching() { base.OnDetaching(); AssociatedObject.PreviewMouseLeftButtonDown += DataGridCellPreviewMouseLeftButtonDown; } void DataGridCellPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e) { DataGridCell cell = sender as DataGridCell; if (cell != null && !cell.IsEditing && !cell.IsReadOnly) { if (!cell.IsFocused) { cell.Focus(); } DataGrid dataGrid = LogicalTreeWalker.FindParentOfType<DataGrid>(cell); //FindVisualParent<DataGrid>(cell); if (dataGrid != null) { if (dataGrid.SelectionUnit != DataGridSelectionUnit.FullRow) { if (!cell.IsSelected) cell.IsSelected = true; } else { DataGridRow row = LogicalTreeWalker.FindParentOfType<DataGridRow>(cell); //FindVisualParent<DataGridRow>(cell); if (row != null && !row.IsSelected) { row.IsSelected = true; } } } } } } 

3.创build一个样式并设置附加属性

  <Style TargetType="{x:Type DataGridCell}"> <Setter Property="Behaviors:DataGridCellAttachedProperties.IsSingleClickEditMode" Value="True"/> </Style> 

总结答案和这个伟大的文章混合行为样式 ,我来到这个通用的简短和方便的解决scheme:

我做了generics类,可以被任何行为inheritance。

 public class AttachableForStyleBehavior<TComponent, TBehavior> : Behavior<TComponent> where TComponent : System.Windows.DependencyObject where TBehavior : AttachableForStyleBehavior<TComponent, TBehavior> , new () { public static DependencyProperty IsEnabledForStyleProperty = DependencyProperty.RegisterAttached("IsEnabledForStyle", typeof(bool), typeof(AttachableForStyleBehavior<TComponent, TBehavior>), new FrameworkPropertyMetadata(false, OnIsEnabledForStyleChanged)); public bool IsEnabledForStyle { get { return (bool)GetValue(IsEnabledForStyleProperty); } set { SetValue(IsEnabledForStyleProperty, value); } } private static void OnIsEnabledForStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { UIElement uie = d as UIElement; if (uie != null) { var behColl = Interaction.GetBehaviors(uie); var existingBehavior = behColl.FirstOrDefault(b => b.GetType() == typeof(TBehavior)) as TBehavior; if ((bool)e.NewValue == false && existingBehavior != null) { behColl.Remove(existingBehavior); } else if ((bool)e.NewValue == true && existingBehavior == null) { behColl.Add(new TBehavior()); } } } } 

所以你可以简单地重复使用它,像这样的很多组件:

 public class ComboBoxBehaviour : AttachableForStyleBehavior<ComboBox, ComboBoxBehaviour> { ... } 

而在XAML中足以说明:

  <Style TargetType="ComboBox"> <Setter Property="behaviours:ComboBoxBehaviour.IsEnabledForStyle" Value="True"/> 

所以基本上,AttachableForStyleBehavior类做了xaml的东西,注册风格的每个组件的行为的实例。 有关更多详细信息,请参阅链接。

我还有一个想法,为了避免为每个行为创build一个附属的属性:

  1. 行为创build者界面:

     public interface IBehaviorCreator { Behavior Create(); } 
  2. 小帮手收集:

     public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { } 
  3. 辅助行为的助手类:

     public static class BehaviorInStyleAttacher { #region Attached Properties public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached( "Behaviors", typeof(BehaviorCreatorCollection), typeof(BehaviorInStyleAttacher), new UIPropertyMetadata(null, OnBehaviorsChanged)); #endregion #region Getter and Setter of Attached Properties public static BehaviorCreatorCollection GetBehaviors(TreeView treeView) { return (BehaviorCreatorCollection)treeView.GetValue(BehaviorsProperty); } public static void SetBehaviors( TreeView treeView, BehaviorCreatorCollection value) { treeView.SetValue(BehaviorsProperty, value); } #endregion #region on property changed methods private static void OnBehaviorsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { if (e.NewValue is BehaviorCreatorCollection == false) return; BehaviorCreatorCollection newBehaviorCollection = e.NewValue as BehaviorCreatorCollection; BehaviorCollection behaviorCollection = Interaction.GetBehaviors(depObj); behaviorCollection.Clear(); foreach (IBehaviorCreator behavior in newBehaviorCollection) { behaviorCollection.Add(behavior.Create()); } } #endregion } 
  4. 现在你的行为实现了IBehaviorCreator:

     public class SingleClickEditDataGridCellBehavior:Behavior<DataGridCell>, IBehaviorCreator { //some code ... public Behavior Create() { // here of course you can also set properties if required return new SingleClickEditDataGridCellBehavior(); } } 
  5. 现在在xaml中使用它:

     <Style TargetType="{x:Type DataGridCell}"> <Setter Property="helper:BehaviorInStyleAttacher.Behaviors" > <Setter.Value> <helper:BehaviorCreatorCollection> <behaviors:SingleClickEditDataGridCellBehavior/> </helper:BehaviorCreatorCollection> </Setter.Value> </Setter> </Style> 

我无法find原始文章,但我能够重新创build效果。

 #region Attached Properties Boilerplate public static readonly DependencyProperty IsActiveProperty = DependencyProperty.RegisterAttached("IsActive", typeof(bool), typeof(ScrollIntoViewBehavior), new PropertyMetadata(false, OnIsActiveChanged)); public static bool GetIsActive(FrameworkElement control) { return (bool)control.GetValue(IsActiveProperty); } public static void SetIsActive( FrameworkElement control, bool value) { control.SetValue(IsActiveProperty, value); } private static void OnIsActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var behaviors = Interaction.GetBehaviors(d); var newValue = (bool)e.NewValue; if (newValue) { //add the behavior if we don't already have one if (!behaviors.OfType<ScrollIntoViewBehavior>().Any()) { behaviors.Add(new ScrollIntoViewBehavior()); } } else { //remove any instance of the behavior. (There should only be one, but just in case.) foreach (var item in behaviors.ToArray()) { if (item is ScrollIntoViewBehavior) behaviors.Remove(item); } } } #endregion 
 <Style TargetType="Button"> <Setter Property="Blah:ScrollIntoViewBehavior.IsActive" Value="True" /> </Style> 

将个人行为/触发声明为资源:

 <Window.Resources> <i:EventTrigger x:Key="ET1" EventName="Click"> <ei:ChangePropertyAction PropertyName="Background"> <ei:ChangePropertyAction.Value> <SolidColorBrush Color="#FFDAD32D"/> </ei:ChangePropertyAction.Value> </ei:ChangePropertyAction> </i:EventTrigger> </Window.Resources> 

将它们插入集合中:

 <Button x:Name="Btn1" Content="Button"> <i:Interaction.Triggers> <StaticResourceExtension ResourceKey="ET1"/> </i:Interaction.Triggers> </Button> 

行为代码需要一个Visual,所以我们可以只在视觉上添加它。 所以我能看到的唯一select是添加到ControlTemplate中的一个元素,以便获得添加到样式的行为并影响特定控件的所有实例。

文章WPF中的“附加行为简介”实现了仅使用“样式”的附加行为,也可能是相关的或有帮助的。

“附加行为简介”文章中的技术完全避免了使用Style的Interactivity标签。 我不知道这是否仅仅是因为它是一个更为过时的技术,或者,如果在某些情况下仍然给予某些好处的话。

我喜欢Roman Dvoskin和Jonathan Allen在这个主题中的答案。 当我第一次学习这个技术的时候,我从这个博客文章中受益匪浅,它提供了关于这个技术的更多的解释。 为了在上下文中看到所有的东西,下面作者在他的博客文章中谈到的整个类的源代码