This concerns an approval field on a form. In the database, it's a bool field, an int field containing a FK to approvers, and a date-time field that, together, indicate whether something was approved and if so, who approved it and when. On the form, this has to translate into something like "Approved by John Smith on 01/02/03 04:05."
I handle this with a navigation bar. When the binding source position changes, the event is trapped and the code formats the calculated fields, like this (what the code does it not that important).
private void ctlNavBar1_displayCurrent(object sender, EventArgs e)
{
var drv = talsBindingSource.Current as DataRowView;
if (drv != null)
{
ctlBoundCheckButton1.lblText = $"Submitted {drv.Row.Field<DateTime>("SubmitDate").ToString("MM/dd/yy hh:mm tt")}";
ctlBoundCheckButton1.setControls(true);
if (drv.Row.Field<bool>("Approved"))
{
var sup = talsSupervisorsBindingSource.Current as DataRowView;
ctlBoundCheckButton2.lblText = $"Approved by {sup.Row.Field<string>("FullName")} on {drv.Row.Field<DateTime>("ApproveDate").ToString("MM/dd/yy")}";
ctlBoundCheckButton2.setControls(true);
}
}
else
{
using (DialogCenteringService centeringService = new DialogCenteringService(this))
{
MessageBox.Show("No TALs to Approve", "Confirm", MessageBoxButtons.OK);
}
Close();
}
}
The problem is that
public TALsApprove()
{
InitializeComponent();
talsTableAdapter.FillForApproval(timeTrackDataSet.TALs, User.ID);
usersTableAdapter.FillBySupervisor(timeTrackDataSet.Users, User.ID);
timeSlipsTableAdapter.FillBySupervisor(timeTrackDataSet.TimeSlips, User.ID);
ctlNavBar1.displayCurrent = ctlNavBar1_displayCurrent;
ctlNavBar1.bindingSource = talsBindingSource;
// this assignment doesn't fire Position Changed (or anything else, as far as I can tell)
}
the binding source event PositionChanged does not fire when the binding source is first assigned. I've worked around that by using the form Shown event, like this.
private void TALsApprove_Shown(object sender, EventArgs e)
{
ctlNavBar1_displayCurrent(null, new EventArgs());
}
So my questions are:
1.- Does calling event handlers directly like this mess with any of .NET's internals? (e.g. memory leaks, stack problems, etc.)
2.- Is there a less kludgy way of handling the calculation of fields when the binding source is first initialized, as well as when the contents of the current record change? I experimented with the binding source events CurrentChanged and CurrentItemChanged, but they seem to over-fire, firing even when no actual field value had changed.
CodePudding user response:
There are a couple of ways I might think to tidy this up:
1 ) Use a calculated column
Assumptions:
- You have a strongly typed DataSet with two tables like Applications and Users
- You have the following columns in Applications: Approved(bool), ApproveDate(datetime), ApprovedByUserId(int)
- You have a single datarelation between Applications and Users that maps Applications.ApprovedByUserId (many) to User.UserId (one), and UserId is also an int
Process:
- In your dataset click in your Applications table, and add a string column
- Set its Expression property to something like:
IIF([Approved] = False,'Not approved','Approved by' Parent.Username ' on ' [ApproveDate])
You have some process alreadythat fills good data into the table:
When you run the app it becomes the datatable's problem to build the narrative:
Let's edit another detail in at runtime:
When you finish the edit and move off the row it will be committed to the table and the narrative updates automatically
You can read more about what syntax you can use in DataColumn.Expression
If you don't have a bool approved you could add one or use some other test like IIF(ApprovedByUserId IS NULL,'Not Appproved,'App...'). If yo uhave multiple datarelations coming off Applications you specify the name of the relation after Parent like
Parent(App_User).UserName` assuming the datarelation is called App_User
- Bind different things on the UI
Noone ever said you only had to bind Text. If you had a "Approved" bool column in your dataset, you could have several labels in a row:
--label1----- --label2------ --label3-- --label4--
"Approved by" BindParentName " on " BindDate
You can bind every one of their Visible properties to the Approved bool so the labels disappear if the user navs to an unapproved row.
The easiest way to get the parent user name into the Applications data table (because all these labels are bound to a indingsource that sits on the Applications table, right?) is to use an Expression
on a new column like above, but simpler (just Parent.UserName
or Parent(App_User).UserName`) to import the user name into the Applications datatable.
There are other ways, involving multiple binding sources that bind to datarelations.. We can even jiggle a combo box into doing it - the combo has a DataSource of the users table, but a DataMember of "ApprovedByUserId" from the applications table; it will perform 2 way lookup of the ApprovedByUserId <--> UserId
equivalence