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