gpt4 book ai didi

c# - 为什么将TextBox的Value重置为先前的值而不显示错误?

转载 作者:行者123 更新时间:2023-11-30 16:04:58 25 4
gpt4 key购买 nike

我有一个示例,其中我将视图模型的属性与某些TextBox控件(包括验证规则)绑定在一起。在大多数情况下,这可以正常工作。但是,当我尝试包括绑定的IsFocusedTextBox属性时,在控件中输入无效数字的情况下,我遇到了麻烦。

当我在直接绑定到视图模型属性的TextBox控件中输入错误的数字时,错误将按预期显示(TextBox周围的红色边框)。但是在与TextBox绑定的MultiBinding中,该IsFocused既包含视图模型属性又包含TextBoxTextBox属性,则不会显示错误,并且该值将重置为先前的有效值。

例如,如果小于10的数字无效,并且我输入3,则当TextBox失去焦点时,TextBox中通常会出现红色边框,表示错误。但是在包含IsFocused作为其绑定源的TextBox中,该值会变回先前的有效值(如果在输入3之前有39,则TextBox会变回39)。

使用下面的代码,您可以重现该问题:

TestViewModel.cs

public class TestViewModel
{
public double? NullableValue { get; set; }
}


MainWindow.xaml

<Window x:Class="TestSO34204136TextBoxValidate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO34204136TextBoxValidate"
Title="MainWindow" Height="350" Width="525">

<Window.DataContext>
<l:TestViewModel/>
</Window.DataContext>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Nullable: "/>
<TextBox VerticalAlignment="Top" Grid.Column="1">
<TextBox.Text>
<MultiBinding Mode="TwoWay">
<Binding Path="NullableValue"/>
<Binding Path="IsFocused"
RelativeSource="{RelativeSource Self}"
Mode="OneWay"/>
<MultiBinding.ValidationRules>
<l:ValidateIsBiggerThanTen/>
</MultiBinding.ValidationRules>
<MultiBinding.Converter>
<l:TestMultiBindingConverter/>
</MultiBinding.Converter>
</MultiBinding>
</TextBox.Text>
</TextBox>
<TextBox VerticalAlignment="Top" Grid.Column="2"/>
</Grid>
</Window>


TestMultiBindingConverter.cs

public class TestMultiBindingConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values[0] != null)
return values[0].ToString();
return DependencyProperty.UnsetValue;
}

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value != null)
{
double doubleValue;
var stringValue = value.ToString();
if (Double.TryParse(stringValue, out doubleValue))
{
object[] values = { doubleValue };
return values;
}
}

object[] values2 = { DependencyProperty.UnsetValue };
return values2;
}
}


ValidateIsBiggerThanTen.cs

public class ValidateIsBiggerThanTen : ValidationRule
{
private const string errorMessage = "The number must be bigger than 10";

public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var error = new ValidationResult(false, errorMessage);

if (value == null)
return new ValidationResult(true, null);

var stringValue = value.ToString();
double doubleValue;
if (!Double.TryParse(stringValue, out doubleValue))
return new ValidationResult(true, null);

if (doubleValue <= 10)
return error;
return new ValidationResult(true, null);
}
}


为什么在上面的示例中 不会显示错误?

最佳答案

您所看到的行为的原因具体是您已将TextBoxIsFocused属性绑定到MultiBinding中。当焦点改变时,这直接具有迫使更新绑定目标的效果。

在验证失败的情况下,会在很短的时间内触发验证规则,设置了错误,但是焦点实际上尚未更改。但这一切发生得太快,以至于用户看不到。而且由于验证失败,因此绑定源不会更新。

因此,当IsFocused属性值更改时,在对输入值进行验证和拒绝后,接下来要发生的事情是重新评估绑定(因为源属性之一已更改!)以更新目标。并且由于实际源值从未更改,因此目标(TextBox)将从您键入的内容恢复为源中存储的内容。


您应该如何解决?这取决于所需的确切行为。您有三个基本选项:


继续绑定到IsFocused,然后添加UpdateSourceTrigger="PropertyChanged"。这将保持基本的当前行为,即失去焦点时将旧值复制回去,但至少会在编辑值时为用户提供立即的验证反馈。
完全删除与IsFocused的绑定。这样,绑定的目标将不再依赖于此,并且在焦点更改时也不会重新评估。问题解决了。 :)
继续绑定到IsFocused,并添加逻辑,以便与验证进行交互不会导致将过时的值复制回TextBox


根据我们的来回评论,似乎上面的第三个选项是您的方案的首选,因为您希望在控件具有焦点时与没有控件时不同地设置值的文本表示形式。


我对用户界面的智慧表示怀疑,该界面根据控件是否聚焦而不同地格式化数据。当然,对焦点进行更改以影响整个视觉呈现是完全有意义的,但这通常涉及诸如下划线,突出显示等内容。根据控件是否聚焦而显示完全不同的字符串似乎可能会干扰用户的理解和理解。可能还会惹恼他们。

但是,我同意这是一个主观要点,并且显然,在您的情况下,您具有此特定行为,这对于您的规范而言是理想的,需要得到支持。因此,考虑到这一点,让我们看一下如何实现这种行为……


如果您希望能够绑定到IsFocused属性,但是如果尚未真正更新源(即,如果验证错误阻止了该更改的发生),则无需进行更改以将焦点复制到控件的当前内容上,那么您还可以绑定到Validation.HasError属性,并使用该属性控制转换器的行为。例如:

class TestMultiBindingConverter : IMultiValueConverter
{
private bool _hadError;

public object Convert(object[] values,
Type targetType, object parameter, CultureInfo culture)
{
bool? isFocused = values[1] as bool?,
hasError = values[2] as bool?;

if ((hasError == true) || _hadError)
{
_hadError = true;
return Binding.DoNothing;
}

if (values[0] != null)
{
return values[0].ToString() + (isFocused == true ? "" : " (+)");
}

return DependencyProperty.UnsetValue;
}

public object[] ConvertBack(object value,
Type[] targetTypes, object parameter, CultureInfo culture)
{
if (value != null)
{
double doubleValue;
var stringValue = value.ToString();
if (Double.TryParse(stringValue, out doubleValue))
{
object[] values = { doubleValue };
_hadError = false;
return values;
}

}
object[] values2 = { DependencyProperty.UnsetValue };
return values2;
}
}


上面添加了一个字段 _hadError来“记住”控件最近发生的情况。如果在验证检测到错误时调用该转换器,则该转换器将返回 Binding.DoNothing(其效果暗示其名称为:)),并设置该标志。此后,无论发生什么情况,只要设置了该标志,转换器将始终不执行任何操作。

清除标志的唯一方法是,如果用户最终输入有效的文本。然后,将调用转换器的 ConvertBack()方法来更新源,并且这样做可以清除 _hadError标志。这样可以确保控件内容永远不会由于绑定更新而被覆盖,除非自上次更新源以来没有错误。

这是上面更新的XAML示例,以使用其他绑定输入:

<Window x:Class="TestSO34204136TextBoxValidate.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:TestSO34204136TextBoxValidate"
Title="MainWindow" Height="350" Width="525">

<Window.DataContext>
<l:TestViewModel/>
</Window.DataContext>

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Nulleable: "/>
<TextBox x:Name="textBoxWrapper" Grid.Column="1" VerticalAlignment="Top">
<TextBox.Text>
<MultiBinding x:Name="TextBoxBinding" Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding Path="NulleableValue"/>
<Binding Path="IsFocused"
RelativeSource="{RelativeSource Self}"
Mode="OneWay"/>
<Binding Path="(Validation.HasError)"
RelativeSource="{RelativeSource Self}"
Mode="OneWay"/>
<MultiBinding.ValidationRules>
<l:ValidateIsBiggerThanTen/>
</MultiBinding.ValidationRules>
<MultiBinding.Converter>
<l:TestMultiBindingConverter/>
</MultiBinding.Converter>
</MultiBinding>
</TextBox.Text>
</TextBox>
<TextBox VerticalAlignment="Top" Grid.Column="2"/>
</Grid>
</Window>


我应该指出,以防万一: _hadError字段用于转换器本身。为了使以上内容正常工作,您需要为转换器所应用的每个绑定单独设置转换器的实例。有其他方法可以唯一地跟踪每个控件的此类标志,但我认为对此方面的选项进行的扩展讨论不在此问题的范围内。如果您无法自行解决问题,请自行探索,并发布有关该方面的新问题。

关于c# - 为什么将TextBox的Value重置为先前的值而不显示错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34204136/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com