Pasar dos parámetros de comando utilizando un enlace WPF

155

Tengo un comando que estoy ejecutando desde mi archivo XAML usando la siguiente sintaxis estándar:

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

Esto funcionó bien hasta que me di cuenta de que necesitaba DOS datos de la vista para que esta operación se completara de la manera que los usuarios esperan (el ancho y la altura del lienzo específicamente).

Parece que es posible pasar una matriz como argumento a mi comando, pero no veo que haya una manera de especificar el enlace a mis dos propiedades de lienzo en el CommandParameter:

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

¿Cómo paso ambos Anchura y Altura a mi comando? No parece que esto sea posible usando comandos de XAML y necesito conectar un controlador de clic en mi código detrás para que esta información pase a mi método de zoom.

JasonD
fuente
[ stackoverflow.com/questions/58114752/… la solución anterior. Tuve el mismo problema.)
user1482689

Respuestas:

240

En primer lugar, si está haciendo MVVM, normalmente tendrá esta información disponible para su VM a través de propiedades separadas vinculadas desde la vista. Eso le ahorra tener que pasar cualquier parámetro a sus comandos.

Sin embargo, también podría hacer un enlace múltiple y usar un convertidor para crear los parámetros:

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

En tu convertidor:

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

    ...
}

Luego, en su lógica de ejecución de comando:

public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
}
Kent Boogaart
fuente
1
Gracias Kent, eso era exactamente lo que estaba buscando. Me gusta más su primer enfoque para que la VM conozca el "estado" de la vista a través de un enlace sin que tenga que pasar parámetros, pero aún así puedo probarlo. No estoy seguro de que eso me funcione aquí, ya que necesito la vista para hacer que el lienzo sea lo más grande posible y pasar este valor a la VM. Si lo ato, ¿no tendré que establecer el ancho en la VM? ¿En qué caso, la VM está vinculada a la vista?
JasonD
@ Jason: puedes hacerlo de cualquier manera. Es decir, haga que la vista empuje los cambios al modelo de vista, o haga que la vista empuje los cambios a la vista. Un enlace de TwoWay hará que cualquiera de las opciones esté disponible para usted.
Kent Boogaart
en mi programa, el parámetro del método OnExecute es una matriz con valores nulos pero, en el convertidor, los valores son los esperados
Alex David
2
Encuentro que el parámetro es nulo en el método OnExecute, también YourConverter.Convert () no se llamó después de hacer clic en el botón. ¿Por qué?
SubmarineX
3
Esto no funciona, cuando se presiona un botón, los parámetros son nulos
adminSoftDK
38

En el convertidor de la solución elegida, debe agregar valores. Clone () de lo contrario, los parámetros en el comando terminan nulo

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

    ...
}
Daniel
fuente
66
Hola, esta adición con Clone () hace que funcione :) ¿Puede explicar qué diferencia hace? Porque no entiendo por qué necesita que Clone () funcione. Gracias.
adminSoftDK
Podría estar equivocado, pero esto (línea 1267) parece que podría ser la razón para mí: referencesource.microsoft.com/#PresentationFramework/src/…
maxp
14

Use Tuple en Converter, y en OnExecute, devuelva el objeto de parámetro a 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
fuente
5

Si sus valores son estáticos, puede usar 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
fuente
" Si sus valores son estáticos ": ¿Qué es un recurso estático? Por ejemplo, la pregunta menciona el ancho y la altura del lienzo. Estos valores no son constantes, pero ¿son estáticos? ¿Cuál sería el XAML en este caso?
minutos
2
Debería haber escrito "constante" en lugar de "estático". Un recurso estático es un recurso que no cambia durante la ejecución. Si usa, SystemColorspor ejemplo, debe usar en DynamicResourcelugar deStaticResource porque el usuario puede cambiar los colores del sistema a través del Panel de control durante la ejecución. Canvas Widthy Heightno son recursos y no son estáticos. Hay propiedades de instancia heredadas de FrameworkElement.
Maxence
2

Sobre el uso de Tuple en Converter, sería mejor usar 'objeto' en lugar de 'cadena', de modo que funcione para todos los tipos de objetos sin limitación del objeto 'cadena'.

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

Entonces la lógica de ejecución en Command podría ser así

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

y enlace múltiple con convertidor para crear los parámetros (con dos objetos 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
fuente
Me gusta este ya que está más claro cuántos parámetros admite el convertidor. ¡Bueno por solo dos parámetros! (Además, mostró la función de ejecución XAML y Command para una cobertura completa)
Caleb W.