WPF: BindingExpression и Nullable<T>

    • .NET
    • C#
    • WPF
    • XAML
    • Binding
    • Nullable
  • modified:
  • reading: 2 minutes

Если прописывать Path в Binding на типы Nullable<T> на свойство HasValue, такие как int?, bool? и другие, то можно увидеть неожиданные результаты, а точнее работать данный биндинг не будет. Давайте рассмотрим данный случай подробнее.

Итак, предположим в нашем коде присутствует следующая модель данных:

using System; 
namespace WpfApplicationHasValue 
{ 
    class SampleModel 
    { 
        public SampleModel() 
        { 
         SampleValue2 = 0; 
        } 
        public int? SampleValue { get; set; } 
        public int? SampleValue2 { get; set; } 
    } 
}

Получается, что SampleValue равен null и SampleValue.HasValue возвращает false. Используем данную модель в WPF приложении:

<Window x:Class="WpfApplicationHasValue.MainWindow" 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       xmlns:WpfApplicationHasValue="clr-namespace:WpfApplicationHasValue" 
       Title="MainWindow" Height="240" Width="500" 
       DataContext="{DynamicResource ResourceKey=SampleModel}"> 
    <Window.Resources> 
        <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> 
        <WpfApplicationHasValue:SampleModel x:Key="SampleModel" /> 
    </Window.Resources> 
    <StackPanel > 
        <TextBlock Visibility="{Binding Path=SampleValue.HasValue, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}">
                Must be InVisible
        </TextBlock> 
        <TextBlock Visibility="{Binding Path=SampleValue2.HasValue, Converter={StaticResource ResourceKey=BooleanToVisibilityConverter}}">
                Must be Visible
        </TextBlock> 
    </StackPanel> 
</Window>

На размещенных двух TextBlock’ах мы повесили Binding на свойства Visiblility с конвертером из bool в Visibility. Конечно мы ожидаем, как я и написал в телах TextBlock такого результата, что первый должен быть не видимым, но такого не произойдет, вместо этого в Output окне мы увидим:

System.Windows.Data Error: 40 : BindingExpression path error: 'HasValue' property not found on 'object' ''Int32' (HashCode=0)'. BindingExpression:Path=SampleValue2.HasValue; DataItem='SampleModel' (HashCode=6257078); target element is 'TextBlock' (Name=''); target property is 'Visibility' (type 'Visibility')

Проблема конечно же в разборе самого Path нашего Binding, и очевидно, что сам WPF тут не причем. Разбор данного пути будет проходить в два этапа:

1. Получаем PropertyInfo для свойства “SampleValue” и вызываем pi.GetValue(item) для получения значения x.
2. Получаем PropertyInfo для свойства “HasValue” и вызываем pi.GetValue(x) для получения финального результата.

Ну и проблема, собственно, в том, что несмотря на то, что pi в первом шаге имеет PropertyType = typeof(Nullable<int>) как и следовало ожидать, величина x возвращаемая методом GetValue возвращается или int или null, но не Nullable<int>. Следовательно шаг 2 не удается, поскольку x не имеет свойство “HasValue”.

В итоге, WPF не может поддерживать свойства HasValue (или Value) в пути биндига, пока CLR (Reflection) не изменит логику в отношении возвращаемых значений типов Nullable<T>, а такого, скорее всего, не будет.

Само собой, это не является проблемой, и если даже и возникнет такая ситуация, то решить ее можно легко. В основном, данный топик просто для предоставления некоторых основ WPF.

See Also