1. Trang chủ
  2. » Công Nghệ Thông Tin

Professional C# 2008 phần 7 potx

185 313 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 185
Dung lượng 2,43 MB

Nội dung

1050 Part V: Presentation Define what folder should be the base or root folder. Return the currently selected folder. Provide the ability to delay loading of the file structure. This should be a good starting point. One requirement has been satisfied by the fact that the TreeView control will be the base of the new control. The TreeView control displays data in a hierarchical format. It displays text describing the object in the list and optionally an icon. This list can be expanded and contracted by clicking an object or using the arrow keys. Create a new Windows Control Library project in Visual Studio .NET named FolderTree , and delete the class UserControl1 . Add a new class and call it FolderTree . Because FolderTree will be derived from TreeView , change the class declaration from: public class FolderTree to: public class FolderTree : System.Windows.Forms.TreeView At this point, you actually have a fully functional and working FolderTree control. It will do everything that the TreeView can do, and nothing more. The TreeView control maintains a collection of TreeNode objects. You can ’ t load files and folders directly into the control. You have a couple of ways to map the TreeNode that is loaded into the Nodes collection of the TreeView and the file or folder that it represents. For example, when each folder is processed, a new TreeNode object is created, and the text property is set to the name of the file or folder. If at some point additional information about the file or folder is needed, you have to make another trip to the disk to gather that information or store additional data regarding the file or folder in the Tag property. Another method is to create a new class that is derived from TreeNode . New properties and methods can be added and the base functionality of the TreeNode is still there. This is the path that you use in this example. It allows for a more flexible design. If you need new properties, you can add them easily without breaking the existing code. You must load two types of objects into the control: folders and files. Each has its own characteristics. For example, folders have a DirectoryInfo object that contains additional information, and files have a FileInfo object. Because of these differences, you use two separate classes to load the TreeView control: FileNode and FolderNode . You add these two classes to the project; each is derived from TreeNode . This is the listing for FileNode : namespace FormsSample.SampleControls { public class FileNode : System.Windows.Forms.TreeNode { string _fileName = “”; FileInfo _info; public FileNode(string fileName) { _fileName = fileName; _info = new FileInfo(_fileName); base.Text = _info.Name; if (_info.Extension.ToLower() == “.exe”) this.ForeColor = System.Drawing.Color.Red; } public string FileName ❑ ❑ ❑ c31.indd 1050c31.indd 1050 2/19/08 5:27:29 PM2/19/08 5:27:29 PM Chapter 31: Windows Forms 1051 { get { return _fileName; } set { _fileName = value; } } public FileInfo FileNodeInfo { get { return _info; } } } } The name of the file being processed is passed into the constructor of FileNode . In the constructor, the FileInfo object for the file is created and set to the member variable _info . The base.Text property is set to the name of the file. Because you are deriving from TreeNode , this sets the TreeNode ’ s Text property. This is the text displayed in the TreeView control. Two properties are added to retrieve the data. FileName returns the name of the file and FileNodeInfo returns the FileInfo object for the file. The following is the code for the FolderNode class. It is very similar in structure to the FileNode class, but you have a DirectoryInfo property instead of FileInfo , and instead of FileName you have FolderPath : namespace FormsSample.SampleControls { public class FolderNode : System.Windows.Forms.TreeNode { string _folderPath = “”; DirectoryInfo _info; public FolderNode(string folderPath) { _folderPath = folderPath; _info = new DirectoryInfo(folderPath); this.Text = _info.Name; } public string FolderPath { get { return _folderPath; } set { _folderPath = value; } } public DirectoryInfo FolderNodeInfo { get { return _info; } } } } Now you can construct the FolderTree control. Based on the requirements, you need a property to read and set the RootFolder . You also need a ShowFiles property for determining if files should be shown in the tree. A SelectedFolder property returns the currently highlighted folder in the tree. This is what the code looks like so far for the FolderTree control: using System; using System.Windows.Forms; using System.IO; using System.ComponentModel; (continued) c31.indd 1051c31.indd 1051 2/19/08 5:27:29 PM2/19/08 5:27:29 PM 1052 Part V: Presentation namespace FolderTree { /// < summary > /// Summary description for FolderTreeCtrl. /// < /summary > public class FolderTree : System.Windows.Forms.TreeView { string _rootFolder = “”; bool _showFiles = true; bool _inInit = false; public FolderTree() { } [Category(“Behavior”), Description(“Gets or sets the base or root folder of the tree”), DefaultValue(“C:\\ “)] public string RootFolder { get {return _rootFolder;} set { _rootFolder = value; if(!_inInit) InitializeTree(); } } [Category(“Behavior”), Description(“Indicates whether files will be seen in the list. “), DefaultValue(true)] public bool ShowFiles { get {return _showFiles;} set {_showFiles = value;} } [Browsable(false)] public string SelectedFolder { get { if(this.SelectedNode is FolderNode) return ((FolderNode)this.SelectedNode).FolderPath; return “”; } } } } Three properties were added: ShowFiles , SelectedFolder , and RootFolder . Notice the attributes that have been added. You set Category , Description , and DefaultValues for the ShowFiles and (continued) c31.indd 1052c31.indd 1052 2/19/08 5:27:30 PM2/19/08 5:27:30 PM Chapter 31: Windows Forms 1053 RootFolder . These two properties will appear in the property browser in design mode. The SelectedFolder really has no meaning at design time, so you select the Browsable=false attribute. SelectedFolder does not appear in the property browser. However, because it is a public property, it will appear in IntelliSense and is accessible in code. Next, you have to initialize the loading of the file system. Initializing a control can be tricky. Both design - time and runtime initializing must be well thought out. When a control is sitting on a Designer, it is actually running. If there is a call to a database in the constructor, for example, this call will execute when you drop the control on the Designer. In the case of the FolderTree control, this can be an issue. Here ’ s a look at the method that is actually going to load the files: private void LoadTree(FolderNode folder) { string[] dirs = Directory.GetDirectories(folder.FolderPath); foreach(string dir in dirs) { FolderNode tmpfolder = new FolderNode(dir); folder.Nodes.Add(tmpfolder); LoadTree(tmpfolder); } if(_showFiles) { string[] files = Directory.GetFiles(folder.FolderPath); foreach(string file in files) { FileNode fnode = new FileNode(file); folder.Nodes.Add(fnode); } } } showFiles is a Boolean member variable that is set from the ShowFiles property. If true , files are also shown in the tree. The only question now is when LoadTree should be called. You have several options. It can be called when the RootFolder property is set. That is desirable in some situations, but not at design time. Remember that the control is “ live ” on the Designer, so when the RootNode property is set, the control will attempt to load the file system. To solve this, check the DesignMode property, which returns true if the control is in the Designer. Now you can write the code to initialize the control: private void InitializeTree() { if (!this.DesignMode) { FolderNode rootNode = new FolderNode(_rootFolder); LoadTree(rootNode); this.Nodes.Clear(); this.Nodes.Add(rootNode); } } If the control is not in design mode and _rootFolder is not an empty string, the loading of the tree will begin. The Root node is created first and this is passed into the LoadTree method. Another option is to implement a public Init method. In the Init method, the call to LoadTree can happen. The problem with this option is that the developer who uses your control is required to make the Init call. Depending on the situation, this might be an acceptable solution. c31.indd 1053c31.indd 1053 2/19/08 5:27:30 PM2/19/08 5:27:30 PM 1054 Part V: Presentation For added flexibility, implement the ISupportInitialize interface. ISupportInitialize has two methods, BeginInit and EndInit . When a control implements ISupportInitialize , the BeginInit and EndInit methods are called automatically in the generated code in InitializeComponent . This allows the initialization process to be delayed until all of the properties are set. ISupportInitialize allows the code in the parent form to delay initialization as well. If the RootNode property is being set in code, a call to BeginInit first will allow the RootNode property as well as other properties to be set or actions to be performed before the control loads the file system. When EndInit is called, the control initializes. This is what BeginInit and EndInit code looks like: #region ISupportInitialize Members public void ISupportInitialize.BeginInit() { _inInit = true; } public void ISupportInitialize.EndInit() { if(_rootFolder != “”) { InitializeTree(); } _inInit = false; } #endregion In the BeginInit method, all that is done is that a member variable _inInit is set to true . This flag is used to determine if the control is in the initialization process and is used in the RootFolder property. If the RootFolder property is set outside of the InitializeComponent class, the tree will need to be reinitialized. In the RootFolder property you check to see if _inInit is true or false . If it is true , then you don ’ t want to go through the initialization process. If inInit is false , you call InitializeTree . You can also have a public Init method and accomplish the same task. In the EndInit method, you check to see if the control is in design mode and if _rootFolder has a valid path assigned to it. Only then is InitializeTree called. To add a final professional - looking touch, you have to add a bitmap image. This is the icon that shows up in the Toolbox when the control is added to a project. The bitmap image should be 16 × 6 pixels and 16 colors. You can create this image file with any graphics editor as long as the size and color depth are set properly. You can even create this file in Visual Studio .NET: Right - click the project and select Add New Item. From the list, select Bitmap File to open the graphics editor. After you have created the bitmap file, add it to the project, making sure that it is in the same namespace and has the same name as the control. Finally, set the Build Action of the bitmap to Embedded Resource: Right - click the bitmap file in the Solution Explorer and select Properties. Select Embedded Resource from the Build Action property. To test the control, create a TestHarness project in the same solution. The TestHarness is a simple Windows Forms application with a single form. In the references section, add a reference to the FolderTreeCtl project. In the Toolbox window, add a reference to the FolderTreeCtl.DLL . FolderTreeCtl should now show up in the toolbox with the bitmap added as the icon. Click the icon and drag it to the TestHarness form. Set the RootFolder to an available folder and run the solution. This is by no means a complete control. Several things could be enhanced to make this a full - featured, production - ready control. For example, you could add the following: Exceptions — If the control tries to load a folder that the user does not have access to, an exception is raised. ❑ c31.indd 1054c31.indd 1054 2/19/08 5:27:30 PM2/19/08 5:27:30 PM Chapter 31: Windows Forms 1055 Background loading — Loading a large folder tree can take a long time. Enhancing the initialization process to take advantage of a background thread for loading is a good idea. Color codes — You can make the text of certain file types a different color. Icons — You can add an ImageList control and add an icon to each file or folder as it is loaded. User Control User controls are one of the more powerful features of Windows Forms. They enable you to encapsulate user interface designs into nice reusable packages that can be plugged into project after project. It is not uncommon for an organization to have a couple of libraries of frequently used user controls. Not only can user interface functionality be contained in user controls, but common data validation can be incorporated in them as well, such as formatting phone numbers or ID numbers. A predefined list of items can be in the user control for fast loading of a list box or combo box. State codes or country codes fit into this category. Incorporating as much functionality that does not depend on the current application as possible into a user control makes the control that much more useful in the organization. In this section, you create a simple address user control. You also will add the various events that make the control ready for data binding. The address control will have text entry for two address lines: city, state, and zip code. To create a user control in a current project, just right - click the project in Solution Explorer and select Add; then select Add New User Control. You can also create a new Control Library project and add user controls to it. After a new user control has been started, you will see a form without any borders on the Designer. This is where you drop the controls that make up the user control. Remember that a user control is actually one or more controls added to a container control, so it is somewhat like creating a form. For the address control there are five TextBox controls and three Label controls. The controls can be arranged any way that seems appropriate (see Figure 31 - 4 ). ❑ ❑ ❑ Figure 31-4 c31.indd 1055c31.indd 1055 2/19/08 5:27:31 PM2/19/08 5:27:31 PM 1056 Part V: Presentation The TextBox controls in this example are named as follows: txtAddress1 txtAddress2 txtCity txtState txtZip After the TextBox controls are in place and have valid names, add the public properties. You might be tempted to set the visibility of the TextBox controls to public instead of private. However, this is not a good idea because it defeats the purpose of encapsulating the functionality that you might want to add to the properties. The following is a listing of the properties that must be added: public string AddressLine1 { get{return txtAddress1.Text;} set{ if(txtAddress1.Text != value) { txtAddress1.Text = value; if(AddressLine1Changed != null) AddressLine1Changed(this, EventArgs.Empty); } } } public string AddressLine2 { get{return txtAddress2.Text;} set{ if(txtAddress2.Text != value) { txtAddress2.Text = value; if(AddressLine2Changed != null) AddressLine2Changed(this, EventArgs.Empty); } } } public string City { get{return txtCity.Text;} set{ if(txtCity.Text != value) { txtCity.Text = value; if(CityChanged != null) CityChanged(this, EventArgs.Empty); } } } public string State ❑ ❑ ❑ ❑ ❑ c31.indd 1056c31.indd 1056 2/19/08 5:27:31 PM2/19/08 5:27:31 PM Chapter 31: Windows Forms 1057 { get{return txtState.Text;} set{ if(txtState.Text != value) { txtState.Text = value; if(StateChanged != null) StateChanged(this, EventArgs.Empty); } } } public string Zip { get{return txtZip.Text;} set{ if(txtZip.Text != value) { txtZip.Text = value; if(ZipChanged != null) ZipChanged(this, EventArgs.Empty); } } } The instances of the get property are fairly straightforward. They return the value of the corresponding TextBox control ’ s text property. The instances of the set property, however, are doing a bit more work. All of the set s work the same way. A check is made to see whether or not the value of the property is actually changing. If the new value is the same as the current value, then a quick escape can be made. If there is a new value sent in, set the text property of the TextBox to the new value and test to see if an event has been instantiated. The event to look for is the changed event for the property. It has a specific naming format, propertynameChanged , where propertyname is the name of the property. In the case of the AddressLine1 property, this event is called AddressLine1Changed . The properties are declared as follows: public event EventHandler AddressLine1Changed; public event EventHandler AddressLine2Changed; public event EventHandler CityChanged; public event EventHandler StateChanged; public event EventHandler ZipChanged; The purpose of the events is to notify binding that the property has changed. Once validation occurs, binding will make sure that the new value makes its way back to the object that the control is bound to. One other step should be done to support binding. A change to the text box by the user will not set the property directly. So, the propertynameChanged event must be raised when the text box changes as well. The easiest way to do this is to monitor the TextChanged event of the TextBox control. This example has only one TextChanged event handler and all of the text boxes use it. The control name is checked to see which control raised the event and the appropriate propertynameChanged event is raised. The following is the code for the event handler: private void controls_TextChanged (object sender, System.EventArgs e) { switch(((TextBox)sender).Name) { case “txtAddress1” : if(AddressLine1Changed != null) (continued) c31.indd 1057c31.indd 1057 2/19/08 5:27:31 PM2/19/08 5:27:31 PM 1058 Part V: Presentation AddressLine1Changed(this, EventArgs.Empty); break; case “txtAddress2” : if(AddressLine2Changed != null) AddressLine2Changed(this, EventArgs.Empty); break; case “txtCity” : if(CityChanged != null) CityChanged(this, EventArgs.Empty); break; case “txtState” : if(StateChanged != null) StateChanged(this, EventArgs.Empty); break; case “txtZip” : if(ZipChanged != null) ZipChanged(this, EventArgs.Empty); break; } } This example uses a simple switch statement to determine which text box raised the TextChanged event. Then a check is made to verify that the event is valid and not equal to null. Then the Changed event is raised. One thing to note is that an empty EventArgs is sent ( EventArgs.Empty ). The fact that these events have been added to the properties to support data binding does not mean that the only way to use the control is with data binding. The properties can be set in and read from code without using data binding. They have been added so that the user control is able to use binding if it is available. This is just one way of making the user control as flexible as possible so that it might be used in as many situations as possible. Because a user control is essentially a control with some added features, all of the design - time issues discussed in the previous section apply here as well. Initializing user controls can bring on the same issues that you saw in the FolderTree example. Care must be taken in the design of user controls so that you avoid giving access to data stores that might not be available to other developers using your control. Also similar to the control creation are the attributes that can be applied to user controls. The public properties and methods of the user control are displayed in the properties window when the control is placed on the Designer. In the example of the address user control it is a good idea to add Category , Description , and DefaultValue attributes to the address properties. A new AddressData category can be created and the default values would all be “ ” . The following is an example of these attributes applied to the AddressLine1 property: [Category(“AddressData”), Description(“Gets or sets the AddressLine1 value”), DefaultValue(“”)] public string AddressLine1 { get{return txtAddress1.Text;} set{ if(txtAddress1.Text != value) { (continued) c31.indd 1058c31.indd 1058 2/19/08 5:27:32 PM2/19/08 5:27:32 PM Chapter 31: Windows Forms 1059 txtAddress1.Text = value; if(AddressLine1Changed != null) AddressLine1Changed(this, EventArgs.Empty); } } } As you can see, all that needs to be done to add a new category is to set the text in the Category attribute. The new category is automatically added. There is still a lot of room for improvement. For example, you could include a list of state names and abbreviations in the control. Instead of just the state property, the user control could expose both the state name and state abbreviation properties. Exception handling should also be added. You could also add validation for the address lines. Making sure that the casing is correct, you might ask yourself whether AddressLine1 could be optional or whether apartment and suite numbers should be entered on AddressLine2 and not on AddressLine1 . Summary This chapter has given you the basics for building Windows client - based applications. It explained each of the basic controls by discussing the hierarchy of the Windows.Forms namespace and examining the various properties and methods of the controls. The chapter also showed you how to create a basic custom control as well as a basic user control. The power and flexibility of creating your own controls cannot be emphasized enough. By creating your own toolbox of custom controls, Windows - based client applications will become easier to develop and to test because you will be reusing the same tested components over and over again. The next chapter, “ Data Binding, ” covers how to link a data source to controls on a form. This will allow you to create forms that automatically update the data and keep the data on the form in sync. c31.indd 1059c31.indd 1059 2/19/08 5:27:32 PM2/19/08 5:27:32 PM [...]... When a control is initially created, its BindingContext property is null When the control is added to the Controls collection of the form, the BindingContext is set to that of the form 1 077 c32.indd 1 077 2/19/08 5: 27: 50 PM Part V: Presentation To bind a control to a form, an entry needs to be added to its DataBindings property, which is an instance of ControlBindingsCollection The following code creates... controls (see Chapter 37, “ASP.NET Pages”) Simple Binding A control that supports single binding typically displays only a single value at once, such as a text box or radio button The following example shows how to bind a column from a DataTable to a TextBox: DataSet ds = CreateDataSet(); textBox.DataBindings.Add(“Text”, ds , “Products.ProductName”); 1 075 c32.indd 1 075 2/19/08 5: 27: 49 PM Part V: Presentation... dvm.DataViewSettings[“Customers”].RowFilter = “Country=’UK’”; dataGrid.SetDataBinding(dvm, “Customers”); Figure 32-9 shows the output of the DataSourceDataViewManager sample code Figure 32-9 1 070 c32.indd 1 070 2/19/08 5: 27: 47 PM Chapter 32: Data Binding IListSource and IList Interfaces The DataGridView also supports any object that exposes one of the interfaces IListSource or IList IListSource has only one... DataGrid One major omission was the display of drop-down columns within the DataGrid — this functionality is now provided for the DataGridView in the form of the DataGridViewComboBoxColumn 1 072 c32.indd 1 072 2/19/08 5: 27: 48 PM Chapter 32: Data Binding When you specify a data source for the DataGridView, by default it will construct columns for you automatically These will be created based on the data types... as Name FROM Employees UNION SELECT 0,’(None)’”; da = new SqlDataAdapter(select, con); da.Fill(ds, “Managers”); // Construct the columns in the grid view SetupColumns(ds); (continued) 1 073 c32.indd 1 073 2/19/08 5: 27: 48 PM Part V: Presentation (continued) // Set the default height for a row dataGridView.RowTemplate.Height = 100 ; // Then set up the datasource dataGridView.AutoGenerateColumns = false;... dataGridView.Columns.Add(forenameColumn); DataGridViewTextBoxColumn surnameColumn = new DataGridViewTextBoxColumn(); surnameColumn.DataPropertyName = “LastName”; surnameColumn.HeaderText = “Surname”; 1 074 c32.indd 1 074 2/19/08 5: 27: 49 PM Chapter 32: Data Binding surnameColumn.Frozen = true; surnameColumn.ValueType = typeof(string); dataGridView.Columns.Add(surnameColumn); DataGridViewImageColumn photoColumn = new... however, the actual value will be either the Original value selected from the database (if ModifiedOriginal is chosen) or the current value in the DataColumn (if ModifiedCurrent is chosen) 10 67 c32.indd 10 67 2/19/08 5: 27: 46 PM Part V: Presentation Sorting Rows Apart from filtering data, you might also have to sort the data within a DataView To sort data in ascending or descending order, simply click the... of the ControlBindingsCollection shown in Figure 32-15: textBox1.DataBindings.Add(“Text”, ds, “Products.ProductName”); This line adds a Binding object to the ControlBindingsCollection 1 076 c32.indd 1 076 2/19/08 5: 27: 50 PM Chapter 32: Data Binding Object Binding BindingContext BindingManagerBase PropertyManager CurrencyManager MarshalByRefObject BaseCollection BindingCollection ControlBindingCollection... public DateTime DateOfBirth { get { return _dateOfBirth; } set { _dateOfBirth = value; } } private string _name; private Sex _sex; private DateTime _dateOfBirth; } enum Sex (continued) 1 071 c32.indd 1 071 2/19/08 5: 27: 48 PM Part V: Presentation (continued) { Male, Female } The display shows several instances of the Person class that were constructed within the PersonList class See Figure 32-10 In some... provided that this is the first time that data from the given source has been bound The purpose of this class is to define the position of the current record within the data source and to 1 078 c32.indd 1 078 2/19/08 5: 27: 50 PM Chapter 32: Data Binding coordinate all list bindings when the current record is changed Figure 32-18 displays two fields from the Products table and includes a way to move between . “txtAddress1” : if(AddressLine1Changed != null) (continued) c31.indd 1057c31.indd 10 57 2/19/08 5: 27: 31 PM2/19/08 5: 27: 31 PM 1058 Part V: Presentation AddressLine1Changed(this, EventArgs.Empty); . the form in sync. c31.indd 1059c31.indd 1059 2/19/08 5: 27: 32 PM2/19/08 5: 27: 32 PM c31.indd 1060c31.indd 1060 2/19/08 5: 27: 32 PM2/19/08 5: 27: 32 PM Data Binding This chapter builds on the content. made). Unchanged Lists all rows that have not changed in any way. c32.indd 1067c32.indd 10 67 2/19/08 5: 27: 46 PM2/19/08 5: 27: 46 PM Part V: Presentation 1068 Sorting Rows Apart from filtering data,

Ngày đăng: 12/08/2014, 23:23

TỪ KHÓA LIÊN QUAN