TaskVision Solution Overview: Design and Implementation
Release Date: 08/20/2004
| Update Date: 08/20/2004
Vertigo Software, Inc.
Applicable to: .NET FrameworkWindows Form
Abstract: This article introduces the design and architecture decision making of TaskVision solutions. This example demonstrates how to use the .NET Framework's Windows Form Class and XML Web services to generate a smart client task management application.
Note: From May 7, 2003, the code is not provided. We will soon re-release it.
Download this solution code: TaskVision client TaskVision server TaskVision Source code
This page
Overview Solutions Architecture Learning experience
Overview
What is a TaskVision solution?
TaskVision is an example intelligent client task management application that uses Microsoft® .NET Framework (a vital Windows® component, supporting the next-generation application and XML Web services) Windows form class generated. TaskVision allows authenticated users to view, modify, and add items and tasks shared with other users. It can be used in a variety of scenarios, from errors to manage jobs or customer service requests, can be used. Its main use is to provide high quality sample source code for developers who interested using .NET Framework to generate smart client applications and XML web services. Figure 1 shows the TaskVision application.
Figure 1 TaskVision interface
The TaskVision solution demonstrates many technologies provided by .NET Framework, including:
• Application Offline and Online Model • Update Model (None Contact Deployment) via HTTP • Control User Access to Application Functions • Printing and Printing Preview • Windows XP Topics • Dynamic Properties Localization Support • Auxiliary function support (limited) • Form authentication using the Username / Password Database • Asynchronous XML Web Service Call • ADO.NET Data Access to use the SQL store • Graphics development • Based on .NET Framework Integration between code and COM INTEROP
This white paper has been discussed in-depth discussion on TaskVision, providing detailed information about its architecture from the perspective of developers of the solution. In addition, this document also analyzes many of the main application functions and technologies to implement these functions, explaining how TaskVision is used as templates that can generate smart client applications. For complete information about TaskVision, please visit the TaskVision home page.
TaskVision solutions were developed using Microsoft® Visual Studio .NET®, written in C # and Visual Basic .NET programming languages. TaskVision has been ported to the PocketPC platform. For information on how to generate a PocketPC version of TaskVision, see Creating The Pocket Taskvision Application on MSDN.
Taskvision
The easiest way to experience TaskVision is to download and install TaskVision Live Client V1.0 MSI. The Live Client contains the compiled executable and configured to send and retrieve the data it needs from a public XML web service.
The server application corresponding to the client application is TaskVision Server V1.1 MSI. The Server installer will create a database and install an XML web service and a Web site that carries client V1.1 updates (this update will be retrieved and executed through contactless deployment) ("http: // localhost / taskvisionws" and "Http:// localhost / taskvisionupdates"). The installer is especially suitable for developers who want to run throughout the solution locally without compiling code. For those who want to access the source code, you can use the released TaskVision Source Code V1.1 MSI, which will create a database and install the client application V1.1 (for TaskVision Live Client, is through contactless Deploy the source code for downloading and executing the version, as well as the source code for the XML web service. For those who do not have the software necessary to install the MSI, some source code can be viewed online with TaskVision Source Code Viewer.
For the system requirements and installation instructions for MSI, please refer to the instructions below.
Note You cannot install TaskVision Server V1.1 MSI to the TaskVision Source Code V1.1 MSI to install on the same computer because the two installations use the same database and IIS virtual directory name.
TaskVision Live Client V1.0 MSI installation
Download and install the appropriate MSI. When you start the app (from the START menu), a standard login screen will be prompted, you will need to enter "JDOE" as the username, enter "Welcome" as a password. Once you are logged in, you can modify the data and test the application function, but please note that all the data on the public server will be reset every night, so the changes made on a certain day cannot be retained the next day.
Minimum requirements:
• Windows 2000 / XP or later • Microsoft .NET Framework 1.0 • LAN / Dial Internet Connection • Microsoft Excel 2002 (Recommended; No Need - To actually view COM Interop, you need to export data to Excel).
The application does not support ISA clients or web agents.
TaskVision Server V1.1 MSI installation
Before installing, make sure the following conditions are met:
• You have administrator privileges on your local computer. • The SQL Server default instance is named "Local" (instance installed by default), and your Windows account has SQL Server administrator privileges with integrated security databases. • ASP.NET file extensions (.aspx and .asmx) must be registered to Internet Information Services (IIS). (If IIS is installed after installation .NET Framework, you must run the following location: c: /windows/microsoft.net/framework/v1.0.3705/ASPNET_REGIIS.EXE - "i.) • Finally, make sure SQL Server and IIS services are running. Download and install the appropriate MSI. After installing the XML web service and updating the Web site and the database, navigate to include TaskVision client applications (installed by TaskVision Live Client V1.0 MSI) The catalog, and edit the taskvision.exe.config file to point to the local server URL, as shown below. (This file is in "1.0.0.0", it is also possible to be in the "1.1.0.0" subdirectory.) Note below In the example, we use "localhost" as a server. This applies to the case where the client and server application runs on the same computer. If the server is running on a different computer, please use the computer of the run server. URL instead "localhost". If the server runs on a different computer, you will need to make the same changes in the task.exe.config file, which is under TaskVisionUpdates / 1.1.0.0 "under the TaskVision Server directory. < AppSettings>
->
appsettings>
configure>
Minimum requirements:
• Windows 2000 / XP or later • Microsoft .NET Framework 1.0 • IIS 5.0 • SQL Server 2000
TaskVision Source Code V1.1 MSI installation
Before installing, make sure the following conditions are met:
• You must have administrator privileges on your local computer. • The SQL Server default instance is named "Local" (instance installed by default), and your Windows account has SQL Server administrator privileges with integrated security databases. • ASP.NET file extension (.aspx and .asmx) must be registered to IIS. (If IIS is installed after ASP.NET, you must run the following location: c: /windows/microsoft.net/framework/v1.0.3705/ASPNET_REGIIS.EXE -I.) • Finally, ensure SQL Server and IIS The service is in operation. Download and install the appropriate MSI. After installing XML Web services and databases, use Visual Studio .NET to open the TaskVision solution. Note that if Excel 2002 is not installed, if you do not delete the code (located in the Excel class and the main form class), or turn these code as a comment, the solution will not be compiled. .
When compiling projects and launching applications, a standard login screen will be prompted. You need to enter "JDOE" as the username and enter "Welcome" as a password.
Minimum requirements:
• Windows 2000 / XP or later • Visual Studio .NET 2002 • IIS 5.0 • SQL Server 2000 • Microsoft Excel 2002 (recommended)
Back to top
Solution architecture
Our task management solutions contain three main components: database, XML web services, and smart client applications generated using Windows Forms (see Figure 2).
The database is accessed by XML Web, and these XML Web services have only permissions to run stored procedures on the database. By limiting the content that the XML web service can access on the database, you can ensure that only our query can run on the database.
In our sample solution, the XML Web service is implemented to run on a public server, and any application can access it via the Internet. Of course, you can run them on the intranet of the company to limit data access to the internal network. (Note: Although any server that can access the server running an XML Web service can access the XML web service, only applications that can provide valid usernames and passwords can use them.)
Intelligent client applications authenticate users by passing usernames and passwords to authentication XML web services. After the authentication is successful, the XML Web service will pass an encrypted ticket to the smart client application, which will be stored and submitted to the data XML Web service every time you request data. The data XML web service will verify that the plus ticket is processed and processes the data request. (Note: In your own solution, when the credentials passing through the XML web service or the encryption ticket can be used to access sensitive information or resources, or when the sensitive data itself is passed back and forth, we recommend that you use the security socket layer ( SSL). This layer encryption can prevent potential attackers from attacks.)
Figure 2 TaskVision application architecture
database
All shared data are stored in the SQL Server database. Application specific data or configuration settings are not included. This allows developers to create custom applications, each application from a single unique data store extracting data. With the same server-side function, we can create a .NET Compact Framework version in this way, called Pocket TaskVision (will be released in March). This section provides an overview of the database used in the TaskVision solution. Database architecture
TaskVision's database architecture (shown in Figure 3) is quite simple, but it is sufficient to support this task management solution.
Figure 3 TaskVision database architecture
Stored procedure
The TaskVision solution uses a stored procedure to encapsulate all database queries. The stored procedure provides a clear isolation between the database and the intermediate layer data access layer. This simplifies maintenance because changes to the database architecture are invisible to data access components. Because caches in the database and certain local processing can reduce the required number of network requests, for some architectural schemes, the use of stored procedures can also bring some performance benefits.
XML Web Service
The combination of XML web services effectively acts as the main intermediate layer, responsible for processing authentication and data requests from any client application accessing their client applications.
We chose to divide the XML Web service functionality into two categories:
• Authentication - Ming-text credentials can be submitted to provide login information, or it can be configured to run under SSL (although it is not currently implemented in this example). • Data - Send and receive non-critical data (after a form of authentication) without causing SSL system overhead. If this solution is used in the actual application, the data XML Web service will be able to run under SSL to prevent potential attackers from accessing serialization.
Authentication XML Web Service
Authentication Service (Figure 4) Following very simple principles: (using a stored procedure) Verify the username and password for the database, then return to the unique plus ticket to nested the user ID. If the username and password fail, do not return anything.
After sending this ticket, it will cache its value for two minutes (cache in the static cache object of the web application). In this way, we can maintain a list of server-ended server-oriented server, all code running in the same application domain (as shown later). Due to only two minutes of tickets in this list, the client application has to re-authentication, which helps to prevent "Replay Attack", in this attack mode, the attacker steals from the network Take a ticket and use it to pretend to be verified.
The ticket is created using the System.Web.Security.FormSauthenticationalTicket class, because it can nested data in the ticket itself, for example, the user ID.
Figure 4 TaskVision identity verification process
'Create the Ticket
Dim Ticket As New FormsauthenticationalTicket (UserID, False, 1)
DIM EncryptedticKet As String = Formsauthentication.Encrypt (Ticket)
'Get The Ticket Timeout in Minutes
DIM ConfigurationAppSettings As AppsettingSreader = New AppsettingSReader ()
DIM TIMEOUT AS INTEGER = _CINT (ConfigurationAppSettings.GetValue ("AuthenticationTicket.timeout", _
Gettype (Integer))
'cache the ticket
Context.cache.insert (Encryptedticket, UserId, Nothing, _
DateTime.now.addminutes (timeout), TimeSpan.zero)
Data XML Web Service
Data XML web services provide client applications to retrieve and change data, and provide authentication services to verify each request for users.
Both XML web services run in the same application domain (in this case, for the same IIS web application), which enables the data service to access the authentication service for saving a copy of the valid authentication ticket for the same cache memory .
Each public web approach supported by the data service requires authentication tickets. Check if the ticket is checked in the cache before returning any data. If the ticket exists, the username and password are verified in the last two minutes; otherwise the ticket will be invalid or expired. As an additional safety measures, we extract the nested user IDs from the ticket, and verify the user ID for the database to ensure that the user account is not (another administrator), and has a TaskVision administrator privilege for Requires the function of the administrator status.
Private function isticTValid (Byval Ticket As String, Byval Isadmincall_
As boolean) as boolean
IF Ticket Is Nothing Oralse Context.cache (Ticket) Is Nothing Then
'Not Authenticated
Return False
Else
'Check The User Authorization
DIM Userid as integer = _
Cint (FormSauthentication.Decrypt (Ticket) .name)
DIM DS AS Dataset
Try
DS = SQLHELPER.EXECUtedataset (DBCONN, "GetUserInfo", UserID)
Finally
dbconn.close ()
END TRY
DIM UserInfo As New UserInformation ()
With ds.tables (0) .rows (0)
Userinfo.isadministrator = CBool (.ISADMinistrator ")))
Userinfo.isaccountlocked = cbool (.ISACCOUNTLOCKED ")))
End with
IF userinfo.isaccountlocked kil
Return False
Else
'Check Admin Status (for Admin Required Calls)
IF isadmincall and not userinfo.isadministrator THEN
Return False
END IF
Return True
END IF
END IF
END FUNCTION
_
Public Function GetTasks (Byval Ticket As String, Byval ProjectID _
As integer) AS Datasettasks
'ife ticket is not valid, returnif not istesetvalid (ticket) TEN RETURN NOTHING
DIM DS AS New Datasettasks ()
Datasks.selectCommand.Parameters ("@ projectid"). Value = ProjectID
Datasks.Fill (DS, "Tasks")
Return DS
END FUNCTION
Windows Form Intelligent Client
Intelligent client applications are the most prominent part of this solution because it is a tool for end users to manage projects and tasks. As mentioned earlier, TaskVision is used to demonstrate some key intelligent client technology and solutions. Below, we will demonstrate some of the technologies and programs. For this section, there is a valuable supplemental material: TaskVision Source Code Viewer, which provides a detailed analysis of many more interesting parts in source code.
User interface form
Before studying the core technology and its use, various parts of the application - various forms and their uses - a short description may be necessary.
Login Form (Figure 5) User-enabled authentication by entering their TaskVision credentials. (Similarly, the default login credentials used to access this application are "JDOE" and "Welcome", represent the username and password, respectively.)
Figure 5 TaskVision login form
Main form (Figure 6) shows data retrieved from the data XML web service (or from offline file). The main form acts as the foundation of our event-driven application and is a core place for the user experience. The form itself mainly includes a main menu, a toolbar with a button, several custom panels with ComboBox, charts, and links, another custom panel of a DataGrid, preview pane, two separators, and one The status bar for displaying activity information (eg, the number of items, and online status).
In DataGrid occupying most spaces in the form, you can see a summary of the tasks listed for the currently selected item in the database. Under DataGrid, a more detailed information of the selected task is displayed. The left side of the DataGrid is a control for selecting other items and filing the displayed tasks. There are two GDI charts below these controls, showing information about the task. Below them, there is a control that displays the history of the selected task (about it creates information about any modification), in this screen, the control is not visible. At the top of the form, there are some menus and buttons to manage TaskVision users, switch languages, create new items, and tasks, export data in the DataGrid to Excel, and work offline (or when the application is currently offline) Online work).
Figure 6 TaskVision Main Form
Double-click the task in the DataGrid of the Main Form, which will display the Edit Task form (Figure 7). By editing controls edited by the user, the form can modify the task - its deadline, responsible for its staff and task priority, summary, description, and current completion tasks. In addition, this dual functional form can also display any associated history of a given task on the History tab (Figure 8). It should be noted that when the user clicks on the New Task button on the Main form or click the New Task item under the File menu, the same form will be launched to define a new task.
Figure 7 Edit Task
Figure 8 Edit Task's History tab Manage UserS form (Figure 9) is started by the user in the Manage menu at the top of the main form. It lists all users of the application. The Edit button will start the Edit User Form (Figure 10), which is used to make changes to the user. Note that the Manage menu item is only available when you log in as a TaskVision administrator.
Figure 9 Manage Uses Form
Figure 10 Edit User Form
Change Password Form (Figure 11) Started by the Change Password item in the File menu, used to change the current user's password, not required TaskVision administrator privileges. This feature is only available when the application is in online mode.
Figure 11 Change Password Form
Search form (Figure 12) is started in the current project in all tasks in the current item through the search menu or by clicking the Search button at the top of the Main Form.
Figure 12 Search Form
Customize Columns Form (Figure 13) Starting the Customize Columns item in the View menu, used to manipulate the tablestyle of the DataGrid, enabling the user to control the appearance and feel of the column layout.
Figure 13 Customize Column Form
Add Project Form (Figure 14) Starts the TaskVision administrator to add a new project to the remote database through the Add Project item in the Manage menu.
Figure 14 Add Project Form
Data layer component
The Datalayer class is an XML web service packaging and is a data manager for a client application.
For the application itself, there is a visible structure and design pattern related to data processing. Figure 15 shows the object owner relationship related to the Datalayer class and the form class.
When the MAIN form processing event (for example, open the Search form), the Datalet object is passed to a new form, providing access to the data that the main form has access to the access rights.
Figure 15 Object owner relationship in the TaskVision class hierarchy
Project information, task information, user information, and all other information retrieved from XML Web services are all all of the Datalayer class. These data can be accessed through a common member of the Datalet class, and various UI forms can be freely read and change these local data. Operations from XML web service updates or retrieve data can only be done by using public methods in the Datalet class. These public methods include: getProjects, GetTasks, and UpdateTasks.
Datalayer class is designed for a single-threaded environment, by calling these methods in the main thread, ensuring that information retrieved from the XML web service call can be correctly synchronized into local data, and the data binding UI control is not Refresh their graphics on the background thread.
Most of these function methods (such as the following code) have similar design: use the current authentication ticket to the data XML web service request data (or send data to the data XML web service), re-identify when necessary Verify and process anything that abnormally, merge returns, then return a DataletarayerResult to call the code to indicate whether the operation is successful or failed.
Public function getProjects () AS DatalayerResult'this is The Ds That Gets Returned from The WS
DIM DS AS DATASETPROJECTS
Try
'Request the ds and pass the ticket
DS = m_wsdata.getProjects (m_ticket)
'All TaskVision Web Services Return Nothing
'(or -1 for integer requests to indeicate an expeged ticket
IF ds is nothing then
'Get a new ticket and try the call again
Dim TicketResult As DatalayerResult = GetAuthorizationTicket ()
'IF The Ticket Failed Return ITS Error As Our OWN
IF TicketResult <> DatalayerResult.success Then
Return TicketResult
END IF
'Try the call again
DS = m_wsdata.getProjects (m_ticket)
'this next block beld never happen.
'it means the ws ticket evired to quickly
IF ds is nothing then
Return DatalayerResult.AuthenticationFailure
END IF
END IF
Catch exception
Return HandleException (EX)
END TRY
DSProjects.clear ()
DSProjects.Merge (DS)
Return DatalayerResult.success
END FUNCTION
Public Enum DatalayerResult
None = 0
SUCCESS = 1
ServiceFailure = 2
UnknownFail = 3
ConnectionFailure = 4
AuthenticationFail = 5
END ENUM
For the above ENUM, the explanation is as follows:
• DatalayerReSult.suCcess means that the public method has successfully achieved its purpose. • DatalayerReSult.ServiceFailure means an exception that occurs in the XML Web Services and XML Web services. • DatalayerResult.connectionFailure means a problem when connecting to an XML Web service (the problem is in the local Internet connection or the response of the XML web service). • The current username and password (setting by login form) are no longer valid, DatalayerRent.AuthenticationFailure will be used.
So far, all XML web service calls I have introduced are executed synchronously in the main thread of the application. In the application, there are two instances that implement an asynchronous XML web service call (ie, the call is executed in other threads other than the primary application thread). This allows the application to work properly when waiting for an asynchronous XML web service to complete its task in the background.
The first solution is to retrieve project history - a list of all changes to all tasks in the project. It's not difficult to imagine, this is easy to generate a lot of data and cause the download time too long. Therefore, it is best to retrieve these read-only data in the background to avoid hindrance to the application. Our MAIN form contains a timer that regularly updates history information. Calling the BegingeTProjectHistory method will return an IASYNCRESULT object. In the future timer timing event, the object is checked until the call is completed. After completing, call the endgetProjectHistory method to complete the merged data, similar to the synchronization method discussed earlier.
Public Function BegingeTProjectHistory (Byval ProjectID AS Integer) AS IASYNCRESULT
Try
'NOTE: THE IS An Assumption Here That Our Ticket IS Always Valid
'Because this Method Is Called Immediately After a Project Or Task Request.
'Start an async call for the
Return M_WSData.BegingeTProjectHistory (m_ticket, projectid, _
Nothing, New Object () {projectid})
Catch exception
LOGERROR.WRITE (EXMESSAGE & VBNEWLINE & EX.STACKTRACE)
Return Nothing
END TRY
END FUNCTION
Public Function EndgetProjectHistory (Byval AR AS IASYNCRESULT) AS DATALAYERRESULT
DIM DS AS DatasetProjectHistory
Try
'Grab the New DataSet
DS = m_wsdata.endgetProjectHistory (AR)
IF ds is nothing then
Return DatalayerResult.AuthenticationFailure
END IF
Catch exception
Return HandleException (EX)
END TRY
DSProjectHistory.ProjectHistory.clear ()
DSProjectHistory.Merge (DS)
Return DatalayerResult.success
END FUNCTION
The second place XML Web service call in TaskVision occurs when updating the MAIN form is used to populate the DataGrid task DataSet.
The main form contains another timer for updating the task DataSet. Here, the real difference is when to doynchronize. Unlike regular or forced XML web service calls, this timer will stop and reset frequently when the user modifies the data. If the application idle time is sufficient to make a timer, the asynchronous request is started for each timer after the timer, and the request is completed. If the request has been completed, the data will be merged into the local data. If the user has made any changes in asynchronous request (by interactively updating the data), the request will give up the request because it has expired.
Data conflict
There are many ways to handle data conflicts. Common cases is that the client attempts to update or delete data in the database, and these data has been changed from the client last time, or does not exist at all. Usually, this can be processed by triggering an error or simply using any content in the database in the database. The first solution will cause the client's work to be invalid. The risks brought about by the second solution are important data that has been ignored and deleted since the client instead of checking the database. TaskVision introduces this issue into a simple solution, mainly on the DataSet object of the ADO.NET library in the .NET Framework. (DataSet is an object that contains cache from the database.) In order to manage the Dataset of the TaskVision task, we chose the System.Data.sqlClclient class in the system.data.sqlclient namespace, which can be used to select, Update, insert, and delete features into an object. DataAdapter's Update method will check each DATAROW RowState in the DataSet to determine that DataRow is new, deleted, or changed, and then performs the appropriate stored procedure. Then, DataAdapter will ensure that the counts of the affected rows in the database are greater than zero. (For updates and delete operations, it is not more than zero means that the stored procedure cannot find target data.)
Be sure to note that the UPDATE stored procedure is only available in the DataSet that is able to verify the database in the DataSet that has been stored in the client (ie, there is no data conflict) will be updated. The stored procedure is shown below:
Create Procedure [UpdateTask]
(
@Taskid Int,
@ProjectID INT,
@Modifiedby Int,
@Ssignedto int,
@Tasksummary varchar (70),
@TaskDescription VARCHAR (500),
@PriorityID INT,
@Statusid Int,
@Progress Int,
@Indeled Bit,
@Datedue datetime,
@Datemodified datetime,
@Datecreated datetime,
@ORIGINAL_PROJECTID INT,
@ORIGINAL_MODIFIEDBY INT,
@ORIGINAL_ASSIGNEDTO INT,
@ORIGINAL_TASKSUMMARY VARCHAR (70),
@ORIGINAL_TASKDESCRIPTION VARCHAR (500),
@ORIGINAL_PRIORITYID INT,
@ORIGINAL_STATUSID INT,
@ORIGINAL_PROGRESS INT,
@ORIGINAL_ISDELETED BIT,
@ORIGINAL_DATEDUE DATETIME,
@ORIGINAL_DATEMODIFIED DATETIME,
@ORIGINAL_DATECREATED DATETIME
)
AS
SET NOCOUNT OFF;
- Note We are for useing convert to varchar on the date comparison so this sproc The PC APP APP Stores Offline Data Which Only Supports A 4 byte datetime.
Update Tasks
SET ProjectID = @ProjectID, ModifiedBy = @ModifiedBy, AssignedTo = @AssignedTo, TaskSummary = @TaskSummary, TaskDescription = @TaskDescription, PriorityID = @PriorityID, StatusID = @StatusID, Progress = @Progress, IsDeleted = @IsDeleted, DateDue = @DateDue , DateModified = @DateModifiedWHERE (TaskID = @TaskID) AND (ProjectID = @Original_ProjectID) AND (ModifiedBy = @Original_ModifiedBy) AND (AssignedTo = @Original_AssignedTo) AND (TaskSummary = @Original_TaskSummary) AND (TaskDescription = @Original_TaskDescription) AND (ProjectID = @Original_ProjectID) AND (StatusID = @Original_StatusID) AND (Progress = @Original_Progress) AND (IsDeleted = @Original_IsDeleted) AND (convert (varchar (20), DateDue) = convert (varchar (20), @Original_DateDue)) AND (convert (varchar (20), DateModified) = convert (varchar (20), @Original_DateModified)) AND (convert (varchar (20), DateCreated) = convert (varchar (20), @Original_DateCreated)) AND (PriorityID = @Original_PriorityID) ;
SELECT TaskID, ProjectID, ModifiedBy, AssignedTo, TaskSummary, TaskDescription, PriorityID, StatusID, Progress, IsDeleted, DateDue, DateModified, DateCreated FROM Tasks WHERE (TaskID = @TaskID)
Go
As shown above, the WHERE clause checked the data conflict very thoroughly. You may not be very clear that the "original" value used to check the data conflict in the WHERE clause comes from. By default, each DataRow in the DataSet tracks the original value returned from the database (when creating a DataSet) and the current value being updated.
In our SQLDataAdapter implementation, it will stop updating when Datarow returns zero-affected row and triggeting DBConcurrency Exception. We are handling data conflicts through this exception.
By referring to the code block below, we can see that we have entered a loop (later will be introduced), and if there is no exception, it will exit the loop. If you capture DBConcurrency Exception, we will first try to retrieve task records (via Taskid) and determine that database records still exist, or actually deleted. Delete is relatively easy, because database records are not physically deleted by our client applications, but can only be deleted by the system administrator. We recognize the result to delete so that our client will lose records and any pending changes. The initial loop is purely to ensure that DATAROW is deleted without the exiting method and returns the client, and the update process is restarted (the reason is that these code is performed in the XML Web service). If the record still exists, it means that it does not match the WHERE clause, we should allow the user to make a decision on the record. The current task is to return the pending changes and the new values in the database to the user. The user is no longer required to think that it is the original value that it is changing, because the database record has been updated using the new value provided by another client. After understanding this, the DATAROW can save two groups of values, we have made a copy of the change to change, applying the new value, and re-copy the pending changes back to DataRow. DataROW now contains the latest database items and users' pending changes. The original value is effectively copied. After completing these work, we returned DataSet to the TaskVision intelligent client application from the XML Web service so that it can display an error for the user (Figure 16). Our application displays two sets of values in a form (Collision form), allowing users to decide to continue their changes or cancel the operation, and retain the current value in the database.
Figure 16 Conflict solving the form
'We're doing a loop on the update function and breaking out on a successful update
'or Returning Prematurely from a data collision, this allows us to handle
'missing data without returbing to the client.
DO
Try
'Try to Update the DB
Datasks.Update (Dstasks, "Tasks")
EXIT DO 'this is The MOST Common Path
Catch dbex as dbconcurrencyexception
'We're Here Because Either The Row Was Changed by Someone Else
'or deleded by the dba, let's try get the updated row
DIM DS AS New DataSet ()
DIM CMD AS New Sqlcommand ("Getonetask", Dbconn)
cmd.commandtype = commandtype.storedProcedure
'get the updated row
DIM DA AS New SqldataAdapter (CMD)
Da.selectCommand.Parameters.add ("@ taskid", dbex.row.Item ("taskid")))
Da.fill (DS)
'if The Row Still EXISTS
If DS.TABLES (0) .rows.count> 0 thendim proposedrow as dataAROW = dbex.row.table.newrow ()
Dim Databaseerow as dataAROW = ds.tables (0) .rows (0)
'Copy the Attempted Changes
Proposedrow.ItemArray = dbex.row.ItemRay
'set the row with what's in the database and then re-coply
'The proposed Changes
With dbex.row
.Table.columns ("taskid"). Readonly = FALSE
.Itemarray = Databaseerow.ItemArray
.Acceptchanges ()
.Itemarray = proposedrow.ItemRay
.Table.columns ("taskid"). Readonly = true
End with
'NOTE: BECAUSE THIS ROW TRIGGERED An ADO.NET Exception, The Row
'WAS tagged with a Rowerror Property Which We'll Leave for the
'Client App
Return Dstasks
Else
'Row Was Deleted from Underneath User, Deletion Always Wins
dbex.row.delete ()
dbex.row.acceptchanges ()
END IF
END TRY
Loop
Offline-online data model
When in online mode, the TaskVision client application will manage all the data in the memory and rely on the XML Web service call to verify the data changes each time you change each time you change. However, TaskVision client applications also support offline mode.
Offline mode is manually called by the end user (by click on the offline toolbar button). Several events occur when doing this:
• First, save the DataSet as XML to the local hard drive. Be sure to pay attention, at this time, the data represents the last known status of the database. • Next, set a global Boolean object to false to prevent future applications from sending changes to XML web services (this is because the data is maintained local before the user attempts to recover online). • Finally, the GUI is updated to reflect its offline state.
When the application runs in offline mode, the change will be saved to DataSet, which will be marked as "change".
If the user exits the application in offline mode, the DataRow has been changed to the disk as a separate XML file (please remember, the three main DataSets of Projects, Tasks, and Lookuptables have been saved, as mentioned above).
If there is offline data (when the application is loaded again), it is assumed that the previous state is offline mode. These XML files will be used to populate the primary DataSet, and then the application will check the change file (please note why the AcceptChanges method is called for the XML file). By do not call the AcceptChanges method, these merged rows will continue to be marked as "Changed", thereby providing the same value as it is previously (before the user exits the application). Try
'Check for the offline files
IF file.exists (m_mydocumentspath & c_offlinetasksfile) Andalso_
File.exists (m_mydocumentspath & c_offlineprojectsfile) Andalso_
FILE.EXISTS (M_MyDocumentsPath & C_offlinelookuptables File) THEN
Try
'Engage Offline Mode
ChangeOnlinestatus (false)
'Try to Read the Offline Data
m_datalayer.dsprojects.readxml (M_MyDocumentsPath & _
c_offlineprojectsfile, XmlreadMode.Readschema)
m_datalayer.dstasks.readxml (M_MyDocumentsPath & _
c_offlinetasksfile, XmlreadMode.Readschema)
m_datalayer.dslookuptables.readxml (M_MydocumentsPath & _
c_offlinelookuptablesfile, XmlReadMode.Readschema)
'Workaround: Scheme Doesn't include AutoIncrement
m_dataletarayer.dstasks.tasks.columns ("taskid"). AutoIncrement = true
'We now Have The Exact Data When THE USER WENT OFFLINE
m_datalayer.dstasks.acceptchanges ()
'IF we have any changes the read Them in
IF file.exists (m_mydocumentspath & c_offlinetaskchangesfile) THEN
m_datalayer.dstasks.readxml (M_MyDocumentsPath & _
c_offlinetaskchangesfile, xmlreadmode.diffgram)
END IF
'Becauseur Project Could Have Come from The Registry Let's Verify IT
'OtherWise Choose The First Project ID
If m_datalayer.dsprojects.projects.rows.find (m_projectID) _
Is nothing then
m_ProjectID = _
Ctype (m_datalayer.dsprojects.projects.rows (0) ("ProjectID"), _
Integer
END IF
Catch exception
LOGERROR.WRITE (EXMESSAGE & VBNEWLINE & EX.STACKTRACE) 'We don't care what the error is, lets dump it and move on
DIM MBRESULT As DialogResult = _
Messagebox.show (M_ResourceManager.getstring (_
"Messagebox.show (there_was_an_error_reading_theoffline_files)")
& vbnewline & vbnewline & _
M_ResourceManager.getstring ("do_you_want_to_go_online"), _
"", MessageBoxButtons.yesno, MessageBoxicon.Error, _
MessageBoxDefaultButton.Button1, _
MessageBoxOptions.defaultdesktoponly)
Me.refresh ()
If mbresult = DialogResult.yes dam
'User choose to go online
ChangeOnlinestatus (True)
DeleteofflineFiles ()
m_datalayer.dsprojects.clear ()
m_datalayer.dstasks.clear ()
m_dataletayer.dslookuptables.clear ()
Else
Throw new exitException ()
END IF
END TRY
END IF
Thereafter, if the end user selects the recovery online status, the task Dataset will send the task Dataset to the data XML web service, and process each change normally (finally send the result to the client application). If the XML web service connection is successful, the application will reset the global Boolean object to True and will not prohibit future XML web service requests.
.NET UPDATER components
The .NET Application Updater component makes the .NET Framework smart client application automatically updates yourself, the method is to download this version after the updated version on the remote web server.
This process actually contains two parts: a STUB (or Helper) executable file and a component built into the smart client application itself.
Stub executable AppStart.exe is responsible for launching the appropriate version of the TaskVision application. (Note that the shortcut icon installed from the client MSI actually points to the appstart.exe file instead of the taskvision.exe file.)
The STUB executable work is to read a local configuration file appStart.config to determine the latest version of the smart client application. Then, start a new process to run TaskVision.exe in the directory named in the configuration file. After starting TaskVision.exe in a new process, it does not perform any operation, just waiting to close it.
When the TaskVision intelligent client application is running, the components mentioned earlier will work in the background, check if there is an update to the application, download the update and redirect the STUB executable, so that it starts the updated version Not the original version.
This component is to complete this task by polling an XML file UpdateVersion.xml on the server.
If the version number listed in the file is larger than the version of the local TaskVision application, the component will find the new version of the file according to the path in the updateversion.xml file, create a new local directory, then download the new version to this directory. . After the download is complete, the component will edit the local configuration file, redirect the Stub executable to the new local directory of the updated version (so, when the next run the STUB executable, the latest version will start). We configure this component to prompt users after downloading new versions, allowing users to restart applications and load updates, or continue the application session, so that the updated version can be seen when the executable file STUB is started next time.
DataGrid column style
The DataGrid class in the Windows Form Library is a ready-made control that displays information similar to the basic spreadsheet (shown in Figure 17 as the most basic form).
Figure 17 DataGrid class
By applying the DataGrid table style, developers can customize the appearance and functionality of each column. We created three custom columns and applied them to our TableStyle to handle our UI demand.
The first goal is to rewrite the functionality of the DataGridTextBoxColumn class, which is included in the .NET Framework for processing a standard text column. By default, DataGridTextBoxColumn will highlight the text in the cell when the user clicks the cell, and enables the user to copy the text.
To avoid this, we have to rewrite a base class EDIT method to rewrite the DataGridTextBoxColumn class (first from the Framework DataGridTextBoxColumn class or inherited), so that it does not perform any action, thereby preventing the cell to get focus, and make the text unable Copy.
Protected Overloads Overrides Sub Edit (Byval Source AS _
System.Windows.Forms.currencymanager, ByVal Rownum as integer, _
Byval Bounds as system.drawing.rectangle, byval isreadonly as _
Boolean, Byval InstantText As String, ByVal Cellisvisible As Boolean
'Do nothing
End Sub
Next is the DataGridPriorityColumn class, which displays priority images in the DataGrid. DataGridPriorityColumn category The value provided is the file name of the .GIF image to display, and the file name should be located in the application's image directory.
Protected Overloads Overrides Sub Paint (Byvalg as system.drawing.graphics, _
Byval Bounds as system.drawing.Rectangle, Byval Source AS_
System.Windows.Forms.currencymanager, ByVal Rownum as integer, _
Byval backbrush as system.drawing.brush, ByVal Forebrush As _
System.drawing.brush, Byval Aligntoright As Boolean
DIM BVAL As Object = GetColumnValueatrow (Source, Rownum)
DIM IMAGETODRAW As Image'We're Caching The Image in a Hashtable
IF m_htimages.containskey (bval) THEN
ImagetoDraw = ctype (m_htimages (bval), system.drawing.image
Else
'get the image from disk and cache it
Try
Image.fromfile (C_PriorityImagesPath & _
CType (BVAL, String) & ".gif")
M_HTIMAGES.ADD (BVAL, IMAGETODRAW)
Catch
'Display Error MSG
Return
END TRY
END IF
'ness the current row is this row, Draw the Selection Back Color
If Me.DataGridtablesty.DataGrid.currentrowindex = Rownum Then
g.FillRectangle (New Solidbrush (Me.DataGridtablestyle.selectionbackColor), _
Bounds)
Else
g.FillRectangle (Backbrush, Bounds)
END IF
'Now draw the image
G.DrawImage (imagetodraw, new point (bounds.x, bounds.y))
End Sub
Finally, the DataGridProgressBarcolumn class displays the progress bar indicating the progress of each task (mapped to the Progress column in the DataTable). To do this, we use the Graphics object in the PAINT method to draw a color box according to the provided value and the string of the value (eg, "75%)).
Protected Overloads Overrides Sub Paint (Byvalg as system.drawing.graphics, _
Byval Bounds as system.drawing.Rectangle, Byval Source AS_
System.Windows.Forms.currencymanager, ByVal Rownum as integer, _
Byval backbrush as system.drawing.brush, ByVal Forebrush As _
System.drawing.brush, Byval Aligntoright As Boolean
Dim ProgressVal as integer = ctype (getColumnValueatrow (Source, Rownum), Integer
Dim percentage as salesle = ctype ((ProgressVal / 100), Single)
'ness the current row is this row, Draw the Selection Back Color
If Me.DataGridtablesty.DataGrid.currentrowindex = Rownum Then
g.FillRectangle (New Solidbrush (Me.DataGridtablestyle.selectionbackColor), _
Bounds)
Else
g.FillRectangle (Backbrush, Bounds)
END IF
IF Percentage> 0.0 Then
'Draw the Progress Bar and The Text
G.FillRectangle (New Solidbrush (Color.Fromargb (163, 189, 242)), _
Bounds.x 2, Bounds.y 2, Convert.Toint32 ((Percentage * _ _
Bounds.width - 4)), Bounds.Height - 4)
g.drawstring (ProgressVal.toString () & "%", _
Me.DataGridtablestyle.DataGrid.font, forebrush, bouss.x 6, _
Bounds.y 2)
Else
'DRAW THE TEXT
If Me.DataGridtablesty.DataGrid.currentrowindex = Rownum Then
g.drawstring (ProgressVal.toString () & "%", _
Me.DataGridtablestyle.DataGrid.font, New_
Solidbrush (me.datagridtablestyle.selectionforeColor), _
Bounds.x 6, bounds.y 2)
Else
g.drawstring (ProgressVal.toString () & "%", _
Me.DataGridtablesTyle.DataGrid.font, forebrush, _
Bounds.x 6, bounds.y 2)
END IF
END IF
End Sub
Print and print preview
In the .NET Framework, create a document is very simple. We need to be familiar with three classes: PrintDialog class, PrintPreviewDialog class, and PrintDocument classes.
The PrintDialog class provides a print prompt and can be used to access the printer settings, print the document to the screen, which is sent to the user before you send any data to the printer, and the PrintDocument class contains the actual printout and enable the printing process.
Show PrintDialog is very simple: set the document property to reference our PrintDocument and call the showdialog method. The PrintDialog class does not automatically print output; instead, we need to check DialogResult and call the PrintDocument's Print method. Display printpreviewDialog is equally simple, different from DialogResult without check. If the user wants to print from the dialog, the dialog will call the Print method.
Dim PDialogResult As DialogResult = PrintDialog1.showdialog ()
If PDIALOGRESULT = DialogResult.ok kil1nTdocument1.print ()
Now, we have already understood how to print, let's take a look at how TaskVision creates the actual output to be printed. Typically, an instance of the PrintDocument class can be created, setting the properties that describe how to print, and call the print method to start the printing process. The PrintPage event can then be processed, specify the output to be printed (by using the Graphics object included in the PrintPageEventArgs). TaskVision will handle the PrintPage event and pass the Graphics object to the class that is called DataGridprinter, to demonstrate how to print the information displayed in the DataGrid. The DataGridprinter class decomposes the task of drawing the output to two parts, the first is the header (column name), then all rows that contain data.
In order to draw header (see the code below), we created a box and using a Graphics object to draw this box with a gray background. Then, cycles through the DataGrid column to find the currently displayed columns (Width> 0). For each display column, we created a rectangle to display the plotted location, then use the Graphics object to actually draw the column name in the box. (Please note that these rectangles will not be drawn, they just determine the borders of the draw.)
Private Sub DrawPageHeader (ByVal G AS Graphics)
'Create The Header Rectangle
Dim Headerbounds as New Rectanglef (C_LEFTMARGIN, C_TOPMARGIN, _
M_PageWidthminusMargins, M_DataGrid.Headerfont.sizeInpoints _
c_verticalcellleeway
'Draw the Header Rectangle
g.FillRectangle (New Solidbrush (M_DataGrid.HeaderBackColor), HeaderBounds)
DIM XPSITION As Single = C_LEFTMARGIN 12 ' 12 for Some Padding
'Use this format when Drawing Later
Dim CellFormat As new stringformat ()
CellFormat.trimming = StringTrimming.Word
Cellformat.formatflags = stringformatflags.nowrap OR_
StringFormatflags.LineLimit
'Find The Column Names from the TableStyle
DIM CS AS DataGridColumnStyle
For each cs in m_datagrid.tablestyles (0) .gridcolumnstyles
IF cs.width> 0 THEN
'Temp Width to Draw this Column
DIM columnwidth as integer = cs.width
'Scale the Summary Column Width
'NOTE: JUST A Quick Way To Fit The Text To The Page Width
'this is not the best woman to do this but it it is handles the MOST
'CommON UI Path for this Demo APP
If cs.mappingname = "tasksummary" and m_istoowide then
ColumnWidth - = m_adjcolumnby
Elseif cs.mappingname = "tasksummary" THEN
ColumnWidth = m_adjcolumnby
END IF
'Create a Layout Rectangle to Draw Within.
Dim Cellbounds As New Rectanglef (XPosition, C_topMargin, ColumnWidth, _
M_DataGrid.Headerfont.sizeInpoints C_Verticalcellleeway)
'DRAW the column name
g.drawstring (cs.Headertext, M_DataGrid.Headerfont, New Solidbrush (M_DataGrid.HeaderForeColor), Cellbounds, CellFormat
'Adjust the next x pos
XPosition = ColumnWidth
END IF
NEXT
End Sub
Drawing lines are similar to drawing headers, different situations in the latter first cycle DataTable, and for each DATAROW, all columns are cycled to determine if the value in the cell should be displayed (Width> 0). Then, check the column name to determine whether the text value should be printed directly or print the image corresponding to the value.
Expander Control and Expander list control
The Expander class is an actual control that contains the Priority and Overall Progress charts and the Task History panel. The ExpanderList class is created as a container that is an Expander object. Type check is performed when any control is added to the ExpanderList control container. If the added control type is Expander, the ExpanderList object will make a subscription to the ControlColLapsed and ControlexPanded events of the added Expander object. The event handler will adjust the location attribute of all Expander controls in programming to adjust their up and down positions as needed. In addition, the ExpanderList control includes support for automatic and positioning of the drag and drop EXPANDER control.
Public Sub ControlexPanded (Byval X As Xpander)
DIM CTL AS Control
DIM enumerator as idictionaryenumerator = m_controllist.getenumerator ()
While enumerator.movenext
CTL = ctype (enumerator.Value, Control)
IF CTL.TOP> X.top Then
CTL.TOP = x.expandedheight - x.captionheight
END IF
End while
End Sub
Although the ExpanderList class and the Expander class are included as the compiled library, what we have to demonstrate how to use GDI to draw the Blue tilt top of the Expander control. Note: LinearGradientBrush accepts the start color (color.White) and end color (CaptionColor represents Color.Fromargb (198, 210, 248)). Protected Overrides Sub onpaint (Byval E As PainteventArgs)
DIM RC As New Rectangle (0, 0, Me.Width, CaptionHeight)
Dim B As New LineargradientBrush (RC, Color.White, Captioncolor, _
LineargradientMode.horzontal)
'Now Draw The Caption Area At the top
E.Graphics.FillRectangle (B, RC)
Custom Chart control
The CustomChartControl class is included as the compiled library, which is implemented by setting the DataTable and DataMember (column name), which uses these objects that will draw a pie chart sector that represents the respective values of the column. Decompose section. In our example, the chart is drawn using GDI to show priority exploded views of the current item.
ChartPriority.DataTable = m_datalayer.dstasks.tasks
ChartPriority.DataMember = "priorityText"
Progress Chart control
Similar to the Custom Chart control, the Progress Chart control is also included as a compiled library, which is used to display a rectangular chart that uses GDI drawn to represent the average progress of the current project.
ChartProgress.DataTable = m_dataletarayer.dstasks.tasks
ChartProgress.DataMember = "Progress"
History Panel control
The TaskHistoryPanel class is a simple control that iterates the DataView of the Task History Row and adds a clickable LinkLabel to the UI for each row that matches the current taskid.
When adding each LinkLabel to the control, the inherited control.tag will be used to store an integer representing each record, and the LinkLabel handled in the booking code is scheduled.
'Create a LinkLabel
Dim NewLinkLabel As New Linklabel ()
NewlinkLabel.Text = dateprefix & ctype (row.Item (m_displaymember), string)
NewLinkLabel.Location = new system.drawing.point (c_linklabelstartingx, _
(c_linklabelstartingy (Numlinks * c_linklabelheight)))))))))
Newlinklabel.size = new system.drawing.size ((me.width - _ _ _ _ _ _ _ _ _ _
(C_LinkLabelstartingX * 2)), c_linklabelheight
NewlinkLabel.name = "linklabel" & numlinksnewlinklabel.tag = (Numlinks)
Newlinklabel.tabstop = true
NewlinkLabel.Flatstyle = flatstyle.system
'add the link label
Me.Controls.add (NewLinkLabel)
AddHandler NewLinkLabel.Click, Addressof LinkLabel_Click
'increment the number of matching links
Numlinks = 1
In our clicking event handler, we trigger a custom event to the Main form, including some values to help determine which history of which history is actually clicked, and eventually displays the history to the user.
Private Sub LinkLabel_Click (Byval e AS _BYVAL E AS _
System.EventArgs)
'Re-raise the Click Event with Our Own Parameters
DIM LINK AS LINKLABEL = CTYPE (Sender, LinkLabel)
RaiseEvent HistoryLinkClicked (m_selectedtaskid, _
CType (Link.tag, Integer)
End Sub
DataProtecion class
Since the client application stores password information in the registry, we will need a way to prevent potential attackers from accessing the passwords of other users. There are many ways to accomplish this task, but we have chosen using the Windows 2000 / XP Data Protection API (DPAPI) function CryptProtectData and CryptunProtectData, so we can protect secret information without direct management secrets.
The DataProtection class in our project is actually just a package to access the DPAPI function. The following is provided with a method of setting an registry key and retrieve uncrypted text.
For more information on DPAPI, see Windows Data Protection on MSDN.
'set the registry key value with the encrypted text
Dim Regkey As RegistryKey = registry.currentuser.createSubkey (c_registryKey)
Regkey.setValue ("Password", _
DataProtection.ProtectData (TxtPassword.Text, "TaskVisionPassword")))
'set the string value to decrypted registry key text
Dim Password As string = string.empty
Password = dataprotection.unprotectdata (CType (RegKey.GetValue ("password"), _
String))
Supported features
• Localization Support - Localization is a process of translating the application's resources to the localized version of each country culture (ie, language and calendar differences) that will support. .NET Framework mainly uses the concept of resource manager, resource file, and accessory set to provide an application-localized architecture. Visual Studio .Net simplifies the process of creating these resource files and assemblies by setting a number of properties in the Windows Form Designer. TaskVision version 1.1 achieves German localization and contains an accessory set, each of which can extract resources and attribute values from the program. In short, there is a default resource file in each form, which contains properties and images for default culture. In addition, each form has a "
TaskVision demonstrates dynamic properties: it stores the URL of the UpDater Component Update URL and authentication and data web services to the taskvision.exe.config file. • Windows XP Topics - Windows XP contains a new version of the Shell Common Controls library (ComctL32.dll version 6.0). The library contains new improved color controls, such as buttons, and tabs (see the image below). To use a new version of the public control, the application must explicitly request new versions by providing an application list. The application list can be provided to Windows XP as a separate file or resource attached to the executable. As a separate file, the list file must be located in the same directory as the executable, and must have the same name as the executable, followed by ".manifest". For example, a manifest file of TaskVision.exe should be TaskVision.exe.manifest. In addition to providing a list, many controls require setting the FlatStyle property to System to use public controls. Figure 18 Application without a list file
Figure 19 Application with inventory file
Back to top
Learning experience
TaskVision is an example solution to demonstrate numerous powerful features of smart client applications generated using .NET Framework.
Like many projects, in the development phase, Taskvision also has experienced certain development changes. Below, we list what you should be implemented in different ways if you start developing now.
Data layer component
As you are constantly familiar with the client application, you may find that our data architecture will result in some slight negative UI effects. The Windows Form Control supports data binding, providing a way to automatically populate (and maintained) itself using the value from the data source. Since we use an XML web service, you can't really send objects, modify it, and then automatically update controls all references it. In contrast, our two options are: After each XML Web service call, the updated DataSet merges with the current DataSet, or updates the data binding of the relevant controls after each XML web service call. We have chosen the merger updated and existing DataSet because this can keep data binding. For example, you may notice that some of the slight side effects generated - the current record in the DataGrid will lose the focus after the update. If there is a chance to start, we may choose to update the data binding to update the data each time the XML Web service calls to avoid these side effects.
XML Web Service Security
Very worth mentioning is the Web Services Enhancements (WSE) 1.0 for Microsoft .NET, which is a toolkit, providing Visual Studio .NET and .NET Framework developers with some of the newly proposed XML Web service specification (including WS- Security, WS-Routing, WS-Attachments, and DIMEs. However, WSE has not yet been facing during development. So we failed to demonstrate some of the components support for WS-Security to provide more flexibility and control to developers in protecting XML Web services. We plan to demonstrate this feature in the second important example application that will be issued later this year. You can learn more about WSE in the WSE home page on MSDN.
Back to top
Get more information