DataErrorValidationWithNegativeInteger

IDataErrorInfo

WPF的資料繫結功能強大,可是預設的繫結處理方式不見得符合我們的需要,必須加以客製化。

參考Data Binding Overview的內容,尤其是Data Validation裡的Validation Process小節,對資料繫結的運作有較深刻的了解,幫助我們客製化所需的繫結。

下面,我們考慮這樣的繫結。繫結目標為文字方塊的Text屬性,繫結來源的屬性Number之類別為int?,而且我們希望Number的合格數值為null或者介於-3與20間的整數。

我們從標準繫結開始,根據缺失逐步改良繫結:

  • 用標準繫結(下面的Step 1 至 Step 5),資料錯誤時,完全沒有提示。
  • 改良前述繫結(下面的Step 6),讓繫結目標的文字方塊採用客製化的樣式來提供錯誤提示,了解錯誤提示尚待改進的地方。
  • 改良前述繫結(下面的Step 7),讓繫結的資料轉換器採用客製化的轉換器,了解錯誤提示尚待改進的地方。
  • 改良前述繫結(下面的Step 8),將繫結的驗證規則利用繫結來源物件的IDataErrorInfo介面,提供合乎理想的錯誤提示,看看最後的結果。

Step 1

Visual Studio > New Project > WPF Application > 取名 WpfDataValidation > OK

建立名稱為WpfDataValidation的WPF Application專案

Step 2

Solution Explorer > 專案 WpfDataValidation 右鍵 > Add >
Class > 取名 MainWindowViewModel.cs

在專案中加入MVVM模式所需要的ViewModel,類別為MainWindowViewModel。

Step 3

為了讓 MainWindowViewModel 可以當作繫結的來源物件,在MainWindowViewModel.cs中的MainWindowViewModel類別實作INotifyPropertyChanged介面:

檔案的最前面加上

using System.ComponentModel;

MainWindowViewModel類別的第一行由

class MainWindowViewModel

改成

class MainWindowViewModel :  INotifyPropertyChanged

加入程式碼:

public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

Step 4

需要在MainWindowViewModel類別裡實作屬性Number,類別為int?。當Number發生改變時,觸發 PropertyChanged 事件。實作如下:

private int? _Number;
public int? Number
{
    get
    {
        return _Number;
    }
    set
    {
        if (value !=_Number)
        {
            _Number = value;
            OnPropertyChanged("Number");
        }
    }
}

Step 5

必須為繫結的目標端做些安排:

為了在MainWindow知道它的繫結來源物件之所在,在MainWindow.xaml.cs裡,

InitializeComponent();

的後面增加

DataContext = new MainWindowViewModel();

變成

InitializeComponent();
DataContext = new MainWindowViewModel();

為了設定繫結目標的物件與屬性,在MainWindow.xaml裡,將

<Grid>
</Gid>

改成

<StackPanel>
    <StackPanel Orientation="Horizontal"  Margin="5">
        <TextBlock Width="60" Text="Number:" />
       <TextBox Width="100"
       Text="{Binding Number,
           UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True}" />
    </StackPanel>
</StackPanel>

此時,執行程式。在文字方塊中輸入整數(例如,12)的時候表現正常,一旦輸入的字串無法轉變成整數(例如,12a)的時候,系統會將文字方塊加上紅色的外框,代表資料有誤。這樣,雖然告訴使用者資料錯誤,但沒告知發生了甚麼錯誤,必須加以改良。

Step 6

本步驟將改良文字方塊顯示錯誤的樣式,並提供錯誤訊息。

在MainWindow.xaml裡, <Window>的裡面,增加<Window.Resources>

<Window.Resources>
    <Style x:Key="TextErrorStyle" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <Trigger Property="Validation.HasError" Value="True">
                <!--<Setter Property="Background" Value="Red"/>-->
                <Setter Property="ToolTip"
    Value="{Binding RelativeSource={x:Static RelativeSource.Self},
    Path=(Validation.Errors)[0].ErrorContent}"></Setter>
            </Trigger>
        </Style.Triggers>
        <Setter Property="Validation.ErrorTemplate">
            <Setter.Value>
                <ControlTemplate >
                    <DockPanel>
                        <Border BorderBrush="Red" BorderThickness="1"
                         Padding="2" CornerRadius="2">
                            <AdornedElementPlaceholder/>
                        </Border>
                    </DockPanel>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

<TextBox Width="100"
Text="{Binding Number,
    UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True}" />

改成

<TextBox Width="100" Text="{Binding Number,
   UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True}"
   Style="{StaticResource TextErrorStyle}"  />

此時,執行程式。

在文字方塊中輸入整數(例如,12)的時候表現正常,一旦輸入的字串無法轉變成整數(例如,12a)的時候,文字方塊加上紅色的外框,紅色外框與方塊本身有少許的距離,代表資料有誤。此時,若將滑鼠hover在文字方塊上的時候,出現"input string was not in correct fromat"的提示。這樣,不只告訴使用者資料錯誤,而且告知發生了甚麼錯誤。

  • 可是,將文字方塊的字串改成空白的時候,提示的錯誤訊息為"input string was not in correct fromat"。這個不理想,因為我們希望空字串轉換成null。
  • 而且我們希望Number屬性的可能值為null或介於-3與20間的整數,但是輸入123的時候不會顯示錯誤。

所以,還需要進一步的改良。顯然,WPF如何將字串轉換成int?的預設轉換器(Converter)不能符合我們的需要,必須客製化自己的轉換器。

Step 7

本節,我們製作想要的轉換器,並且加以使用。

Solution Explorer > 專案 WpfDataValidation 右鍵 > Add > Class >
取名 StringToNullableNumber.cs。

在專案中加入檔案StringToNullableNumber.cs。

在 StringToNullableNumber.cs裡,檔案的前面加上兩行

using System.Globalization;
using System.Windows.Data;

將StringToNullableNumber.cs裡的StringToNullableNumber類別實作成:

public class StringToNullableNumber : 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)
    {
        string stringValue = value as string;
        if (stringValue != null)
        {
            if (targetType == typeof(int?))
            {
                int result;
                if (int.TryParse(stringValue, out result))
                    return result;
                //return null;
                if (stringValue == "")
                    return null;
                else
                    return value;
            }
            if (targetType == typeof(decimal?))
            {
                decimal result;
                if (decimal.TryParse(stringValue, out result))
                    return result;
                //return null;
                if (stringValue == "")
                    return null;
                else
                    return value;
            }
        }
        return value;
    }
}

在MainWindow.xaml裡,</Window.Resource>的前面加上

<local:StringToNullableNumber x:Key="stringToNullableNumber" />

在MainWindow.xaml裡,將

<TextBox Width="100" Text="{Binding Number,
   UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True}"
   Style="{StaticResource TextErrorStyle}"  />

改成

<TextBox Width="100"
  Text="{Binding Number,
  UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True,
  Converter={StaticResource stringToNullableNumber}}"
  Style="{StaticResource TextErrorStyle}"  />

此時,執行程式。

  • 將文字方塊的字串改成空白的時候,不會有錯誤訊息,這是我們想要的。
  • 將文字方塊的字串改成"-"的時候有錯誤提示"input string was not in correct fromat",這是正常的,因為"-"無法轉成整數。
  • 將文字方塊的字串改成"-8"或"23"的時候,不會有錯誤提示,這不符合我們的需要,因為-8<-3以及23>20。因此,仍有改進的空間。

下面我們在繫結來源物件實作IDataErrorInfo介面,透過 IDataErrorInfo 提供更好的錯誤提示,完成我們的目標。

Step 8

在MainWindowViewModel類別裡實作IDataErrorInfo:

MainWindowViewModel類別的第一行由

class MainWindowViewModel :  INotifyPropertyChanged

改成

class MainWindowViewModel : IDataErrorInfo, INotifyPropertyChanged

加入程式碼:

public string this[string columnName]
{
    get
    {
        if (columnName == "Number")
        {
            if (_Number < -3 || _Number > 20)
                return string.Format("Number is {0}, but should be empty or integer between -3 and 20.",_Number);
            else
                return null;
        }
        return null;
    }
}
public string Error
{
    get
    {
        throw new NotImplementedException();
    }
}

在MainWindow.xaml裡,將

<TextBox Width="100"
  Text="{Binding Number,
  UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True,
  Converter={StaticResource stringToNullableNumber}}"
  Style="{StaticResource TextErrorStyle}"  />

改成

<TextBox Width="100" Text="{Binding Number,
    UpdateSourceTrigger=PropertyChanged,ValidatesOnExceptions=True,
    Converter={StaticResource stringToNullableNumber},
    ValidatesOnDataErrors=True}"
     Style="{StaticResource TextErrorStyle}"  />

此時,執行程式。

  • 將文字方塊的字串改成空白的時候,不會有錯誤訊息,這是我們想要的。
  • 將文字方塊的字串改成"-"的時候有錯誤提示"input string was not in correct fromat",這是正常的,因為"-"無法轉成整數。
  • 將文字方塊的字串改成"-8"的時候,會有錯誤提示"Number is -8, but should be empty or integer between -3 and 20.",符合我們的需要。
  • 事實上,只有輸入的字串為空白或者介於-3與20間的整數的時候才不會有錯誤提示。

Share this

Previous
Next Post »

技術提供:Blogger.