มีวิธีเชื่อมโยงตัวแปลงค่าหลายตัวใน XAML หรือไม่


124

ฉันมีสถานการณ์ที่ฉันต้องแสดงค่าจำนวนเต็มโดยผูกไว้กับคุณสมบัติในบริบทข้อมูลของฉันหลังจากใส่ผ่านการแปลงสองรายการ:

  1. ย้อนกลับค่าภายในช่วง (เช่นช่วงคือ 1 ถึง 100 ค่าใน datacontext คือ 90 ผู้ใช้เห็นค่า 10)
  2. แปลงตัวเลขเป็นสตริง

ฉันรู้ว่าฉันสามารถทำได้ทั้งสองขั้นตอนโดยการสร้างตัวแปลงของฉันเอง (ซึ่งใช้ IValueConverter) อย่างไรก็ตามฉันมีตัวแปลงค่าแยกต่างหากซึ่งทำเพียงขั้นตอนแรกและขั้นตอนที่สองครอบคลุมโดย Int32Converter

มีวิธีใดบ้างที่ฉันสามารถเชื่อมโยงคลาสที่มีอยู่สองคลาสนี้ใน XAMLโดยไม่ต้องสร้างคลาสเพิ่มเติมที่รวมคลาสเหล่านั้นไว้

หากฉันต้องการชี้แจงเรื่องนี้โปรดแจ้งให้เราทราบ :)

ขอบคุณ

คำตอบ:


198

ฉันใช้วิธีนี้โดย Gareth Evans ในโครงการ Silverlight ของฉัน

นี่คือการใช้งานของฉัน:

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
}

ซึ่งสามารถใช้ใน XAML ดังนี้:

<c:ValueConverterGroup x:Key="InvertAndVisibilitate">
   <c:BooleanInverterConverter/>
   <c:BooleanToVisibilityConverter/>
</c:ValueConverterGroup>

3
เป็นการดีที่สุดสำหรับการใช้งาน ConvertBack เพื่อสร้างสำเนาของคอลเลกชันและย้อนกลับจากนั้นรวมมากกว่านั้น ดังนั้น ConvertBack จะเป็นreturn this.Reverse<IValueConverter>().Aggregate(value, (current, converter) => converter.ConvertBack(current, targetType, parameter, culture));
Nick Udell

5
@DLeh นี่ไม่สง่างามจริงๆเพราะไม่ได้ผล ให้ผู้แปลงทุกคนมีประเภทเป้าหมายสุดท้ายแทนที่จะเป็นประเภทเป้าหมายที่ถูกต้อง ...
Aleksandar Toplek

ฉันจะใช้สิ่งนี้กับ MultiValueConverter เป็นตัวแปลงแรกได้อย่างไร
LightMonk

1
@Town เพื่อนร่วมงานเพิ่งเจอคำถามนี้และทำให้ฉันมองขึ้นมาอีกครั้งเพราะเห็นแก่ความคิดถึง ฉันเพิ่งสังเกตว่าคุณไม่ได้รับเครดิตที่คุณสมควรได้รับ (ฉันยอมรับคำตอบของฉันเอง !) ดังนั้นฉันจึงทำเครื่องหมายคำตอบของคุณว่ายอมรับแล้ว เพียงประมาณ 9 ปีที่ผ่านมา ... : facepalm:
Mal Ross

@MalRoss ฮ่า ๆ ! ขอบคุณ! ดีใจที่ได้ยินว่ามันยังมีประโยชน์ตอนนี้ฉันไม่ได้สัมผัส Silverlight มาประมาณ 8 ปีแล้ว แต่นี่ก็ยังคงเป็นคำตอบยอดนิยมของฉัน :)
Town

54

พบว่าสิ่งที่ผมกำลังมองหามารยาทของจอชสมิ ธ : ท่อราคาแปลง (ลิงค์ archive.org)

เขากำหนดValueConverterGroupคลาสซึ่งการใช้ XAML เป็นไปตามที่ฉันหวังไว้ นี่คือตัวอย่าง:

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

สิ่งที่ดี ขอบคุณ Josh :)


2
ในโซลูชันนี้ตัวแปลงแต่ละตัวต้องจัดการกับประเภทเดียวเท่านั้น (ต้องประกาศในแอตทริบิวต์ ValueConversion-single) โซลูชัน @Town สามารถรับมือกับมัลติคอนเวอร์เตอร์ได้เช่นกัน
Y. Shoham

9
กรุณาโพสต์การใช้งาน; มิฉะนั้น linkrot
Jake Berger

9

การดำเนินโครงการ SilverlightของGareth Evans ของTownนั้นยอดเยี่ยมมาก แต่ก็ไม่รองรับพารามิเตอร์ตัวแปลงที่แตกต่างกัน

ฉันแก้ไขเพื่อให้คุณสามารถระบุพารามิเตอร์โดยคั่นด้วยจุลภาค (เว้นแต่คุณจะหลีกเลี่ยงค่าเหล่านี้แน่นอน)

Converter:

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

หมายเหตุ: ConvertBack ไม่ได้ใช้งานที่นี่โปรดดูGistของฉันสำหรับเวอร์ชันเต็ม

การดำเนินงาน:

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

6

ใช่มีหลายวิธีในการแปลงโซ่ แต่ดูไม่สวยและคุณไม่จำเป็นต้องใช้ที่นี่ หากคุณต้องการสิ่งนี้ให้ถามตัวเองว่านั่นคือหนทางที่จะไปจริงๆหรือ? Simple ทำงานได้ดีกว่าเสมอแม้ว่าคุณจะต้องเขียนตัวแปลงของคุณเองก็ตาม

ในกรณีเฉพาะของคุณสิ่งที่คุณต้องทำคือจัดรูปแบบมูลค่าที่แปลงเป็นสตริง StringFormatสถานที่ให้บริการBindingคือเพื่อนของคุณที่นี่

 <TextBlock Text="{Binding Value,Converter={StaticResource myConverter},StringFormat=D}" />

5
หากคุณใช้การผูกอย่างมากการเขียนตัวแปลงที่กำหนดเองไปยังตัวแปลงโซ่จะกลายเป็นตัวแปลงใบ้จำนวนมากสำหรับการกำหนดค่าทุกประเภท ในกรณีนี้คำตอบที่ได้รับการยอมรับคือทางออกที่ยอดเยี่ยม
Jacek Gorgoń

0

นี่คือส่วนขยายเล็กน้อยของคำตอบของ Townเพื่อรองรับการเชื่อมโยงหลายแบบ:

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
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.