Melewati dua parameter perintah menggunakan pengikat WPF

155

Saya memiliki perintah yang saya jalankan dari file XAML saya menggunakan sintaks standar berikut:

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

Ini bekerja dengan baik sampai saya menyadari bahwa saya membutuhkan DUA potong informasi dari tampilan untuk membuat operasi ini melengkapi seperti yang diharapkan pengguna (lebar dan tinggi kanvas secara khusus).

Sepertinya mungkin untuk melewatkan array sebagai argumen ke perintah saya, tapi saya tidak melihat ada cara untuk menentukan pengikatan ke dua properti kanvas saya di CommandParameter:

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

Bagaimana cara menyampaikan Lebar dan Tinggi ke perintah saya? Sepertinya tidak mungkin menggunakan perintah dari XAML dan saya perlu memasang handler klik di codebehind saya untuk mendapatkan informasi ini agar lolos ke metode zoom saya.

JasonD
sumber
[ stackoverflow.com/questions/58114752/… solusi di atas. Saya memiliki masalah yang sama.)
user1482689

Jawaban:

240

Pertama, jika Anda melakukan MVVM, Anda biasanya memiliki informasi ini tersedia untuk VM Anda melalui properti terpisah yang terikat dari tampilan. Itu menghemat Anda harus melewati parameter apa pun untuk perintah Anda.

Namun, Anda juga bisa mengikat banyak dan menggunakan konverter untuk membuat parameter:

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

Di konverter Anda:

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

    ...
}

Kemudian, dalam logika eksekusi perintah Anda:

public void OnExecute(object parameter)
{
    var values = (object[])parameter;
    var width = (double)values[0];
    var height = (double)values[1];
}
Kent Boogaart
sumber
1
Terima kasih Kent - itulah tepatnya yang saya cari. Saya suka pendekatan pertama Anda lebih baik sehingga VM tahu "keadaan" dari pandangan melalui ikatan tanpa saya harus melewati parameter sama sekali, tapi saya masih bisa mengujinya. Saya tidak yakin itu akan bekerja untuk saya di sini, karena saya memerlukan pandangan untuk membuat kanvas sebesar mungkin dan meneruskan nilai ini ke VM. Jika saya mengikatnya, bukankah saya harus mengatur lebar di VM? Dalam hal ini, VM terikat ke tampilan?
JasonD
@Jason: Anda bisa melakukannya dengan cara apa pun. Yaitu, minta perubahan tampilan kembali ke model tampilan, atau minta perubahan model tampilan ke tampilan. Penjilidan TwoWay akan menghasilkan opsi yang tersedia untuk Anda.
Kent Boogaart
dalam program saya parameter metode OnExecute adalah array dengan nilai nol tetapi, di konverter nilainya seperti yang diharapkan
Alex David
2
Saya menemukan bahwa parameter nol dalam metode OnExecute, juga YourConverter.Convert () tidak dipanggil setelah mengklik tombol. Mengapa?
SubmarineX
3
Ini tidak berfungsi, ketika tombol ditekan, parameternya nol
adminSoftDK
38

Dalam konverter dari solusi yang dipilih, Anda harus menambahkan nilai. Mengkloning () jika tidak, parameter di akhir perintah nol

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

    ...
}
Daniel
sumber
6
Hai, penambahan ini dengan Clone () membuatnya berfungsi :) Bisakah Anda jelaskan, apa bedanya. Karena saya tidak mengerti mengapa Clone () perlu bekerja? Terima kasih.
adminSoftDK
Saya mungkin salah, tapi ini (baris 1267) sepertinya bisa menjadi alasan bagi saya: Referenceource.microsoft.com/#PresentationFramework/src/…
maxp
14

Gunakan Tuple di Konverter, dan di OnExecute, masukkan objek parameter kembali ke 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
sumber
5

Jika nilai Anda statis, Anda dapat menggunakan 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
sumber
" Jika nilai Anda statis ": Apa itu sumber daya statis? Misalnya pertanyaan menyebutkan Kanvas Lebar dan Tinggi. Nilai-nilai ini tidak konstan, tetapi apakah itu statis? Apa yang akan menjadi XAML dalam kasus ini?
menit
2
Saya seharusnya menulis "konstan", bukan "statis". Sumber daya statis adalah sumber daya yang tidak berubah selama eksekusi. Jika Anda menggunakan SystemColorsmisalnya, Anda harus menggunakan DynamicResourcebukan StaticResourcekarena pengguna dapat mengubah warna sistem melalui Control Panel selama eksekusi. Kanvas Widthdan Heightbukan sumber daya dan tidak statis. Ada properti contoh yang diwarisi dari FrameworkElement.
Maxence
2

Tentang menggunakan Tuple di Konverter, akan lebih baik menggunakan 'objek' daripada 'string', sehingga berfungsi untuk semua jenis objek tanpa batasan objek 'string'.

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

Maka logika eksekusi di Command bisa seperti ini

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

dan multi-bind dengan konverter untuk membuat parameter (dengan dua objek 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
sumber
Saya suka yang ini karena lebih jelas berapa banyak parameter yang didukung konverter. Bagus hanya untuk dua parameter! (Plus Anda menunjukkan fungsi XAML dan Perintah jalankan untuk cakupan penuh)
Caleb W.