如果绑定 属性 为空,C# WPF 绑定转换器不会触发

C# WPF Binding Converter not firing if bound property is null

我目前在使用 WPF MVVM 数据绑定时遇到了一个奇怪的问题。

我们的想法是使用 ValueConverter,如果它不为空则显示绑定值,否则显示 ConverterParameter 中定义的字符串。

Person class 具有以下属性:Name (string)Surname (string)Country (class).

Country class 具有以下属性:CountryName (string).

当 TextBlock 绑定到 Person.Country.CountryNamePerson.Country 为空时,IValueConverter 的使用失败 IValueConverter 方法 Convert 甚至不触发 来检查绑定值是否为 null。

同时 IMultiValueConverter 工作得很好,无论 Person.Country 是否为空,每次都会触发。

思路为什么会这样?尝试搜索任何 Microsoft 文章,但一无所获。

感谢任何帮助。

源 GitHub 项目可在此处找到:https://github.com/kevintw86/WpfBindingConverterIssue.git

绑定到 IValueConverter(当 Person.Country==null 时无法正常工作):

<TextBlock 
          Text="{Binding Person.Country.CountryName, 
          UpdateSourceTrigger=PropertyChanged, 
          Converter={StaticResource PersonConverter}, 
          ConverterParameter=- Not set -}"
          FontSize="16"/>

个人转换器:

public class PersonConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null)
                return parameter;

            if (value == DependencyProperty.UnsetValue)
                return parameter;

            if (string.IsNullOrWhiteSpace(value.ToString()))
                return parameter;

            return value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

绑定到 IMultiValueConverter(即使 Person.Country==null 也能正常工作):

<TextBlock FontSize="16">
                <TextBlock.Text>
                        <MultiBinding Converter="{StaticResource PersonMultiConverter}"
                                  ConverterParameter="- Not set -">
                            <MultiBinding.Bindings>
                                <Binding Path="Person.Country.CountryName"
                                     UpdateSourceTrigger="PropertyChanged"/>
                            </MultiBinding.Bindings>
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>

PersonMultiConverter:

public class PersonMultiConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            if (values == null)
                return parameter;

            if (values[0] == DependencyProperty.UnsetValue)
                return parameter;

            if (values[0] == null)
                return parameter;

            if (string.IsNullOrWhiteSpace(values[0].ToString()))
                return parameter;

            return values[0];
        }

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

人class:

public class Person : INotifyPropertyChanged
{
    private string _Name;
    public string Name
    {
        get { return _Name; }
        set 
        { 
            _Name = value;
            OnPropertyChanged();
        }
    }

    private string _Surname;
    public string Surname
    {
        get { return _Surname; }
        set 
        { 
            _Surname = value;
            OnPropertyChanged();
        }
    }

    private Country _Country;
    public Country Country
    {
        get { return _Country; }
        set 
        {
            _Country = value;
            OnPropertyChanged();
        }
    }

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }

    #endregion
}

国家class:

public class Country : INotifyPropertyChanged
{
    private string _CountryName;
    public string CountryName
    {
        get { return _CountryName; }
        set 
        { 
            _CountryName = value;
            OnPropertyChanged();
        }
    }

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }

    #endregion
}

MainWindowViewModel:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public MainWindowViewModel()
    {
        this.Person = new Person
        {
            Name = "John",
            Surname = null,
            Country = null,
        };
    }

    #region Properties

    private Person _Person;
    public Person Person
    {
        get { return _Person; }
        set 
        { 
            _Person = value;
            OnPropertyChanged();
        }
    }

    #endregion

    #region INotifyPropertyChanged implementation

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = "")
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }

    #endregion
}

考虑使用 TargetNullValue and FallbackValue 而不是转换器:

<TextBlock 
      Text="{Binding Person.Country.CountryName, 
      TargetNullValue='- Not set -', 
      FallbackValue='- Not set -'}"