📜 ⬆️ ⬇️

WPF: Several parameters for the converter

In my first article on converters, I described how to use converters as a markup extension. Continuing the topic of converters, I would like to talk about the parameters of the converter.

Often, in order to properly convert the source of the binding for presenting a single object in the UI, it is necessary to transfer some parameter to the converter that specifies exactly how the object should be converted. Returning to the date example, let's say that the date in our program can be represented in several calendars, and for correct conversion, you need to tell the converter which calendar date is the source of the binding, this can be done as follows:
<Label Content="{Binding Path=Date, Converter={StaticResource dateConverter}, ConverterParameter='Gregorian'}" /> 

Similarly, when using a converter like MarkupExtension:
 <Label Content="{Binding Path=Date, Converter={converters:DateTimeToString}, ConverterParameter='Gregorian'}" /> 

When this converter is called, its input parameter “parameter” will have the value “Gregorian” and we will already know that the date is in the Gregorian calendar:
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date = (DateTime)value; string calendar = (string)parameter; if (calendar == "Gregorian") return DateTimeHelpers.GregorianToString(calendar); else if (calendar == "Gregorian") return DateTimeHelpers.GregorianToString(calendar); } 

Several parameters


This is of course wonderful, but what if you need to transfer not one but two or more parameters to the converter? Standard means it will not work. But, as suggested, the habrouser urrri can be easily implemented, if the converter is at the same time MarkupExtension. With his permission, I will describe this method.

As a basis we will use the code from the first article . First, let's change our base class a bit:
  public abstract class ConvertorBase<T> : MarkupExtension, IValueConverter where T : class, new() { /// <summary> /// Must be implemented in inheritor. /// </summary> public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture); /// <summary> /// Override if needed. /// </summary> public virtual object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } public override object ProvideValue(IServiceProvider serviceProvider) { return this; } } 

After the modification, we return not the static instance of our class, but the current one.
Suppose that our converter needs two parameters: the date and format of the output. Change the converter:
  public class DateTimeToString : ConvertorBase<DateTimeToString> { /// Format for converting DateTime to string. /// </summary> public string Format { set; private get; } /// <summary> /// Date of what calendar current instance is representing. /// </summary> public string Calendar { set; private get; } public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date = (DateTime)value; if (calendar == "Gregorian") return DateTimeHelpers.GregorianToString(date, Format); else if (calendar == "Gregorian") return DateTimeHelpers.GregorianToString(date, Format); } } 

Example of using the converter:
 <Label Content="{Binding Path=Date, Converter={converters:DateTimeToString Calendar='Gregorian', Format ='Today is {0}'}}" /> 

If the project is rebuilt after the converter is added, then the editor will offer us options for the properties:

I want to note that if some of the parameters are optional, you can omit them:
 <Label Content="{Binding Path=Date, Converter={converters:DateTimeToString}" /> 

When calling the converter, the corresponding parameters will be empty. Naturally, such situations should be foreseen. Suppose that the format we need is mandatory, and the calendar is an additional parameter; by default, we assume that the calendar is Gregorian. Let's add to the converter a check of input parameters:
  [ValueConversion(typeof(DateTime), typeof(String))] public class DateTimeToString : ConvertorBase<DateTimeToString> { /// <summary> /// Format string. /// </summary> public string Format { set; private get; } /// <summary> /// Date of what calendar current instance is representing. /// </summary> public string Calendar { set { _calendar = value; } private get { return _calendar; } } /// <summary> /// Default calendar. /// </summary> private string _calendar = "Gregorian"; public override object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime date = (DateTime)value; Debug.Assert(date != null, "Date is missing."); Debug.Assert(Format != null, "Format is missing."); if (Calendar == "Gregorian") return DateTimeHelpers.GregorianToString(date, Format); else if (Calendar == "Gregorian") return DateTimeHelpers.GregorianToString(date, Format); } } 

Now, in the case of sending a date to the converter, or if the format is not specified, we will have such a nice window here that can greatly help with debugging:

I also added the ValueConversion (typeof (DateTime), typeof (String)) attribute to the class. It indicates that this converter converts a DateTime to a string. It is a good practice to supply all of your converters with such an attribute, because then when you look at the class header, it becomes immediately clear what and what it converts into.

As a result, we studied the way to transfer any number of parameters to the converter, and these parameters will be named. The only drawback of this method is that for each element in which the converter is used, its own copy of the converter will be created. So, perhaps, the method will work poorly on a collection with a large number of elements.

')

Source: https://habr.com/ru/post/141107/


All Articles