Передача двох параметрів команди за допомогою прив'язки 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, і мені потрібно підключити обробник кліків у своєму коді, щоб отримати цю інформацію для мого методу збільшення.


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

Відповіді:


240

По-перше, якщо ви робите MVVM, ви, як правило, маєте цю інформацію доступною для свого VM через окремі властивості, зв'язані з поданням. Це позбавить вас від необхідності взагалі передавати будь-які параметри вашим командам.

Однак ви також можете багатокористуватися та використовувати перетворювач для створення параметрів:

<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
Спасибі Кент - саме це я шукав. Мені подобається ваш перший підхід краще, щоб ВМ знав "стан" перегляду через прив'язку, без мене взагалі потрібно було проходити параметри, але я все одно можу його перевірити. Я не впевнений, що для мене це тут спрацює, оскільки мені потрібен вид, щоб полотно було максимально великим і передавало це значення VM. Якщо я прив'язую його, чи не доведеться встановлювати ширину в VM? У якому випадку VM прив’язаний до подання?
JasonD

@Jason: це можна зробити в будь-якому випадку. Тобто змінити поштовий вигляд перегляду назад до моделі перегляду чи змінити модель перегляду для перегляду. Прив'язка в два способи призведе до того, що вам буде доступний будь-який варіант.
Кент Богаарт

у моїй програмі параметр методу OnExecute - це масив із нульовими значеннями, але, у перетворювачі, значення є такими, як очікувалося
Алекс Девід

2
Я вважаю, що параметр у методі OnExecute недійсний, також YourConverter.Convert () не викликався після натискання кнопки. Чому?
SubmarineX

3
Це не працює, якщо натиснути кнопку, параметри є недійсними
adminSoftDK

38

У перетворювачі обраного рішення слід додати значення.Clone (), інакше параметри в команді закінчуються null

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

    ...
}

6
Привіт, це доповнення з Clone () змушує працювати :) Чи можете ви поясніть, яку різницю це робить? Тому що я не розумію, для чого потрібен клонінг () для роботи? Дякую.
adminSoftDK

Я можу помилятися, але це (рядок 1267) виглядає так, що це може бути причиною для мене: referenceource.microsoft.com/#PresentationFramework/src/…
maxp

14

Використовуйте Tuple в конвертері та в 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>

" Якщо ваші значення статичні ": Що таке статичний ресурс? Наприклад, питання зазначає ширину і висоту полотна. Ці значення не є постійними, але вони статичні? Якою була б XAML в цьому випадку?
хв

2
Я повинен був написати "констант" замість "статичний". Статичний ресурс - це ресурс, який не змінюється під час виконання. Якщо ви використовуєте, SystemColorsнаприклад, ви повинні використовувати DynamicResourceзамість того, StaticResourceщо користувач може змінювати кольори системи через панель керування під час виконання. Полотно Widthі Heightне є ресурсами і не є статичними. Є властивості екземпляра, успадковані від FrameworkElement.
Maxence

2

Щодо використання Tuple в конверторі, краще було б використовувати «об’єкт», а не «рядок», щоб він працював для всіх типів об’єктів без обмеження об’єкта «рядок».

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

і багатозв'язувати з перетворювачем для створення параметрів (з двома об'єктами 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 та Command Execute для повного покриття)
Caleb W.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.