This post will show my attempt at return to using my message
framework to support the delivery of messages between components in conjunction
with my previous post on the use of Windows panels. This is a continuation of the ExpenseIt non WPF (Windows Presentation
Foundation) application example of Microsoft that has been part of a
series of posts. However, this one is
concerned with pairing Windows Forms with the use of the framework to deliver
messages. My previous attempt failed
when it seemed to hang after sending a message or, at least, after the response
was returned. (See the previous posts
“C# Displays Follow On for ExpenseIt Showing Version of Navigation Bar”,
"C# Displays Follow On for ExpenseIt Showing Use of Panels" and those
before these two.)
My desire was to be able to use the C# message framework
along with Windows Forms to allow framework components, such as that to access
a database, to be independent of the Windows Form class. This would allow such a component to be
moved to an entirely different application as well as hiding the workings of
the such components from the Windows form class.
I selected ExpenseIt as the application since that is the
one that I have been working with recently.
And the database as the interface to be encapsulated in a framework
message component since that is the obvious choice and components are needed in
order to test whether the framework could be used in conjunction with Windows
forms.
This attempt got me involved with my previous C# Display
application of a number of years ago (2011) in which it used an A661 like
protocol to communicate with framework applications that were written in
Ada. This application could display a
number of Windows forms to display A661 widgets (that are just Windows
controls) to act upon A661 protocol commands that display different forms
depending upon their A661 layer. Unlike
ExpenseIt, these forms were built "on-the-fly" via the program at
runtime rather than via the form [Design] window by dragging controls to the
window at build-time.
But the point involved is that there was support to send and
receive messages – just that the messages were not communicated via the current
C# based framework. Therefore, I began
analyzing the old Display application to find out how I had implemented it that
allowed the display of Windows forms while there existed other non-Forms
threads. Among points that seemed
pertinent was its use of a loop at the end of the main entry point of
while(true) // pause this thread to run
others
{
// Sleep for 3 seconds.
if
(System.Threading.Thread.CurrentThread.Name == "MainThread")
{
System.Threading.Thread.Sleep(3000);
System.TimeSpan delay =
System.TimeSpan.Zero;
System.Threading.Thread.Sleep(delay); //.Infinite
}
}
where the launch thread was renamed to MainThread. In addition, the runtime created Windows
forms were run via
try
{
if
(openMethod == 0) Application.Run(Display.userForm[formIndex]);
else if (openMethod == 1) Display.userForm[formIndex].ShowDialog();
else if (openMethod == 2) Display.userForm[formIndex].Show();
//
Note:
// There can be no code that
gets executed after Run or
// ShowDialog or Show since it
would run after the form is
// closed in the previous
instance of a user form.
}
The relevant line here is that which Runs the particular
userForm.
Previous
to and in conjunction with this investigation into what I had previously
programmed (which incidentally amazed me concerning all that I had developed at
the time and had since forgotten about) I had begun creating the C# classes to
make use of the code of the previous application in which panels were created
for the form via the Form1.cs[Design] window.
As with the Display application of the previous EP (exploratory
project), I created a dummy Form1.cs that did nothing as well as using
ExpenseItForm.cs (renamed from Form1.cs of the previous post) to be displayed
such as was done with the Display application userForm array forms. But, of course, with no need to create the
forms at runtime. To complete the
description of the controls, I also renamed Form1.Designer.cs along with its
namespace and class of previous panel application to ExpenseItForm.Designer.cs
since it contains the InitializeComponent
method needed by the form.
After determining how I had displayed the user forms of the
old Display application and kept the Windows events (button clicks and the
like) active, I added the DoEvents loop to the end of the C# Program class
that's entered first (where this class is a clone of the standard framework
application class) and added
ExpenseItForm expenseItForm;
expenseItForm = new
ExpenseItForm();
Application.Run(expenseItForm);
to the Install of the application unique classes that is
invoked from Program.
I immediately tried running the application and the ExpenseItForm displayed and the button clicks of the Menu
panel worked (that is, displayed the requested panel) and the arrow buttons of
the Navigation panel also worked.
Somewhat to my surprise that it should be so easy.
Of course, I had yet to implement the use of the framework
to send messages to the Employee and Expenses databases as well as obtaining
the responses by the form. When I
attempted that I found that the framework components to send an ExpenseItForm
request message to the database was never received because their threads had
never been created. Since that is done
at the end of the Program class, I moved the three lines to Run the
ExpenseItForm to inside the DoEvents loop and found that the form never returns
for the Run command. Recognizing that
the loop that contains Application.DoEvents was now superfluous, I removed
it. Therefore, the Windows events were
being handled without the use of the invocation of DoEvents and it hadn't been
needed in the 2011 Display application.
The application seems to run ok with the framework sending
messages to a Database component from an interfacing component that waits for
the response and then returns it to the form.
So my entire problem when I first tried to use a form along with the
framework may just have been how I displayed the form. That is, the lack of the use of
Application.Run. Or, perhaps, how I
waited for a response in the Windows form rather than in the ComExpenseIt
framework component.
This made me wonder whether a rename of ExpenseItForm back
to Form1 could be made to work with Application.DoEvents so I created a Try2
folder, changed the ExpenseItForm references back to Form1 references along
with the copied and renamed ExpenseItForm.Designer.cs, and created such a
Windows Form application (replacing the created Form1.cs file with the copied
one). Then I had its Program.cs do the
framework startup along with the Application Run of new Form1() while using the
ComExpenseIt component to interface to ComDatabase.
The same hang-up occurred as it had a couple of posts ago
(C# Displays Follow On for ExpenseIt Using Access Database) after sending the
first request and getting the response back.
The next key click (the Left Arrow button) wasn't treated. Therefore, it made no difference that I was
doing the wait for response in ComExpenseIt rather than in the Form1 event
handler as I had tried before.
I then found that Application.DoEvents() wasn't going to do
anything (and didn't) since it was to output events not allow them to be
received.
Therefore, I wondered if the use of a dummy Form2 could take
the place of the dummy Form1. So I
added a Form2 via Project|Add Windows Form...
That was also a no go. Didn't
help at all.
So I thought that the application being described in this
post worked because it was created as a Windows Form Application to create the
dummy Form1. I renamed Form1.cs of the
Panels application of the previous post to ExpenseItForm.cs in the folder being
used to produce the application and used File|Open File to open this renamed
file and included it into the project while correcting the class name along
with the constructor etc. As well as
doing the same thing with Form1.Designer.cs of the previous application as
renamed to ExpenseItForm.Designer.cs.
So this ExpenseItForm class wasn't added via Project|Add
Windows Form… but rather just by opening the file and adding it to the
application. This was, in effect, what
occurred in the 2011 Display application.
That is, in the 2011 Display application the forms were created at
runtime so only the original empty Form1 class would have been created via
Project|New Project…|Windows Forms Application.
This seems to be what allows the Windows events to be
received in spite of the Project|New Project…|Windows
Forms Application created form failing to treat button, etc events since it
had no event handlers. That is, when
the Windows Form Application created form contains the interface to the
framework to send the request to the component (the Database component in this
situation) and wait for the response (either in the event handler of the
previous trial or the ComExpenseIt component), the interface to Windows loses
its ability to respond to Windows events.
But if the code of the same form is added to the project via File|Open
File… and then "File|Move name into" (where name is the opened file)
Windows must treat it differently.
That is, it must be that Windows continues to supply events
to the application in spite of the non-Windows threads of the framework
components when the use of those threads are not from a Windows Forms
Application created form. Plus it must
not distinguish where the event handler is located. Whereas, for some reason, this is not the case for a Windows Form
added to the application via Windows Forms Application of Project|New Project…
and, most likely, the add of additional forms via Project|Add Windows Form…
I modified the previous attempt to include the message
framework with a Windows form by invoking public methods of a special
ComExpenseIt component to have it send the request message to the ComDatabase
component and receive the response. In
the previous attempt the Windows form published the request from its event handler
and waited for the response to be stored directly to a public string. In this new application, ComExpenseIt waits
for the response from ComDatabase in the normal way and then returns the
response to ExpenseItForm. This means
that the ExpenseItForm thread isn't the one waiting
for the response as in my first attempt to use a Windows form along with the
framework. The first attempt attempted
to Sleep in the Windows form until a response was available so it would have
been sleeping the form's thread. Now
each ComExpenseIt interface method sleeps until the
response is available in a framework thread.
See the ComExpenseIt code.
Form1
The Form1 class of Form1.cs is a dummy windows form that
does nothing except for introducing the use of Windows.Forms and declaring the
project as a Windows Forms Application.
It was used because the C# Display application of 2011 used it and had
worked while having its own threads to send and receive messages to and from
other applications.
using
System;
using
System.Collections.Generic;
using
System.ComponentModel;
using
System.Data;
using
System.Drawing;
using
System.Linq;
using
System.Text;
using
System.Windows.Forms;
namespace
Apps
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
}
}
Program.cs
The
naming of the CurrentThread was done because it was done in the Exploratory
Project of my EP folder for the Display application back in 2011. In that project there was a forever loop at
the end that invoked Application.DoEvents(); every 3 seconds if the
CurrentThread was MainThread and I thought it must have been useful. However, while doing this project, I found
that there was no return from Application.Run so I removed it as unnecessary.
Therefore, the Program class launches the
application the same as usual for the framework and then, after the framework
has had all its support classes and component classes instantiated and the
framework thread pool created, the actual Windows form is run in the launch
thread. That is, the form that is added
to the project in the abnormal way. The
now dummy Form1 isn't run at all.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
namespace Apps
{
static
class Program
{
///
<summary>
///
The main entry point for the application.
///
</summary>
[STAThread]
static void Main()
{
if (System.Threading.Thread.CurrentThread.Name == null)
{
System.Threading.Thread.CurrentThread.Name =
"MainThread";
}
currentProcess = Process.GetCurrentProcess();
Component.ApplicationId appId; // the Program.cs version
appId.name = "App 1";
// of the Program class
appId.id = 1;
// ids the first application
App.Launch(appId);
// Install the components of this application.
Try1.Install();
// Now, after all the components have been installed, create the
// threads for the components to run in.
// o In addition, the
TimingScheduler thread will be created.
// o There will be a return from
Create and then the created threads
//
will begin to execute. And the
main procedure application
// thread will no longer be
executed -- instead, the created
// threads will be the only
threads running.
Threads.Create();
// Except, that with this framework plus Windows Forms application,
// this main procedure application thread will continue to be
// executed in the ExpenseItForm.
ExpenseItForm expenseItForm;
expenseItForm = new ExpenseItForm();
Application.Run(expenseItForm);
} //
end Main
static private Process currentProcess;
} // end
class Program
} // end namespace
Application Specific Components
There are two application specific components that are
installed from Try1.Install() that follows the install of the framework via
App.Launch(appId) – appId identifies the particular application. These are ComDatabase and ComExpenseIt.
ComDatabase is the component that accesses the two Access
databases (Employees.mdb and Expenses.mdb) and is a normal framework
component. That is, it can reside in
any application and communicates via framework topic messages. It need not be in the same application as
that of the Windows form.
ComExpenseIt is an unusual component since it receives its
transactions from the Windows form rather than from a framework message. This violates the framework constraint that
components do not interface with other components. Thus ComExpenseIt and ExpenseItForm must be treated as a unit and
makes, in effect, the Windows form a part of the component.
This is accomplished by a collection of public methods to be
invoked by the event handlers of the form.
Each of these builds a message topic to be treated by the ComDatabase
component and publishes the message that will then be delivered to ComDatabase.
They then wait for the response from ComDatabase that is returned via a
typical framework callback. To allow
the method invoked by the form to return the response, that method waits for an
indication that its response was received.
For instance, to remove an employee from the Employees
database, the public RemoveEmployee method is invoked for the employee. It then builds a framework request
containing the Remove keyword and the employee's first and last names,
Publishes the request, and awaits the response. The RemoveEmployee method then has nothing left to do but to
return the received response.
static public string RemoveEmployee(string firstName, string lastName)
{
responseReceived = false;
request = "Remove";
string message = request + ":" + firstName + ":" +
lastName;
Delivery.Publish(requestTopic, componentKey, message);
// Wait until response received
while (true)
{
if ((request == "Remove") && (!responseReceived))
{
Thread.Sleep(250); // millisecs
}
else
{
break; // exit loop
}
}
// Return the response
responseReceived = false;
return receivedResponse;
} //
end RemoveEmployee
The common response topic callback moves the data of the
response to the static receivedResponse string and sets the static
responseReceived boolean to true to allow the particular form invoked method to
detect that the response has been received and act upon it.
For ComDatabase and ComExpenseIt to cooperate there, of
course, has to be agreement between them as to the format of the messages.
ComDatabase
ComDatabase is larger than the non-component
EmployeeDatabase and ExpensesDatabase classes that were invoked directly from
the Windows form of the previous post.
Mostly because it treats both the databases but somewhat due the need to
parse the received request topic messages to determine what request to process.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading;
namespace Apps
{
static
class ComDatabase
{
//
This class implements a component that interfaces with a database of
//
employees and another of their expenses.
//
// It
is an on demand component running whenever a message topic is
//
received.
//
//
This class can be moved to another application if need be to access
//
the databases.
static string myEmployeesConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data
Source=C:\\Source\\XP2\\Employees.mdb;" +
"Persist Security Info=True;";
//+
//
"Jet OLEDB:Database Password=myPassword;";
static OleDbConnection myEmployeesConnection =
new
OleDbConnection(myEmployeesConnectionString);
static string myExpensesConnectionString =
@"Provider=Microsoft.Jet.OLEDB.4.0;" +
"Data
Source=C:\\Source\\XP2\\Expenses.mdb;" +
"Persist Security Info=True;";
static OleDbConnection myExpensesConnection =
new
OleDbConnection(myExpensesConnectionString);
static private Component.ParticipantKey componentKey;
static private DisburseForward.DisburseTableType forward =
new
DisburseForward.DisburseTableType();
static DisburseForward queue;
static private Topic.TopicIdType requestTopic;
static private Topic.TopicIdType responseTopic;
static public void Install()
{
Console.WriteLine("in Try1 ComDatabase
Install");
// Build the Disburse forward list
forward.count = 1;
forward.list[0].topic.topic = Topic.Id.DATABASE;
forward.list[0].topic.ext = Topic.Extender.REQUEST;
forward.list[0].forward = DatabaseRequestCallback;
// Instantiate the queue with the forward list
queue = new DisburseForward("ComDatabase", forward);
// Register this component
Component.RegisterResult result;
result = Component.Register
("ComDatabase", Threads.ComponentThreadPriority.NORMAL,
MainEntry, queue);
componentKey = result.key;
if (result.status == Component.ComponentStatus.VALID)
{
Library.AddStatus status;
requestTopic.topic = Topic.Id.DATABASE; // EMPLOYEEADD;
requestTopic.ext = Topic.Extender.REQUEST;
// Register to consume EMPLOYEE ADD topic via a callback
status = Library.RegisterTopic
(requestTopic, result.key,
Delivery.Distribution.CONSUMER, null);
Console.WriteLine("ComDatabase EMPLOYEE ADD Register {0}",
status);
responseTopic.topic = Topic.Id.DATABASE; // EMPLOYEEADD;
responseTopic.ext = Topic.Extender.RESPONSE;
status = Library.RegisterTopic
(responseTopic, result.key,
Delivery.Distribution.PRODUCER, null);
}
} //
end Install
//
Periodic entry point
static void MainEntry()
{
while (true) // loop forever
{
// Wait for event.
string xxx = queue.queueName;
queue.EventWait();
}
// end forever loop
} //
end MainEntry
static Component.ParticipantKey from; // component that sent request
// Callback
to treat the DATABASE topic
static void DatabaseRequestCallback(Delivery.MessageType message)
{
Console.WriteLine("ComDatabase DatabaseRequestCallback Read message
{0} {1}",
message.header.id.topic,
message.data);
from = message.header.from;
int i = message.data.IndexOf(':');
int l = 0;
if (i > 0)
{
string command = message.data.Substring(0, i);
l = message.data.Length - i - 1;
string rest = message.data.Substring(i + 1, l);
if (command == "Add")
{
TreatAddEmployee(rest);
}
else if (command ==
"Modify")
{
TreatModifyEmployee(rest);
}
else if (command == "Remove")
{
TreatRemoveEmployee(rest+": "); // with blank department
}
else if (command == "List")
{
TreatListOfEmployees();
}
else if (command == "AddExpense")
{
TreatAddExpense(rest);
}
else if (command == "DisplayExpenses")
{
TreatDisplayExpenses(rest + ": "); // with blank department
}
else
{
Console.WriteLine("Error: Invalid Request");
}
}
} //
end DatabaseRequestCallback
static string firstName = "";
static string lastName = "";
static string department = "";
static string description = "";
static decimal amount = 0m;
static DateTime date = DateTime.Now;
static void GetNamesAndDepartment(string data)
{
firstName = "";
lastName = "";
department = "";
string rest = "";
int i = data.IndexOf(':');
int l = 0;
if (i > 0)
{
// Is substring prior to delimiter the message id?
firstName = data.Substring(0, i);
l = data.Length - i - 1;
rest = data.Substring(i + 1, l);
i = rest.IndexOf(':');
if (i > 0)
{
lastName = rest.Substring(0, i);
}
else
{ // error for no last name
}
l = rest.Length - i - 1;
if (l > 0)
{
department = rest.Substring(i + 1, l);
}
else
{ // error for no department
}
}
else
{ // error for no first name
}
// Note: There should be no errors for lack of names since this should
// be checked before a
message is sent to the ComDatabase.
} //
end GetNamesAndDepartment
static void GetNamesAndExpenseData(string data)
{
firstName = "";
lastName = "";
description = "";
amount = 0m;
date = DateTime.Now;
string rest = "";
int i = data.IndexOf(':');
int l = 0;
if (i > 0)
{
// Is substring prior to delimiter the message id?
firstName = data.Substring(0, i);
l = data.Length - i - 1;
rest = data.Substring(i + 1, l);
i = rest.IndexOf(':');
if (i > 0)
{
lastName = rest.Substring(0, i);
l = rest.Length - i - 1;
rest = rest.Substring(i + 1,
l);
i = rest.IndexOf(':');
if (i > 0)
{
description = rest.Substring(0, i);
l = rest.Length - i - 1;
rest = rest.Substring(i
+ 1, l);
i = rest.IndexOf(':');
if (i > 0)
{
string amt = rest.Substring(0, i);
bool canConvert = decimal.TryParse(amt,
out amount);
if (!canConvert)
{
}
l = rest.Length - i - 1;
if (l > 0)
{
string d = rest.Substring(i + 1,
l);
canConvert = DateTime.TryParse(d,
out date);
if (!canConvert)
{
}
}
else
{ // error for no date
}
}
}
else
{ // error for no description
}
}
else
{ // error for no last name
}
}
else
{ // error for no first name
}
// Note: There should be no errors for
lack of names, etc since this
// should be checked before
a message is sent to the ComDatabase.
}
//
Open Employee Database connection
static bool OpenEmployeeDatabase()
{
bool opened = false;
try
{
// Open OleDb Connection
myEmployeesConnection.ConnectionString = myEmployeesConnectionString;
myEmployeesConnection.Open();
opened = true;
}
catch (Exception ex)
{
Console.WriteLine("OLEDB Employees Connection FAILED: " +
ex.Message);
string responseMessage = "OLEDB Employees Connection FAILED:
";
Delivery.Publish(responseTopic,
componentKey,
from, responseMessage);
}
return opened;
} //
end OpenEmployeeDatabase
//
Open Expenses Database connection
static bool OpenExpensesDatabase()
{
bool opened = false;
try
{
// Open OleDb Connection
myExpensesConnection.ConnectionString = myExpensesConnectionString;
myExpensesConnection.Open();
opened = true;
}
catch (Exception ex)
{
Console.WriteLine("OLEDB Expenses Connection FAILED: " +
ex.Message);
string responseMessage = "OLEDB Expenses Connection FAILED: ";
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
}
return opened;
} //
end OpenExpensesDatabase
static void TreatAddEmployee(string data)
{
string responseMessage = "";
// Get first and last name and department of request message
GetNamesAndDepartment(data);
// Open connection to database
bool opened = OpenEmployeeDatabase();
// Add the employee. An error
will occur if a duplicate first
// and last name.
if (opened)
{
OleDbCommand cmd = myEmployeesConnection.CreateCommand();
string insertInto = "INSERT INTO Employees (EmployeeID, LastName,
FirstName, DepartmentName, Salary) ";
string values = "VALUES (?, ?, ?, ?, ?)";
cmd.CommandText = insertInto + values;
cmd.Parameters.AddWithValue("EmployeeID", 0);
cmd.Parameters.AddWithValue("LastName", lastName);
cmd.Parameters.AddWithValue("FirstName", firstName);
cmd.Parameters.AddWithValue("DepartmentName", department);
cmd.Parameters.AddWithValue("Salary", 0);
bool success = true;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// return the exception to display as an error message
success = false;
string exceptionString = ex.ToString();
string restOf = "";
string except = "";
int ii = exceptionString.IndexOf(':');
if (ii > 0)
{
int ll = exceptionString.Length - ii - 1;
restOf = exceptionString.Substring(ii + 1,
ll);
ii = restOf.IndexOf('\r');
except = restOf.Substring(0, ii);
}
responseMessage = "error: " + except;
Console.WriteLine("send error message {0}", responseMessage);
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
}
if (success)
{
Console.WriteLine("send success: message ");
responseMessage = "success";
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
}
// Close the connection
myEmployeesConnection.Close();
}
} //
AddEmployeeCallback
static void TreatModifyEmployee(string data)
{
string responseMessage = "";
// Get first and last name and department of request message
GetNamesAndDepartment(data);
// Open Connection to database
bool opened = OpenEmployeeDatabase();
// Check that name is exists.
if (opened)
{
OleDbCommand cmd = myEmployeesConnection.CreateCommand();
cmd = new OleDbCommand("SELECT COUNT(*) FROM Employees " +
"WHERE LastName = ? AND
FirstName = ?",
myEmployeesConnection);
cmd.Parameters.AddWithValue("LastName", lastName);
cmd.Parameters.AddWithValue("FirstName", firstName);
OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
dataAdapter.SelectCommand = cmd;
// Name combination valid?
int result;
bool success = true;
try
{
result = (int)cmd.ExecuteScalar();
if (result == 0)
{
responseMessage = "error: not in
database";
success = false;
}
}
catch (Exception ex)
{
// return the exception to display as an error message
success = false;
string exceptionString = ex.ToString();
string restOf = "";
string except = "";
int ii = exceptionString.IndexOf(':');
if (ii > 0)
{
int ll = exceptionString.Length - ii - 1;
restOf = exceptionString.Substring(ii + 1,
ll);
ii = restOf.IndexOf('\r');
except = restOf.Substring(0, ii);
}
responseMessage = "error: " + except;
}
if (success)
{
cmd = new OleDbCommand("UPDATE Employees " +
"SET DepartmentName = ?" +
"WHERE LastName = ?
AND FirstName = ?",
myEmployeesConnection);
cmd.Parameters.AddWithValue("DepartmentName", department);
cmd.Parameters.AddWithValue("LastName", lastName);
cmd.Parameters.AddWithValue("FirstName", firstName);
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// return the exception to display as an
error message
success = false;
string exceptionString = ex.ToString();
string restOf = "";
string except = "";
int ii = exceptionString.IndexOf(':');
if (ii > 0)
{
int ll = exceptionString.Length - ii -
1;
restOf = exceptionString.Substring(ii +
1, ll);
ii = restOf.IndexOf('\r');
except = restOf.Substring(0, ii);
}
responseMessage = "error: " +
except;
}
}
if (success)
{
responseMessage = "success";
}
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
// Close the connection and return the response message.
myEmployeesConnection.Close();
}
// opened
} //
end TreatModifyEmployee
static void TreatRemoveEmployee(string data)
{
string responseMessage = "";
// Get first and last names with unused blank department
GetNamesAndDepartment(data);
// Open Connection to database
bool opened = OpenEmployeeDatabase();
// Check that name is exists.
if (opened)
{
OleDbCommand cmd = myEmployeesConnection.CreateCommand();
cmd = new OleDbCommand("SELECT
COUNT(*) FROM Employees " +
"WHERE LastName = ? AND
FirstName = ?",
myEmployeesConnection);
cmd.Parameters.AddWithValue("LastName", lastName);
cmd.Parameters.AddWithValue("FirstName", firstName);
OleDbDataAdapter dataAdapter = new OleDbDataAdapter();
dataAdapter.SelectCommand = cmd;
// Name combination valid?
int result;
bool success = true;
try
{
result = (int)cmd.ExecuteScalar();
if (result == 0)
{
responseMessage = "error: not in
database";
success = false;
}
}
catch (Exception ex)
{
// return the exception to display as an error message
success = false;
string exceptionString = ex.ToString();
string restOf = "";
string except = "";
int ii = exceptionString.IndexOf(':');
if (ii > 0)
{
int ll = exceptionString.Length - ii - 1;
restOf = exceptionString.Substring(ii + 1,
ll);
ii = restOf.IndexOf('\r');
except = restOf.Substring(0, ii);
}
responseMessage = "error: " + except;
}
if (success)
{
cmd = new OleDbCommand("DELETE * FROM Employees " +
"WHERE LastName = ? AND FirstName = ?",
myEmployeesConnection);
cmd.Parameters.AddWithValue("LastName", lastName);
cmd.Parameters.AddWithValue("FirstName", firstName);
dataAdapter.DeleteCommand = cmd;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// return the exception to
display as an error message
success = false;
string exceptionString = ex.ToString();
string restOf = "";
string except = "";
int ii =
exceptionString.IndexOf(':');
if (ii > 0)
{
int ll = exceptionString.Length - ii -
1;
restOf = exceptionString.Substring(ii +
1, ll);
ii = restOf.IndexOf('\r');
except = restOf.Substring(0, ii);
}
responseMessage = "error: " +
except;
}
}
if (success)
{
responseMessage = "success";
}
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
// Close the connection and
return the response message.
myEmployeesConnection.Close();
}
// end opened
} //
end TreatRemoveEmployee
private struct EmployeeDataType
{
public string firstName;
public string lastName;
public string department;
}
private class EmployeeListType
{
public int count;
public EmployeeDataType[] list =
new EmployeeDataType[50];
}
static void TreatListOfEmployees()
{
// Initialize list to return in response
EmployeeListType employeeData = new EmployeeListType();
employeeData.count = 0;
string responseMessage = "";
// Open Connection to database
bool opened = OpenEmployeeDatabase();
if (opened)
{
string select = "SELECT * FROM Employees";
OleDbCommand cmd = new OleDbCommand(select, myEmployeesConnection);
using (OleDbDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
employeeData.list[employeeData.count].lastName =
reader["LastName"].ToString();
employeeData.list[employeeData.count].firstName =
reader["FirstName"].ToString();
employeeData.list[employeeData.count].department
=
reader["DepartmentName"].ToString();
employeeData.count++;
}
}
}
// Close the connection and return the response message.
myEmployeesConnection.Close();
if (employeeData.count > 0)
{
responseMessage = "success:";
responseMessage += employeeData.count + ":";
for (int i = 0; i < employeeData.count; i++)
{
responseMessage += employeeData.list[i].lastName + "\t";
responseMessage += employeeData.list[i].firstName + "\t";
responseMessage += employeeData.list[i].department + "\n";
}
}
else
{
responseMessage = "No employees found";
}
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
} //
end TreatListOfEmployees
static void TreatAddExpense(string data)
{
string responseMessage = "";
// Get first and last names, description, amount, and date
GetNamesAndExpenseData(data);
//
Open connection to database
bool opened = OpenExpensesDatabase();
if (opened)
{
OleDbCommand cmd = myExpensesConnection.CreateCommand();
string insertInto =
"INSERT INTO Expenses (LastName, FirstName, Description,
AmountSpent, DatePurchased)";
string values = "VALUES (?, ?, ?, ?, ?)";
cmd.CommandText = insertInto + values;
cmd.Parameters.AddWithValue("LastName", lastName);
cmd.Parameters.AddWithValue("FirstName", firstName);
cmd.Parameters.AddWithValue("Description", description);
cmd.Parameters.AddWithValue("AmountSpent", amount);
cmd.Parameters.AddWithValue("DataPurchased", date);
bool success = true;
try
{
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
// return the exception to display as an error message
success = false;
string exceptionString = ex.ToString();
string restOf = "";
string except = "";
int ii = exceptionString.IndexOf(':');
if (ii > 0)
{
int ll = exceptionString.Length - ii - 1;
restOf = exceptionString.Substring(ii + 1,
ll);
ii = restOf.IndexOf('\r');
except = restOf.Substring(0, ii);
}
responseMessage = "error: " + except;
}
if (success)
{
responseMessage = "success";
}
// Close the connection and return the response message.
myExpensesConnection.Close();
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
} // end if opened
} //
end TreatAddExpense
public struct ExpensesDataType
{
public string description;
public string date;
public string amount;
}
public class ExpensesListType
{
public int count;
public ExpensesDataType[] list =
new ExpensesDataType[30];
}
static void TreatDisplayExpenses(string data)
{
string responseMessage = "";
// Get first and last names with unused blank department
GetNamesAndDepartment(data);
// Initialize list to return in response
ExpensesListType expenseData = new ExpensesListType();
expenseData.count = 0;
// Open Connection to database
bool opened = OpenExpensesDatabase();
if (opened)
{
string select = "SELECT * FROM Expenses";
OleDbCommand cmd = new OleDbCommand(select, myExpensesConnection);
using (OleDbDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
if
((reader["LastName"].ToString() == lastName) &&
(reader["FirstName"].ToString() == firstName))
{
expenseData.list[expenseData.count].description =
reader["Description"].ToString();
expenseData.list[expenseData.count].date =
reader["DatePurchased"].ToString();
expenseData.list[expenseData.count].amount =
reader["AmountSpent"].ToString();
expenseData.count++;
}
}
}
}
// Close the connection and return the response message.
// Note: The response can't use ":" separators as usual since
the
// date also has time and
it uses ":" between the time fields.
myExpensesConnection.Close();
if (expenseData.count > 0)
{
responseMessage = "success#";
responseMessage +=
expenseData.count + "#";
for (int i = 0; i < expenseData.count; i++)
{
responseMessage += expenseData.list[i].description + "#";
responseMessage += expenseData.list[i].amount + "#";
responseMessage += expenseData.list[i].date + "#";
}
}
else
{
// No expenses for the employee
responseMessage = "success#";
responseMessage += expenseData.count + "#";
}
Delivery.Publish(responseTopic, componentKey,
from, responseMessage);
} //
end TreatDisplayExpenses
} // end
class ComDatabase
} // end namespace
ComExpenseIt
The complete ComExpenseIt class follows. There is one public method for each database
query needed by ExpenseItForm. And one
common response topic callback to be run when a response is forwarded to the
class's queue. As previously noted, the
receive of the response message triggers the particular ExpenseItForm interface
method to return the response. Each
ExpenseItForm interface method has a wait loop to await the receive of its
response. At times, as can be seen, the
framework response is reformatted for return to ExpenseItForm.
The need for this component is, of course, extra code from
directly accessing the particular database access class of the previous
post. But is necessary to separate the
database access to allow it to be moved to a different application. And, of course, as a test of whether Windows
forms can be used in conjunction with the framework.
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading;
namespace Apps
{
public
class ComExpenseIt
{
//
This class implements a component that interfaces with ExpenseItForm
//
which does a Windows Form with panels.
It must be in the same
//
application as ExpenseItForm.
//
// It
is an on demand component running whenever a message topic is
//
received and passes the response topic back to ExpenseItForm for
//
display.
static private Component.ParticipantKey componentKey;
static private DisburseForward.DisburseTableType forward =
new DisburseForward.DisburseTableType();
static DisburseForward queue;
static private Topic.TopicIdType requestTopic;
static private Topic.TopicIdType responseTopic;
static private string request; // request being sent to ComDatabase
static private bool responseReceived = false;
static private string receivedResponse; // response received
static public void Install()
{
Console.WriteLine("in Try1 ComExpenseIt Install");
// Build the Disburse forward list
forward.count = 1;
forward.list[0].topic.topic = Topic.Id.DATABASE;
forward.list[0].topic.ext = Topic.Extender.RESPONSE;
forward.list[0].forward = DatabaseResponseCallback;
// Instantiate the queue with the forward list
queue = new DisburseForward("ComExpenseIt", forward);
// Register this component
Component.RegisterResult result;
result = Component.Register
("ComExpenseIt", Threads.ComponentThreadPriority.LOWER,
MainEntry, queue);
componentKey = result.key;
if (result.status == Component.ComponentStatus.VALID)
{
Library.AddStatus status;
requestTopic.topic = Topic.Id.DATABASE;
requestTopic.ext = Topic.Extender.REQUEST;
// Register to produce the EMPLOYEE ADD topic and consume the response
status = Library.RegisterTopic
(requestTopic, result.key,
Delivery.Distribution.PRODUCER, null);
Console.WriteLine("ComDatabase DATABASE Register {0}",
status);
responseTopic.topic = Topic.Id.DATABASE;
responseTopic.ext = Topic.Extender.RESPONSE;
status = Library.RegisterTopic
(responseTopic, result.key,
Delivery.Distribution.CONSUMER, null);
}
} //
end Install
//
Entry point
static void MainEntry()
{
while (true) // loop forever
{
// Wait for event.
string xxx = queue.queueName;
queue.EventWait();
}
// end forever loop
} //
end MainEntry
//
Callback to treat the response from ComDatabase
static
void DatabaseResponseCallback(Delivery.MessageType message)
{
receivedResponse = message.data;
responseReceived = true;
} //
end AddEmployeeResponseCallback
static public string TreatAddEmployee(string firstName, string lastName,
string department)
{
responseReceived = false;
request = "Add";
string message = request + ":" + firstName + ":" +
lastName + ":" + department;
Delivery.Publish(requestTopic, componentKey, message);
// Wait until response received
while (true)
{
if ((request == "Add") && (!responseReceived))
{
Thread.Sleep(250); // millisecs
}
else
{
break; // exit loop
}
}
// Return the response
responseReceived = false;
return receivedResponse;
} //
end TreatAddEmployee
static public string ModifyEmployeeData(string firstName, string
lastName,
string department)
{
responseReceived = false;
request = "Modify";
string message = request + ":" + firstName + ":" +
lastName + ":" +
department;
Delivery.Publish(requestTopic, componentKey, message);
// Wait until response received
while (true)
{
if ((request == "Modify") &&(!responseReceived))
{
Thread.Sleep(250); // millisecs
}
else
{
break; // exit loop
}
}
// Return the response
responseReceived = false;
return receivedResponse;
} //
end ModifyEmployeeData
static public string RemoveEmployee(string firstName, string lastName)
{
responseReceived = false;
request = "Remove";
string message = request + ":" + firstName + ":" +
lastName;
Delivery.Publish(requestTopic, componentKey, message);
// Wait until response received
while (true)
{
if ((request == "Remove") && (!responseReceived))
{
Thread.Sleep(250); // millisecs
}
else
{
break; // exit loop
}
}
// Return the response
responseReceived = false;
return receivedResponse;
} //
end RemoveEmployee
static public string AddExpense(string firstName, string lastName,
string description, string
amt, string date)
{
responseReceived = false;
request = "AddExpense";
string message = request + ":" + firstName + ":" +
lastName + ":" +
description + ":" + amt +
":" + date;
Delivery.Publish(requestTopic, componentKey, message);
// Wait until response received
while (true)
{
if ((request == "AddExpense") && (!responseReceived))
{
Thread.Sleep(250); // millisecs
}
else
{
break; // exit loop
}
}
// Return the response
responseReceived = false;
return receivedResponse;
} //
end AddExpense
public struct EmployeeDataType
{
public string employee;
}
public class EmployeeListType
{
public int count;
public EmployeeDataType[] list =
new EmployeeDataType[50];
}
static public void ListOfEmployees(out string result,
out
EmployeeListType listOut)
{
responseReceived = false;
request = "List";
string message = request + ":";
Delivery.Publish(requestTopic, componentKey, message);
EmployeeListType list = new EmployeeListType();
list.count = 0;
// Wait until response received
while (true)
{
if ((request == "List") && (!responseReceived))
{
Thread.Sleep(250); //
millisecs
}
else
{
break; // exit loop
}
}
result = "error"; // overwrite with parsed result
responseReceived
= false;
// Decode the response
int i = receivedResponse.IndexOf(':');
int l = 0;
if (i > 0)
{
result = receivedResponse.Substring(0, i);
l = receivedResponse.Length - i - 1;
string data = receivedResponse.Substring(i + 1, l);
if (result != "success")
{
// Return the negative response
}
// Decode the count
int ii = data.IndexOf(':');
if (ii > 0)
{
int ll = data.Length - ii - 1;
Int16 count = Convert.ToInt16(data.Substring(0, ii));
string rest = data.Substring(ii+1,ll);
for (int j = 0; j < count; j++)
{
ii = rest.IndexOf('\n');
string item = rest.Substring(0, ii);
list.list[j].employee = item;
ll = rest.Length - ii - 1;
rest = rest.Substring(ii + 1, ll);
}
list.count = count;
listOut = list;
}
}
else
{
// Return the negative response
listOut = list;
}
listOut = list;
} //
end ListOfEmployees
public struct ExpensesDataType
{
public string description; //expenses;
public string amount;
public string date;
}
public class ExpensesListType
{
public int count;
public ExpensesDataType[] list =
new ExpensesDataType[30];
}
static public void ListOfExpenses(string firstName, string lastname,
out string result,
out ExpensesListType
expenses)
{
responseReceived = false;
request = "DisplayExpenses";
string message = request + ":" + firstName + ":" +
lastname;
Delivery.Publish(requestTopic, componentKey, message);
ExpensesListType expense = new ExpensesListType();
expense.count = 0;
// Wait until response received
while (true)
{
if ((request == "DisplayExpenses") &&
(!responseReceived))
{
Thread.Sleep(250); // millisecs
}
else
{
break; // exit loop
}
}
result = "error"; // overwrite with parsed result
responseReceived = false;
// Decode the response
// Note: The response can't use ":" separators as usual since
the
// date also has time and
it uses ":" between the time fields.
int i = receivedResponse.IndexOf('#');
int l = 0;
if (i > 0)
{
result = receivedResponse.Substring(0, i);
l = receivedResponse.Length - i - 1;
string data = receivedResponse.Substring(i + 1, l);
if (result != "success")
{
// Return the negative response
}
else
{
// Decode the count
int ii = data.IndexOf('#');
if (ii > 0)
{
int ll = data.Length - ii - 1;
Int16 count =
Convert.ToInt16(data.Substring(0, ii));
expense.count = count;
string rest = data.Substring(ii + 1, ll);
for (int j = 0; j < count; j++)
{
ii = rest.IndexOf('#');
string item = rest.Substring(0, ii);
expense.list[j].description = item;
ll = rest.Length - ii - 1;
rest = rest.Substring(ii + 1, ll);
ii = rest.IndexOf('#');
item = rest.Substring(0, ii);
expense.list[j].amount = item;
ll = rest.Length - ii - 1;
rest = rest.Substring(ii + 1, ll);
ii = rest.IndexOf('#');
item = rest.Substring(0,
ii);
expense.list[j].date = item;
ll = rest.Length - ii - 1;
rest = rest.Substring(ii + 1, ll);
}
}
}
}
else
{
}
// Return parsed expenses
expenses = expense;
} //
end ListOfExpenses
} // end
ComExpenseIt class
} // end namespace
ExpenseItForm
ExpenseItForm is the same as the previous post's Form1
except for when there was an invocation of one of the two database
classes. For instance, the constructor
now instantiates
databaseInterface = new ComExpenseIt();
although there is no reference to databaseInterface so this
line isn't needed.
Getting an employee's expenses to display, for instance, is
now done by
// Obtain employee's expenses
ComExpenseIt.ExpensesListType employeeExpenses
=
new ComExpenseIt.ExpensesListType();
string response = "";
ComExpenseIt.ListOfExpenses(listFirstName,
listLastName,
out response,
out
employeeExpenses);
etc where the structure was provided by ComExpenseIt.
In the previous attempt to use the message framework, the
publish to the database component and the wait for the response was done from
within the Windows form. For instance,
to add an employee
ComDatabaseAdd.AddEmployeePublish(firstName, lastName, department);
while (true)
{
if (interClassComm == "success")
{
ClearTextBoxes();
interClassComm = "";
break; // exit loop
}
else if (interClassComm != "")
{
errorMessageBox.Text = interClassComm;
interClassComm = "";
break; // exit loop
}
Thread.Sleep(100);
}
That is, following the publish of the request a while loop
was entered to await the response. The
ComDatabaseAdd component put the response into the public string of the form
and the form event handler cycled until it contained a non-null value.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Apps
{
public
partial class ExpenseItForm : Form
{
private ComExpenseIt databaseInterface;
private Navigation navigation;
public enum PanelSelection
{
NONE,
MENU,
ADDEMPLOYEE,
UPDATEEMPLOYEE,
ENTEREXPENSES,
DISPLAYEXPENSES,
UPDATEEMPLOYEELIST,
ENTEREXPENSESLIST,
DISPLAYEXPENSESLIST
}
public SelectionQueue.ParentDataType[] parentData =
new SelectionQueue.ParentDataType[SelectionQueue.size];
public enum SelectionDirection
{
LEFT,
RIGHT
}
//
Employee selected from ListPanel
private string listFirstName = "";
private string listLastName = "";
private string listDepartment = "";
public ExpenseItForm() // constructor
{
InitializeComponent();
// Allow access to the ComExpenseIt class to communicate with the
// databases
databaseInterface = new ComExpenseIt();
// Activate the Navigation Panel
navigation = new Navigation(this, this.leftArrow, this.rightArrow);
// Select the Menu Panel as the first panel below the Navigation Panel
navigation.SelectPanel(PanelSelection.MENU);
// Build the structure of the panels
SupplyPanelStructure();
} //
end constructor
/**********************************************************************
*
Navigation panel actions
*/
private void leftArrow_Click(object sender, EventArgs e)
{
navigation.ArrowClicked(SelectionDirection.LEFT);
} //
end leftArrow_Click
private void rightArrow_Click(object sender, EventArgs e)
{
navigation.ArrowClicked(SelectionDirection.RIGHT);
} //
end rightArrow_Click
private void SupplyPanelStructure()
{
parentData[(int)PanelSelection.NONE].panel = null;
parentData[(int)PanelSelection.NONE].parent = PanelSelection.NONE;
parentData[(int)PanelSelection.NONE].child = PanelSelection.NONE;
parentData[(int)PanelSelection.NONE].displayFirst = true;
parentData[(int)PanelSelection.MENU] =
parentData[(int)PanelSelection.NONE];
parentData[(int)PanelSelection.MENU].panel = MenuPanel;
parentData[(int)PanelSelection.ADDEMPLOYEE] =
parentData[(int)PanelSelection.NONE];
parentData[(int)PanelSelection.ADDEMPLOYEE].panel = AddEmployeesPanel;
parentData[(int)PanelSelection.UPDATEEMPLOYEE] =
parentData[(int)PanelSelection.NONE];
parentData[(int)PanelSelection.UPDATEEMPLOYEE].panel =
UpdateEmployeesPanel;
parentData[(int)PanelSelection.UPDATEEMPLOYEE].child =
PanelSelection.UPDATEEMPLOYEELIST;
parentData[(int)PanelSelection.ENTEREXPENSES].parent =
PanelSelection.NONE;
parentData[(int)PanelSelection.ENTEREXPENSES].panel = EnterExpensesPanel;
parentData[(int)PanelSelection.ENTEREXPENSES].child =
PanelSelection.ENTEREXPENSESLIST;
parentData[(int)PanelSelection.ENTEREXPENSES].displayFirst = false;
parentData[(int)PanelSelection.DISPLAYEXPENSES] =
parentData[(int)PanelSelection.ENTEREXPENSES];
parentData[(int)PanelSelection.DISPLAYEXPENSES].panel =
DisplayExpensesPanel;
parentData[(int)PanelSelection.DISPLAYEXPENSES].child =
PanelSelection.DISPLAYEXPENSESLIST;
parentData[(int)PanelSelection.UPDATEEMPLOYEELIST].panel = ListPanel;
parentData[(int)PanelSelection.UPDATEEMPLOYEELIST].parent =
PanelSelection.UPDATEEMPLOYEE;
parentData[(int)PanelSelection.UPDATEEMPLOYEELIST].child =
PanelSelection.NONE;
parentData[(int)PanelSelection.UPDATEEMPLOYEELIST].displayFirst = false;
parentData[(int)PanelSelection.ENTEREXPENSESLIST].panel = ListPanel;
parentData[(int)PanelSelection.ENTEREXPENSESLIST].parent =
PanelSelection.ENTEREXPENSES;
parentData[(int)PanelSelection.ENTEREXPENSESLIST].child =
PanelSelection.NONE;
parentData[(int)PanelSelection.ENTEREXPENSESLIST].displayFirst = true;
parentData[(int)PanelSelection.DISPLAYEXPENSESLIST].panel = ListPanel;
parentData[(int)PanelSelection.DISPLAYEXPENSESLIST].parent =
PanelSelection.DISPLAYEXPENSES;
parentData[(int)PanelSelection.DISPLAYEXPENSESLIST].child =
PanelSelection.NONE;
parentData[(int)PanelSelection.DISPLAYEXPENSESLIST].displayFirst = true;
} //
end SupplyPanelStructure
public void SwitchPanels(ExpenseItForm.PanelSelection to)
{
MenuPanel.Visible = false;
AddEmployeesPanel.Visible = false;
UpdateEmployeesPanel.Visible = false;
EnterExpensesPanel.Visible = false;
DisplayExpensesPanel.Visible = false;
ListPanel.Visible = false;
// Note: Special initializations needed for particular panels are
// done here since that
allows them to be done in one place.
// They are needed when
there are modifications to a panel
// rather than have
multiple versions of the panel.
switch (to)
{
case PanelSelection.NONE:
{
break;
}
case PanelSelection.MENU:
{
Controls.Add(MenuPanel);
MenuPanel.Visible = true;
break;
}
case PanelSelection.ADDEMPLOYEE:
{
Controls.Add(AddEmployeesPanel);
Department.Visible = true;
AddEmployeesPanel.Visible = true;
break;
}
case PanelSelection.UPDATEEMPLOYEE:
{
Controls.Add(UpdateEmployeesPanel);
// Modify visibility of selected panel
controls
Modify.Visible = true;
Remove.Visible = true;
DepartmentLabel.Visible = false;
SelectDepartment.Visible = false;
UpdateButton.Visible = false;
ErrorMessage.Visible = false;
SelectErrorMessage.Visible = false; //
because have an extra box
// Display ListPanel selected employee
if ((listFirstName != "")
&& (listLastName != ""))
{
SelectFirstName.Text = listFirstName;
SelectLastName.Text = listLastName;
SelectDepartment.Text = listDepartment;
}
// Make panel visible
UpdateEmployeesPanel.Visible = true;
break;
}
case PanelSelection.UPDATEEMPLOYEELIST:
{
Controls.Add(ListPanel);
// Display the particular ListPanel title
ListPanelTitle.Text = "Update
Employee";
ListPanel.Visible = true;
LoadEmployeeList();
break;
}
case PanelSelection.ENTEREXPENSES:
{
Controls.Add(EnterExpensesPanel);
EnterExpensesPanel.Visible = true;
// Display ListPanel
selected employee
if ((listFirstName != "")
&& (listLastName != ""))
{
EnterExpensesFirstName.Text =
listFirstName;
EnterExpensesLastName.Text =
listLastName;
EnterExpensesDepartment.Text =
listDepartment;
}
break;
}
case PanelSelection.ENTEREXPENSESLIST:
{
Controls.Add(ListPanel);
ListPanelTitle.Text = "Enter
Expenses";
ListPanel.Visible = true;
LoadEmployeeList();
break;
}
case PanelSelection.DISPLAYEXPENSES:
{
Controls.Add(DisplayExpensesPanel);
DisplayExpensesPanel.Visible = true;
// Display ListPanel selected employee
if ((listFirstName != "")
&& (listLastName != ""))
{
DisplayExpensesFirstName.Text =
listFirstName;
DisplayExpensesLastName.Text = listLastName;
DisplayExpensesDepartment.Text =
listDepartment;
}
DescListBox.Items.Clear();
DateListBox.Items.Clear();
AmtListBox.Items.Clear();
// Obtain employee's expenses
ComExpenseIt.ExpensesListType
employeeExpenses =
new ComExpenseIt.ExpensesListType();
string response = "";
ComExpenseIt.ListOfExpenses(listFirstName,
listLastName,
out response,
out
employeeExpenses);
// Display employee's expenses
string description, date, amount;
string datetime;
for (int i = 0; i <
employeeExpenses.count; i++)
{
description =
employeeExpenses.list[i].description;
DescListBox.Items.Add(description);
datetime =
employeeExpenses.list[i].date;
int ii = datetime.IndexOf(' '); // find
" 12:00:00 AM"
if (ii > 0)
{
date = datetime.Substring(0, ii);
}
else
{
date = datetime;
}
DateListBox.Items.Add(date);
amount =
employeeExpenses.list[i].amount;
AmtListBox.Items.Add(amount);
}
DescListBox.Height =
DescListBox.PreferredHeight;
DateListBox.Height =
DateListBox.PreferredHeight;
AmtListBox.Height =
AmtListBox.PreferredHeight;
break;
}
case PanelSelection.DISPLAYEXPENSESLIST:
{
Controls.Add(ListPanel);
ListPanelTitle.Text = "Display
Expenses";
ListPanel.Visible = true;
LoadEmployeeList();
break;
}
}
// end switch
} //
end SwitchPanels
//
Common method for UpdateEmployee, EnterExpenses, and UpdateExpenses
private void LoadEmployeeList()
{
// Clear the list box
employeeList.Items.Clear();
// Obtain the list of employees
ComExpenseIt.EmployeeListType employees =
new ComExpenseIt.EmployeeListType();
string response = "";
ComExpenseIt.ListOfEmployees(out response, out employees);
// Transfer to the list box
if (response == "success")
{
for (int i = 0; i < employees.count; i++)
{
employeeList.Items.Add(employees.list[i].employee);
}
}
// Display the list box
employeeList.Visible = true;
} //
end LoadEmployeeList
/**********************************************************************
*
Menu panel actions
*/
private void AddEmployee_Click(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.ADDEMPLOYEE);
} //
AddEmployee_Click
private void UpdateEmployee_Click(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.UPDATEEMPLOYEE);
} //
end UpdateEmployee_Click
private void Expenses_Click(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.ENTEREXPENSESLIST);
} //
end Expenses_Click
//
Note: Due to problems getting the panels laid out using
// Form1.cs[Design], when I
went to use the Display_Click
// I was prevented from
doing so although I don't see an
// entry for it in
Form1.Designer.cs as it became finalized.
private void Display_Click_1(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.DISPLAYEXPENSESLIST);
}
/**********************************************************************
*
AddEmployees panel actions
*/
private void AddEnteredEmployee_Click(object sender, EventArgs e)
{
// Clear any error message
ErrorMessageBox.Clear();
// Obtain entered data
string firstName, lastName, department;
firstName = FirstName.Text;
lastName = LastName.Text;
department = Department.Text;
// Check that values supplied
if ((firstName == "") || (lastName == "") ||
(department == ""))
{
ErrorMessageBox.Text = "Error: all fields must be supplied";
return;
}
// Update database
string response = ComExpenseIt.TreatAddEmployee
(firstName, lastName, department);
if (response == "success")
{
FirstName.Clear();
LastName.Clear();
Department.Clear();
}
else
{
ErrorMessageBox.Text = response;
}
} //
end AddEnteredEmployee_Click
/**********************************************************************
*
UpdateEmployees panel actions
*/
private void ShowList_Click(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.UPDATEEMPLOYEELIST);
} //
end ShowList_Click
private void UpdateButton_Click(object sender, EventArgs e)
{
// Clear any error message
ErrorMessage.Clear();
// Obtain entered data
string firstName, lastName, department;
firstName = SelectFirstName.Text;
lastName = SelectLastName.Text;
department = SelectDepartment.Text;
// Check that values supplied
if ((firstName == "") || (lastName == "") ||
(department == ""))
{
ErrorMessage.Text = "Error: all fields must be supplied";
ErrorMessage.Show();
return;
}
// Update database
string response =
ComExpenseIt.ModifyEmployeeData
(firstName, lastName,
department);
if (response == "success")
{
ErrorMessage.Clear();
ErrorMessage.Hide();
SelectFirstName.Clear();
SelectLastName.Clear();
DepartmentLabel.Hide();
SelectDepartment.Clear();
SelectDepartment.Hide();
UpdateButton.Hide();
Modify.Show();
Remove.Show();
}
else
{
ErrorMessage.Text = response;
ErrorMessage.Show();
}
} //
end UpdateButton_Click
private void Remove_Click(object sender, EventArgs e)
{
// Clear any error message
ErrorMessage.Clear();
ErrorMessage.Hide();
DepartmentLabel.Hide();
Department.Hide();
// Obtain entered data
string firstName, lastName;
firstName = SelectFirstName.Text;
lastName = SelectLastName.Text;
// Check that values supplied
if ((firstName == "") || (lastName == ""))
{
ErrorMessage.Text = "Error: all
fields must be supplied";
ErrorMessage.Show();
return;
}
// Update database
string response = ComExpenseIt.RemoveEmployee
(firstName, lastName);
if (response == "success")
{
SelectFirstName.Clear();
SelectLastName.Clear();
ErrorMessage.Hide();
}
else
{
ErrorMessage.Text = response;
ErrorMessage.Show();
}
} //
end Remove_Click
private void Modify_Click(object sender, EventArgs e)
{
Modify.Visible = false;
Remove.Visible = false;
DepartmentLabel.Visible = true;
SelectDepartment.Visible = true;
UpdateButton.Visible = true;
} //
end Modify_Click
/**********************************************************************
* EnterExpenses panel actions
*/
private void EnterExpensesButton_Click(object sender, EventArgs e)
{
// Capture data
string firstName = EnterExpensesFirstName.Text;
string lastName = EnterExpensesLastName.Text;
string department = EnterExpensesDepartment.Text;
string description = EnterExpensesDescription.Text;
string amount = EnterExpensesAmount.Text;
string date = EnterExpensesDate.Text;
// Check that values supplied
if ((firstName == "") || (lastName == "") ||
(department == "") ||
(description == "") || (amount == "") || (date ==
""))
{
ErrorExpensesErrorMessage.Text = "Error: all fields must be
supplied";
ErrorExpensesErrorMessage.Show();
return;
}
// Check that amount is decimal and date is formatted correctly
Decimal amt;
bool canConvert = decimal.TryParse(amount, out amt);
if (!canConvert)
{
ErrorExpensesErrorMessage.Text = "Error: Amount must be xx.xx
format";
ErrorExpensesErrorMessage.Show();
return;
}
DateTime d;
canConvert = DateTime.TryParse(date, out d);
if (!canConvert)
{
ErrorExpensesErrorMessage.Text = "Error: Date must be mm/dd/yy
format";
ErrorExpensesErrorMessage.Show();
return;
}
// Add expense to database
// string response =
ExpensesDatabase.AddExpense(firstName, lastName, description, amt, d);
string response = ComExpenseIt.AddExpense
(firstName, lastName, description, amount, date);
if (response == "success")
{
ErrorExpensesErrorMessage.Hide();
EnterExpensesDescription.Clear();
EnterExpensesAmount.Clear();
EnterExpensesDate.Clear();
}
else
{
ErrorExpensesErrorMessage.Text = response;
ErrorExpensesErrorMessage.Show();
}
} //
end EnterExpensesButton_Click
private void
EnterExpensesDone_Click(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.MENU);
} //
end EnterExpensesDone_Click
/**********************************************************************
*
DisplayExpenses panel actions
*/
private void DisplayExpensesDone_Click(object sender, EventArgs e)
{
navigation.SelectPanel(PanelSelection.MENU);
} //
end DisplayExpensesDone_Click
/**********************************************************************
*
List panel actions
*/
private void CancelEmployeeList_Click(object sender, EventArgs e)
{
navigation.SelectPanel(navigation.SelectParentPanel());
} //
end CancelEmployeeList_Click
private void employeeList_SelectedIndexChanged(object sender, EventArgs
e)
{
// Clear the employee identification that is to be used by the
// parent panel
listFirstName = "";
listLastName = "";
listDepartment = "";
// Get currently selected entry
string curItem = employeeList.SelectedItem.ToString();
int index = employeeList.FindString(curItem);
// If the item was not found in employeeList display a message box,
// otherwise select it in employeeList.
if (index == -1)
MessageBox.Show("Error: Item is not available in employee
list");
else
{
int i = curItem.IndexOf('\t');
if (i > 0)
{
listLastName = curItem.Substring(0, i);
int l = curItem.Length - i - 1;
string restOf = curItem.Substring(i + 1, l);
i = restOf.IndexOf('\t');
if (i > 0)
{
listFirstName = restOf.Substring(0, i);
l = restOf.Length - i - 1;
listDepartment =
restOf.Substring(i + 1, l);
}
}
// Select the parent panel to use the selected employee.
navigation.SelectPanel(navigation.SelectParentPanel());
}
} //
end employeeList_SelectedIndexChanged
} // end
class ExpenseItForm
} // end namespace
Navigation and SelectionQueue are unchanged from the
previous "C# Displays Follow On for ExpenseIt Showing Use of Panels"
post except for changing references to ExpenseItForm from Form1.
Try1
Try1 is similar to the previous App1, App2, etc classes to
install the application specific components.
For this application, there are only the two such components.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace Apps
{
static
class Try1 //App1
{
//
Install the components of Application 1
static public void Install()
{
// Install into the framework each of the components
// of the application.
ComDatabase.Install();
ComExpenseIt.Install();
} //
end Install
} // end
class Try1
} // end namespace
Otherwise, the framework classes of Delivery, Disburse,
Format, Library, NamedPipe, Receive, ReceiveInterface, Remote, Threads, Topic
(except for the addition of the DATABASE topic), and Transmit are unchanged and
can be located in previous posts.
No comments:
Post a Comment