วิธีเพิ่มพฤติกรรมผสมผสานใน Style Setter


88

ฉันได้เสนอพฤติกรรมการผสมผสานสำหรับปุ่ม ฉันจะตั้งค่าให้เป็นปุ่มทั้งหมดในแอปได้อย่างไร

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

อย่างไรก็ตามเมื่อฉันลอง:

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

ฉันได้รับข้อผิดพลาด

คุณสมบัติ "Behaviors" ไม่มีตัวตั้งค่าที่สามารถเข้าถึงได้

คำตอบ:


76

ฉันมีปัญหาเดียวกันและหาทางแก้ไขได้ ฉันพบคำถามนี้หลังจากที่ฉันแก้ไขแล้วและฉันเห็นว่าวิธีแก้ปัญหาของฉันเหมือนกันมากกับของมาร์ค อย่างไรก็ตามแนวทางนี้แตกต่างกันเล็กน้อย

ปัญหาหลักคือพฤติกรรมและทริกเกอร์เชื่อมโยงกับอ็อบเจ็กต์เฉพาะดังนั้นคุณจึงไม่สามารถใช้อินสแตนซ์เดียวกันของพฤติกรรมสำหรับอ็อบเจ็กต์ที่เกี่ยวข้องหลาย ๆ อัน เมื่อคุณกำหนดพฤติกรรมของคุณ 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);
    }
}

และคุณก็มีพฤติกรรมและทริกเกอร์ที่ใช้งานได้อย่างสมบูรณ์ผ่านสไตล์


สิ่งที่ยอดเยี่ยมนี้ทำงานได้อย่างสวยงาม ฉันสังเกตว่าถ้าคุณใส่สไตล์เช่นในทรัพยากร UserControl e.NewValue อาจเป็นโมฆะในตอนแรก (อาจขึ้นอยู่กับการควบคุมที่ใช้ - ฉันใช้สิ่งนี้กับ XamDataTreeNodeControl ใน Infragistics XamDataTree) ดังนั้นฉันจึงเพิ่มการตรวจสอบความมีสติเล็กน้อยใน OnPropertyTriggersChanged: if (e.NewValue! = null)
MetalMikester

มีใครมีปัญหากับแนวทางนี้เมื่อใช้ Setter ในรูปแบบโดยนัยหรือไม่? ฉันได้รับมันให้ทำงานได้ดีกับรูปแบบที่ไม่เป็นนัย (แบบที่มีคีย์) แต่ฉันได้รับข้อยกเว้นการอ้างอิงแบบวนรอบหากอยู่ในรูปแบบโดยนัย
Jason Frank

1
ทางออกที่ดี แต่น่าเสียดายที่มันไม่ทำงานใน WinRT เนื่องจาก x: Shared ไม่มีอยู่บนแพลตฟอร์มนี้ ...
Thomas Levesque

1
ฉันสามารถยืนยันได้ว่าวิธีนี้ใช้ได้ผล ขอบคุณมากที่นำมาแบ่งปัน ฉันยังไม่ได้ลองใช้สไตล์โดยปริยายแม้ว่า
Golvellius

2
@ Jason Frank ขอบคุณเช่นเดียวกับการอ้างอิงสำหรับคนอื่น ๆ ... ฉันทำให้มันใช้ได้ทั้งสองกรณี: โดยนัยและชัดเจน อันที่จริงฉันถามคำถามที่ฉันจะใส่รหัสทั้งหมดของฉันเพื่อช่วยเหลือผู้อื่น แต่มีบางคนคาดว่าคำถามของฉันซ้ำกัน ฉันไม่สามารถตอบคำถามของตัวเองในทุกสิ่งที่ฉันพบ ฉันคิดว่าฉันได้ค้นพบสิ่งที่ดีงาม :-( ... ฉันหวังว่ามันจะไม่เกิดขึ้นบ่อยเกินไปเพราะพฤติกรรมดังกล่าวกีดกันผู้ใช้รายอื่นจากข้อมูลที่เป็นประโยชน์
Eric Ouellet

27

สรุปคำตอบและบทความที่ยอดเยี่ยมนี้ผสมผสานพฤติกรรมในสไตล์ฉันมาถึงวิธีแก้ปัญหาสั้น ๆ และสะดวกสบายทั่วไปนี้:

ฉันสร้างคลาสทั่วไปซึ่งสามารถสืบทอดได้จากพฤติกรรมใด ๆ

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());
                }    
            }
        }
    }

So you could simply reuse it with lot of components like this:

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

And in XAML enough to declare:

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

So basicly the AttachableForStyleBehavior class made xaml things, registering the instance of behavior for each component in style. For more details, please see the link.


Works like a charm! With my Scrollingbehavior combined i got rid of Inner RowDetailsTemplate-Datagrids not scrolling the parent Datagrids.
Philipp Michalski

Glad to help, enjoy=)
Roma Borodov

1
what about data binding with dependency properties in the Behavior?
JobaDiniz

I don't know how to contact user or decline edit with negative feedback personally. So dear @Der_Meister and other editors, please read code carefully before you trying to edit it. It could affect other users and my reputation too. In this case, by removing IsEnabledForStyle property and insistently replacing it with static methods, you destroying the possibility of binding to it in xaml, which is the main point of this question. So looks like you didn't read code till the end. Saddly I can't reject your editing with great minus, so just please be careful in future.
Roma Borodov

1
@RomaBorodov, everything works in XAML. It's a correct way to define attached property (which is different from dependency property). See documentation: docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/…
Der_Meister

19

1.Create Attached Property

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.Create a Behavior

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.Create a Style and set the attached property

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

When I try to access the DependencyProperty from the style it says IsSingleClickEditMode is not recognized or no accessible?
Igor Meszaros

Sorry my bad.. as soon as I commented I realised GetIsSingleClickEditMode should match the string you pass in to the DependencyProperty.RegisterAttached
Igor Meszaros

OnDetaching adds another event handler, this should be fixed (cannot modify a single character when editing a post...)
BalintPogatsa

11

I have another idea, to avoid the creation of a attached property for every behavior:

  1. Behavior creator interface:

    public interface IBehaviorCreator
    {
        Behavior Create();
    }
    
  2. Small helper collection:

    public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { }
    
  3. Helper class which attaches the behavior:

    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. Now your behavior, which implements 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. And now use it in xaml:

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

5

I couldn't find the original article but I was able to recreate the effect.

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

Having to write this for each behavior is a bit of a PITA though.
Stephen Drew

0

Behavior code expects a Visual, so we can add it only on a visual. So the only option I could see is to add to one of the element inside the ControlTemplate so as to get the behavior added to the Style and affect on all the instance of a particular control.


0

The article Introduction to Attached Behaviors in WPF implements an attached behavior using Style only, and may also be related or helpful.

The technique in the "Introduction to Attached Behaviors" article avoids the Interactivity tags altogether, using on Style. I don't know if this is just because it is a more dated technique, or, if that still confers some benefits where one should prefer it in some scenarios.


2
This isn't a Blend behavior, it's a "behavior" through a simple attached property.
Stephen Drew


0

Declare individual behavior/trigger as Resources :

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

Insert them in the collection :

<Button x:Name="Btn1" Content="Button">

        <i:Interaction.Triggers>
             <StaticResourceExtension ResourceKey="ET1"/>
        </i:Interaction.Triggers>

</Button>

4
How does it answer the OP? The trigger is not added through a style in your answer.
Kryptos

0

Based on this answer I made a simpler solution, with just one class needed and there is no need to implement something else in your behaviors.

public static class BehaviorInStyleAttacher
{
    #region Attached Properties

    public static readonly DependencyProperty BehaviorsProperty =
        DependencyProperty.RegisterAttached(
            "Behaviors",
            typeof(IEnumerable),
            typeof(BehaviorInStyleAttacher),
            new UIPropertyMetadata(null, OnBehaviorsChanged));

    #endregion

    #region Getter and Setter of Attached Properties

    public static IEnumerable GetBehaviors(DependencyObject dependencyObject)
    {
        return (IEnumerable)dependencyObject.GetValue(BehaviorsProperty);
    }

    public static void SetBehaviors(
        DependencyObject dependencyObject, IEnumerable value)
    {
        dependencyObject.SetValue(BehaviorsProperty, value);
    }

    #endregion

    #region on property changed methods

    private static void OnBehaviorsChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
    {
        if (e.NewValue is IEnumerable == false)
            return;

        var newBehaviorCollection = e.NewValue as IEnumerable;

        BehaviorCollection behaviorCollection = Interaction.GetBehaviors(depObj);
        behaviorCollection.Clear();
        foreach (Behavior behavior in newBehaviorCollection)
        {
            // you need to make a copy of behavior in order to attach it to several controls
            var copy = behavior.Clone() as Behavior;
            behaviorCollection.Add(copy);
        }
    }

    #endregion
}

and the sample usage is

<Style TargetType="telerik:RadComboBox" x:Key="MultiPeriodSelectableRadComboBox">
    <Setter Property="AllowMultipleSelection" Value="True" />
    <Setter Property="behaviors:BehaviorInStyleAttacher.Behaviors">
        <Setter.Value>
            <collections:ArrayList>
                <behaviors:MultiSelectRadComboBoxBehavior
                        SelectedItems="{Binding SelectedPeriods}"
                        DelayUpdateUntilDropDownClosed="True"
                        SortSelection="True" 
                        ReverseSort="True" />
            </collections:ArrayList>
        </Setter.Value>
    </Setter>
</Style>

Don't forget to add this xmlns to use ArrayList:

xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.