Jak dodać zachowanie mieszania w ustawieniach stylów

88

Utworzyłem zachowanie Blend dla przycisku. Jak mogę to ustawić dla wszystkich moich przycisków w aplikacji.

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

Jednak gdy próbuję:

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

Otrzymuję błąd

Właściwość „Zachowania” nie ma dostępnej metody ustawiającej.

Jobi Joy
źródło

Odpowiedzi:

76

Miałem ten sam problem i znalazłem rozwiązanie. Znalazłem to pytanie po rozwiązaniu go i widzę, że moje rozwiązanie ma wiele wspólnego z rozwiązaniem Marka. Jednak to podejście jest trochę inne.

Głównym problemem jest to, że zachowania i wyzwalacze są skojarzone z określonym obiektem, więc nie można użyć tego samego wystąpienia zachowania dla wielu różnych skojarzonych obiektów. Gdy definiujesz swoje zachowanie, wbudowany kod XAML wymusza tę relację jeden do jednego. Jednak gdy spróbujesz ustawić zachowanie w stylu, styl ten może zostać ponownie użyty dla wszystkich obiektów, do których ma zastosowanie, co spowoduje zgłoszenie wyjątków w podstawowych klasach zachowania. W rzeczywistości autorzy dołożyli wszelkich starań, aby powstrzymać nas przed próbą zrobienia tego, wiedząc, że to nie zadziała.

Pierwszym problemem jest to, że nie możemy nawet skonstruować wartości ustawiającej zachowanie, ponieważ konstruktor jest wewnętrzny. Potrzebujemy więc własnego zachowania i klas kolekcji wyzwalaczy.

Następnym problemem jest to, że właściwości związane z zachowaniem i wyzwalaczem nie mają metod ustawiających, dlatego można je dodawać tylko za pomocą wbudowanego kodu XAML. Ten problem rozwiązujemy za pomocą naszych własnych dołączonych właściwości, które manipulują podstawowym zachowaniem i właściwościami wyzwalacza.

Trzeci problem polega na tym, że nasza kolekcja zachowań jest dobra tylko dla jednego celu stylu. Ten problem rozwiązujemy, wykorzystując rzadko używaną funkcję XAML, x:Shared="False"która tworzy nową kopię zasobu za każdym razem, gdy występuje do niego odwołanie.

Ostatnim problemem jest to, że zachowania i wyzwalacze nie są podobne do innych osób wyznaczających styl; nie chcemy zastępować starych zachowań nowymi, ponieważ mogą robić bardzo różne rzeczy. Jeśli więc zaakceptujemy, że po dodaniu zachowania nie można go usunąć (i tak właśnie obecnie działają zachowania), możemy wywnioskować, że zachowania i wyzwalacze powinny być addytywne i można to obsłużyć za pomocą naszych dołączonych właściwości.

Oto próbka wykorzystująca to podejście:

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

W przykładzie zastosowano wyzwalacze, ale zachowania działają w ten sam sposób. W przykładzie pokazujemy:

  • styl można zastosować do wielu bloków tekstu
  • kilka typów powiązań danych działa poprawnie
  • akcja debugująca, która generuje tekst w oknie wyjściowym

Oto przykład zachowania, nasz DebugAction. Właściwie jest to działanie, ale poprzez nadużycie języka nazywamy zachowania, wyzwalacze i działania „zachowaniami”.

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

Wreszcie nasze kolekcje i dołączone właściwości, aby to wszystko działało. Analogicznie Interaction.Behaviorsdo docelowej właściwości jest wywoływana, SupplementaryInteraction.Behaviorsponieważ ustawiając tę ​​właściwość, dodasz zachowania do Interaction.Behaviorsi podobnie dla wyzwalaczy.

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

i oto masz w pełni funkcjonalne zachowania i wyzwalacze zastosowane poprzez style.

Rick Sladkey
źródło
Świetnie, to działa pięknie. Zauważyłem, że jeśli umieścisz styl, na przykład, w zasobach UserControl, to e.NewValue może być początkowo zerowa (może zależeć od użytej kontrolki - używam tego na XamDataTreeNodeControl w Infragistics XamDataTree). Więc dodałem trochę kontroli poczytalności w OnPropertyTriggersChanged: if (e.NewValue! = Null)
MetalMikester
Czy ktoś miał problem z tym podejściem podczas stosowania setera w niejawnym stylu? Udało mi się to dobrze współpracować ze stylem niejawnym (jednym z kluczem), ale otrzymuję cykliczny wyjątek odwołania, jeśli jest w stylu niejawnym.
Jason Frank
1
Fajne rozwiązanie, ale niestety nie działa w WinRT, ponieważ x: Shared nie istnieje na tej platformie ...
Thomas Levesque
1
Mogę potwierdzić, że to rozwiązanie działa. Bardzo dziękuję za udostępnienie. Jednak nie próbowałem jeszcze tego z ukrytym stylem.
Golvellius,
2
@Jason Frank, Dzięki, Tak jak odniesienia dla innych ... Zrobiłem to w obu przypadkach: niejawne i jawne. Właściwie zadaję pytanie, w którym umieściłbym cały swój kod, aby pomóc innym, ale ktoś ocenia, że ​​moje pytanie było duplikatem. Nie mogę odpowiedzieć na własne pytanie, podając wszystko, co znalazłem. Myślę, że odkrywam całkiem fajne rzeczy. :-( ... mam nadzieję, że nie zdarza się to zbyt często, ponieważ takie zachowanie pozbawia innych użytkowników przydatnych informacji.
Eric Ouellet
27

Podsumowując odpowiedzi i ten wspaniały artykuł Blend Behaviors in Styles , doszedłem do tego ogólnego, krótkiego i wygodnego rozwiązania:

Stworzyłem klasę ogólną, która może być odziedziczona przez dowolne zachowanie.

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

Możesz więc po prostu użyć go ponownie z wieloma komponentami takimi jak ten:

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

A w XAML wystarczy zadeklarować:

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

Tak więc zasadniczo klasa AttachableForStyleBehavior tworzyła elementy Xaml, rejestrując wystąpienie zachowania dla każdego składnika w stylu. Aby uzyskać więcej informacji, zobacz link.

Roma Borodov
źródło
Działa jak marzenie! Po połączeniu mojego Scrollingbehavior pozbyłem się Inner RowDetailsTemplate-Datagrids, które nie przewijają macierzystych Datagridów.
Philipp Michalski
Cieszę się, że
mogłem
1
a co z wiązaniem danych z właściwościami zależności w zachowaniu?
JobaDiniz
Nie wiem, jak skontaktować się z użytkownikiem lub osobiście odrzucić edycję z negatywną opinią. Tak więc drodzy @Der_Meister i inni redaktorzy, przeczytaj uważnie kod, zanim spróbujesz go edytować. Może to wpłynąć na innych użytkowników i moją reputację. W tym przypadku, usuwając właściwość IsEnabledForStyle i uparcie zastępując ją metodami statycznymi, niszczysz możliwość wiązania się z nią w xaml, co jest głównym punktem tego pytania. Wygląda na to, że nie przeczytałeś kodu do końca. Niestety nie mogę odrzucić Twojej edycji z wielkim minusem, więc po prostu bądź ostrożny w przyszłości.
Roma Borodov
1
@RomaBorodov, wszystko działa w XAML. Jest to poprawny sposób definiowania dołączonej właściwości (która różni się od właściwości zależności). Zobacz dokumentację: docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/…
Der_Meister
19

1. Utwórz dołączoną właściwość

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. Stwórz zachowanie

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. Utwórz styl i ustaw dołączoną właściwość

        <Style TargetType="{x:Type DataGridCell}">
            <Setter Property="Behaviors:DataGridCellAttachedProperties.IsSingleClickEditMode" Value="True"/>
        </Style>
Roman Dvoskin
źródło
Kiedy próbuję uzyskać dostęp do DependencyProperty ze stylu, w którym jest napisane, IsSingleClickEditMode nie jest rozpoznawany lub niedostępny?
Igor Meszaros
Przepraszam, moja wina ... jak tylko skomentowałem, zdałem sobie sprawę, że GetIsSingleClickEditMode powinien pasować do ciągu przekazanego do DependencyProperty.RegisterAttached
Igor Meszaros
OnDetaching dodaje kolejną procedurę obsługi zdarzeń, należy to naprawić (nie można modyfikować pojedynczego znaku podczas edytowania postu ...)
BalintPogatsa
11

Mam inny pomysł, aby uniknąć tworzenia dołączonej właściwości dla każdego zachowania:

  1. Interfejs kreatora zachowań:

    public interface IBehaviorCreator
    {
        Behavior Create();
    }
    
  2. Mała kolekcja pomocnicza:

    public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { }
    
  3. Klasa pomocnicza, która dołącza zachowanie:

    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. Teraz twoje zachowanie, które implementuje 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. A teraz użyj go w XAML:

    <Style TargetType="{x:Type DataGridCell}">
      <Setter Property="helper:BehaviorInStyleAttacher.Behaviors" >
        <Setter.Value>
          <helper:BehaviorCreatorCollection>
            <behaviors:SingleClickEditDataGridCellBehavior/>
          </helper:BehaviorCreatorCollection>
        </Setter.Value>
      </Setter>
    </Style>
    
I ja
źródło
5

Nie mogłem znaleźć oryginalnego artykułu, ale udało mi się odtworzyć efekt.

#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>
Jonathan Allen
źródło
Konieczność napisania tego dla każdego zachowania jest jednak trochę PITA.
Stephen Drew
0

Kod zachowania oczekuje wizualizacji, więc możemy dodać ją tylko do wizualizacji. Więc jedyną opcją, jaką mogłem zobaczyć, jest dodanie do jednego z elementów wewnątrz ControlTemplate, aby uzyskać zachowanie dodane do Style i wpłynąć na wszystkie wystąpienie określonej kontrolki.

Jobi Joy
źródło
0

Artykuł Wprowadzenie do dołączonych zachowań w WPF implementuje dołączone zachowanie tylko przy użyciu Style i może być również powiązany lub pomocny.

Technika opisana w artykule „Wprowadzenie do przywiązanych zachowań” całkowicie pozwala uniknąć tagów Interaktywność, używając stylu. Nie wiem, czy dzieje się tak tylko dlatego, że jest to bardziej przestarzała technika, czy też nadal zapewnia pewne korzyści, w przypadku których należy ją preferować w niektórych scenariuszach.

Rachunek
źródło
2
To nie jest zachowanie Blend, to „zachowanie” poprzez prostą dołączoną właściwość.
Stephen Drew
0

Podoba mi się podejście przedstawione w odpowiedziach Romana Dvoskina i Jonathana Allena w tym wątku. Jednak kiedy po raz pierwszy uczyłem się tej techniki, skorzystałem z tego wpisu na blogu, który zawiera więcej wyjaśnień na temat tej techniki. Aby zobaczyć wszystko w kontekście, oto cały kod źródłowy klasy, o którym autor mówi w swoim poście na blogu.

Jason Frank
źródło
0

Zadeklaruj indywidualne zachowanie / wyzwalacz jako zasoby:

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

Wstaw je do kolekcji:

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

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

</Button>
AnjumSKhan
źródło
4
Jak odpowiada na PO? Wyzwalacz nie jest dodawany poprzez styl w Twojej odpowiedzi.
Kryptos
0

Na podstawie tej odpowiedzi stworzyłem prostsze rozwiązanie, wymagające tylko jednej klasy i nie ma potrzeby implementowania czegoś innego w swoich zachowaniach.

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
}

a przykładowe użycie to

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

Nie zapomnij dodać tych xmlns, aby używać ArrayList:

xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
technopriest
źródło