Passando due parametri di comando usando un'associazione WPF


155

Ho un comando che sto eseguendo dal mio file XAML usando la seguente sintassi standard:

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

Questo ha funzionato bene fino a quando mi sono reso conto che avevo bisogno di DUE informazioni dalla vista per rendere questa operazione completa come gli utenti si aspettano (la larghezza e l'altezza della tela in modo specfico).

Sembra che sia possibile passare un array come argomento al mio comando, ma non vedo che ci sia un modo per specificare l'associazione alle mie due proprietà di tela in CommandParameter:

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

Come posso passare sia Larghezza che Altezza al mio comando? Non sembra che ciò sia possibile utilizzando i comandi di XAML e ho bisogno di collegare un gestore di clic nel mio codebehind per ottenere queste informazioni per passare al mio metodo di zoom.


[ stackoverflow.com/questions/58114752/… la soluzione sopra. Ho avuto lo stesso problema.)
user1482689

Risposte:


240

Innanzitutto, se stai eseguendo MVVM, in genere queste informazioni sarebbero disponibili per la tua macchina virtuale tramite proprietà separate associate alla vista. Ciò ti evita di dover passare qualsiasi parametro ai tuoi comandi.

Tuttavia, è possibile anche eseguire il multi-bind e utilizzare un convertitore per creare i parametri:

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

Nel tuo convertitore:

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

    ...
}

Quindi, nella logica di esecuzione del comando:

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

1
Grazie Kent - era esattamente quello che stavo cercando. Mi piace meglio il tuo primo approccio in modo che la VM conosca lo "stato" della vista attraverso un'associazione senza che io debba assolutamente passare i parametri, ma posso ancora testarlo. Non sono sicuro che funzionerà per me qui, in quanto ho bisogno della vista per rendere la tela più grande possibile e passare questo valore alla VM. Se lo rilevo, non dovrò impostare la larghezza nella VM? In tal caso, la VM è associata alla vista?
JasonD,

@Jason: puoi farlo in entrambi i modi. In altre parole, fai in modo che la visualizzazione invii le modifiche al modello di visualizzazione o che il modello di visualizzazione invii le modifiche alla visualizzazione. Un'associazione TwoWay comporterà la disponibilità di entrambe le opzioni.
Kent Boogaart,

nel mio programma il parametro del metodo OnExecute è un array con valori null ma, nel convertitore i valori sono quelli previsti
Alex David

2
Trovo che il parametro sia nullo nel metodo OnExecute, anche YourConverter.Convert () non è stato chiamato dopo aver fatto clic sul pulsante. Perché?
SottomarinoX

3
Questo non funziona, quando si preme un pulsante, i parametri sono null
adminSoftDK

38

Nel convertitore della soluzione scelta, è necessario aggiungere valori.Clone () altrimenti i parametri nel comando terminano null

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

    ...
}

6
Ciao, questa aggiunta con Clone () lo fa funzionare :) Puoi spiegarmi, che differenza fa. Perché non capisco perché ha bisogno che Clone () funzioni? Grazie.
adminSoftDK,

Potrei sbagliarmi, ma questo (riga 1267) sembra che potrebbe essere la ragione per me: riferimentiource.microsoft.com/#PresentationFramework/src/…
maxp

14

Utilizzare Tuple in Converter e in OnExecute, riportare l'oggetto parametro su 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

Se i tuoi valori sono statici, puoi utilizzare 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>

" Se i tuoi valori sono statici ": che cos'è una risorsa statica? Ad esempio, la domanda menziona Larghezza e altezza della tela. Questi valori non sono costanti, ma sono statici? Quale sarebbe l'XAML in questo caso?
minuti

2
Avrei dovuto scrivere "costante" anziché "statico". Una risorsa statica è una risorsa che non cambia durante l'esecuzione. Se si utilizza SystemColorsad esempio, è necessario utilizzare DynamicResourceanziché StaticResourceperché l'utente può modificare i colori del sistema tramite il Pannello di controllo durante l'esecuzione. Canvas Widthe Heightnon sono risorse e non sono statici. Esistono proprietà dell'istanza ereditate da FrameworkElement.
Maxence,

2

Informazioni sull'uso di Tuple in Converter, sarebbe meglio usare 'oggetto' invece di 'stringa', in modo che funzioni per tutti i tipi di oggetti senza limitazione dell'oggetto 'stringa'.

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

Quindi la logica di esecuzione in Command potrebbe essere così

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

e multi-bind con convertitore per creare i parametri (con due oggetti 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>

Mi piace questo perché è più chiaro quanti parametri supporta il convertitore. Buono solo per due parametri! (Inoltre hai mostrato XAML e Command eseguono la funzione per una copertura completa)
Caleb W.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.