Free!  FoxPro to WPF screen converter
   Almost free!  One day of FREE VFP to WPF evaluation of your FoxPro app ONSITE
   Free!  Read the foreword to Les' new book (available here)...
Skip Navigation Links

Using dialog forms in C# to collect parameters for your reports

You can use a WPF form to collect report parameters about as easily as you can in VFP; but the implementation is a little different...


Les Pinter

Login or register to download source code

Most database applications produce reports, and most reports have optional parameters that are used to filter the report content. We do the same thing in C# (or VB), of course, but the syntax is a little different. And since it took me longer than I'm willing to admit to discover how it's done, I'm going to share it with you so that you won't have to waste time like I did.

<rant>In FoxPro, I could usually figure out how to do things in a few minutes; in C# or VB, it's hours, days, weeks or worse. <\rant>

Collecting parameters in VFP

In FoxPro, you can pass parameters in the DO FORM statement:

DO FORM GetParms WITH EyeColor, StartDate, EndDate [TO Canceled]

The [TO Canceled] part is optional; if present, you can return, for example, a boolean value set to .T. if the called form was canceled. That can be handy, because you're likely to use a variable named Canceled in more than one form, so if you use modeless forms, changing its value in another form will confuse your users and your programs. TO THISFORM.Canceled is even better.

However, passing parameters in doesn't make much sense in VFP. Unlike C# or Visual Basic, you can't pass parameters to a form by reference in VFP. Parameters passed in the Init of a VFP form are meant to go in one direction only. If you don't need to pass them to the parameter collection form (e.g. as default values), just use PUBLIC variables. When you assign them values in the GetParms form, you don't have to pass them back; they're PUBLIC.

The parameter will go out of scope as soon as you exit the Init() routine, so save the values if you need to refer to them later.

In the called form's Init

LPARAMETERS EyeColor, StartDate, EndDate
THISFORM.txtEyeColor.Value  = EyeColor
THISFORM.txtStartDate.Value = StartDate
THISFORM.txtEndDate.Value   = EndDate
...

When you click Save, store the values that were entered back to the PUBLIC variables and close the form, setting THISFORM.Canceled to the appropriate value so that it will be available to the Unload event that hancles "DO FORM .. TO Result" :

Save:Click event code:

EyeColor  = THISFORM.txtEyeColor.Value
StartDate = THISFORM.txtStartDate.Value
EndDate   = THISFORM.txtEndDate.Value
THISFORM.Canceled = .F.
THISFORM.Release<

Cancel:Click event code:

THISFORM.Canceled = .T.
THISFORM.Release

Note that in this example, when values ate assigned to StartDate and EndDate, they are GLOBAL VARIABLES. They're not being passed back as parameters. They're only passed in as parameters. If we didn't do this, we'd create some form properties and store the parameters that we passed in to those properties. Either way is fine. (As you'll see below, in C#, the parameter values are actually changed when you return.)

If you use the optional [TO var] feature, you have to return the value in the UNLOAD event:

Unload event:
RETURN THISFORM.Canceled

Back in the calling form, you construct your SQL SELECT statement using the returned values, if the query wasn't canceled, and if the parameters aren't empty.

IF NOT THISFORM.Canceled
   IF EyeColor <> ""
      SQLStmt = SQLStmt + " AND EyeColor = '" + EyeColor + "'";
   ENDIF
   IF NOT EMPTY ( StartDate )
      SQLStmt = SQLStmt + " AND BETWEEN ( DOB, StartDate, EndDate )"
   ENDIF
   SQLEXEC ( handle, SQLStmt )
   ...
ENDIF

Parameter collection forms in C#

In C#, the constructor isn't called "Init"; it has the same name as the form class itself. And the Init method has a standard signature: No parameters. If the form name is getParms, here's the constructor:

public getParms()
 { InitializeComponent(); }

But in C#, you can have multiple copies of the same method, as long as each has a different signature (number/type of parameters). So you'll add a second constructor with the exact same name as the default constructor; however, this one will have a parameter, passed byref so that it will transport the selected values back to the caller. Can't do that in VFP! And since after your custom constructor is called you want it to also call the base constructor, so that "components are initialized" (some day I'm going to step through and see what it's doing...)

You might as well create an object containing one property per parameter, so that you pass one object which actually contains all of your parameters to the getParms form. I call it loParms. In FoxPro, we'd use AddProperty to add each parameter. In C#, the closest equialent is the ExpandoObject, which just requires that you assign some named properties; the properties are created when the values are assigned.

Here's how you do it. In the calling form, declare a dynamic object, but instantiate it as an ExpandoObject. Then, just assign values to the property names you want to use for parameters. That creates them (like AddProperty in VFP).

In the getParms form, right after the parameterless constructor (which just calls Initializecomponent()), add another getParms constructor. But this time, include a ref parameter:

public getParms(ref dynamic loParms): this()      // call the "standard" constructor as well...    

Finally (and this is where it's different from FoxPro), you'll want to create a companion object (called a field in C#) in which to store a reference to the parameter object. It has to be declared immediately after the class declaration, and then instantiated in the second constructor. This is necessary because the parameter will go out of scope as soon as you depart the constructor.

public partial class getParms : Window
{
	public dynamic parmobj;
	public getParms()
	  {  InitializeComponent(); }

	public getParms(ref dynamic loParms): this()
	{ parmobj = loParms;
	  txtEyeColor.Text  = loParms.EyeColor;
	  txtStartDate.Text = loParms.StartDate;
	  txtEndDate.Text   = loParms.EndDate;
	}
}

The assignment of loParms to parmobj really just points back to the original object; so after you return, the parameter object contains the values that were assigned to the corresponding object in frmParms.

private void btnSave_Click(object sender, RoutedEventArgs e)
{
  parmobj.StartDate = txtDate.StartDate;
  parmobj.EndDate   = txtDate.EndDate;
  parmobj.EyeColor  = txtColor.Text;
  parmobj.Canceled  = false;
  Close();
}

Since parmobj is simply a pointer to loParms, upon returning, loParms will contain the values entered in the getParms form.

Note that in the calling form, the initial declaration creates loParms as a dynamic object:

public dynamic loParms; // requires using System.Dynamic;

     NOTE: This must be a field; it can't be a property. Don't know why. I'll get back to you on that. Oh, I remember; I don't care.

However, it's instantiated as an ExpandoObject, which is very like a FoxPro custom class object:

private void btnGetParms_Click(object sender, RoutedEventArgs e)
{ loParms = new ExpandoObject();
  loParms.EyeColor	= null;				// If you want to pass default parameters to the form,
  loParms.StartDate = null;				//  here's your chance.
  loParms.EndDate   = null;
  loParms.Canceled  = false;

  getParms P = new getParms(ref loParms); P.ShowDialog();

  if(!loParms.Canceled)
    { EyeColor	= loParms.EyeColor;
      StartDate = loParms.StartDate;
      EndDate	= loParms.EndDate;
      GetData();
    }
}

You can then construct your query:

void GetData()
{  var query = mgr.Customer;
   if (StartDate != null) query = query.Where(x=>x.DOB >= StartDate && x.DOB <= EndDate);
   if (EyeColor  != null) query = query.Where(x=>x.EyeColor == EyeColor);
   foreach(Customer C in query) { customers.Add(C); }
}

Conclusion

To summarize, in your calling form, declare a dynamic object, but instantiate it as an ExpandoObject. Then, in the parameter collection form, declare a dynamic field and assign the parameter object to it. In the parameter collection form, create two constructors, and in the one that declares your parameter, assign it to the local object. When you Save, assign the collected values to the respective parameter properties of your local object. Since it points back to the parameter object, which was passed by value, when you return, the parameter object will contain your collected values. Simple as that. Not as simple as FoxPro, but not terrible.

See 'ya.

Les

qqq


Copyright(C) Pinter Consulting, 2012Tel: +1 (650) 464-6924
Automated conversion between C# and VB by Visible C#/Visible VB from Tangible Software Solutions