Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 53 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
53
Dung lượng
765,77 KB
Nội dung
6. Move the Name column to the top of the column list and change its AutoSizeMode property to AllCells. 7. For the EndingId column: a. Change the HeaderText property to Ending. b. Change the ColumnType property to DataGridViewComboBoxColumn. c. Change the DataSource property to the Ending table, found under Other Data Sources ➪ Project Data Sources ➪ FolktaleDBDataSet, as shown in Figure 4-6. Figure 4-6: The Ending table data source d. Change the DisplayMember property to EndingType. e. Change the ValueMember property to EndingId. f. Change the Width property to 120. 8. For the ClassificationId column: a. Change the HeaderText property to Classification. b. Change the ColumnType property to DataGridViewComboBoxColumn. c. Change the DataSource property to the Classification table, found under Other Data Sources ➪ Project Data Sources ➪ FolktaleDBDataSet. d. Change the DisplayMember property to Classification. e. Change the ValueMember property to ClassificationId. f. Change the Width property to 120. 9. Change the AutoSizeMode property of the Summary column to Fill. 10. Add the following event handler for the storyBindingSource.AddingNew event: private void storyBindingSource_AddingNew(object sender, AddingNewEventArgs e) { // Get data table view DataView dataTableView = storyBindingSource.List as DataView; // Create row from view 133 Modifying Data 44063c046.5.qxd 9/15/06 12:46 PM Page 133 DataRowView rowView = dataTableView.AddNew(); // Configure defaults rowView[“StoryId”] = Guid.NewGuid(); rowView[“SourceId”] = (sourceBindingSource.Current as DataRowView)[“SourceId”]; rowView[“EndingId”] = (endingBindingSource[0] as DataRowView)[“EndingId”]; rowView[“ClassificationId”] = (classificationBindingSource[0] as DataRowView)[“ClassificationId”]; rowView[“Name”] = “New story”; rowView[“Summary”] = “Story summary”; // Set new row e.NewObject = rowView; // Navigate to new row storyBindingSource.MoveLast(); } 11. In the design view for Form1, add a CharacterStoryTableAdapter component to the form called characterStoryTableAdapter, and a StoryLocationTableAdapter component called storyLocationTableAdapter. If these components do not appear in the Toolbox, then com- pile the project first. 12. Modify Form1_Load() as follows: private void Form1_Load(object sender, EventArgs e) { this.classificationTableAdapter.Fill(this.folktaleDBDataSet.Classification); this.endingTableAdapter.Fill(this.folktaleDBDataSet.Ending); this.storyTableAdapter.Fill(this.folktaleDBDataSet.Story); this.sourceTableAdapter.Fill(this.folktaleDBDataSet.Source); this.characterStoryTableAdapter.Fill(this.folktaleDBDataSet.CharacterStory); this.storyLocationTableAdapter.Fill(this.folktaleDBDataSet.StoryLocation); } 13. In the design view for Form1, set the DeleteItem property of the sourceBindingNavigator control to (none). 14. Add a new event handler for the Delete button as follows: private void bindingNavigatorDeleteItem_Click(object sender, EventArgs e) { if (Validate() && (sourceBindingSource != null)) { // Flag bool deleteRow = true; // Get row to be deleted DataRowView rowView = sourceBindingSource.Current as DataRowView; if (rowView == null) { return; } 134 Chapter 4 44063c046.5.qxd 9/15/06 12:46 PM Page 134 FolktaleDBDataSet.SourceRow row = rowView.Row as FolktaleDBDataSet.SourceRow; // Check for child rows FolktaleDBDataSet.StoryRow[] childRows = row.GetStoryRows(); if (childRows.Length > 0) { DialogResult userChoice = MessageBox.Show(“If you delete this source “ + “row you will also delete its child story rows. Continue?”, “Warning”, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); if (userChoice == DialogResult.Yes) { // Delete row and child rows foreach (FolktaleDBDataSet.StoryRow childStory in childRows) { // Delete child CharacterStory rows FolktaleDBDataSet.CharacterStoryRow[] characterStoryRows = childStory.GetCharacterStoryRows(); foreach (FolktaleDBDataSet.CharacterStoryRow childCharacterStory in characterStoryRows) { childCharacterStory.Delete(); } // Delete child StoryLocation rows FolktaleDBDataSet.StoryLocationRow[] storyLocationRows = childStory.GetStoryLocationRows(); foreach (FolktaleDBDataSet.StoryLocationRow childStoryLocation in storyLocationRows) { childStoryLocation.Delete(); } // Delete Story row childStory.Delete(); } } else { deleteRow = false; } } // Delete row? if (deleteRow) { sourceBindingSource.RemoveCurrent(); sourceBindingSource.EndEdit(); } } } 135 Modifying Data 44063c046.5.qxd 9/15/06 12:46 PM Page 135 15. Modify the event handler for the Save Data button as follows: private void sourceBindingNavigatorSaveItem_Click(object sender, EventArgs e) { this.Validate(); this.sourceBindingSource.EndEdit(); this.storyBindingSource.EndEdit(); // Save added / edited parent rows FolktaleDBDataSet.SourceDataTable tableChanges = folktaleDBDataSet.Source.GetChanges( DataRowState.Added | DataRowState.Modified) as FolktaleDBDataSet.SourceDataTable; if (tableChanges != null) { sourceTableAdapter.Update(tableChanges); } // Save child row modifications characterStoryTableAdapter.Update(folktaleDBDataSet.CharacterStory); storyLocationTableAdapter.Update(folktaleDBDataSet.StoryLocation); storyTableAdapter.Update(folktaleDBDataSet.Story); // Save deleted parent rows FolktaleDBDataSet.SourceDataTable tableDeletes = folktaleDBDataSet.Source.GetChanges(DataRowState.Deleted) as FolktaleDBDataSet.SourceDataTable; if (tableChanges != tableDeletes) { sourceTableAdapter.Update(tableDeletes); } // Accept changes folktaleDBDataSet.AcceptChanges(); } 16. Run the application and modify some data. The interface should look similar to Figure 4-7. Figure 4-7: The Story editing interface 136 Chapter 4 44063c046.5.qxd 9/15/06 12:46 PM Page 136 17. Test the functionality of adding and removing data. Remove a source with story rows (and accepting the confirmation) and then add a new source and add a story to the source before sav- ing all changes. The operation should be successful. 18. Close the application and Visual C# Express. How It Works In this example, you first added a related table to the form by using a nested item in the Data Sources window. Doing this means that you automatically bind a filtered view of the data to the control, so with no additional code whatsoever you get a list of child rows in a DataGridView, where the Story rows displayed are determined by the selected item in the Source table. If you use the top-level version of the Story table as displayed in the Data Sources window, this filtering is not automatic, and you’d have to write your own code to achieve that functionality. The Data Sources window is far from perfect in this respect. For more complex relationships, you will have to write your own code. Hierarchical relationships also cause problems — and have even been known to confuse Visual C# Express to the point of crashing. In the FolktaleDB database, the Character table is hierarchical — it has MotherId and FatherId foreign key fields that refer to other rows in the same table. You might like to experiment with data binding to this table in your own applications, but beware the results! For this table you really need to write custom code, which is something you’ll see later in the book. Back to the current example, the next thing you did was format the columns used in the DataGridView. You hid the primary key field (there is no real need to display GUID values to users, after all) and replaced two of the three foreign keys with drop-down selectors. The third primary key, SourceId, was removed because it was redundant (the parent source row is already displayed on the form). You also formatted the layout of the remaining columns to fill the available area, keeping enough space for each to display all information — with the exception of the Summary column, which you deal with properly in the next section. Then you added the custom functionality required to add new Story rows, delete rows from multiple tables, and save data to multiple tables in the right order. Adding rows to the Story table uses much the same code as adding rows to the Source table. Apart from adding the code as an event handler for the AddingNew event of a different BindingSource control, the only real change was in the default values used: // Configure defaults rowView[“StoryId”] = Guid.NewGuid(); rowView[“SourceId”] = (sourceBindingSource.Current as DataRowView)[“SourceId”]; rowView[“EndingId”] = (endingBindingSource[0] as DataRowView)[“EndingId”]; rowView[“ClassificationId”] = (classificationBindingSource[0] as DataRowView)[“ClassificationId”]; rowView[“Name”] = “New story”; rowView[“Summary”] = “Story summary”; Note that the foreign keys are configured here to give initial values. The SourceId field comes from the parent row, and the other two foreign keys ( EndingId and ClassificationId) are set to the first row in the respective parent tables. 137 Modifying Data 44063c046.5.qxd 9/15/06 12:46 PM Page 137 Next, you added the additional code required to delete items. The first step here was to include the related information in the CharacterStory and StoryLocation tables. Because those tables were not bound to anything, you had to add the table adapters manually, and added code in Form1_Load() to populate them. With that data in place, it is possible to remove child rows in these tables when Story rows are deleted, as you will see shortly. To implement a custom scheme for deleting Source table rows, you first had to replace the existing code for deleting items from the Source table. You disabled the existing code by clearing the sourceBindingNavigator.DeleteItem property, which was initially set to the id of the Delete button. This property hooks up the specified button to internal deletion code, which you wanted to replace so that you could add additional validation and functionality. The code you added started with some basic checks to see that deletion was possible, and then obtained the row to be deleted (exiting immediately if no row is found): private void bindingNavigatorDeleteItem_Click(object sender, EventArgs e) { if (Validate() && (sourceBindingSource != null)) { // Flag bool deleteRow = true; // Get row to be deleted DataRowView rowView = sourceBindingSource.Current as DataRowView; if (rowView == null) { return; } Next comes the important bit — the code checks to see if the row to be deleted from the Source table has child rows in the Story table: FolktaleDBDataSet.SourceRow row = rowView.Row as FolktaleDBDataSet.SourceRow; // Check for child rows FolktaleDBDataSet.StoryRow[] childRows = row.GetStoryRows(); if (childRows.Length > 0) { If rows exist, a dialog box is displayed to confirm the deletion of child rows: DialogResult userChoice = MessageBox.Show(“If you delete this source “ + “row you will also delete its child story rows. Continue?”, “Warning”, MessageBoxButtons.YesNo, MessageBoxIcon.Warning); If the user clicks Yes, the child rows are deleted (as well as child rows for the Story row in the CharacterStory and StoryLocation tables, if any exist): if (userChoice == DialogResult.Yes) { // Delete row and child rows 138 Chapter 4 44063c046.5.qxd 9/15/06 12:46 PM Page 138 foreach (FolktaleDBDataSet.StoryRow childStory in childRows) { // Delete child CharacterStory rows FolktaleDBDataSet.CharacterStoryRow[] characterStoryRows = childStory.GetCharacterStoryRows(); foreach (FolktaleDBDataSet.CharacterStoryRow childCharacterStory in characterStoryRows) { childCharacterStory.Delete(); } // Delete child StoryLocation rows FolktaleDBDataSet.StoryLocationRow[] storyLocationRows = childStory.GetStoryLocationRows(); foreach (FolktaleDBDataSet.StoryLocationRow childStoryLocation in storyLocationRows) { childStoryLocation.Delete(); } // Delete Story row childStory.Delete(); } } Alternatively, a flag is set to prevent deletion of the parent row if the user clicks No: else { deleteRow = false; } } Finally, the parent row is deleted if necessary: // Delete row? if (deleteRow) { sourceBindingSource.RemoveCurrent(); sourceBindingSource.EndEdit(); } } } The last code modification was the code for updating the database, which followed the scheme laid out prior to this example. First the form is validated, and pending changes to the underlying data set are committed: private void sourceBindingNavigatorSaveItem_Click(object sender, EventArgs e) { this.Validate(); this.sourceBindingSource.EndEdit(); this.storyBindingSource.EndEdit(); 139 Modifying Data 44063c046.5.qxd 9/15/06 12:46 PM Page 139 Next, parent row additions and modifications are committed to the database: // Save added / edited parent rows FolktaleDBDataSet.SourceDataTable tableChanges = folktaleDBDataSet.Source.GetChanges( DataRowState.Added | DataRowState.Modified) as FolktaleDBDataSet.SourceDataTable; if (tableChanges != null) { sourceTableAdapter.Update(tableChanges); } That’s followed by all child row modifications: // Save child row modifications characterStoryTableAdapter.Update(folktaleDBDataSet.CharacterStory); storyLocationTableAdapter.Update(folktaleDBDataSet.StoryLocation); storyTableAdapter.Update(folktaleDBDataSet.Story); There’s no need here to worry about rows being added to the CharacterStory or StoryLocation tables, because that functionality does not exist in the application. Instead, you only have to worry about deleting a Story row with child rows in these tables, so performing updates in this order is enough to avoid errors. Finally, you make the parent row deletions: // Save deleted parent rows FolktaleDBDataSet.SourceDataTable tableDeletes = folktaleDBDataSet.Source.GetChanges(DataRowState.Deleted) as FolktaleDBDataSet.SourceDataTable; if (tableChanges != tableDeletes) { sourceTableAdapter.Update(tableDeletes); } Then it’s necessary to clear all the changes in the data set so that no attempt is made to commit them a second time: // Accept changes folktaleDBDataSet.AcceptChanges(); } And that completes the code for this example. Now you can modify data in a far more user-friendly way and have the capability to automatically delete child rows if required. 140 Chapter 4 44063c046.5.qxd 9/15/06 12:46 PM Page 140 Updating Long Text Data for DataGridView Displays In Chapter 3 you saw how to view long text data in a DataGridView control by using a pop-up dialog. It is possible to write similar code to ease the editing of long text data in a pop-up window. There isn’t a lot to say about this because the code should mostly be familiar to you, so the best way to see what to do is through another Try It Out. Try It Out Text Modification 1. Copy the project C:\BegVC#Databases\Chapter04\Ex0403 - Multi-Table Modification to a new project, C:\BegVC#Databases\Chapter04\Ex0404 - Text Modification , and then open the new project. 2. Open Form1 in design mode, and then open the Edit Columns dialog box for the storyDataGridView control. 3. Add a new unbound column called EditSummaryButton of type DataGridViewButtonColumn, with the header text Edit Summary, as shown in Figure 4-8. Figure 4-8: The EditSummaryButton column 4. Move the new column to a position just before the Summary column, change its Text property to Edit Summary, and change its UseColumnTextForButtonValue property to True. 5. Set the ReadOnly property of the Summary column to True. 6. Accept the column changes, and add a new form to the project called SummaryEditor. 7. Change the Text property of the new form to Edit Summary. 8. Add a multiline TextBox and two Button controls to the form called summaryBox, okButton, and cancelButton. Set the Text property for okButton and cancelButton to OK and Cancel, respectively. Set the Anchor properties of the controls to Top, Bottom, Left, Right for the text box and Bottom, Left for the buttons. Finally, set the ScrollBars property of the text box to Vertical. The layout of the controls appears in Figure 4-9. 9. Set the AcceptButton property of the SummaryEditor form to okButton and the CancelButton property to cancelButton. 141 Modifying Data 44063c046.5.qxd 9/15/06 12:46 PM Page 141 Figure 4-9: The SummaryEditor form 10. Add event handlers for the two buttons as follows: private void okButton_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.OK; } private void cancelButton_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; } 11. Add the following public property to the SummaryEditor form by editing the code for the form directly: public string SummaryText { get { return summaryBox.Text; } set { summaryBox.Text = value; } } 12. Switch back to Form1, and add an event handler for the CellContentClick event by double- clicking the storyDataGridView control. Add code as follows: private void storyDataGridView_CellContentClick( object sender, DataGridViewCellEventArgs e) { // Check for summary column if (storyDataGridView.CurrentCell.OwningColumn.HeaderText == “Edit Summary”) { // Check for nulls if (storyDataGridView.CurrentRow.DataBoundItem == null) 142 Chapter 4 44063c046.5.qxd 9/15/06 12:46 PM Page 142 [...]... changes? 4 Copy the project C:\BegVC #Databases\ Chapter 04\ Ex 040 1 - Simple Data Modification to a new project, C:\BegVC #Databases\ Chapter 04\ Q 040 4 - Canceling Edits Modify the project by adding a button to the BindingNavigator control to cancel edits 5 What event would you handle in a Windows application if you want to prevent data from being lost when the application is terminated? 147 44 063c 046 .5.qxd... DialogResult.Yes) { // Save data before quitting SaveData(); } } } 145 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 146 Chapter 4 4 Run the application, make some changes, and then close the application The dialog box shown in Figure 4- 11 should appear Figure 4- 11: The SummaryEditor form in action 5 Select No to abort changes, and then close Visual C# Express How It Works The two things you did in this example... handling the FormClosing event, as demonstrated in the following Try It Out Try It Out Saving Data on Application Termination 1 Copy the project C:\BegVC #Databases\ Chapter 04\ Ex 040 4 - Text Modification to a new project, C:\BegVC #Databases\ Chapter 04\ Ex 040 5 - Handling FormClosing 2 Open the code for the Form1 form and copy the code from the sourceBindingNavigatorSaveItem_Click() event hander into a new... simply a standard text editing form that you’ve probably used many times in applications The only thing to remark on it is how the text displayed is exposed as a public property 143 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 144 Chapter 4 The text property is accessed in the event handler that is called when a cell of the DataGridView is clicked The event handler starts by checking the column that was clicked:... editor.ShowDialog(); And, if the user clicks OK, the text is used to update the column: if (result == DialogResult.OK) { // Update data dataRow.Summary = editor.SummaryText; } } } 144 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 145 Modifying Data All in all, there’s nothing that complicated about this example However, it provides a user-friendly way for users to edit long text information, and it is well worth... Server 2005 Express service With this functionality enabled, you can connect to any database hosted in SQL Server Express To see these databases, you can use Microsoft’s SQL Server Management Studio Express (SSMSE), which you were introduced to in Chapter 2 Among other things, SSMSE (see Figure 5 -4) enables you to create and edit databases, as well as connect to existing database files 1 54 440 63c05.qxd:WroxBeg... edits 5 What event would you handle in a Windows application if you want to prevent data from being lost when the application is terminated? 147 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 148 44 063c05.qxd:WroxBeg 9/12/06 10:36 PM Page 149 5 Databases and the Internet Data binding in web applications is a little different than the data binding in Windows applications that you’ve looked at in previous chapters... All Programs ➪ Microsoft SQL Server 2005 ➪ Configuration Tools ➪ SQL Server Configuration Manager Open it, expand SQL Server 2005 Network Configuration, select Protocols for SQLEXPRESS, right-click Named Pipes, and click Enable — as shown in Figure 5-2 153 44 063c05.qxd:WroxBeg 9/12/06 10:36 PM Page 1 54 Chapter 5 Figure 5-2: Enabling remote access for SQL Server 2005 Express You can also use the TCP/IP... Directory property of the local database file, Visual C# Express may copy a fresh version of the database to the output directory of your project each time the project is executed You learned how this can be advantageous in testing, but how it can be useful to disable that functionality to persist changes 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 147 Modifying Data ❑ How to modify simple database data.. .44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 143 Modifying Data { return; } // Get row FolktaleDBDataSet.StoryRow dataRow = (storyDataGridView.CurrentRow.DataBoundItem as DataRowView).Row as FolktaleDBDataSet.StoryRow; // Get summary . being lost when the application is terminated? 147 Modifying Data 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 147 44 063c 046 .5.qxd 9/15/06 12 :46 PM Page 148 5 Databases and the Internet Data binding in web. would you apply the changes? 4. Copy the project C:BegVC #Databases Chapter 04 Ex 040 1 - Simple Data Modification to a new project, C:BegVC #Databases Chapter 04 Q 040 4 - Canceling Edits. Modify the project. Application Termination 1. Copy the project C:BegVC #Databases Chapter 04 Ex 040 4 - Text Modification to a new project, C:BegVC #Databases Chapter 04 Ex 040 5 - Handling FormClosing. 2. Open the code for