如何在样式二传手添加混合行为如何在样式二传手添加混合行为(How to add a Blend Be

2019-05-08 15:33发布

我已经装箱的按钮混合行为。 我怎么可以设置所有我的应用程序的按钮。

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

然而,当我尝试:

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

我得到的错误

属性“行为”不具有可访问的制定者。

Answer 1:

我有同样的问题,我已经想出了一个解决方案。 我发现这个问题后,我解决了它,我看到我的解决方案蕴藏着很多共同的马克。 然而,这种方法有点不同。

主要的问题是,行为和触发器与相关联的特定对象,所以你不能使用多个不同的关联对象行为的同一个实例。 当你定义自己的行为内嵌XAML强制执行此一比一的关系。 但是,当您尝试设置在样式行为,样式可以重新使用它适用于所有对象,这将扔在基本行为的类例外。 事实上,作者去相当大的努力,以阻止我们甚至试图做到这一点,知道这是行不通的。

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

接下来的问题是,行为和触发附加属性没有制定者,所以他们只能被添加到在线XAML。 这个问题我们与操纵的主要行为和触发器属性自己的附加属性解决。

第三个问题是,我们的行为集合是只针对单一型靶好。 这是我们解决了利用很少使用XAML的功能x:Shared="False" ,其创建的每个被引用的时间资源的新副本。

最后一个问题是,行为和触发器不像其他的风格制定者; 我们不希望以取代旧的行为与新的行为,因为他们可以做的疯狂不同的事情。 因此,如果我们承认,一旦你添加一个行为,你不能把它拿走(这就是行为目前的工作方式),我们可以得出结论,行为和触发器应该是添加剂,这可以通过我们的附加属性进行处理。

下面是使用这种方法的例子:

<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>

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

  • 风格可以适用于多个文本块
  • 几种类型的数据的正确绑定的所有工作
  • 在输出窗口中生成的文本调试动作

下面是一个例子行为,我们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);
    }
}

有你有它,全功能的行为,并触发应用通过样式。



Answer 2:

总结答案,这篇大文章的风格混合行为 ,我来到这个普通的短,方便易的解决方案:

我做了泛型类,这可以通过任何行为被继承。

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"/>

所以basicly的AttachableForStyleBehavior类所做的事情XAML,登记行为的实例在风格上每个组件。 有关详细信息,请参阅链接。



Answer 3:

1.创建附加属性

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.创建一个行为

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.创建一个样式,并设置附加属性

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


Answer 4:

我还有一个想法,以避免附加属性的每一个行为的产生:

  1. 行为创造者界面:

     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> 


Answer 5:

我无法找到原来的文章,但我能够重新创建的效果。

#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>


Answer 6:

声明个体行为/触发的资源:

<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>


Answer 7:

行为的代码需要一个视觉,所以我们只能在视觉添加。 所以我能看到的唯一选择是添加到控件模板内的元素之一,以获得行为添加到样式上的具体控制的所有实例的影响。



Answer 8:

文章介绍附加行为WPF中只使用风格实现了一个附加的行为,也可以与或有帮助。

在“简介附加行为”一文的技术避免了交互标签干脆,使用上的样式。 我不知道这是否只是因为它是一个比较过时的技术,或者,如果仍然赋予了一些好处,其中一个应该更喜欢它在某些情况下。



Answer 9:

我喜欢在这个线程的答案被罗马Dvoskin和乔纳森·艾伦所示的方法。 当我第一次学习这种技术虽然,我受益匪浅这个博客帖子 ,提供有关的技术更多的解释。 而看到的来龙去脉, 这里是整个源代码的类,在他的博客关于作者会谈。



文章来源: How to add a Blend Behavior in a Style Setter