擴展 CheckBoxField 類別 - 支援非布林值的雙向繫結
摘要
在「讓 CheckBoxField 繫結非布林值(0 或 1)欄位」一文中有介紹了如何修改 CheckFieldBox 去繫結 0 或 1 的非布林值,其作法是將非布林直接使用 CBool 函式將欄位值強制轉型為布林值。 不過有時繫結的欄位值並無法直接使用 CBool 轉型為布林值,例如 "T/F"、"是/否" 之類的資料,若希望使用 CheckBoxField 來顯示就比較麻煩,一般的作法都是轉為 TemplateField,自行撰寫資料繫結的函式,而且只能支援單向繫結。在本文中我們將直接改寫 CheckBoxField 類別,讓 CheckBoxField 可以直接雙向繫結 "T/F" 或 "是/否" 之類的資料。
擴展 CheckBoxField 類別
我們繼承 CheckBoxField 命名為 TBCheckBoxField,新增 DefineValue 屬性,用來做布林值轉換定義(格式為 "True/False");若設定 DefineValue="是/否",即欄位值為 "是" 對應到 True,而 "否" 對應到 False。
當讀取欄位值要繫結至控制項時會引發 OnDataBindField 方法;故覆寫 OnDataBindField 方法,判斷若有設定 DefineValue 屬性,則利用 ValueToBoolean 方法將欄位值依 DefineValue 的定義轉換為布林值。例如將 "男/女" 轉為 "True/False",再與控制項做繫結。
當要寫入資料時會由控制項擷取出目前的欄位值,此時會引發 ExtractValuesFromCell 方法;故覆寫 ExtractValuesFromCell 方法,此時會擷取 CheckBoxField 內含的 CheckBox 控制項的 Checked 屬性值(布林值),若有設定 DefineValue 屬性時,會利用 BooleanToValue 方法將布林值依 DefineValue 的定義轉換為欄位值。
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace WebControls
< _
Description("核取方塊欄位") _
> _
Public Class TBCheckBoxField
Inherits CheckBoxField
Private FDefineValue As String = String.Empty
''' <summary>
''' 布林值轉換定義,格式為 "True/False"。
''' </summary>
''' <returns>
''' 當設定為 "T/F" 時,表示 T 為 True,F 為 False。
''' 當設定為 "T/*" 時,表示 T 為 True,其他為 False。
''' </returns>
Public Property DefineValue() As String
Get
Return FDefineValue
End Get
Set(ByVal value As String)
FDefineValue = value
End Set
End Property
''' <summary>
''' 解析布林值轉換定義。
''' </summary>
''' <param name="DefineValue">布林值轉換定義。</param>
''' <param name="TrueValue">True 的對應值。</param>
''' <param name="FalseValue">False 的對應值。</param>
Private Sub ParserDefineValue(ByVal DefineValue As String, ByRef TrueValue As String, ByRef FalseValue As String)
Dim oParts() As String
oParts = Split(Me.DefineValue, "/")
If oParts.Length <> 2 Then
Throw New HttpException("DefineValue 格式錯誤")
End If
TrueValue = oParts(0)
FalseValue = oParts(1)
End Sub
''' <summary>
''' 欄位值依 DefineValue 的定義轉換為布林值。
''' </summary>
''' <param name="Value">欄位值。</param>
Private Function ValueToBoolean(ByVal Value As Object) As Boolean
Dim sFieldValue As String
Dim sTrueValue As String
Dim sFalseValue As String
sFieldValue = CStr(Value)
sTrueValue = String.Empty
sFalseValue = String.Empty
ParserDefineValue(Me.DefineValue, sTrueValue, sFalseValue)
If sFieldValue = sTrueValue Then
Return True
Else
Return False
End If
End Function
''' <summary>
''' 布林值依 DefineValue 的定義轉換為欄位值。
''' </summary>
''' <param name="Value">布林值。</param>
Private Function BooleanToValue(ByVal Value As Boolean) As Object
Dim sTrueValue As String
Dim sFalseValue As String
sTrueValue = String.Empty
sFalseValue = String.Empty
ParserDefineValue(Me.DefineValue, sTrueValue, sFalseValue)
If Value Then
Return sTrueValue
Else
Return sFalseValue
End If
End Function
''' <summary>
''' 將欄位值繫結至 TBCheckBoxField 物件中的核取方塊。
''' </summary>
''' <param name="sender">作用的控制項。</param>
''' <param name="e">事件引數。</param>
Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)
Dim oControl As Control = DirectCast(sender, Control)
Dim oNamingContainer As Control = oControl.NamingContainer
Dim oFieldValue As Object = Me.GetValue(oNamingContainer)
If Not TypeOf oControl Is CheckBox Then
Throw New HttpException("繫結的控制項非核取方塊")
End If
If IsDBNull(oFieldValue) Then
DirectCast(oControl, CheckBox).Checked = False
ElseIf TypeOf oFieldValue Is Boolean Then
DirectCast(oControl, CheckBox).Checked = CBool(oFieldValue)
Else
If Not Me.DesignMode Then
Try
If Me.DefineValue <> String.Empty Then
DirectCast(oControl, CheckBox).Checked = ValueToBoolean(oFieldValue)
Else
DirectCast(oControl, CheckBox).Checked = CBool(oFieldValue)
End If
Catch exception As FormatException
Throw New HttpException("無法將值轉為布林值", exception)
End Try
End If
End If
DirectCast(oControl, CheckBox).Text = Me.Text
End Sub
''' <summary>
''' 使用指定 DataControlFieldCell 物件的值填入指定的 System.Collections.IDictionary 物件。
''' </summary>
''' <param name="dictionary">用於儲存指定儲存格的值。</param>
''' <param name="cell">包含要擷取值的儲存格。</param>
''' <param name="rowState">資料列的狀態。</param>
''' <param name="includeReadOnly">true 表示包含唯讀欄位的值,否則為 false。</param>
Public Overrides Sub ExtractValuesFromCell(ByVal Dictionary As IOrderedDictionary, ByVal Cell As DataControlFieldCell, _
ByVal RowState As DataControlRowState, ByVal IncludeReadOnly As Boolean)
Dim oControl As Control = Nothing
Dim sDataField As String = Me.DataField
Dim oFieldValue As Object = Nothing
If (Cell.Controls.Count > 0) Then
oControl = Cell.Controls.Item(0)
Dim oCheckBox As CheckBox = TryCast(oControl, CheckBox)
If ((Not oCheckBox Is Nothing) AndAlso (IncludeReadOnly OrElse oCheckBox.Enabled)) Then
If Me.DefineValue = String.Empty Then
oFieldValue = oCheckBox.Checked
Else
oFieldValue = BooleanToValue(oCheckBox.Checked)
End If
End If
End If
If (Not oFieldValue Is Nothing) Then
If Dictionary.Contains(sDataField) Then
Dictionary.Item(sDataField) = oFieldValue
Else
Dictionary.Add(sDataField, oFieldValue)
End If
End If
End Sub
End Class
End Namespace
測試程式
我們使用 Northwind 資料庫的 Employees 資料表做測試,在 Employees 加入一個 Checked 欄位,資料型別為 nvarchar(1),欄位值為 "是" 或 "否";我們使用 TBCheckBoxField 來繫結 Checked 欄位做測試。
我們分別使用 BoundField 及 TBCheckBoxField 同時繫結 Checked 欄位,BoundField 為唯讀供顯示對照使用,而 TBCheckBoxField 則提供編輯 Checked 欄位值,其中設定 TBCheckBoxField.DefineValue="是/否"。
<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataSourceID="SqlDataSource1"
EmptyDataText="沒有資料錄可顯示。" AllowPaging="True" DataKeyNames="EmployeeID">
<Columns>
<asp:CommandField ShowEditButton="True" />
<asp:BoundField DataField="EmployeeID" HeaderText="EmployeeID" InsertVisible="False"
ReadOnly="True" SortExpression="EmployeeID" />
<asp:BoundField DataField="LastName" HeaderText="LastName" SortExpression="LastName" />
<asp:BoundField DataField="FirstName" HeaderText="FirstName" SortExpression="FirstName" />
<asp:BoundField DataField="Checked" HeaderText="Checked" SortExpression="Checked" ReadOnly="True" />
<bee:TBCheckBoxField DataField="Checked" HeaderText="Checked(TBCheckBoxField)" SortExpression="Checked" DefineValue="是/否" />
</Columns>
</asp:GridView>
執行程式,瀏覽資料的畫面如下。
按下「編輯」鈕進入編輯狀態,使用 TBCheckBoxField 來編輯 Checked 欄位。
按下「更新」鈕後,會發現 Checked 欄位值被正常更新了。