2017年8月9日 星期三

ASP.NET 2.0 使用資料表動態產生TreeView的樹狀結構

資料來源 :http://ithelp.ithome.com.tw/articles/10028121

VS.NET2005已經將TreeView放入基本的元件,不過他的資料來源限定為XML檔案或者是Site Map,不過如果我們希望不同的使用者,依照不同的權限來瀏覽不同的網頁時,總不能每個使用者都訂立一個XML檔案來管理,於是小喵就開始尋找如何結合資料表的方式。
此外,當使用者進入系統後,如果每次瀏覽網頁都需要從資料庫撈取一次資料,並且組合一次Tree,小喵覺得似乎有點浪費資源,因此也順便透過Session機制,判斷如果沒有建立過Tree的話,就從資料庫撈資料建立TreeView,當在次瀏覽相關網頁時,如果判斷曾經建立過TreeView,就直接從Session中取回TreeView的結構並且直接顯示,這樣只需再配合一個小小的判斷最後變更權限的時間,來處理Session的存在與否,就能夠減少系統的資源浪費(重複建立撈取相同的TreeView結構)
樹狀結構的資料表設計:
要設計樹狀結構,希望能夠設計出無限層(無限深度)、無限項目的數,小喵的資料表欄位設計如下:
說明:
•NoteId:節點代號,自動編號
•ParnetId:父層結點代號
•sText:節點Text
•sValue:節點Value
•sURL:超鏈結網址
•sTarget:超鏈結Target
•Chger:資料修改者
•ChgTime:資料修改時間
ASP.NET前置作業:
接著開啟一個aspx檔案,並且再畫面上安排一個PlaceHolder控制項,然後在空白地方點兩下,開始撰寫PageOnLoad事件的相關程式
Imports
由於會存取資料庫,所以先將資料庫相關的命名空間Imports進來
Imports System.Data 
Imports System.Data.SqlClient 
編輯Page_Load事件
我們希望在Page_Load的時候,檢查一下是否曾經建立過TreeView,如果有,直接從Session取得TreeView,如果沒有→建立TreeView。
接著就是設定TreeView的ImageSet後,放入PlaceHolder中,相關程式碼如下:
Protected Sub Page_Load() Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    '*****取得Tree物件*****
    '利用Session 當Tree已經建立過,不重新建立,直接從Session中取得,加快速度
    If Session("Tree1") Is Nothing Then
        'Tree不曾建立過→呼叫建立TreeView程序
        Call InitTree() '初始化Tree
        Call BuildTree()    '建立Tree內容
    End If
    '宣告Tree物件
    Dim Tree1 As TreeView
    '從Session中取得Tree物件
    Tree1 = Session("Tree1")

    '設定Tree的ImageSet
    Tree1.ImageSet = TreeViewImageSet.XPFileExplorer
    '置放於PlaceHolder
    Me.PlaceHolder1.Controls.Add(Tree1)
End Sub
初始化TreeView:
在Page_Load中有呼叫初始化TreeView的Sub,接著就來看看初始化TreeView中做了哪些事情。由於一開始還沒有TreeView物件,所以先宣告並實體化TreeView,並且設定【根目錄】節點的相關內容,並指定他回首頁的超鏈結,這部份是靜態的,跟資料庫無關。最後把該Tree放入Session中。
Sub InitTree() InitTree()
    '********初始化Tree********

    '定義TreeView物件並實體化
    Dim Tree1 As New TreeView
    '定義一個TreeNode並實體化
    Dim tmpNote As New TreeNode
    '設定【根目錄】相關屬性內容
    tmpNote.Text = "首頁"
    tmpNote.Value = "0"
    tmpNote.NavigateUrl = "PtestTreeView3.aspx"
    tmpNote.Target = "_Top"

    'Tree建立該Node
    Tree1.Nodes.Add(tmpNote)

    '將Tree存放入Session中
    Session("Tree1") = Tree1
End Sub
建立樹狀結構程序:
在InitTree程序後,接著是要建立樹狀結構的程序。首先會檢查一下資料是否曾經取得過,如果沒有的話,呼叫取得資料的程序GetDataTable()。接著取得根目錄的節點,然後呼叫新增子節點的Function AddNodes並且傳入【根目錄】節點與根目錄的代號【0】
相關的程式如下:
Sub BuildTree() BuildTree()
    '********建立樹狀結構********

    '宣告TreeView
    Dim Tree1 As TreeView

    '如果Session中沒有Tree,初始化Tree
    If Session("Tree1") Is Nothing Then
        Call InitTree()
    End If
    Tree1 = Session("Tree1")

    '取得根目錄節點
    Dim RootNode As TreeNode
    RootNode = Tree1.Nodes(0)
    Dim rc As String

    '呼叫建立子節點的函數
    rc = AddNodes(RootNode, 0)
    Session("Tree1") = Tree1

End Sub
取得資料的程序:
接著是取得資料,並且把DataTable放入Session的相關程式內容如下:
Sub GetDataTable() GetDataTable()
    '取得DataTable

    '宣告相關變數
    Dim ConnStr As String
    Dim Conn As SqlConnection
    Dim Da As SqlDataAdapter
    Dim Ds As DataSet
    Dim dt As DataTable
    Dim SqlTxt As String

    Try
        '設定連接字串,請修改符合您的資料來源的ConnectionString
        ConnStr = "Data Source=Local\SQLEXPRESS;Persist Security Info=True;User ID=sa;Password=xxxxxxxxxxx;Initial Catalog=DTest"
        '建立Connection
        Conn = New SqlConnection(ConnStr)
        Conn.Open()

        '設定資料來源T-SQL
        SqlTxt = "SELECT * FROM TTestTree"    '請修改您的資料表名稱
        '實體化DataAdapter並且取得資料
        Da = New SqlDataAdapter(SqlTxt, Conn)
        '實體化DataSet
        Ds = New DataSet
        '資料填入DataSet
        Da.Fill(Ds)
        '設定DataTable
        dt = New DataTable
        dt = Ds.Tables(0)
        '將DataTable放入Session中
        Session("Dt") = dt
        '關閉連線
        Conn.Close()

    Catch ex As Exception
        Me.lblMessage.Text = ex.Message
    Finally

        '資源回收
        Ds = Nothing
        Da = Nothing
        Conn = Nothing
    End Try
End Sub
VS.NET2005已經將TreeView放入基本的元件,不過他的資料來源限定為XML檔案或者是Site Map,不過如果我們希望不同的使用者,依照不同的權限來瀏覽不同的網頁時,總不能每個使用者都訂立一個XML檔案來管理,於是小喵就開始尋找如何結合資料表的方式。
此外,當使用者進入系統後,如果每次瀏覽網頁都需要從資料庫撈取一次資料,並且組合一次Tree,小喵覺得似乎有點浪費資源,因此也順便透過Session機制,判斷如果沒有建立過Tree的話,就從資料庫撈資料建立TreeView,當在次瀏覽相關網頁時,如果判斷曾經建立過TreeView,就直接從Session中取回TreeView的結構並且直接顯示,這樣只需再配合一個小小的判斷最後變更權限的時間,來處理Session的存在與否,就能夠減少系統的資源浪費(重複建立撈取相同的TreeView結構)
遞回建立子節點函數(AddNodes)
此函數使用遞迴的方式,依照傳入的節點與節點代號,將傳入的節點以下的子節點取出並且建立節點,相關程式內容如下:
Function AddNodes() AddNodes(ByRef tNode As TreeNode, ByVal PId As Integer) As String
    '******** 遞迴增加樹結構節點 ********
    Try
        '如果Session中沒有DataTable→取得DataTable
        If Session("dt") Is Nothing Then
            Call GetDataTable()
        End If
        '定義DataTable
        Dim Dt As DataTable
        '從Session中取得DataTable
        Dt = Session("Dt")

        '定義DataRow承接DataTable篩選的結果
        Dim rows() As DataRow
        '定義篩選的條件
        Dim filterExpr As String
        filterExpr = "ParentId = " & PId
        '資料篩選並把結果傳入Rows
        rows = Dt.Select(filterExpr)

        '如果篩選結果有資料
        If rows.GetUpperBound(0) >= 0 Then


            Dim row As DataRow
            Dim tmpNodeId As Long
            Dim tmpsText As String
            Dim tmpsValue As String
            Dim tmpsUrl As String
            Dim tmpsTarget As String
            Dim NewNode As TreeNode
            Dim rc As String

            '逐筆取出篩選後資料
            For Each row In rows
                '放入相關變數中
                tmpNodeId = row(0)
                tmpsText = row(2)
                tmpsValue = row(3)
                tmpsUrl = row(4)
                tmpsTarget = row(5)

                '實體化新節點
                NewNode = New TreeNode
                '設定節點各屬性
                NewNode.Text = tmpsText
                NewNode.Value = tmpsValue
                NewNode.NavigateUrl = tmpsUrl
                NewNode.Target = tmpsTarget
                '將節點加入Tree中
                tNode.ChildNodes.Add(NewNode)

                '呼叫遞回取得子節點
                rc = AddNodes(NewNode, tmpNodeId)

            Next
        End If
        '傳回成功訊息
        AddNodes = "Success"

    Catch ex As Exception
        Me.lblMessage.Text = ex.Message
        AddNodes = "False"

    End Try
End Function
結語:
以上的示範是把樹狀結構使用資料表的方式存放,並且動態的反應到TreeView中,沒有包含權限控管的部份,如果需要權限控管,可以建立相關權限控管的資料表再與樹狀結構的內容Join。而為了避免每次都要讀取資料庫、建立樹狀結構等相關程式重複執行,透過Session的方式記錄了第一次建立後的結果。並且使用ADO.NET的特色,將撈回來的所有資料先存放到DataTable中,然後之後的資料篩選都是透過離線的DataTable來處理。各位實際在使用的時候可以搭配MasterPage來實做。
提供給大家參考討論^_^

2017年8月7日 星期一

[ASP.NET] 資料元件按鈕 取得編號

Repeater的按鈕取得元件項目/編號
Protected Sub LinkBut_RoleEdit_Click(sender As Object, e As System.EventArgs)
        Dim but As LinkButton = CType(sender, LinkButton)
        'Dim commandArgument As String = but.CommandArgument
        Dim item As RepeaterItem = CType(but.NamingContainer, RepeaterItem)
        Dim index As Integer = item.ItemIndex

        Dim lb_role_id As Label = Repeater1.Items(index).FindControl("lb_role_id")

    End Sub

或

Protected Sub DropDownList1_SelectedIndexChanged(sender As Object, e As System.EventArgs)
    Dim index As String = CType(CType(sender, System.Web.UI.WebControls.DropDownList).Parent, RepeaterItem).ItemIndex
    Dim v_code As String = CType(Repeater1.Items(index).FindControl("DropDownList1"), DropDownList).SelectedValue
    Dim v_remark As TextBox = CType(Repeater1.Items(index).FindControl("tb_remark"), TextBox)

End Sub

DataList按鈕取得元件項目/編號
Dim index As Integer = CType(CType(sender, LinkButton).BindingContainer, DataListItem).ItemIndex
Dim fileType As Label = DataList1.Items(index).FindControl("lb_fileType")


GridView按鈕取得元件項目/編號
 Protected Sub but_update_Click(sender As Object, e As System.EventArgs)
        Dim index As Integer = CType(CType(sender, System.Web.UI.WebControls.Button).Parent.Parent, GridViewRow).RowIndex
        Dim id As Integer = GridView1.DataKeys(index)("menu_id").ToString()
        Dim menu_value As String = CType(GridView1.Rows(index).FindControl("tb_menu_value"), TextBox).Text
        Dim disabled As String = CType(GridView1.Rows(index).FindControl("cb_EditDisabled_yn"), CheckBox).Checked.ToString

2017年7月24日 星期一

Session TimeOut設定

Asp.Net的相關TimeOut設定
https://dotblogs.com.tw/rainmaker/archive/2010/01/11/12955.aspx

如何讓 ASP.NET 使用 Session 資料時不要再自動消失
https://blog.miniasp.com/post/2008/01/12/How-to-avoid-Session-data-loss-in-ASPNET.aspx

2017年7月14日 星期五

將使用者重新導向至其他網頁

使用瀏覽器將使用者重新導向至其他網頁

  1. 將 Response 物件的 BufferOutput 屬性設定為 true。
  2. 呼叫 Response 物件的 Redirect 方法,然後將想要重新導向使用者的網頁 URL 傳遞給它。
    下列程式碼範例,示範了如何根據區域變數 UserLanguage (在別處設定) 的內容來重新導向網頁。
    Response.BufferOutput = True
    If UserLanguage = "English" Then
        Response.Redirect("http://www.microsoft.com/gohere/look.htm")
    ElseIf UserLanguage = "Deutsch" Then
        Response.Redirect("http://www.microsoft.com/gohere/look_deu.htm")
    ElseIf UserLanguage = "Español" Then
        Response.Redirect("http://www.microsoft.com/gohere/look_esp.htm")
    End If
    
    Response.BufferOutput = true;
    if (UserLanguage == "English")
    {
        Response.Redirect("http://www.microsoft.com/gohere/look.htm");
    }
    else if (UserLanguage == "Deutsch")
    {
        Response.Redirect("http://www.microsoft.com/gohere/look_deu.htm");
    }
    else if (UserLanguage == "Español")
    {
        Response.Redirect("http://www.microsoft.com/gohere/look_esp.htm");
    }
    

若要使用伺服器端方法將使用者重新導向至其他網頁

  • 呼叫 Transfer 方法,然後將想要重新導向使用者的網頁名稱傳遞給它。
    下列程式碼範例,示範了如何重新導向至其他網頁。
    Protected Sub Button1_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button1.Click
            Server.Transfer("Page2.aspx", True)
    End Sub
    
    protected void Button1_Click(object sender, System.EventArgs e)
    {
       Server.Transfer("Page2.aspx", true);
    }

Response.Redirect與Server.Transfer差異

簡單來說,Response.Redirect會由Client送出兩次的Request,Server.Transfer則是一次。

詳細說明可參考這篇文章:
https://dotblogs.com.tw/jimmyyu/2009/11/10/11503

2017年6月22日 星期四

note (片段)

----------------------------------------------------------------------------------------------------------------------------------------
GridView 數字欄位加千分號

<asp:Label ID="lb_text" runat="server" Text='<%# Eval("CONSOLIDATED_REVENUE","{0:N0}") %>'></asp:Label>
or
<asp:BoundField DataField="CONSOLIDATED_REVENUE" HeaderText="合併營收" DataFormatString="{0:N0}"
    SortExpression="CONSOLIDATED_REVENUE" ItemStyle-HorizontalAlign="Center"  >
</asp:BoundField>

----------------------------------------------------------------------------------------------------------------------------------------

GridView & DataList --DataBind取值方法

##############
http://scenic0327-tek.blogspot.tw/2012/03/blog-post_29.html
##############
http://www.blueshop.com.tw/board/FUM20041006161839LRJ/BRD20110730191358HNV.html

If e.Row.RowType = DataControlRowType.DataRow Then
            Dim lnkView As New LinkButton()
            lnkView.ID = "patent_id"
            lnkView.Text = TryCast(e.Row.DataItem, DataRowView).Row("patent_id").ToString()
            AddHandler lnkView.Click, AddressOf ViewDetails
            lnkView.CommandArgument = TryCast(e.Row.DataItem, DataRowView).Row("patent_id").ToString()
            e.Row.Cells(1).Controls.Add(lnkView)

            'For i As Integer = 0 To e.Row.Cells.Count - 1 'row的值

            '    xxx.Text += e.Row.Cells(i).Text & "<br/>"
            '    'If e.Row.Cells(i).ClientID = "img" Then
            '    '    xxx.Text = "Y"
            '    'End If
            'Next

            For i As Integer = 0 To GridView1.HeaderRow.Cells.Count - 1 '標題的值
                If GridView1.HeaderRow.Cells(i).Text = "簡圖" Then
                    If TryCast(e.Row.DataItem, DataRowView).Row("img").ToString() <> "" Then
                        Dim img As New Image()
                        img.ID = "img"
                        img.Width = 100

                        img.ImageUrl = "FileStagingPath/" & TryCast(e.Row.DataItem, DataRowView).Row("img").ToString()
                        e.Row.Cells(i).Controls.Add(img)
                    End If
                End If
            Next

            'Dim txtCountry As New TextBox()
            'txtCountry.ID = "name"
            'txtCountry.Text = TryCast(e.Row.DataItem, DataRowView).Row("name").ToString()
            'e.Row.Cells(2).Controls.Add(txtCountry)
        End If
##############


Imports System.IO

'判斷FileUpload檔案是否存在
If (FileUpload_Img.HasFile) Then
End If

'取得上傳檔案的集合
Dim hfc As HttpFileCollection = Request.Files

For j As Integer = 0 To hfc.Count - 1
   Dim fileName As String = Mid(hfc(j).FileName, InStrRev(hfc(j).FileName, "\") + 1, Len(hfc(j).FileName)) '檔案名稱(含副檔名)
   Dim fileNameEx As String = Mid(hfc(j).FileName, InStrRev(hfc(j).FileName, "."), Len(hfc(j).FileName)) '副檔名
   Dim fileSize As String = hfc(j).ContentLength '檔案大小
Next


'判斷上船的目錄路徑是否存在
If My.Computer.FileSystem.DirectoryExists(saveDir1) Then
End If

'建立目錄路徑
My.Computer.FileSystem.CreateDirectory(saveDir1)

'判斷目錄是否有相同檔案名稱
If File.Exists(filePath) Then
End if

'變更檔案名稱
File.Move("ttt.txt", "ttt-1.txt")


https://msdn.microsoft.com/zh-tw/library/cc148994.aspx

#####################
資料繫結

<%# DataBinder.Eval(Container, "DataItem.description")%>

######################
DataList

DataList刪除
<asp:ImageButton ID="imgBut_delete" runat="server" ImageUrl="images/CNSREJ16.GIF" CommandName="delete" />

要加入DataKeyField
<asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" DataKeyField="detailed_id" >

Protected Sub DataList1_DeleteCommand(source As Object, e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles DataList1.DeleteCommand
    Dim keyID As Integer = Convert.ToInt32(DataList1.DataKeys(e.Item.ItemIndex))
End Sub


DataList 資料內容處理
 Protected Sub DataList1_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.DataListItemEventArgs) Handles DataList1.ItemDataBound
        If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
            '處理子層DataList2的資料
            Dim dl2 As DataList = CType(e.Item.FindControl("DataList2"), DataList)
            'Dim hidden2 As HiddenField = DirectCast(e.Item.FindControl("lb_needDate"), HiddenField)
            'Dim QID As String = hidden2.Value

            'If QID = 33 Then
            For Each li As DataListItem In dl2.Items
                Dim lbl As Label = DirectCast(e.Item.FindControl("lb_needDate"), Label)
                If lbl IsNot Nothing Then
                    If lbl.Text = "Self Directed" Or lbl.Text = "Systems" Then
                        lbl.Visible = False
                    End If
                End If
            Next
            'End If
        End If
    End Sub


#####
檔案上傳
多個檔案上傳




2017年6月14日 星期三

CSS 語法,文字換行,強迫不換行。

CSS 文字換行的 style 為 word-wrap ,透過這個屬性可以指定換行的方式,並可以強制文字不換行。
而 CSS 還有個空白長度的設定,透過 white-space 可以移除連續空白。
  • 文字強迫換行 word-break: break-all;
  • 依單字換行 word-wrap:break-word;
  • 死都不換行 white-space:nowrap;

換行設定, word-wrap & word-break

文字強迫換行,太長的英文字,會自動切成兩半。

  • word-break: break-all; 若一個單字過長,超過畫面的寬度,則將單字切斷,並換行。

word-break: break-all;
Before Moammar Long language string Long language string Long language string Long language string, there were the Phoenicians.
從上面的 DEMO 中可以看到 language 這個單字被切成兩斷。

文字自動換行,長英文字不切斷

  • word-wrap:break-word; 若一個單字過長,超過畫面的寬度,則將單字換行(不切斷單字)。

word-wrap:break-word;
Before Moammar Long language string Long language string Long language string Long language string, there were the Phoenicians.
不切斷英字單字對於一般的讀者是比較好懂的,唯一的缺點是右邊會出現很大的一塊空白。

空白設定 : white-space

保留原始空白與換行

  • white-space:pre; 保留原始資料的空白與換行,功能與 HTML tag 中的 <pre> 相同。
white-space:pre;
Before Moammar Gadhafi, there were the Phoenicians. And the Greeks.

強迫文字不換行

  • white-space:nowrap; 移除連續空白,移除換行符號(n r), 強迫文字不換行。
white-space:nowrap;
Before Moammar Gadhafi, there were the Phoenicians. And the Greeks. 

保留原始空白,但文字自動換行

  • white-space:pre-wrap; 保留所有空白,自動換行。
white-space:pre-wrap;
Before Moammar Gadhafi, there were the Phoenicians. And the Greeks. The Romans.

連續空白,取代成一個空白。

  • white-space: pre-line ,這個語法可以會自動將多個空白轉換成一個空白
white-space: pre-line;
Before Moammar Gadhafi, there were the Phoenicians. And the Greeks. The Romans. 

2017年6月3日 星期六

OracleConnection.BeginTransaction 方法 ()

Public Sub RunOracleTransaction(ByVal connectionString As String)
    Using connection As New OracleConnection(connectionString)
        connection.Open()

        Dim command As OracleCommand = connection.CreateCommand()
        Dim transaction As OracleTransaction

        ' Start a local transaction
        transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted)
        ' Assign transaction object for a pending local transaction
        command.Transaction = transaction

        Try
            command.CommandText = _
                "INSERT INTO Dept (DeptNo, Dname, Loc) values (50, 'TECHNOLOGY', 'DENVER')"
            command.ExecuteNonQuery()
            command.CommandText = _
                "INSERT INTO Dept (DeptNo, Dname, Loc) values (60, 'ENGINEERING', 'KANSAS CITY')"
            command.ExecuteNonQuery()
            transaction.Commit()
            Console.WriteLine("Both records are written to database.")
        Catch e As Exception
            transaction.Rollback()
            Console.WriteLine(e.ToString())
            Console.WriteLine("Neither record was written to database.")
        End Try
    End Using
End Sub