WPF 绑定:当源值在输入后未更改时,如何防止目标更新

本文关键字:更新 目标 何防止 绑定 输入 WPF | 更新日期: 2023-09-27 18:25:01

在与转换器TextBox绑定时,我遇到了一个问题:当用户更改文本时,值不仅流向源,而且始终流回 UI - 即使在转换后的值与输入之前完全相同的情况下,即源值不会更改。

这能预防吗?

我的方案是这样的:我希望用户输入一个以空格分隔的学术头衔列表,这些头衔由模型中代码列表中的数值表示。在有一个有效的标题并且用户按空格以便能够编写第二个标题后出现问题 - 由于转换器容忍空格,"TITLE1"被转换为与"TITLE1"完全相同的数字,但由于触发了目标的更新,文本框输入将更改回"TITLE1", 有效地防止用户添加任何进一步的输入。

模型值为String,XAML绑定如下:

<TextBox Grid.Row="3" Grid.Column="1">
  <TextBox.Text>
    <Binding Path="Model.TitleValuesDelimitedString" Delay="500"
             Converter="{StaticResource TitleValuesDelimitedStringToDisplayStringConv}" 
             ConverterParameter="{x:Static local:UICodeLists.TitleCodeList}"
             UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
         <vrules:TitlesSpaceSeparated />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>

笔记:

  • 源属性是一个简单的 POCO 属性,但我也尝试以一种在值不更改时不会触发的方式实现INotifyPropertyChanged,但没有成功
  • 在绑定上执行显式更新源时也会观察到该行为 (UpdateSourceProperty=Explicit + UpdateSource() (

更新: 转换器代码:

public class TitleValuesDelimitedStringToDisplayStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var ciselnik = parameter as Ciselnik;
        var skrDelimitedList = value as string;
        if (string.IsNullOrEmpty(skrDelimitedList))
            return null;
        var skrList = new List<string>();
        Person.ApplyDelimitedString(skrList, skrDelimitedList);
        StringBuilder sb = new StringBuilder();
        foreach (var skr in skrList)
        {
            if (sb.Length > 0)
                sb.Append(' ');
            sb.Append(ciselnik.FindBySkratka(skr).DisplayName);
        }
        return sb.ToString();
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var ciselnik = parameter as Ciselnik;
        var input = value as string;
        if (string.IsNullOrEmpty(input))
            return Person.CreateDelimitedString(new string[0]);
        List<string> skrList = new List<string>();
        foreach (var titul in input.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
        {
            if (ciselnik.ContainsDisplayName(titul))
                skrList.Add(ciselnik.FindByDisplayName(titul).Skratka);
        }
        return Person.CreateDelimitedString(skrList);
    }
}

WPF 绑定:当源值在输入后未更改时,如何防止目标更新

尝试删除UpdateSourceTrigger="PropertyChanged" 。将应用默认值,即 LostFocus

如果失去焦点的单一源更新不符合您的需求,您可以比较私有TitleValuesDelimitedString字段的修剪版本和 setter 中的值。如果这些相似,请不要提高PropertyChanged.尝试这样的事情:

    private string titleValuesDelimitedString;
    public string TitleValuesDelimitedString
    {
        get { return titleValuesDelimitedString; }
        set
        {
            string fieldComparable = this.titleValuesDelimitedString ?? string.Empty;
            string valueComparable = value ?? string.Empty;
            if (fieldComparable.Trim() != valueComparable.Trim())
            {
                this.titleValuesDelimitedString = value;
                this.OnPropertyChanged("TitleValuesDelimitedString");
            }
        }
    }

更新:

我像这样编辑了您的转换器:

public class TitleValuesDelimitedStringToDisplayStringConverter : IValueConverter
{
    private string latestValueSendToSource = null;
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var ciselnik = parameter as Ciselnik;
        var skrDelimitedList = value as string;
        if (string.IsNullOrEmpty(skrDelimitedList))
            return null;
        var skrList = new List<string>();
        Person.ApplyDelimitedString(skrList, skrDelimitedList);
        StringBuilder sb = new StringBuilder();
        foreach (var skr in skrList)
        {
            if (sb.Length > 0)
                sb.Append(' ');
            sb.Append(ciselnik.FindBySkratka(skr).DisplayName);
        }
        string goingToSendToTarget = sb.ToString();
        if (this.latestValueSendToSource != null && this.latestValueSendToSource.Trim().Equals(goingToSendToTarget)
        {
            return this.latestValueSendToSource;
        }
        else
        {
            return goingToSendToTarget;
        }
    }
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {            
        var ciselnik = parameter as Ciselnik;
        var input = value as string;
        this.latestValueSendToSource = input;
        if (string.IsNullOrEmpty(input))
            return Person.CreateDelimitedString(new string[0]);

        List<string> skrList = new List<string>();
        foreach (var titul in input.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries))
        {
            if (ciselnik.ContainsDisplayName(titul))
                skrList.Add(ciselnik.FindByDisplayName(titul).Skratka);
        }
        return Person.CreateDelimitedString(skrList);
    }
}

请注意变量 latestValueSendToSource 的用法。为了不会导致意外行为,您应该明确绑定获取自己的转换器实例:

<TextBox Grid.Row="3" Grid.Column="1">
    <TextBox.Text>
    <Binding Path="Model.TitleValuesDelimitedString" Delay="500"              
         ConverterParameter="{x:Static local:UICodeLists.TitleCodeList}"
         UpdateSourceTrigger="PropertyChanged">
    <Binding.ValidationRules>
     <vrules:TitlesSpaceSeparated />
  </Binding.ValidationRules>
  <Binding.Converter>
      <local:TitleValuesDelimitedStringToDisplayStringConverter />
  </Binding.Converter>
</Binding>

希望适合您的需求。