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.

JasonD
la source
[ 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];
}
Kent Boogaart
la source
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();
    }

    ...
}
Daniel
la source
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;
}
Melinda
la source
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>
Maxence
la source
" 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>
Alex
la source
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.