97 votes

Comment ajouter un Mélange de Comportement dans un Style Setter

J'ai créé un Mélange de comportement pour le Bouton. Comment puis-je définir qu'à tous mes Boutons dans l'application.

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

Cependant, lorsque j'essaie:

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

Je reçois le message d'erreur

La propriété "Comportements" n'a pas un accessible setter.

83voto

Rick Sladkey Points 23389

J'ai eu le même problème et j'ai trouver une solution. J'ai trouvé cette question après je l'ai résolu, et je vois que ma solution porte beaucoup en commun avec Marc. Cependant, cette approche est un peu différente.

Le principal problème est que les comportements et les déclencheurs de l'associer à un objet spécifique, et donc vous ne pouvez pas utiliser la même instance d'un comportement pour différents objets associés. Lorsque vous définissez votre comportement XAML inline applique ce one-to-one relation. Toutefois, lorsque vous essayez de définir un comportement dans un style, le style peut être à nouveau utilisé pour tous les objets auxquels elle s'applique et cela permettra de lancer des exceptions dans la base de classes de comportement. En fait, les auteurs ont fait des efforts considérables pour nous empêcher de même essayer de faire ça, sachant que ça ne marcherait pas.

Le premier problème est que nous ne pouvons même pas construire un comportement de poseur de valeur parce que le constructeur est interne. Donc, nous avons besoin de notre propre comportement et de déclencher des classes de collection.

Le problème suivant est que le comportement et déclencher des propriétés attachées n'ont pas les setters et donc ils ne peuvent être ajoutés à avec en ligne de XAML. Ce problème, nous avons à résoudre avec nos propres propriétés attachées manipuler la primaire le comportement et les propriétés d'un trigger.

Le troisième problème est que notre comportement collection est seulement bon pour un style unique cible. Cela nous résoudre en utilisant un peu utilisé le XAML fonctionnalité x:Shared="False" qui crée une nouvelle copie de la ressource à chaque fois qu'il est référencé.

Le dernier problème est que les comportements et les déclencheurs ne sont pas comme les autres style setters; nous ne voulons pas remplacer les vieilles habitudes avec les nouveaux comportements parce qu'elles pourraient faire différer considérablement les choses. Donc, si nous acceptons qu'une fois que vous ajoutez un comportement que vous ne peut le prendre loin (et c'est la façon dont les comportements travaille actuellement), nous pouvons conclure que les comportements et les déclencheurs doivent être additif, ce qui peut être pris en charge par nos propriétés attachées.

Voici un exemple de l'utilisation de cette approche:

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

L'exemple utilise des déclencheurs, mais les comportements de travailler de la même façon. Dans l'exemple, nous montrons:

  • le style peut être appliqué à plusieurs blocs de texte
  • plusieurs types de liaison de données fonctionnent toutes correctement
  • une action de débogage qui génère le texte dans la fenêtre de sortie

Voici un exemple de comportement, nos DebugAction. Plus correctement, il est une action, mais par abus de langage, nous appelons les comportements, les déclencheurs et les actions de "comportements".

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

Enfin, nos collections et des propriétés attachées au travail. Par analogie avec l' Interaction.Behaviors, la propriété de la cible est appelé SupplementaryInteraction.Behaviors parce que, par la définition de cette propriété, vous pourrez ajouter des comportements à Interaction.Behaviors et de même pour les déclencheurs.

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

et là vous l'avez, entièrement fonctionnelle des comportements et des déclencheurs appliquée à travers les styles.

19voto

Roman Dvoskin Points 53

1.Créer De La Propriété Attachée

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.Créer un Comportement

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.Créer un Style et définir la propriété jointe

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

5voto

Jonathan Allen Points 23540

Je ne pouvais pas trouver l'article original, mais j'ai été en mesure de recréer l'effet.

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

0voto

Jobi Joy Points 20883

Code de comportement s'attend à un Visuel, de sorte que nous pouvons ajouter que sur un visuel. Donc, la seule option que je pouvais voir, c'est à ajouter à celui de l'élément à l'intérieur de la ControlTemplate de manière à obtenir le comportement ajoutée pour le Style et l'effet sur toutes les instance d'un contrôle particulier.

0voto

Bill Points 520

L'article Introduction aux Comportements Attachés en WPF implémente un comportement attaché à l'aide de Style uniquement, et peuvent aussi être liés ou utile.

La technique de l'Introduction de Comportements Attachés" à l'article évite l'Interactivité des balises au total, l'aide sur le Style. Je ne sais pas si c'est juste parce que c'est un plus en date de la technique, ou si cela confère certains avantages où l'on doit préférer dans certains scénarios.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X