将WPF TextBox与URI转换器一起使用时,无效的输入会擦除文本框

时间:2011-04-25 22:01:55

标签: wpf binding converter

我在表单上有一个WPF文本框,允许输入URI。

我尝试使用数据转换器执行此操作。问题是当文本框绑定更新且文本框不包含有效的URI时,

  • 数据转换器返回null;
  • 将我的model属性设置为null;
  • 导致属性更改事件触发;
  • 将文本框值设置为空字符串,消除用户无效输入

我是一名WPF新手,我无法使用不会导致此行为的数据转换器找到一个简单的模式。我认为必须有一个标准模式可供使用,我知道我是否是一位经验丰富的WPF程序员。

观察Prism 4中包含的示例,似乎使用了两种不同的方法。我不喜欢他们两个。

第一种方法是在我的模型属性设置为null时抛出异常,该属性被捕获并显示为验证错误。问题是我希望该属性能够设置为null - 每次打开表单时,字段都设置为以前的值。如果之前从未运行过该应用程序,则UR​​I将设置为null - 这不应该抛出异常。此外,使用例外验证是丑陋的。

第二种方法是当属性设置为null时,将模型的验证状态设置为包含属性无效,但实际上不更新属性。我认为这很糟糕。它导致模型内部不一致,声称DCSUri无效,但包含DCSUri的先前有效值。

我用来避免这些问题的方法是在我的ViewModel中有一个字符串DCSUri,如果它是一个有效的URI,它只会更新我的模型的Uri类型的DCSUri属性。但我更喜欢一种允许使用转换器并将我的文本框直接绑定到我的模型的方法。

我的转换器代码:

/// <summary>
/// Converter from Uri to a string and vice versa.
/// </summary>
public class StringToUriConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Uri uri = null;
        string stringValue = value as string;
        if (stringValue != null)
            Uri.TryCreate(stringValue, UriKind.Absolute, out uri);
        return uri;
    }
}

文本框的XAML:

    <TextBox Grid.Row="1" Grid.Column="1" Name="DCSUriTextBox" 
             Text="{Binding Path=DCSLoadSettings.DCSUri, Mode=TwoWay, UpdateSourceTrigger=LostFocus, ValidatesOnExceptions=True, NotifyOnValidationError=True, ValidatesOnDataErrors=True, Converter={StaticResource StringToUriConverter} }" 
             HorizontalAlignment="Stretch" Height="Auto" VerticalAlignment="Center" Margin="5,0,20,0" IsReadOnly="{Binding Path=IsNotReady}" Grid.ColumnSpan="2" />

我模型中DCSUri属性的代码:

    /// <summary>
    /// The Uri of the DCS instance being provided configuration
    /// </summary>
    public Uri DCSUri
    {
        get
        {
            return mDCSUri;
        }
        set
        {
            if (!Equals(value, mDCSUri))
            {
                mDCSUri = value;
                this["DCSUri"] = value == null
                    ? "Must provide a Uri for the DCS instance being provided configuration"
                    : string.Empty;
                RaisePropertyChanged(() => DCSUri);
            }
        }
    }

1 个答案:

答案 0 :(得分:5)

您应该使用ValidationRules进行验证,并以正确的方式命名您的转换器;我会像这样接近它(假设您希望能够将Uri设置为null):

public class UriToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        Uri input = value as Uri;
        return input == null ?
            String.Empty : input.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        string input = value as string;
        return String.IsNullOrEmpty(input) ?
            null : new Uri(input, UriKind.Absolute);
    }
}
public class UriValidationRule : ValidationRule
{
    public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
    {
        string input = value as string;
        if (String.IsNullOrEmpty(input)) // Valid input, converts to null.
        {
            return new ValidationResult(true, null);
        }
        Uri outUri;
        if (Uri.TryCreate(input, UriKind.Absolute, out outUri))
        {
            return new ValidationResult(true, null);
        }
        else
        {
            return new ValidationResult(false, "String is not a valid URI");
        }
    }
}

然后像这样使用它(或者将转换器和规则定义为某个地方的资源):

<TextBox MinWidth="100">
    <TextBox.Text>
        <Binding Path="Uri">
            <Binding.ValidationRules>
                <vr:UriValidationRule />
            </Binding.ValidationRules>
            <Binding.Converter>
                <vc:UriToStringConverter/>
            </Binding.Converter>
        </Binding>
    </TextBox.Text>
</TextBox>

如果输入文本未通过验证,则不会调用转换器,这就是为什么我没有TryCreate或类似内容的原因。

CodeProject上有a decent article about input validation您可能会觉得有帮助。


要测试null的值,您可以使用另一个转换器和帮助程序TextBlock:

public class NullToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value == null ?
            "NULL" : value;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}
<TextBlock>
    <TextBlock.Text>
        <Binding Path="Uri">
            <Binding.Converter>
                <vc:NullToStringConverter/>
            </Binding.Converter>
        </Binding>
    </TextBlock.Text>
</TextBlock>