《C#程序設計基礎教程》課件第10章_第1頁
《C#程序設計基礎教程》課件第10章_第2頁
《C#程序設計基礎教程》課件第10章_第3頁
《C#程序設計基礎教程》課件第10章_第4頁
《C#程序設計基礎教程》課件第10章_第5頁
已閱讀5頁,還剩62頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第10章ADO.NET(三)10.1DataSet10.2架構的生成10.3List<T>泛型集合10.4數據展示10.5查看電影信息

10.1DataSet

數據集(DataSet)是ADO.NET的一個重要組成部分,它是數據的脫機容器,承擔著數據的中間存儲工作。DataSet并不直接和數據庫連接,因此它的數據不一定來源于數據庫,而是可以有很多種不同的來源,甚至可以直接從測量設備中讀取。一個DataSet是由一組數據表(DataTable)對象組成的,而每個DataTable對象又是由若干個DataColumn對象和DataRow對象組成的,如圖10-1所示。我們可以看到其結構和數據庫中的數據表非常相似。除了定義數據外,還可以在DataSet中定義表之間的鏈接,即我們在數據庫中常用的到主/從表。DataSet及其常用對象的說明如表10-1所示。我們可以直接通過構造來創建DataSet對象:DataSetds=newDataSet();DataSetds=newDataSet("myds");上面我們分別采用DataSet的兩個構造來創建對象,兩種方式沒有太大的區別,只不過第二種方式DataSet對象多了一個“myds”的別稱而已。和數據庫一樣,DataSet對象本身并不能夠存儲數據,真正承擔這個工作的是DataTable對象,接下來我們來了解DataTable對象。數據表(DataTable)非常類似于SQLServer2008中的數據庫表,它是由一組包含特定屬性的列組成的,可能包含0行或多行數據。和數據庫表一樣,DataTable也可以定義由一個列或者多個列組成的主鍵,列上也可以包含約束。這些信息對應的通用術語稱為DataTable的“架構”。整個DataTable可以訪問的對象如圖10-2所示。在C#中,創建DataTable對象可以有兩種方式:DataTabledt1=newDataTable();DataTabledt2=newDataTable("myTab");兩種方式沒有本質上的區別,只不過dt2對象在具體使用時會更加方便一些。當然現在DataTable對象依然無法存儲數據,因為它還沒有結構,要設定DataTable的結構,我們需要用到DataColumn對象。

10.2架構的生成

在ADO.NET中,DataAdapter(數據適配器)的作用是檢索和保存數據,在使用的過程中它一般都是與Connection對象和Command對象一起使用,以便連接到相應的數據庫并完成指定的操作。另一方面,DataAdapter對象本身并不具備保存數據的能力,因此它又需要和DataSet對象配合使用,才能夠臨時存儲數據,并提供操作數據的接口。DataAdapter無疑是ADO.NET中一個非常特殊的對象,它就像一座橋梁,一頭連接著存儲數據的數據庫,另一頭則連接著作為臨時數據存儲對象的DataSet。它能夠根據SQL語句從數據庫中提取數據,也能夠將更改后的數據更新到數據庫中。DataAdapter對象屬于.NETFramework數據提供程序,不同的數據提供程序有自己的DataAdapter對象,用于OLEDB的是OleDbDataAdapter對象,而用于SQLServer的是SqlDataAdapter對象,這里我們以SqlDataAdapter對象為例來認識DataAdapter對象。我們可通過四種方式來創建SqlDataAdapter對象,如表10-4所示。一般來說第三種方式使用得比較多:stringconn=ConfigurationManager.ConnectionStrings["SQL"].ConnectionString;SqlConnectioncn=newSqlConnection(conn);stringsql="select*fromFilm";SqlDataAdapterda=newSqlDataAdapter(sql,cn);在上面的代碼中我們首先還是要創建一個SqlConnection對象,因為在ADO.NET中一切操作都是以連接為基礎,然后創建一個SqlDataReader對象,并且將一個Select查詢語句和已經創建好的SqlConnection對象作為參數傳遞到它的構造函數中。創建了SqlDataAdapter對象后,我們就可以使用其提供的屬性和方法來完成需要的操作。我們常用到的方法有兩個:Fill():使用SELECT語句從數據源中檢索數據。Update():使用SQL語句將數據集中的數據更新到數據源。當我們需要從數據庫中檢索數據時,可以使用Fill()方法,這時與Select命令關聯的SqlConnection對象必須有效,但不需要將其打開。如果調用Fill()方法之前SqlConnection已關閉,則將其打開以檢索數據,然后再將其關閉。如果調用Fill()方法之前連接已打開,則它將保持打開狀態:stringsql="select*from[User]";SqlDataAdapterda=newSqlDataAdapter(sql,cn);DataSetds=newDataSet();da.Fill(ds);在上面的代碼中我們創建了一個SqlDataAdapter對象,并且通過一條查詢語句從數據庫中查詢數據,然后將結果使用Fill()方法填充到一個數據集對象中。如果在填充數據時遇到錯誤或異常,則錯誤發生之前添加的行將保留在數據集中,操作的剩余部分被中止。如果命令不返回任何行,則不向數據集中添加表,也不引發異常。在實際應用中,DataAdapter對象多用于查詢類型的操作,雖然它也具有其他類型數據操作的能力,但是由于其本身缺乏對數據完整性的驗證能力,因此其他類型的操作我們多是借助于Command對象來完成的。在大部分程序中,數據集在使用的時候都是通過DataAdapter對象來填充的。通過DataAdapter對象的Fill()方法,我們可以將數據源中的數據一次性地填充到數據集中,這個時候數據集的結構和內容都由系統幫助我們自動生成。填充數據集有多種不同的方式,最直接的方式是不做任何設定,一切由系統來決定:stringconn=ConfigurationManager.ConnectionStrings["SQL"].ConnectionString;SqlConnectioncn=newSqlConnection(conn);stringsql="select*fromFilm";SqlDataAdapterda=newSqlDataAdapter(sql,cn);DataSetds=newDataSet();da.Fill(ds);上面的代碼我們沒有做任何其他的設置,僅僅是連接數據庫后讀取Film數據表中的所有數據,然后通過DataAdapter對象的Fill()方法將讀取的數據填充到一個數據集中。因為我們只提供了一些對象,所以這個時候數據集中將由系統自動創建一個名為“Table”的DataTable對象,而它的結構則由我們的查詢語句來決定。例如,我們這里是查詢Film表中的所有信息,因此“Table”表的字段就是數據庫中Film數據表的所有字段,而“Table”表的數據則和Film數據表的數據一樣。后續我們使用這個數據集的時候就只能這樣訪問DataTable:ds.Table["Table"]或者通過索引訪問:ds.Table[0]很顯然,上面的代碼雖然能夠完成任務,但是不確定和不安全的地方太多了,因此我們還需要將程序寫得更加準確些:SqlDataAdapterda=newSqlDataAdapter(sql,cn);da.Fill(ds,"MyFilm");這一次我們將程序做了一點微調,在調用DataAdapter對象的Fill()方法時,我們不但傳遞了數據集對象,同時還給了第二個字符串類型的參數“MyFilm”,這個參數的作用是在進行數據填充的時候,將系統自動創建的DataTable對象命名為“MyFilm”,這樣在后續使用時就可以這樣寫:ds.Table["MyFilm"]相對第一段代碼,這次的程序顯得更加精確,但是依然存在問題,主要是訪問起來比較麻煩,每次都要通過數據集定位到DataTable,然后才能夠訪問行和列,因此我們可以將程序再次進行修改:SqlDataAdapterda=newSqlDataAdapter(sql,cn);DataTabledt=newDataTable();da.Fill(dt);這次程序中找不到DataSet對象了,取而代之的是一個DataTable對象,這也是我們在實際開發中經常會用到的一種方式,道理很簡單,既然數據集的數據是存放在DataTable對象中的,那我們就可以繞過數據集而直接使用DataTable對象來操作數據,當然這樣一來我們就無法再通過數據集來管理DataTable之間的關系了。三種數據集填充方式在實際使用的過程中沒有太大的區別,具體采用哪種方式還是要根據實際開發的情況以及個人的使用習慣來定。

10.3List<T>泛型集合

10.3.1List定義集合的具體介紹詳見C#高級編程的有關教材,下面要介紹的是集合中用得非常廣泛的一種:List<T>。我們首先來看一下它的語法:List<T>list=newList<T>();這是一個泛型集合類,重點在于它里面的類型參數:T。T本身沒有具體含義,它只是類似于占位符一般的存在。它可以是任何類,如果你想創建一個User實體對象的集合,那么,將T改成User就行了。在實際運用中,語法如下:List<User>list=newList<User>();10.3.2數據讀取光有對象是不夠的,List的作用是獲取數據庫中的數據,作為數據源來使用。但是如何將數據表中的數據放到List對象中呢?下面的代碼是整個過程。首先,創建表的實體類,我們要將用戶表(User)的數據讀取出來,先創建User類:publicclassUser{//屬性定義publicintID{get;set;}publicstringName{get;set;}publicstringUserName{get;set;}publicintTypeID{get;set;}publicstringTypeName{get;set;}publicstringDesc{get;set;}publicintState{get;set;}}接下來,使用SqlDataReader對象讀取數據:stringconStr="server=.;initialcatalog=MyFilm;integratedsecurity=SSPI;";stringsql="select*fromUser";SqlConnectionconn=newSqlConnection(conStr);conn.Open();SqlCommandcm=newSqlCommand(sql,conn);SqlDataReaderdr=cm.ExecuteReader();List<User>modelList=newList<User>(); //創建List泛型集合對象Usermodel=null; //在循環外部創建User對象,但不用實例化while(dr.Read()){model=newUser(); //每一次循環讀取數據時,實例化該對象model.ID=int.Parse(dr["ID"].ToString()); //將當前行的ID字段數據賦值給對象ID屬性model.Name=dr["Name"].ToString();model.UserName=dr["Name"].ToString();model.TypeID=int.Parse(dr["TypeID"].ToString());model.TypeName=dr["Name"].ToString();model.Desc=dr["Name"].ToString();model.State=int.Parse(dr["State"].ToString());modelList.Add(model); //最后一定要記得將實體對象添加到集合中}dr.Close(); //一定要記得在讀取完數據后關掉閱讀器conn.Close();當執行完上述操作后,這個集合中就獲得了查詢語句所查詢出來的所有數據了。

10.4數據展示

10.4.1簡單控件對于DataSet來說,最直接的操作就是用簡單控件來顯示其中的數據,當然在這之前我們要知道如何讀取它的值。最常見的就是讀取DataSet中數據表的某行或者某列的值,甚至可能會精確到某一個單元格的值。例如,用戶希望查看數據表中第二部電影的信息,那么我們的程序就可以這樣寫:stringname=(string)ds.Tables["Film"].Rows[1]["Name"];stringaddedBy=(string)ds.Tables["Film"].Rows[1]["AddedBy"];stringactor=(string)ds.Tables["Film"].Rows[1]["Actors"];stringdesc=(string)ds.Tables["Film"].Rows[1]["Desc"];MessageBox.Show("影片"+name+"是由"+actor+"主演!\ny影片簡介:"+desc);上面的代碼可以訪問表中的一行數據,如果需要訪問表中所有行的數據,則需要再增加一個循環結構:foreach(DataRowdrinds.Tables["myTab"].Rows){//讀取數據}事實上,如果是這種逐行讀取數據的操作則DataReader對象是更加合適的對象,因為它消耗的系統資源更少,速度也更快一些。DataSet的主要用途在于更全面地展示數據。知道了如何讀取數據后,接下來使用簡單控件呈現數據的工作就很簡單了:txtName.Text=(string)ds.Tables["Film"].Rows[0]["Name"];txtActors.Text=(string)ds.Tables["Film"].Rows[0]["Actors"];txtPrice.Text=ds.Tables["Film"].Rows[0]["Price"].ToString();txtDesc.Text=(string)ds.Tables["Film"].Rows[0]["Desc"];上面的代碼依然是從數據集中讀取數據,只不過讀取的數據不再使用變量,而是直接使用三個TextBox控件和一個RichTextBox控件來呈現數據。只不過這種方式只能夠讀取一行數據,如果需要一次性展示更多的信息則還需要借助于其他的控件。我們曾用DataSet來讀取數據,那么List<T>是怎么讀取數據的呢?同樣的案例,我們用泛型集合來演示一次。假設讀取數據的泛型集合類為List<Film>,對象名稱為modelList,同樣讀取第二行數據:stringname=modelList[1].Name;stringaddedBy=modelList[1].AddedBy;其余的值依此類推,可見相比較DataSet來講,List<Film>要簡單方便。想要讀取所有的數據,也可以用循環遍歷來實現:foreach(FilmmodelinmodelList){//依上例讀取數據stringname=model.Name;…}控件的數據讀取依上例所示即可。10.4.2列表控件列表類型的控件是系統開發過程中經常會用到的控件,在之前我們用到這類控件的時候,其選擇項大多是直接設定好的固定內容,是否可以讓它們從數據集中動態地獲取選擇內容呢?要實現這個操作本身并不復雜,我們知道列表類型的控件其選擇項都是在Items屬性中保存的,因此只要能夠將數據集中的數據“放置”到Items屬性中就可以了。問題在于Items屬性中的每一個選擇項都是由Text和Value這兩個值構成的,因此我們要做的工作就是將數據表的列和這兩個值對應起來:comboBox1.DropDownStyle=ComboBoxStyle.DropDownList;comboBox1.DisplayMember="Name";comboBox1.ValueMember="ID";comboBox1.DataSource=ds.Tables["Film"];在上面的代碼中我們設定了ComboBox控件的四個屬性。DropDownStyle屬性用來設定其顯示方式,我們設定為ComboBoxStyle.DropDownList,這樣該組合框控件就只能夠選擇而不能夠輸入信息。DisplayMember屬性用來設定每一個Item選擇項的Text屬性所對應的列名,這個列名必須是數據表中存在的列。ValueMember屬性則是用來設定每一個Item選擇項的Value屬性所對應的列名,同樣該列必須是數據表中存在的列,如果在程序中沒有設定ValueMember屬性,則Item選擇項的Value屬性值就會和Text屬性值相同。最后我們設定了組合框控件的DataSource屬性,也就是數據源屬性,這樣系統就會自動在DataSource所指定的數據源中查找相應的列,并將這些列的值一次性填充到組合框中,從而形成選擇項。同樣,上例也用的是DataSet數據源,如果是List集合,則只需要把最后一句代碼的數據源替換即可:comboBox1.DataSource=modelList; //記住,這里的值為整個泛型集合對象從這里開始,后面的數據源將直接使用泛型集合,而不再使用DataSet了。如何知道用戶所選擇的內容呢?這里我們可以通過兩個簡單的屬性取得用戶選擇的信息:intid=(int)comboBox1.SelectedValue;stringname=comboBox1.Text;MessageBox.Show("電影《"+name+"》的編號是:"+id);組合框的SelectedValue屬性可以取得用戶選擇項的Value屬性值,而Text屬性則可以取得用戶選擇項的Text屬性的值。這里很容易產生一個疑問:為什么一個選擇項要設定Text和Value這兩個屬性值?這是因為在實際使用中,難免會出現重復的數據。例如,如果數據庫中存在兩部同名的電影,那么我們如何知道用戶選擇的究竟是哪一部電影呢?很顯然單憑Text屬性根本無法做判斷,這時候如果我們在Value屬性中保存了電影的編號,那么就可以很容易地知道用戶的選擇。因為之前的數據是通過屬性綁定到控件的,我們通過上面的內容可以得到顯示和隱藏的值,但實際上我們可以通過它的SelectedItem屬性得到當前選定項所綁定的整個對象:Filmmodel=(Film)comboBox1.SelectedItem;MessageBox.Show("電影《"+model.Name+"》的編號是:"+model.Id);如果是多選我們該如何操作呢?對于像ListBox這樣的多選類型的控件,其數據顯示部分的操作和ComboBox控件一樣,也就是說我們將前面代碼中的ComboBox換成ListBox控件就可以了:listBox1.SelectionMode=SelectionMode.MultiExtended;listBox1.DisplayMember="Name";listBox1.ValueMember="ID";listBox1.DataSource=modelList;相對于ComboBox控件ListBox控件的讀取相對來說復雜一些,根據DataSource所設定的數據源類型不同,其讀取方式也會有一些細微的差別,其讀取方式為stringstr="選中的電影:";for(inti=0;i<listBox1.SelectedItems.Count;i++){Filmmodel=(Film)listBox1.SelectedItems[i];str+="\n電影名稱:"+model.Name; //如果能讀取到Name,則其他的屬性也能夠得到}MessageBox.Show(str);上面代碼的運行效果如圖10-5所示。10.4.3DataGridView列表類型的控件在使用的時候雖然能夠一次性呈現很多行數據的信息,但是只能夠顯示數據表中的某一列,如果要呈現數據的全貌,還是要借助于更加大型的控件,這其中最常用的就是DataGridView。DataGridView控件是WinForm中經常使用到的一個用于呈現數據的大型控件,它能夠以表格的形式將數據集中的數據表完整地呈現出來,同時還支持根據用戶的需要進行各種不同的設置。使用DataGridView控件時,可以在工具箱的“數據”選項卡中找到它并將其放置到窗體中,如圖10-6所示。將DataGridView控件添加到窗體上后,我們只需要一行代碼就可以將剛才創建的泛型集合在該控件上呈現出來:dataGridView1.DataSource=modelList;DataGridView控件可以以表格的形式將數據集中的數據呈現出來,該控件在使用的時候最重要的屬性就是DataSource屬性,它主要用來設置DataGridView控件數據源,在上面的代碼中我們就將電影信息讀取出來后放置在DataSet對象中并作為數據源賦給了該屬性。其運行效果如圖10-7所示。當然,數據是出來了,但是和實際使用差別太大了,根本就不具備可用性。首先,我們并不需要將所有的字段都展示出來,像ID這樣的字段根本就不需要用戶知道它的存在。其次,列名用英文并沒有問題,但是這里卻使用的是字段名稱,從而將我們的數據結構暴露了出來,這樣甚至會威脅到整個系統的安全。事實上,上面的問題總結起來就是一點,即需要DataGridView控件按照我們設定的方式顯示數據,這就要設置DataGridView控件列,我們可以打開DataGridView控件的智能選項卡,然后點擊“編輯列”來完成。也可以在DataGridView控件的屬性列表中來完成。無論采用哪種方式,我們都可以打開DataGridView控件的列編輯器,如圖10-10所示。在列編輯器的左側是一個列表框,這里列出了當前DataGridView控件中已經添加的列對象,我們可以看到這里有四個列。選中某一個列后,在窗體的右側可以看到一個屬性列表,這當中列出了當前選中列對象的一些常用屬性。在這些屬性中,常用的有以下幾個:Name:列對象的名稱,在程序中必須保證唯一。在命名時一般采用col作為前綴,例如colName。ColumnType:類對象的類型。WinForm中DataGridView控件的列共有六種類型:按鈕列樣式(DataGridViewButtonColumn)、復選框列樣式(DataGridViewCheckBoxColumn)、組合框列樣式(DataGridViewComboBoxColumn)、圖片列樣式(DataGridViewImageColumn)、鏈接列樣式(DataGridViewLinkColumn)和文本框列樣式(DataGridTextBoxColumn,默認樣式)。不同的列樣式會呈現出不同的外觀,同時使用方式也有細微的差別。DataPropertyName:設置列對象所對應的數據源字段名稱。HeaderText:設置列對象的頁眉文本。不同的列樣式對應著不同的使用環境,具體需要采用什么樣式還要根據實際情況來決定,一般情況下如果不能確定則都可以采用文本框樣式來呈現數據,因為它基本上可以顯示任何類型的數據。如果要添加新的列對象,則點擊左側列表框下的“添加”按鈕,這樣就可以打開添加列窗體。在這個窗體上,我們可以設置三個值。“名稱(N):”用來指定新添加列對象的Name屬性值。“類型(T):”下拉列表中可以選擇新添加列對象的樣式,也就是ColumnType屬性值。“頁眉文本(H):”則是用來設定新添加列的HeaderText屬性值。需要注意的是,這里并沒有設置DataPropertyName屬性值,因此在完成列的添加后還需要在圖10-10中找到新添加的列并設置DataPropertyName屬性,否則是無法使用的。列設置完成再次運行我們的程序,會發現凡是數據表我們設置過的字段都按要求在DataGridView控件的指定列當中呈現出來了,但是我們沒有設置的字段卻依然按照先前的樣式顯示在DataGridView控件中,如圖10-12所示。這是因為默認情況下DataGridView控件會自動根據數據源中的表結構來創建相應的列,也就是說數據表中有多少個字段,DataGridView控件就會自動創建多少個列,并顯示數據,而我們這里卻只需要它顯示我們設置的列,其他的列就不需要再自動創建了。要實現這一效果我們可以通過下面的代碼來實現:dgdShowData.AutoGenerateColumns=false;AutoGenerateColumns屬性的作用是用來設置DataGridView控件是否需要根據數據源來自動創建列,將其設置為false后系統就不會再自動創建列,而只會根據我們設置的列來呈現數據,如圖10-13所示。DataGridView是WinForm中最為復雜的大型控件之一,本章我們只是介紹了DataGridView控件的基本使用方式。10.4.4ListViewListView控件是另外一個數據展示控件,和DataGridView控件不同,ListView控件只是提供數據的顯示功能而并不提供對數據的操作功能,但是其顯示方式要比DataGridView控件豐富。下面我們同樣使用ListView控件來顯示電影信息。在工具箱中找到ListView控件并放置到窗體上,如圖10-14所示。和其他控件一樣,ListView控件也有很多屬性,限于篇幅我們不可能一次性將所有的屬性及其使用方式講解完畢,因此本章我們還是圍繞著數據呈現來學習相關的屬性。為了能夠完成這個任務,我們會用到ListView控件的以下屬性:Name:ListView控件的名稱,在代碼中必須唯一。在命名的時候一般采用lsv作為前綴。Columns:設置ListView控件的列。FullRowSelect:指示是否可以一次性選擇整行數據。GridLines:指示ListView在顯示數據的時候是否顯示網格線。View:設置ListView的顯示方式。ListView控件中提供了五種不同的顯示方式,如表10-6所示。使用ListView控件顯示數據要比使用DataGridView控件復雜一些。首先我們需要設置ListView控件的Columns屬性,也就是設置數據顯示的列。在ListView控件的屬性窗口的行為部分找到Columns屬性。點擊右側的按鈕后打開ColumnHeader集合編輯器窗體。在窗體的左側“成員(M):”之下我們可以看到現在Columns屬性所有已經存在的列成員。在成員列表的右側有上下兩個按鈕,選中某列后點擊向上或向下的按鈕就可以調整該列的排列順序,這個排列順序決定了最終數據顯示的時候該列的位置。如果未添加任何列,則該列表為空。點擊成員列表下方的“添加(A)”按鈕就可以添加一個新的列。在成員列表中選中某一列后,在窗體的右側可以看到當前選中列對象的相關屬性,在這些屬性中我們需要關注的是Name和Text。Name屬性是當前選中列對象的名稱,由于在實際操作過程中訪問這些列對象時我們都采用的是下標訪問,因此這里可以采用系統自動生成的名字,如果要命名,可以采用col作為前綴。Text屬性則是用來設定列對象的頁眉,也就是呈現在ListView控件中列標頭的文本。設定完成后,點擊“確定”按鈕后關閉ColumnHeader集合編輯器窗體,接下來需要完成具體的數據顯示工作。首先依然還是要將數據從數據庫中讀取出來,這個過程我們可以使用DataAdapter對象與DataSet對象配合起來實現,因為前面我們已經進行了講解,這里就不再重復說明。由于ListView控件沒有DataGridView控件那樣的自動數據填充功能,因此我們需要將數據集中的數據提取出來,然后放入ListView控件中的相應位置:foreach(FilmmodelinmodelList){ListViewItemitem=newListViewItem();item.SubItems[0].Text=model.Name;item.SubItems.Add(model.Actors);item.SubItems.Add(string.Format("{0:C}",model.Price));lsvShowData.Items.Add(item);}在上面的代碼中,我們首先通過一個foreach循環結構遍歷數據集合的所有行,在循環結構體中我們需要創建一個ListViewItem對象,該對象代表ListView控件的Items屬性中的一個成員。接下來,我們就可以通過這個ListViewItem對象的SubItems屬性將數據行中的數據放置到該對象中。ListViewItem對象每調用一次Add()方法就可以向其中添加一個新的單元格,唯一需要注意的是其第一個單元格需要通過下標訪問,因為在創建該對象的時候系統會默認添加一個單元格。無論是設置哪一個單元格,傳遞的值都需要是字符串類型的。那么,ListViewItem對象又是怎么和ListView控件的列建立起關聯的呢?事實上,這個過程并不復雜,在最終將ListViewItem對象通過Add()方法添加到ListView控件的Items屬性中的時候,系統會按順序將ListViewItem對象的單元格和ListView控件的列對應起來,也就是將ListViewItem對象第一個單元格的值放置到ListView控件的第一個列中,第二個單元格則放置到第二個列中,以此類推。正因為是這樣一個操作過程,所以我們在為ListViewItem對象添加單元格的時候一定要對ListView控件的列組成牢記在心,否則就會出現數據放置錯誤。另外,如果ListViewItem對象的單元格數量多于ListView控件的列數量,則多出來的數據就會被系統舍棄。上面的代碼中另外一個需要我們注意的地方是,在添加價格的時候我們并沒有直接將其轉換成為字符串,而是通過string類的Format()方法將其轉換成了貨幣的格式。Format()方法的作用是將指定的值按照要求轉換成為特定的格式。一般在使用該方法時我們需要提供兩個參數:第一個為字符串類型,用來設定轉換的格式;第二個則是需要轉換的數據或數據集合。每個格式項都采用下面的形式并包含以下組件:{索引[,對齊][:格式字符串]}“索引”(也叫參數說明符)是一個從0開始的數字,用來標識需要轉換的數據對象。也就是說,當索引為0時,其對應需要轉換的第一個數據對象,如果索引為1,則對應第二個數據對象,依次類推,我們可以把它理解成為占位符。通過指定相同的索引,多個格式項可以引用轉換數據對象列表中的同一個元素。每個索引都可以引用要轉換數據對象列表中的任一對象。例如,如果有三個要轉換的數據對象,則可以通過指定類似于“{1}{0}{2}”的復合格式字符串來設置第二、第一和第三個對象的格式。格式項未引用的對象會被忽略。如果索引指定了超出數據對象列表范圍的項,將導致運行時異常:txtFormat.Text=string.Format("貨幣:{0:C};百分比:{1:P};十六進制:{0:X}",12,0.35);上面的代碼分別將數字12轉換成為貨幣格式和十六進制數,而將0.35轉換成為百分比。“對齊”是可選的一個帶符號的整數,指示設置了格式的字段寬度。如果“對齊”值小于設置了格式的字符串的長度,則“對齊”會被忽略,并且使用設置了格式的字符串的長度作為字段寬度。如果“對齊”為正數,則字段中設置了格式的數據為右對齊;如果“對齊”為負數,則字段中設置了格式的數據為左對齊。如果需要填充,則使用空白。如果指定“對齊”,就需要使用逗號:txtFormat.Text=string.Format("右對齊:[{0,10}];左對齊:[{0,-10}];對齊失效:[{0,2}]","Tom");同樣是將Tom進行格式化,當我們使用“{0,10}”這種格式的時候,轉換的結果就是在Tom的前面增加7個空格以補齊10位長度,而使用“{0,-10}”時,則會在其后面添加7個空格以補齊10位長度。但是在“{0,2}”中,對齊的數值小于“Tom”的長度,因此“對齊”就失效了。格式字符串則是用來設定需要設定的轉換格式,表10-7列出了常用的格式字符串。10.4.5TreeView在WinForm中TreeView控件用樹的方式展示層次節點,通過這些節點,我們可以清晰地查看數據及其它們之間的從屬關系。傳統上,節點對象包含值,可以引用其他節點,一個節點可以包含其他節點,這時該節點稱為父節點,它所包含的節點稱為子節點。只有子節點沒有父節點的節點稱為根節點,在WinForm中TreeView控件可以包含多個根節點,如圖10-21所示。作為所有節點的管理者,TreeView控件本身的常用屬性并不多,如表10-8所示。TreeNode是TreeView控件的重要組成部分,在WinForm中TreeView控件的每一個節點都是一個TreeNode類的實例,每一個TreeNode對象又都具有Nodes屬性來設置和管理它的子節點,TreeNode對象的常用屬性如表10-9所示。對于TreeView控件來說,最為重要的就是對其中包含的節點進行相關的操作和管理,因此對TreeView的應用主要就集中在添加節點、取得選中的節點以及用戶選中節點后的操作等幾個方面。為TreeView控件添加節點的方式有兩種。首先,我們可以通過WinForm中的TreeNode編輯器在圖形界面中完成節點的設置,將TreeView控件添加到窗體中后找到其Nodes屬性,點擊后打開TreeNode編輯器。在打開的TreeNode編輯器左側的“選擇要編輯的節點(N):”下方,我們可以看到當前TreeView控件中已經添加的所有節點及其層次結構,選中其中的某一個節點,可以在窗體右側“節點X的屬性(P):”下方看到當前選中節點的常用屬性,如圖10-23所示。在節點列表的下方,我們可以看到兩個按鈕。“添加根(R)”按鈕的作用是為TreeView控件添加一個根節點。而“添加子級(C)”按鈕則可以為當前選中節點添加子節點。在節點列表的右側,自上而下分別是上移按鈕、下移按鈕和刪除按鈕。當我們選中某個節點后點擊上移按鈕,該節點就會向上移動;如果點擊下移按鈕,那么該節點就會向下移動;點擊刪除按鈕就可以刪除該節點及其子節點。通過這幾個按鈕我們就可以根據需要設計出完整的TreeView控件。使用代碼方式添加節點需要先創建TreeNode對象,然后通過調用Add()方法,將其添加到相應節點的Nodes屬性中:TreeNodetn1=newTreeNode("根節點1");trvFilmType.Nodes.Add(tn1);trvFilmType.Nodes.Add(newTreeNode("根節點2"));trvFilmType.Nodes.Add("根節點3");在上面的代碼中我們采用三種方式向TreeView控件中添加了根節點。第一種方式首先創建了一個TreeNode對象,在實例化的時候我們通過其構造向該對象傳遞了一個字符串類型的參數作為其Text屬性的值,然后調用TreeView控件Nodes屬性的Add()方法將其添加到TreeView控件中。第二種方式其實和第一種方式是一樣的,只不過我們沒有再顯式地創建TreeNode對象,而是在調用Add()方法的時候臨時創建了它,同時依然通過其構造方法傳遞了字符串參數作為其文本值。第三種方式直接在調用Add()方法的時候傳遞字符串作為參數,節點則是由系統幫助我們來創建。無論采用哪種方式,只要是通過TreeView控件的Nodes屬性添加的節點都將是樹控件的根節點,如果需要為某個節點添加子節點,就需要通過該節點的Nodes屬性來完成:tn1.Nodes.Add("子節點1");tn1.Nodes.Add(newTreeNode("子節點2"));trvFilmType.Nodes[1].Nodes.Add("子節點3");trvFilmType.Nodes[1].Nodes.Add(newTreeNode("子節點4"));添加子節點的方式我們也給出了兩種。如果我們明確知道當前節點的名稱,則可以通過第一種方式來為它添加子節點。如果不知道當前節點的名稱,我們就只能夠通過第二種方式,也就是當前節點在Nodes中的排列位置找到該節點,然后再為其添加子節點。一般來說,對樹控件進行各種操作的時候都會和循環結合在一起,例如將數據表中的數據填充到TreeView控件中等。樹控件的節點層級越多,所需要的循環結構越復雜,因此在實際應用過程中最好不要創建操作三級的樹控件,否則程序將會變得很復雜。

10.5查看電影信息

10.5.1問題在音像店管理系統中,當用戶成功登錄到系統中以后,接下來就需要根據用戶自己的喜好查找電影并查看電影的詳細信息。這里我們首先需要用一個列表的方式將電影的主要信息展示出來,同時要提供相應的操作方式供用戶查找自己感興趣的電影,如圖10-26所示。該窗體的主要要求如下:(1)窗體運行時要求在屏幕中央,不能最大化和最小化,也不能夠改變大小。(2)顯示電影類型的下拉列表只能夠選擇不能輸入,同時為了方便用戶需要增加一個“選擇全部”選擇項用來查看所有電影。(3)頁面首次加載時需要以列表的形式顯示出所有電影的信息,包括電影名稱、主演、價格和類型。(4)當用戶選擇了某部類型的電影時,可實現對電影信息的查詢功能。雙擊列表中的某一部影片后,以模式窗體打開影片詳細信息窗體并顯示該影片的詳細信息。對于該窗體的要求如下:(1)窗體運行時要求在屏幕中央,不能最大化和最小化,也不能夠改變大小。(2)顯示電影類型的下拉列表只能夠選擇不能輸入,但是不需要添加“選擇全部”項。(3)根據由列表窗體傳遞過來的電影編號查詢電影詳細信息并顯示在窗體的相應位置。(4)由于我們還沒有將整個程序整合,因此“保存”按鈕功能不需要實現,但是“關閉”按鈕功能需要實現。10.5.2需求分析下面我們需要根據上面提出的要求做出詳細的需求分析。1.界面設計電影信息列表窗體的界面元素設計如表10-10所示。電影詳細信息窗體的界面元素設計如表10-11所示。2.添加“選擇全部”項在前面的章節中我們已經學習了如何將數據表中的數據放置到下拉列表控件中,但是在進行操作的時候有一個很重要的限制,即數據一旦通過DataSource屬性綁定到控件上后,就不允許再修改控件中的數據,那么我們該如何將“選擇全部”這樣一個新的選擇項添加到下拉列表中呢?方法當然有很多種,例如我們可以直接在數據庫中添加一條這樣的數據,也可以循環遍歷數據表的Rows屬性,將其中的數據依次通過Add()方法添加到ComboBox控件中。但是本章我們將采用一種新的方式來解決這個問題。事實上,仔細分析一下ComboBox控件和數據表我們就會發現,在使用DataSource屬性進行數據綁定的時候,控件中要顯示什么數據完全是由數據表來決定的,于是我們就有了一個新的思路:不能修改控件的數據,那我們就直接修改數據表中的數據,然后再進行數據綁定,這樣也可以解決問題。整個操作過程其實并不復雜:List<FilmType>types=newList<FilmType>();//在第一條顯示的是全部電影FilmTypet=newFilmType();t.ID=0;t.Name="全部電影";types.Add(t);using(SqlConnectionconn=newSqlConnection(strConn)){stringstrSql="select*fromFilmType";SqlCommandcomm=newSqlCommand(strSql,conn);conn.Open();SqlDataReaderdr=comm.ExecuteReader();FilmTypemodel=null;while(dr.Read()){model=newFilmType();model.ID=(int)dr["ID"];model.Name=dr["Name"].ToString();model.ParentID=(int)dr["ParentID"];model.Desc=dr["Desc"].ToString();model.State=(int)dr["State"];

types.Add(model);}dr.Close();}cboFilmType.DisplayMember="Name";cboFilmType.ValueMember="ID";cboFilmType.DataSource=types;上面的代碼大部分我們都已經很熟悉了,首先依然是創建一個數據庫連接對象,然后通過閱讀器對象SqlDataReader讀取數據,循環添加到List集合中。因為需要在下拉框中第一行出現“選擇全部”,所以,我們在添加數據到集合前,先添加進集合。最后我們用這個數據表對象完成了ComboBox控件的數據綁定,由于在數據表中“選擇全部”位于第一行,因此綁定后該選項就會出現在ComboBox控件的第一項。事實上,在實際開發過程中我們經常會遇到無法修改控件的值,或者修改起來很困難,這個時候就可以通過直接修改控件數據源的方式來實現我們所需要的操作。3.數據查詢數據查詢是我們需要重點實現的功能,根據用戶給出的條件生成相應的查詢語句,在數據庫中執行后得到查詢結果。第一步提取數據的工作并不復雜,而且在前面我們已經做過很多次了:stringconn=ConfigurationManager.ConnectionStrings["SQL"].ConnectionString;SqlConnectioncn=newSqlConnection(conn);stringstrSql="selectF.ID,F.Name,F.Actors,F.Price,T.NameTypeNamefromFilmF";strSql+="innerjoinFilmTypeTonF.TypeID=T.ID";這里要注意的是,有一個條件是“全部電影”,也就是說在組合查詢語句的時候,如果是全部電影則不需要加條件,而只有選中某一個類型的時候才需要加上查詢條件。if(id>0)strSql+="whereF.TypeID="+id.ToString();SqlCommandcomm=newSqlCommand(strSql,conn);conn.Open();SqlDataReaderdr=comm.ExecuteReader();4.顯示電影信息對電影信息的查看我們需要提供兩種方式。首先我們需要用列表的方式將查詢到的電影信息呈現出來,這一步操作我們可以使用ListView控件來實現:lsvShowData.Items.Clear();while(dr.Read()){ListViewItemitem=newListViewItem();item.Tag=(int)dr["ID"]; //該屬性用來保存當前數據的ID值item.SubItems[0].Text=dr["Name"].ToString();item.SubItems.Add(dr["Actors"].ToString());item.SubItems.Add(string.Format("{0:C}",dr["Price"]));item.SubItems.Add(dr["TypeName"].ToString());lsvShowData.Items.Add(item);}dr.Close();操作和我們在前面學習到的方式是一樣的,只不過在循環開始之前我們調用了ListView控件Items屬性的Clear()方法,該方法的作用是將ListView控件中的數據行全部清除。這么做的原因是我們需要用這個控件反復顯示數據,不過不做這個操作數據就會累加在一起。另外一個需要主意的地方是我們使用到了ListViewItem對象的Tag屬性,該屬性主要用來保存和對象相關的數據,這里我們保存的是電影的編號。我們需要做的第二個顯示工作是在電影詳細信息窗體中顯示電影的詳細信息,要完成這個工作我們需要經過幾個步驟。首先需要我們在電影信息列表窗體中獲得用戶選擇的電影的編號,然后將這個編號傳遞到電影詳細信息窗體,最后根據這個編號查詢電影信息并顯示。本次我們設定的操作方式是,用戶在電影信息列表中雙擊某條電影信息就可以打開詳細信息窗體,因此首先我們在ListView控件的事件列表中找到DoubleClick事件,雙擊后在系統自動生成的事件處理程序中完成后續的操作:privatevoidlsvShowData_DoubleClick(objectsender,EventArgse){if(lsvShowData.SelectedItems.Count>0){intid=(int)lsvShowData.SelectedItems[0].Tag;

frmFilmDetailsfd=newfrmFilmDetails(id);fd.ShowDialog();}}在這段處理程序中,我們首先添加了一個if結構,這么做的目的是確保在有電影被選中的情況下才執行該操作,因為我們用的是ListView控件的雙擊事件,有可能會出現用戶在沒有選中任何行的情況下雙擊控件而觸發事件。在if結構中,我們可以通過ListView控件的SelectedItems屬性取得所有選中項,ListView控件本身就支持多選,盡管我們設定了其MultiSelect屬性為false,但是在訪問用戶選中的行時依然需要通過SelectedItems屬性,當然因為只能夠選擇一行,因此下標就只能是零了。通過選中行的Tag屬性,就可以取得電影的編號。接下來的窗體間傳參我們在前面的章節中已經學習過了,這里就不再重復說明。將電影的編號傳遞到詳細信息窗體后,我們就可以根據這個編號到數據庫中查找電影信息并顯示在窗體中,不過在這之前我們還需要首先將電影分類信息綁定到ComboBox控件上,具體做法可參考上一個窗體中電影類型的數據讀取和綁定,不過這一次我們不用加上“選擇全部”了。接下來就是具體的數據顯示了:using(SqlConnectionconn=newSqlConnection(strConn)){stringstrSql="select*fromFilmwhereID="+filmID.ToString();SqlCommandcomm=newSqlCommand(strSql,conn);conn.Open();SqlDataReaderdr=comm.ExecuteReader();while(dr.Read()){txtName.Text=dr["Name"].ToString();txtActors.Text=dr["Actors"].ToString();txtPrice.Text=string.Format("{0:C}",dr["Price"]);txtAmount.Text=dr["Amount"].ToString();txtDesc.Text=dr["Desc"].ToString();cboType.SelectedValue=dr["TypeID"];}dr.Close();}首先創建數據庫連接對象,然后使用SqlDataReader對象來提取數據,當然我們這里的SQL語句添加了根據電影編號查詢的條件,在將數據填充到數據表中以后,我們通過一個if結構對數據表中的數據行做了判斷,已確定成功讀取了數據。具體的數據顯示也沒有復雜之處,我們是根據電影的編號進行查詢的,如果存在數據那么肯定就只有一行數據,因此在讀取數據的時候行下標就賦值為零。需要注意顯示數據的最后一行,ComboBox控件的數據是使用數據表綁定的,因此要通過其SelectedValue設定其選中行才行。10.5.3實現電影查看本章的案例是本書中我們所制作的最復雜的程序,frmFilmList窗體代碼如下:publicpartialclassfrmFilmList:Form{publicfrmFilmList(){InitializeComponent();}stringstrConn=ConfigurationManager.ConnectionStrings["SQL"].ConnectionString;privatevoidfrmFilmList_Load(objectsender,EventArgse){

BindList();}privatevoidBindList(){List<FilmType>types=newList<FilmType>();FilmTypet=newFilmType(); //在第一條顯示的是全部電影t.ID=0;t.Name="全部電影";types.Add(t);using(SqlConnectionconn=newSqlConnection(strConn)){stringstrSql="select*fromFilmType";SqlCommandcomm=newSqlCommand(strSql,conn);conn.Open();SqlDataReaderdr=comm.ExecuteReader();FilmTypemodel=null;while(dr.Read())

{model=newFilmType();model.ID=(int)dr["ID"];model.Name=dr["Name"].ToString();model.ParentID=(int)dr["ParentID"];model.Desc=dr["Desc"].ToString();model.State=(int)dr["State"];types.Add(model);}dr.Close();}cboFilmType.DisplayMember="Name";cboFilmType.ValueMember="ID";cboFilmType.DataSource=types;}privatevoidBindListView(intid){lsvShowData.Items.Clear();using(SqlConnectionconn=newSqlConnection(strConn)){stringstrSql="selectF.ID,F.Name,F.Actors,F.Price,T.NameTypeNamefromFilmF";strSql+="innerjoinFilmTypeTonF.TypeID=T.ID";if(id>0)strSql+="whereF.TypeID="+id.ToString();SqlCommandcomm=newSqlCommand(strSql,conn);conn.Open();SqlDataReaderdr=comm.ExecuteReader();while(dr.Read()){ListViewItemitem=newListViewItem();item.Tag=(int)dr["ID"];item.SubItems[0].Text=dr["Name"].ToString();item.SubItems.Add(dr["Actors"].ToString());

item.SubItems.Add(string.Format("{0:C}",dr["Price"]));item.SubItems.Add(dr["TypeName"].ToString());lsvShowData.Items.Add(item);}dr.Close();}}privatevoidcboFilmType_SelectedIndexChanged(objectsender,EventArgse){intid=(int)cboFilmType.SelectedValue;BindListView(id);}privatevoidlsvShowData_DoubleClick(objectsender,EventArgse){if(lsvShowData.SelectedItems.Count>0)

{intid=(int)lsvShowData.SelectedItems[0].T

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論