Передача двух параметров команды с использованием привязки WPF

155

У меня есть команда, которую я выполняю из моего файла XAML, используя следующий стандартный синтаксис:

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

Это работало нормально, пока я не понял, что мне нужно ДВА фрагмента информации из представления, чтобы эта операция завершилась так, как ожидают пользователи (в частности, ширина и высота холста).

Кажется, что возможно передать массив в качестве аргумента моей команде, но я не вижу способа указать привязку к двум моим свойствам холста в CommandParameter:

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

Как передать ширину и высоту моей команде? Не похоже, что это возможно при использовании команд из XAML, и мне нужно подключить обработчик щелчков в моем коде, чтобы эта информация передавалась моему методу масштабирования.

JasonD
источник
[ stackoverflow.com/questions/58114752/… вышеуказанное решение. У меня была такая же проблема.)
user1482689

Ответы:

240

Во-первых, если вы работаете с MVVM, эта информация обычно доступна для вашей виртуальной машины через отдельные свойства, связанные с представлением. Это избавляет вас от необходимости передавать какие-либо параметры вашим командам.

Тем не менее, вы также можете использовать несколько связей и использовать конвертер для создания параметров:

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

В вашем конвертере:

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

    ...
}

Затем в вашей логике выполнения команды:

public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
}
Кент Бугаарт
источник
1
Спасибо Кент - это было именно то, что я искал. Мне больше нравится ваш первый подход, так что виртуальная машина знает «состояние» представления через привязку, и мне вообще не нужно передавать параметры, но я все еще могу проверить это. Я не уверен, что это сработает для меня, так как мне нужно представление, чтобы сделать холст как можно большим и передать это значение виртуальной машине. Если я свяжу это, не придется ли мне устанавливать ширину в ВМ? В каком случае виртуальная машина привязана к представлению?
JasonD
@ Джейсон: вы можете сделать это в любом случае. То есть имейте толчок изменения представления назад к модели представления, или сделайте, чтобы модель представления толкала изменения к представлению. Привязка TwoWay приведет к тому, что вам будет доступен любой из этих вариантов.
Кент Бугаарт
в моей программе параметр метода OnExecute представляет собой массив с нулевыми значениями, но в конвертере значения соответствуют ожидаемым
Alex David
2
Я считаю, что в методе OnExecute параметр имеет значение null, также после вызова кнопки не вызывался YourConverter.Convert (). Зачем?
SubmarineX
3
Это не работает, когда кнопка нажата, параметры нулевые
adminSoftDK
38

В конвертере выбранного решения вы должны добавить values.Clone (), иначе параметры в команде end null

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

    ...
}
Даниил
источник
6
Привет, это дополнение с Clone () заставляет его работать :) Можете ли вы объяснить, какая разница. Потому что я не понимаю, зачем это Clone () для работы? Спасибо.
adminSoftDK
Я могу ошибаться, но это (строка 1267) выглядит так, как будто это может быть причиной для меня: referenceource.microsoft.com/#PresentationFramework/src/…
maxp
14

Используйте Tuple в Converter и в OnExecute приведите объект параметра обратно в 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

Если ваши значения статичны, вы можете использовать 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
источник
« Если ваши значения статичны »: что такое статический ресурс? Например, вопрос упоминает ширину и высоту холста. Эти значения не являются постоянными, но являются ли они статическими? Каким будет XAML в этом случае?
мин
2
Я должен был написать «постоянный» вместо «статический». Статический ресурс - это ресурс, который не изменяется во время выполнения. Если вы используете, SystemColorsнапример, вы должны использовать DynamicResourceвместо, StaticResourceпотому что пользователь может изменить системные цвета через панель управления во время выполнения. Холст Widthи Heightне являются ресурсами и не являются статичными. Есть свойства экземпляра, унаследованные от FrameworkElement.
Максенс
2

Что касается использования Tuple в Converter, было бы лучше использовать «объект» вместо «строка», чтобы он работал для всех типов объектов без ограничения объекта «строка».

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

Тогда логика выполнения в Command может быть такой

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

и multi-bind с конвертером для создания параметров (с двумя объектами 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>
Алекс
источник
Мне нравится этот, так как более понятно, сколько параметров поддерживает конвертер. Хорошо только для двух параметров! (Кроме того, вы показали XAML и функцию выполнения команд для полного охвата)
Калеб В.