So fügen Sie einem Stylesetter ein Mischverhalten hinzu

86

Ich habe ein Mischverhalten für Button erstellt. Wie kann ich das auf alle meine Schaltflächen in der App einstellen?

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

Wenn ich jedoch versuche:

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

Ich bekomme den Fehler

Die Eigenschaft "Verhalten" hat keinen zugänglichen Setter.

Jobi Joy
quelle

Antworten:

75

Ich hatte das gleiche Problem und habe eine Lösung gefunden. Ich habe diese Frage gefunden, nachdem ich sie gelöst habe, und ich sehe, dass meine Lösung viel mit der von Mark gemeinsam hat. Dieser Ansatz ist jedoch etwas anders.

Das Hauptproblem besteht darin, dass Verhaltensweisen und Auslöser einem bestimmten Objekt zugeordnet sind und Sie daher nicht dieselbe Instanz eines Verhaltens für mehrere verschiedene zugeordnete Objekte verwenden können. Wenn Sie Ihr Verhalten inline definieren, erzwingt XAML diese Eins-zu-Eins-Beziehung. Wenn Sie jedoch versuchen, ein Verhalten in einem Stil festzulegen, kann der Stil für alle Objekte wiederverwendet werden, auf die er angewendet wird, und dies löst Ausnahmen in den Basisverhaltensklassen aus. Tatsächlich haben die Autoren erhebliche Anstrengungen unternommen, um zu verhindern, dass wir dies überhaupt versuchen, da wir wussten, dass es nicht funktionieren würde.

Das erste Problem ist, dass wir nicht einmal einen Verhaltenssetzwert erstellen können, da der Konstruktor intern ist. Wir brauchen also unser eigenes Verhalten und lösen Sammlungsklassen aus.

Das nächste Problem besteht darin, dass das Verhalten und die angehängten Trigger-Eigenschaften keine Setter haben und daher nur mit Inline-XAML hinzugefügt werden können. Dieses Problem lösen wir mit unseren eigenen angehängten Eigenschaften, die das primäre Verhalten manipulieren und Eigenschaften auslösen.

Das dritte Problem ist, dass unsere Verhaltenssammlung nur für ein einzelnes Stilziel geeignet ist. Dies lösen wir, indem wir eine wenig genutzte XAML-Funktion verwenden, x:Shared="False"die bei jedem Verweis eine neue Kopie der Ressource erstellt.

Das letzte Problem ist, dass Verhalten und Auslöser nicht wie bei anderen Stilgebern sind. Wir wollen die alten Verhaltensweisen nicht durch die neuen ersetzen, weil sie ganz andere Dinge tun könnten. Wenn wir also akzeptieren, dass Sie ein einmal hinzugefügtes Verhalten nicht mehr entfernen können (und so funktionieren Verhaltensweisen derzeit), können wir daraus schließen, dass Verhalten und Auslöser additiv sein sollten und dies durch unsere angehängten Eigenschaften behandelt werden kann.

Hier ist ein Beispiel für diesen Ansatz:

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

In diesem Beispiel werden Trigger verwendet, aber das Verhalten funktioniert genauso. Im Beispiel zeigen wir:

  • Der Stil kann auf mehrere Textblöcke angewendet werden
  • Verschiedene Arten der Datenbindung funktionieren ordnungsgemäß
  • Eine Debug-Aktion, die Text im Ausgabefenster generiert

Hier ist ein Beispiel für unser Verhalten DebugAction. Eigentlich ist es eine Handlung, aber durch den Missbrauch der Sprache nennen wir Verhaltensweisen, Auslöser und Handlungen "Verhaltensweisen".

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

Schließlich unsere Sammlungen und angehängten Eigenschaften, damit dies alles funktioniert. In Analogie zu Interaction.Behaviorswird die Eigenschaft aufgerufen, auf die Sie abzielen, SupplementaryInteraction.Behaviorsda Sie durch Festlegen dieser Eigenschaft Verhaltensweisen zu Interaction.Behaviorsund ebenfalls für Trigger hinzufügen .

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

und da haben Sie es, voll funktionsfähige Verhaltensweisen und Trigger, die durch Stile angewendet werden.

Rick Sladkey
quelle
Tolles Zeug, das funktioniert wunderbar. Ich habe festgestellt, dass, wenn Sie den Stil beispielsweise in die UserControl-Ressourcen einfügen, e.NewValue zunächst möglicherweise null ist (dies hängt möglicherweise vom verwendeten Steuerelement ab - ich verwende dies für das XamDataTreeNodeControl in einem Infragistics XamDataTree). Also habe ich in OnPropertyTriggersChanged eine kleine Überprüfung der Gesundheit hinzugefügt: if (e.NewValue! = Null)
MetalMikester
Hat jemand ein Problem mit diesem Ansatz gehabt, als er den Setter in einem impliziten Stil angewendet hat? Ich habe dafür gesorgt, dass es mit einem nicht impliziten Stil (einer mit einem Schlüssel) gut funktioniert, aber ich erhalte eine zyklische Referenzausnahme, wenn es sich um einen impliziten Stil handelt.
Jason Frank
1
Gute Lösung, aber leider funktioniert es nicht in WinRT, weil x: Shared auf dieser Plattform nicht existiert ...
Thomas Levesque
1
Ich kann bestätigen, dass diese Lösung funktioniert. Vielen Dank für das Teilen. Ich habe es jedoch noch nicht mit einem impliziten Stil versucht.
Golvellius
2
@ Jason Frank, danke, genau wie Referenzen für andere ... Ich habe dafür gesorgt, dass es in beiden Fällen funktioniert: implizit und explizit. Tatsächlich stelle ich eine Frage, bei der ich meinen gesamten Code abgelegt hätte, um anderen zu helfen, aber jemand schätzt, dass meine Frage ein Duplikat war. Ich kann meine eigene Frage nicht beantworten und alles geben, was ich gefunden habe. Ich denke, ich entdecke ziemlich schöne Dinge. :-( ... Ich hoffe, es kommt nicht zu oft vor, weil dieses Verhalten andere Benutzer von nützlichen Informationen
Eric Ouellet
25

Durch die Zusammenfassung der Antworten und diesen großartigen Artikel „ Verhalten in Stilen mischen“ kam ich zu dieser generischen kurzen und praktischen Lösung:

Ich habe eine generische Klasse erstellt, die von jedem Verhalten geerbt werden kann.

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

Sie können es also einfach mit vielen Komponenten wie diesen wiederverwenden:

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

Und in XAML genug, um zu erklären:

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

Grundsätzlich hat die AttachableForStyleBehavior-Klasse xaml-Dinge erstellt und die Verhaltensinstanz für jede Komponente im Stil registriert. Weitere Details finden Sie unter dem Link.

Roma Borodov
quelle
Klappt wunderbar! Mit meinem kombinierten Scrolling-Verhalten habe ich Inner RowDetailsTemplate-Datagrids entfernt, ohne die übergeordneten Datagrids zu scrollen.
Philipp Michalski
Gerne helfen, genießen =)
Roma Borodov
1
Was ist mit der Datenbindung mit Abhängigkeitseigenschaften im Verhalten?
JobaDiniz
Ich weiß nicht, wie ich den Benutzer kontaktieren oder die Bearbeitung mit negativem Feedback persönlich ablehnen soll. Also lieber @Der_Meister und andere Editoren, bitte lesen Sie den Code sorgfältig durch, bevor Sie versuchen, ihn zu bearbeiten. Dies könnte sich auch auf andere Benutzer und meinen Ruf auswirken. In diesem Fall zerstören Sie durch Entfernen der IsEnabledForStyle-Eigenschaft und beharrliches Ersetzen durch statische Methoden die Möglichkeit der Bindung in xaml, was der Hauptpunkt dieser Frage ist. Sieht also so aus, als hätten Sie bis zum Ende keinen Code gelesen. Leider kann ich Ihre Bearbeitung nicht mit einem großen Minus ablehnen, seien Sie also bitte in Zukunft vorsichtig.
Roma Borodov
1
@ RomBorodov, alles funktioniert in XAML. Es ist eine korrekte Methode, um angehängte Eigenschaften zu definieren (die sich von Abhängigkeitseigenschaften unterscheiden). Siehe Dokumentation: docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/…
Der_Meister
19

1.Erstellen Sie die angehängte Eigenschaft

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.Erstellen Sie ein Verhalten

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.Erstellen Sie einen Stil und legen Sie die angehängte Eigenschaft fest

        <Style TargetType="{x:Type DataGridCell}">
            <Setter Property="Behaviors:DataGridCellAttachedProperties.IsSingleClickEditMode" Value="True"/>
        </Style>
Roman Dvoskin
quelle
Wenn ich versuche, über den Stil auf DependencyProperty zuzugreifen, heißt es, dass IsSingleClickEditMode nicht erkannt wird oder nicht zugänglich ist.
Igor Meszaros
Tut mir leid, mein schlechtes. Sobald ich einen Kommentar abgegeben habe, wurde mir klar, dass GetIsSingleClickEditMode mit der Zeichenfolge übereinstimmen sollte, die Sie an DependencyProperty übergeben. RegisterAttached
Igor Meszaros
OnDetaching fügt einen weiteren Ereignishandler hinzu, der behoben werden sollte (kann kein einzelnes Zeichen beim Bearbeiten eines Beitrags ändern ...)
BalintPogatsa
11

Ich habe eine andere Idee, um die Erstellung einer angehängten Eigenschaft für jedes Verhalten zu vermeiden:

  1. Benutzeroberfläche zum Erstellen von Verhalten:

    public interface IBehaviorCreator
    {
        Behavior Create();
    }
  2. Kleine Helfer-Sammlung:

    public class BehaviorCreatorCollection : Collection<IBehaviorCreator> { }
  3. Hilfsklasse, die das Verhalten anhängt:

    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. Nun Ihr Verhalten, das IBehaviorCreator implementiert:

    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. Und jetzt benutze es in xaml:

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

Ich konnte den Originalartikel nicht finden, aber ich konnte den Effekt neu erstellen.

#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
quelle
Dies für jedes Verhalten schreiben zu müssen, ist allerdings eine Art PITA.
Stephen Drew
0

Der Verhaltenscode erwartet ein Visual, daher können wir es nur auf einem Visual hinzufügen. Die einzige Option, die ich sehen konnte, ist das Hinzufügen zu einem der Elemente in der ControlTemplate, um das Verhalten dem Stil hinzuzufügen und die gesamte Instanz eines bestimmten Steuerelements zu beeinflussen.

Jobi Joy
quelle
0

Der Artikel Einführung in angehängte Verhaltensweisen in WPF implementiert ein angehängtes Verhalten nur mit Stil und kann auch verwandt oder hilfreich sein.

Mit der Technik im Artikel "Einführung in angehängte Verhaltensweisen" werden die Interaktivitäts-Tags mithilfe von "Stil" vollständig vermieden. Ich weiß nicht, ob dies nur daran liegt, dass es sich um eine veraltete Technik handelt oder ob dies noch einige Vorteile bringt, wenn man es in einigen Szenarien bevorzugen sollte.

Rechnung
quelle
2
Dies ist kein Mischverhalten, sondern ein "Verhalten" durch eine einfache angehängte Eigenschaft.
Stephen Drew
0

Ich mag den Ansatz, den die Antworten von Roman Dvoskin und Jonathan Allen in diesem Thread zeigen. Als ich diese Technik zum ersten Mal lernte, profitierte ich von diesem Blog-Beitrag, der mehr Erklärungen zu dieser Technik enthält. Um alles im Kontext zu sehen, finden Sie hier den gesamten Quellcode für die Klasse, über die der Autor in seinem Blogbeitrag spricht.

Jason Frank
quelle
0

Individuelles Verhalten / Auslöser als Ressourcen deklarieren:

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

Fügen Sie sie in die Sammlung ein:

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

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

</Button>
AnjumSKhan
quelle
4
Wie beantwortet es das OP? Der Auslöser wird nicht durch einen Stil in Ihrer Antwort hinzugefügt.
Kryptos
0

Basierend auf dieser Antwort habe ich eine einfachere Lösung gefunden, bei der nur eine Klasse benötigt wird und es nicht erforderlich ist, etwas anderes in Ihr Verhalten zu implementieren.

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
}

und die Beispielverwendung ist

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

Vergessen Sie nicht, diese XML-Dateien hinzuzufügen, um ArrayList zu verwenden:

xmlns:collections="clr-namespace:System.Collections;assembly=mscorlib"
technopriest
quelle