擴展 CheckBoxField 類別 - 支援非布林值的雙向繫結

擴展 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>

執行程式,瀏覽資料的畫面如下。

image

按下「編輯」鈕進入編輯狀態,使用 TBCheckBoxField 來編輯 Checked 欄位。

image

按下「更新」鈕後,會發現 Checked 欄位值被正常更新了。

image

ASP.NET 魔法學院