Использование WPF TextBox с URI-конвертером, недопустимый ввод стирает текстовое поле - PullRequest
1 голос
/ 26 апреля 2011

У меня есть текстовое поле WPF в форме, чтобы разрешить ввод URI.

Я попытался сделать это с помощью конвертера данных.Проблема заключается в том, что когда привязка текстового поля обновляется, а текстовое поле не содержит действительного URI,

  • Преобразователь данных возвращает значение NULL;
  • , который устанавливает для свойства моей модели значение NULL;
  • , что приводит к запуску события, изменяющего свойство;
  • , который устанавливает значение текстового поля в пустую строку, стирая недопустимый ввод пользователя

I'mновичок WPF, и я затрудняюсь найти простой шаблон, использующий конвертер данных, который не приводит к такому поведению.Я думаю, что должен быть стандартный шаблон для использования, о котором я знал бы, если бы я был опытным программистом WPF.

Глядя на примеры, включенные в Prism 4, кажется, что используются два различных подхода.,Мне они оба не нравятся.

Первый подход - генерировать исключение, когда для моего свойства модели задано значение null, которое перехватывается и отображается как ошибка проверки.Проблема заключается в том, что я хочу, чтобы свойство могло иметь значение null - каждый раз, когда вы открываете форму, поля устанавливаются в свои предыдущие значения.Если приложение никогда ранее не запускалось, для URI будет установлено значение null - это не должно вызывать исключения.Кроме того, использование исключений для проверки является уродливым.

Второй подход заключается в том, что когда для свойства задано значение NULL, задайте состояние проверки модели для включения недействительности свойства, но на самом деле не обновляйте свойство,Который я считаю ужасным.Это приводит к тому, что модель является внутренне несовместимой, утверждая, что DCSUri недействителен, но содержит предыдущее допустимое значение DCSUri.

Подход, который я использую, чтобы избежать этих проблем, заключается в том, чтобы в моем ViewModel была строка DCSUri., который обновляет свойство DCSUri, введенное в Uri моей Модели, только если это действительный URI.Но я бы предпочел подход, который позволяет использовать конвертер и привязывать мое текстовое поле непосредственно к моей модели.

Код моего конвертера:

/// <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 Ответ

5 голосов
/ 26 апреля 2011

Вам следует использовать 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 есть приличная статья о проверке ввода , которая может оказаться вам полезной.


Чтобы проверить значение для нуля, вы можете использовать другой конвертер и вспомогательный 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>
...