Passer deux paramètres de commande à l'aide d'une liaison WPF


155

J'ai une commande que j'exécute à partir de mon fichier XAML en utilisant la syntaxe standard suivante:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand}"/>

Cela a bien fonctionné jusqu'à ce que je réalise que j'avais besoin de DEUX informations de la vue afin de rendre cette opération complète comme les utilisateurs l'attendent (la largeur et la hauteur de la toile en particulier).

Il semble qu'il soit possible de passer un tableau en tant qu'argument à ma commande, mais je ne vois pas de moyen de spécifier la liaison à mes deux propriétés de canevas dans le CommandParameter:

<Button Content="Zoom" 
        Command="{Binding MyViewModel.ZoomCommand" 
        CommandParameter="{Binding ElementName=MyCanvas, Path=Width}"/>

Comment transmettre la largeur et la hauteur à ma commande? Il ne semble pas que cela soit possible en utilisant des commandes de XAML et je dois câbler un gestionnaire de clics dans mon codebehind pour que ces informations soient transmises à ma méthode de zoom.


[ stackoverflow.com/questions/58114752/… la solution ci-dessus. J'ai eu le même problème.)
user1482689

Réponses:


240

Premièrement, si vous utilisez MVVM, ces informations sont généralement disponibles pour votre machine virtuelle via des propriétés distinctes liées à la vue. Cela vous évite de devoir passer des paramètres à vos commandes.

Cependant, vous pouvez également effectuer plusieurs liaisons et utiliser un convertisseur pour créer les paramètres:

<Button Content="Zoom" Command="{Binding MyViewModel.ZoomCommand">
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource YourConverter}">
             <Binding Path="Width" ElementName="MyCanvas"/>
             <Binding Path="Height" ElementName="MyCanvas"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

Dans votre convertisseur:

public class YourConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

Ensuite, dans votre logique d'exécution de commande:

public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
}

1
Merci Kent - c'était exactement ce que je cherchais. J'aime mieux votre première approche pour que la VM connaisse "l'état" de la vue via une liaison sans que je doive passer du tout des paramètres, mais je peux toujours la tester. Je ne suis pas sûr que cela fonctionnera pour moi ici, car j'ai besoin de la vue pour rendre le canevas aussi grand que possible et transmettre cette valeur à la machine virtuelle. Si je le lie, ne devrai-je pas définir la largeur dans la VM? Dans quel cas, la VM est liée à la vue?
JasonD

@Jason: vous pouvez le faire de toute façon. C'est-à-dire que la vue repousse les modifications vers le modèle de vue ou que le modèle de vue envoie les modifications à la vue. Une liaison TwoWay entraînera la disponibilité de l'une ou l'autre option.
Kent Boogaart

dans mon programme, le paramètre de méthode OnExecute est un tableau avec des valeurs nulles mais, dans le convertisseur, les valeurs sont comme prévu
Alex David

2
Je trouve que ce paramètre est nul dans la méthode OnExecute, ainsi YourConverter.Convert () n'a pas été appelé après avoir cliqué sur le bouton. Pourquoi?
SubmarineX

3
Cela ne fonctionne pas, lorsqu'un bouton est enfoncé, les paramètres sont nuls
adminSoftDK

38

Dans le convertisseur de la solution choisie, vous devez ajouter des valeurs .Clone () sinon les paramètres de la commande se terminent par null

public class YourConverter : IMultiValueConverter
{
    public object Convert(object[] values, ...)
    {
        return values.Clone();
    }

    ...
}

6
Salut, cet ajout avec Clone () le fait fonctionner :) Pouvez-vous s'il vous plaît expliquer, quelle différence cela fait. Parce que je ne comprends pas pourquoi il faut que Clone () fonctionne? Je vous remercie.
adminSoftDK

Je me trompe peut-être, mais cela (ligne 1267) semble être la raison pour moi: referencesource.microsoft.com/#PresentationFramework/src/…
maxp

14

Utilisez Tuple dans Converter et dans OnExecute, transtypez l'objet de paramètre en Tuple.

public class YourConverter : IMultiValueConverter 
{      
    public object Convert(object[] values, ...)     
    {   
        Tuple<string, string> tuple = new Tuple<string, string>(
            (string)values[0], (string)values[1]);
        return (object)tuple;
    }      
} 

// ...

public void OnExecute(object parameter) 
{
    var param = (Tuple<string, string>) parameter;
}

5

Si vos valeurs sont statiques, vous pouvez utiliser x:Array:

<Button Command="{Binding MyCommand}">10
  <Button.CommandParameter>
    <x:Array Type="system:Object">
       <system:String>Y</system:String>
       <system:Double>10</system:Double>
    </x:Array>
  </Button.CommandParameter>
</Button>

" Si vos valeurs sont statiques ": Qu'est-ce qu'une ressource statique? Par exemple, la question mentionne la largeur et la hauteur du canevas. Ces valeurs ne sont pas constantes, mais sont-elles statiques? Quel serait le XAML dans ce cas?
min

2
J'aurais dû écrire "constant" au lieu de "statique". Une ressource statique est une ressource qui ne change pas pendant l'exécution. Si vous utilisez SystemColorspar exemple, vous devez utiliser à la DynamicResourceplace de StaticResourcecar l'utilisateur peut modifier les couleurs du système via le Panneau de configuration pendant l'exécution. Canvas Widthet Heightne sont pas des ressources et ne sont pas statiques. Il existe des propriétés d'instance héritées de FrameworkElement.
Maxence

2

À propos de l'utilisation de Tuple dans Converter, il serait préférable d'utiliser «objet» au lieu de «chaîne», afin qu'il fonctionne pour tous les types d'objets sans limitation d'objet «chaîne».

public class YourConverter : IMultiValueConverter 
{      
    public object Convert(object[] values, ...)     
    {   
        Tuple<object, object> tuple = new Tuple<object, object>(values[0], values[1]);
        return tuple;
    }      
} 

Ensuite, la logique d'exécution dans Command pourrait être comme ça

public void OnExecute(object parameter) 
{
    var param = (Tuple<object, object>) parameter;

    // e.g. for two TextBox object
    var txtZip = (System.Windows.Controls.TextBox)param.Item1;
    var txtCity = (System.Windows.Controls.TextBox)param.Item2;
}

et multi-bind avec convertisseur pour créer les paramètres (avec deux objets TextBox)

<Button Content="Zip/City paste" Command="{Binding PasteClick}" >
    <Button.CommandParameter>
        <MultiBinding Converter="{StaticResource YourConvert}">
            <Binding ElementName="txtZip"/>
            <Binding ElementName="txtCity"/>
        </MultiBinding>
    </Button.CommandParameter>
</Button>

J'aime celui-ci car il est plus clair combien de paramètres le convertisseur prend en charge. Bon pour seulement deux paramètres! (De plus, vous avez montré la fonction d'exécution de XAML et de commande pour une couverture complète)
Caleb W.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.