ValueConvertor’ы очень полезная и удобная штука, которая очень часто используется при биндинге (будем по народному)для преобразования одного типа данных в другой, например:
- Вы делаете компонент который отображает прогресс какого-то действия и вам надо менять цвет от зеленого до красного, в зависимости от того, на сколько прогресс движется к концу;
- Вам надо сделать какой-то расчет, например зарплату по количеству часов * рейт работника;
- Или более простые пример, это отформатировать дату или преобразовать цвет по #AABBCCDD в строковое представление;
- Инвертировать отображения элемента, например у вас биндится свойство на CheckBox типо IsDeclined и если оно true, то CheckBox не должен быть отмечен (пример не надуманый, всякое бывает). Если есть возможность, то можно переименовать в Allowed и будет логично, а если нет? Тогда делаем сами уже знаете что.
Конверторы инкапсулируют какую-то логику, которая может меняться, они также могут принимать параметры. Также конверторы используются для отладки биндинга (об этом позже).
Я думаю у многих возникала потребность в редактируемом тексте, например отображается текст в метке “Edit me”, вы щелкаете по тексту, он меняется на TextBox, вы его редактируете, нажимаете Enter и опять метка, круто.
Рассмотрим как это можно сделать с использованием конверторов. Определим правила игры:
- Метка может иметь начальный текст
- Метка переходит в состояние редактирования по щелчку мышки на ней
- TextBox переходит в состояние метки после нажатия клавиши Enter или когда пользователь уводит курсор с TextBox или теряется фокус.
Рассмотрим XAML нашего компонента:
<UserControl x:Class="EditableLabel.Control.EditableText" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Converters="clr-namespace:EditableLabel.Converters" x:Name="editableText"> <UserControl.Resources> <Converters:BooleanToVisibleConverter x:Key="converter" /> </UserControl.Resources> <Grid> <Label x:Name="label" Content="{Binding ElementName=editableTextBox, Path=Text}" MouseLeftButtonDown="OnMouseLeftButtonClick" Visibility="{Binding AllowEdit, ElementName=editableText, Converter={StaticResource converter}, ConverterParameter=True}"/> <TextBox x:Name="editableTextBox" LostFocus="OnTextBoxLostFocus" MouseLeave="OnTextBoxMouseLeave" KeyDown="OnTextBoxKeyDown" Text="{Binding Text, ElementName=editableText}" Visibility="{Binding AllowEdit, ElementName=editableText, Converter={StaticResource converter}, ConverterParameter=False}"/> </Grid> </UserControl>
У нас есть Label и TextBox. Когда AllowEdit свойство True отображается TextBox, в другом случае Label.
Текст для TextBox берется из свойства Text компонента, а текст для метки берется из TextBox. Зависимость выглядит так:
Label.Text < – TextBox.Text < – > UserControl.Text
В ресурсах у нас объявлен конвертер, который используется для свойства Visibility у Label и TextBox.
Visibility="{Binding AllowEdit, ElementName=editableText, Converter={StaticResource converter}, ConverterParameter=False}вот его код:
namespace EditableLabel.Converters { [ValueConversion(typeof(bool), typeof(Visibility))] public class BooleanToVisibleConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { bool flag = (bool) value; bool inverted = false; if (parameter != null) { inverted = Boolean.Parse(parameter.ToString()); } return inverted ? (flag ? Visibility.Hidden : Visibility.Visible) : (flag ? Visibility.Visible : Visibility.Hidden); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } }
Метод ConvertBack очень редко используется, в некоторых случаях при двунаправленном биндинге (расскажу в другой раз), поэтому всегда остается таким и не вызывается в данном случае.
В метод Convert, в параметр value приходит значение, которое мы биндим, в данном случае AllowEdit, а в parameter будет передано значение, которое мы указали в ConverterParameter (если не указали - null соответственно).
ConverterParameter не единственный способ передать параметры в класс конвертора. Если вам надо передать несколько параметров это можно сделать так:
[ValueConversion(typeof(bool), typeof(Visibility))] public class BooleanToVisibleConverter : IValueConverter { public string AdditionalValue { get; set; } public int AnotherValue { get; set; } public object Convert(object value, Type targetType, object parameter, CultureInfo culture) }
и затем использовать в разметке или в коде так:
<Converters:BooleanToVisibleConverter x:Key="converter" AdditionalValue="asd" AnotherValue="2" />
Единственная проблема, если у вас биндятся ListBoxItem’s, например, и у каждого свое значение для конвертора, то такой способ объявления конвертора со свойствами не прокатит. Этот способ хорошо когда конвертор используется 1 раз. В другом случае придется использовать IMultiValueConverter. Принцип тот же самый, только в Convert передается object[] values.
ValueConversion атрибут используется для того, чтоб быстрей было понять, из какого в какой тип происходит конвертация. Этот атрибут не обязателен, просто best practice + удобство чтения.
Код событий:
using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace EditableLabel.Control { /// <summary> /// Interaction logic for EditableText.xaml /// </summary> public partial class EditableText : UserControl { public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof (string), typeof (EditableText)); public static readonly DependencyProperty AllowEditProperty = DependencyProperty.Register("AllowEdit", typeof (bool), typeof (EditableText)); public bool AllowEdit { get { return (bool) GetValue(AllowEditProperty); } set { SetValue(AllowEditProperty, value); } } public string Text { get { return (string) GetValue(TextProperty); } set { SetValue(TextProperty, value); } } public EditableText() { InitializeComponent(); } private void OnMouseLeftButtonClick(object sender, MouseButtonEventArgs e) { AllowEdit = true; editableTextBox.Focus(); } private void OnTextBoxLostFocus(object sender, RoutedEventArgs e) { AllowEdit = false; } private void OnTextBoxMouseLeave(object sender, MouseEventArgs e) { AllowEdit = false; } private void OnTextBoxKeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Enter) { AllowEdit = false; } } } }
Варианты для усовершенствования:
- Cделать чтоб ширна TextBox’a была такой же как у метки;
- Возможно вынести в отдельную сборку для дальнейшего использования и сделать как Custom Control, может потом захочется вид изменить или эффекты прикрутить.
Комментариев нет:
Отправить комментарий