Когда запускаются конвертеры по умолчанию?
-
09-12-2019 - |
Вопрос
С помощью следующего кода, хотя свойство Text привязано к свойству источника DateTime, я заметил, что WPF, похоже, автоматически преобразует текст в DateTime, без необходимости писать ValueConverter .Может кто-нибудь, пожалуйста, пролить некоторый свет на то, как это делается
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525"
>
<StackPanel>
<DatePicker Height="25" Name="datePicker1" Width="213" Text="{Binding Path=DueDate,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
public class P
{
private DateTime? dueDate = DateTime.Now;
public DateTime? DueDate
{
get { return dueDate; }
set
{
dueDate = value;
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
P p = new P();
this.DataContext = p;
}
}
Решение
Он использует DateTimeTypeConverter
из библиотеки базового класса (Редактировать: Ну, он мог бы использовать TypeConverter, однако, похоже, что из Ответ @deviantseev's этого они не сделали).
Конвертеры "по умолчанию", о которых вы говорите, на самом деле являются TypeConverters
(MSDN) и они были частью .NET Framework начиная с версии 2.0, и они используются во всех библиотеках базовых классов.Другим примером преобразователей типов в WPF является ThicknessTypeConverter
для Padding
, Margin
, и BorderThickness
свойства.Он преобразует строку, разделенную запятыми, в Thickness
объект.
Есть такие много из Статьи доступно, если вы хотите разберитесь в них подробнее.
Использование a состоит из двух частей TypeConverter
- реализация класса и последующая разметка ваших свойств / типов с помощью TypeConverterAttribute
.
Например, недавно у меня появился пользовательский элемент управления, который требовал char[]
, который я хотел установить из Xaml
вот так:
<AutoCompleteTextBox MultiInputDelimiters=",;. " />
Использование
[TypeConverter(typeof(CharArrayTypeConverter))]
public char[] MultiInputDelimiters
{
get { return (char[])GetValue(MultiInputDelimitersProperty); }
set { SetValue(MultiInputDelimitersProperty, value); }
}
Реализация
public class CharArrayTypeConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return (Type.GetTypeCode(sourceType) == TypeCode.String);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value is string)
return ((string)value).ToCharArray();
return value;
}
}
Когда следует использовать TypeConverter
?
Вы можете только использовать TypeDescriptors
если вы пишете пользовательский элемент управления, поскольку вам нужно иметь возможность пометить свойство с помощью TypeDescriptorAttribute
.Кроме того, я бы использовал только TypeConverter
если преобразование довольно простое - как в примере выше, где у меня есть строка и я хочу char[]
- или если существует несколько возможных форматов, из которых я хочу конвертировать.
Ты пишешь IValueConverter
когда вы хотите большей гибкости в отношении того, как преобразовывать значение, управляя им с помощью данных или передавая параметр.Например, очень распространенным действием в WPF является преобразование bool
Для Visibility
;существует три возможных результата такого преобразования (Visible
, Hidden
, Collapsed
) и только с двумя входами (true
, false
) трудно решить это в TypeConverter
.
В моих приложениях для решения этой задачи с двумя входами и тремя выходами я написал один BoolToVisibilityConverter
с помощью TrueValue
и FalseValue
свойства, а затем я создаю его экземпляр три раза в моем глобальном ResourceDictionary
. Я опубликую пример кода завтра утром, прямо сейчас он у меня перед глазами..
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BooleanToVisibilityConverter : IValueConverter
{
public Visibility FalseCondition { get; set; }
public Visibility TrueCondition { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value) ? TrueCondition : FalseCondition;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((bool)value)
return TrueCondition;
return FalseCondition;
}
}
<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" FalseCondition="Collapsed" TrueCondition="Visible"/>
<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityCollapsedConverter" FalseCondition="Visible" TrueCondition="Collapsed"/>
<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityHiddenConverter" FalseCondition="Visible" TrueCondition="Hidden"/>
<converters:BooleanToVisibilityConverter x:Key="BoolToVisibilityHiddenWhenFalseConverter" FalseCondition="Hidden" TrueCondition="Visible"/>
Другие советы
Дата datepicker - это пользовательский элемент управления, который был изначально частью инструментария WPF, прежде чем добавляться в качестве стандартного управления в .NET 4.
Я только что пошел в репозиторий исходного кода для контроля, чтобы найти точный исходный код, который отвечает за преобразование текста на сегодняшний день:
#region Text
/// <summary>
/// Gets or sets the text that is displayed by the DatePicker.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
/// <summary>
/// Identifies the Text dependency property.
/// </summary>
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(
"Text",
typeof(string),
typeof(DatePicker),
new FrameworkPropertyMetadata(string.Empty, OnTextChanged, OnCoerceText));
/// <summary>
/// TextProperty property changed handler.
/// </summary>
/// <param name="d">DatePicker that changed its Text.</param>
/// <param name="e">DependencyPropertyChangedEventArgs.</param>
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DatePicker dp = d as DatePicker;
Debug.Assert(dp != null);
if (!dp.IsHandlerSuspended(DatePicker.TextProperty))
{
string newValue = e.NewValue as string;
if (newValue != null)
{
if (dp._textBox != null)
{
dp._textBox.Text = newValue;
}
else
{
dp._defaultText = newValue;
}
dp.SetSelectedDate();
}
else
{
dp.SetValueNoCallback(DatePicker.SelectedDateProperty, null);
}
}
}
private static object OnCoerceText(DependencyObject dObject, object baseValue)
{
DatePicker dp = (DatePicker)dObject;
if (dp._shouldCoerceText)
{
dp._shouldCoerceText = false;
return dp._coercedTextValue;
}
return baseValue;
}
/// <summary>
/// Sets the local Text property without breaking bindings
/// </summary>
/// <param name="value"></param>
private void SetTextInternal(string value)
{
if (BindingOperations.GetBindingExpressionBase(this, DatePicker.TextProperty) != null)
{
Text = value;
}
else
{
_shouldCoerceText = true;
_coercedTextValue = value;
CoerceValue(TextProperty);
}
}
#endregion Text
. В большинстве случаев я считаю, что WPF вызывает TOSTRING () для вас, однако, если вы посмотрите на код для сборщика даты Важная строка -
(string)GetValue(TextProperty)
.
Обратите внимание, что он отличает значение, которое вы назначаете на свойство «Text» в строку?Весь смысл в том, что нет преобразователя по умолчанию в более традиционном чувстве булентноупотребления контроллера или что-то подобное.