Apakah ada cara untuk menghubungkan beberapa konverter nilai di XAML?

123

Saya mendapat situasi di mana saya perlu menunjukkan nilai integer, terikat ke properti pada konteks data saya, setelah meletakkannya melalui dua konversi terpisah:

  1. Membalikkan nilai dalam rentang (misalnya rentang 1 hingga 100; nilai dalam konteks data adalah 90; pengguna melihat nilai 10)
  2. mengubah angka menjadi string

Saya menyadari saya dapat melakukan kedua langkah dengan membuat konverter saya sendiri (yang mengimplementasikan IValueConverter). Namun, saya sudah memiliki konverter nilai terpisah yang hanya melakukan langkah pertama, dan langkah kedua dicakup oleh Int32Converter.

Apakah ada cara agar saya dapat menghubungkan kedua kelas yang ada ini di XAML tanpa harus membuat kelas lebih lanjut yang menggabungkannya?

Jika saya perlu mengklarifikasi semua ini, beri tahu saya. :)

Terima kasih.

Mal Ross
sumber

Jawaban:

198

Saya menggunakan metode ini oleh Gareth Evans dalam proyek Silverlight saya.

Inilah implementasi saya:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Yang kemudian bisa digunakan di XAML seperti ini:

<c:ValueConverterGroup x:Key="InvertAndVisibilitate">
   <c:BooleanInverterConverter/>
   <c:BooleanToVisibilityConverter/>
</c:ValueConverterGroup>
Kota
sumber
3
Apakah yang terbaik, untuk implementasi ConvertBack membuat salinan dari koleksi dan membalikkannya, dan kemudian Mengumpulkan itu? Jadi ConvertBack akanreturn this.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, parameter, culture));
Nick Udell
5
@DLeh Ini tidak benar-benar elegan karena tidak berhasil. Ini menyediakan semua konverter dengan jenis target akhir daripada jenis target yang benar ...
Aleksandar Toplek
Bagaimana saya bisa menggunakan ini dengan MultiValueConverter sebagai Konverter pertama?
LightMonk
1
@ Kota Seorang rekan baru saja menemukan pertanyaan ini dan itu membuat saya mencarinya lagi, demi nostalgia. Hanya, saya baru saja memperhatikan Anda tidak mendapatkan kredit yang layak Anda terima (saya telah menerima jawaban saya sendiri !), Jadi saya sekarang telah menandai jawaban Anda sebagai diterima. Hanya terlambat sekitar 9 tahun ...: Telapak tangan:
Mal Ross
@MalRoss Haha! Terima kasih! Senang mendengarnya masih berguna, saya belum menyentuh Silverlight sekarang selama sekitar 8 tahun, namun ini masih jawaban saya yang paling populer :)
Kota
54

Menemukan apa yang saya cari, dengan izin dari Josh Smith: Piping Value Converters (link archive.org) .

Dia mendefinisikan ValueConverterGroupkelas, yang penggunaannya di XAML persis seperti yang saya harapkan. Berikut contohnya:

<!-- Converts the Status attribute text to a SolidColorBrush used to draw 
     the output of statusDisplayNameGroup. -->
<local:ValueConverterGroup x:Key="statusForegroundGroup">
  <local:IntegerStringToProcessingStateConverter  />
  <local:ProcessingStateToColorConverter />
  <local:ColorToSolidColorBrushConverter />
</local:ValueConverterGroup> 

Barang bagus. Terima kasih, Josh. :)

Mal Ross
sumber
2
Dalam solusi ini, setiap konverter harus berurusan dengan satu jenis saja (itu harus dideklarasikan dalam atribut-ValueConversion-tunggal). Solusi @Town dapat mengatasi multikonverter juga.
Y. Shoham
9
silahkan posting implementasinya; jika tidak, linkrot
Jake Berger
9

Implementasi Town dari proyek Silverlight Gareth Evans sangat bagus, namun tidak mendukung parameter konverter yang berbeda.

Saya memodifikasinya sehingga Anda dapat memberikan parameter, dipisahkan dengan koma (kecuali jika Anda menghindarinya tentunya).

Konverter:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter
{
    private string[] _parameters;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if(parameter != null)
            _parameters = Regex.Split(parameter.ToString(), @"(?<!\\),");

        return (this).Aggregate(value, (current, converter) => converter.Convert(current, targetType, GetParameter(converter), culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    private string GetParameter(IValueConverter converter)
    {
        if (_parameters == null)
            return null;

        var index = IndexOf(converter as IValueConverter);
        string parameter;

        try
        {
            parameter = _parameters[index];
        }

        catch (IndexOutOfRangeException ex)
        {
            parameter = null;
        }

        if (parameter != null)
            parameter = Regex.Unescape(parameter);

        return parameter;
    }
}

Catatan: ConvertBack tidak dilaksanakan di sini, lihat saya Intisari untuk versi lengkap.

Penerapan:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:converters="clr-namespace:ATXF.Converters;assembly=ATXF" x:Class="ATXF.TestPage">
  <ResourceDictionary>
    <converters:ValueConverterGroup x:Key="converters">
      <converters:ConverterOne />
      <converters:ConverterTwo />
    </converters:ValueConverterGroup>
  </ResourceDictionary>

  <Label Text="{Binding InitialValue, Converter={StaticResource converters}, ConverterParameter='Parameter1,Parameter2'}" />
</ContentPage>
Trevi Awater
sumber
6

Ya, ada cara untuk mengonversi konverter tetapi tidak terlihat bagus dan Anda tidak membutuhkannya di sini. Jika Anda pernah membutuhkan ini, tanyakan pada diri Anda, apakah itu benar-benar cara yang harus dilakukan? Sederhana selalu berfungsi lebih baik bahkan jika Anda harus menulis konverter Anda sendiri.

Dalam kasus khusus Anda, yang perlu Anda lakukan hanyalah memformat nilai yang dikonversi menjadi string. StringFormatproperti di a Bindingadalah teman Anda di sini.

 <TextBlock Text="{Binding Value,Converter={StaticResource myConverter},StringFormat=D}" />
wpfwannabe.dll
sumber
5
Jika Anda sering menggunakan binding, menulis konverter khusus ke konverter rantai berakhir dengan banyak konverter bodoh untuk semua jenis konfigurasi. Dalam hal ini, jawaban yang diterima adalah solusi yang bagus.
Jacek Gorgoń
0

Berikut adalah perpanjangan kecil dari jawaban Town untuk mendukung multi-binding:

public class ValueConverterGroup : List<IValueConverter>, IValueConverter, IMultiValueConverter
{
    #region IValueConverter Members

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return this.Aggregate(value, (current, converter) => converter.Convert(current, targetType, parameter, culture));
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        return Convert(values as object, targetType, parameter, culture);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}
Aaron
sumber