Tripous - Forms and Brokers





4.00/5 (2 votes)
A tutorial on how to use Tripous data entry forms in order to display data from different types of data sources.
What is Tripous?
Tripous is an Open Source application framework, written in C#, for rapid implementation of WinForms data entry applications.
The official site can be found at http://tripous-net.com. The project page at SourceForge can be found at http://sourceforge.net/projects/tripous.
Tripous tutorials at CodeProject
- Tripous introduction: A tutorial about Tripous and how to set up your first Tripous data entry application.
- Tripous - Data access: A tutorial on how to use Tripous data access classes.
Introduction
This is a tutorial on how to use Tripous data entry forms in order to display data from different types of data sources.
As the Tripous introduction tutorial describes, Tripous provides two types of data entry forms: the master and the list form. This is a tutorial on master forms.
A master data entry form has two parts: a browse (or list) part and an edit (or item) part. The browse part is just a read only DataGridView
. The edit part may contain data bound controls that are used in editing a single data row or item.
The easiest way to use a data entry form is in conjunction with a Broker and basically a SqlBroker. The Broker is the base business class in Tripous. Most of the time, forms and brokers go together. But this is not the only scenario.
Here is the short hierarchy of Tripous form classes.
BaseForm
: The base form class that provides logic for form initialization and "finalization".DataEntryForm
: The base class for data entry forms. Provides all the logic for browsing and searching in the list, as well as inserting, editing, and deleting a row.DataEntryBrokerForm
: The base class for data entry forms associated to a Broker.
In this tutorial, we are going to investigate four different cases:
- A master form with a SqlBroker
- A master form with a custom Broker
- A master form bound to a single DataTable
- A master form bound to a generic BindingList
The sample application
The sample application is not a full blown Tripous data entry application. The main form does not inherit from Tripous.Forms.SysMainForm
. It is just a plain MDI container main form. Here is an image of that form that displays the four different form cases as menu items.
The main form is initialized in the OnShown()
method.
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
if (!DesignMode)
{
ObjectStore.Initialize();
InitializeForm();
}
}
ObjectStore
is a Tripous static class that is a registry of objects. ObjectStore
has the ability to search assemblies for classes or methods marked with the ObjectStoreItemAttribute
attribute and register them automatically as object factories. Since this is not a full Tripous application, we must initialize ObjectStore
manually.
The InitializeForm()
belongs to the main form and starts the initialization sequence.
void InitializeForm()
{
Sys.Variables["Forms.MainForm"] = this;
ObjectStore.Add("Synchronizer.Primary", this as object);
Logger.Active = true;
InitializeDatabase();
InitializeDescriptors();
/* assign MdiLayout values to menu item tags */
mnuCascade.Tag = MdiLayout.Cascade;
mnuTileHorizontal.Tag = MdiLayout.TileHorizontal;
mnuTileVertical.Tag = MdiLayout.TileVertical;
mnuArrangeIcons.Tag = MdiLayout.ArrangeIcons;
/* events */
mnuExit.Click += new EventHandler(AnyClick);
mnuSqlMonitor.Click += new EventHandler(AnyClick);
mnuCascade.Click += new EventHandler(AnyClick);
mnuTileHorizontal.Click += new EventHandler(AnyClick);
mnuTileVertical.Click += new EventHandler(AnyClick);
mnuArrangeIcons.Click += new EventHandler(AnyClick);
mnuDataEntryBrokerFormWithSqlBroker.Click += new EventHandler(AnyClick);
mnuDataEntryFormWithASingleDataTable.Click += new EventHandler(AnyClick);
mnuDataEntryFormWithAGenericBindingList.Click += new EventHandler(AnyClick);
mnuDataEntryBrokerFormWithACustomBroker.Click += new EventHandler(AnyClick);
}
The InitializeForm()
method informs Tripous that this form is the main form and that it is the primary synchronizer. Our main form implements the ISynchronizer
interface which is used to synchronize thread calls.
Next, the logger is activated. The Logger
class is used to log any kind of information, not exceptions only. Eventually, the logger informs any subscribed listener for any log event. The logger does not persist the log information on its own. This is a job for special log listeners. A full blown Tripous application provides a log listener that saves log information to a database table. There are other log listeners though that just display log information to the user. SqlMonitorForm
is such a listener that displays any SQL statement while it is executed by Tripous. We are going to use SqlMonitorForm
just to inspect some of those statements. So we need to activate the Logger
class.
InitializeDatabase()
and InitializeDescriptors()
come next, and after that, there is code that just links events to event handlers. One of the four forms of this sample application uses SQL database data, so we need a database. We are going to use the excellent Sqlite as our database server. Tripous incorporates Sqlite already.
void InitializeDatabase()
{
/* construct the path to the database file */
string FileName = Path.GetFullPath(@"..\..\TripousDemo.db3");
/* get the proper DataProvider and create the database file */
DataProvider Provider = DataProviders.Find(ConnectionStringBuilder.Sqlite);
Provider.CreateDatabase("", FileName, "", "");
/* construct the connection string to the database */
string ConStr = ConnectionStringBuilder.FormatConnectionString("",
ConnectionStringBuilder.Sqlite, FileName);
/* create and add a Datastore object for the database */
Datastore Datastore = Datastores.Add("MAIN", ConnectionStringBuilder.Sqlite, ConStr);
/* this is the only table in the database */
string CUSTOMER = @"
create table Customer (
Id @PRIMARY_KEY
,Code varchar(32)
,Name varchar(64)
)
";
/* create an Executor and use it to create the table in the database */
Executor Executor = Datastore.CreateExecutor();
Executor.CreateTable(CUSTOMER);
}
The Tripous - Data access tutorial explains a lot about those data access classes such as DataProvider
and Datastore
. Here, this method gets the Sqlite Tripous DataProvider, creates the database if it does not exist, constructs a connection string, adds a Datastore
to Datastores
, creates an Executor
, and uses that Executor
in order to create a table in the database.
InitializeDescriptors()
comes next.
void InitializeDescriptors()
{
/* Create and add a BrokerDescriptor to the Registry.Brokers collection.
The minimum information required for a BrokerDescriptor
is the Name, the MainTableName and the TypeClassName.
This particular Add() used here sets the Name,
MainTableName and Title properties of the broker descriptor
to the passed in string, in this case Customer.
The TypeClassName property is set to SqlBroker.
No other information is given to the descriptor,
so this is a very lazy initialization */
BrokerDescriptor BrokerDes = Registry.Brokers.Add("Customer");
/* Create and add a FormDescriptor to the Registry.Forms collection.
The minimum information required for a FormDescriptor
is the Name, the TypeClassName and the BrokerName.
This particular Add() used here sets the Name and the BrokerName to Customer and
the TypeClassName to an assembly qualified name of a demo form class. */
FormDescriptor FormDes = Registry.Forms.Add("Customer",
typeof(DataEntryBrokerFormWithSqlBroker).AssemblyQualifiedName);
/* Create and add a Command to the Registry.MainProcessor.
The Command class provides the Kind property
which designates the function of the command.
Here the Kind is set to CommandKind.Mdi,
which means that this commands should create an Mdi form.
The Command.Name is required and here is set to Customer.
When a form command such as this command is executed,
it searches the Registry for a FormDescriptor
with the same name as its own name. */
Registry.MainProcessor.Add(CommandKind.Mdi, "Customer");
}
Let's start with the forms now.
A master form with a SqlBroker
This is the easiest case since we already have registered everything we need by the InitializeDescriptors()
.
When a Command
object registered to Registry.MainProcessor
and having its Kind
property set to CommandKind.Mdi
or CommandKind.Modal
is executed, the Registry is searched for a FormDescriptor
with a name as its own name. And then it creates the actual Form and the actual Broker, connects the two, and displays the form. All that happens automatically in a full blown Tripous application. In our tiny sample, we have to do it manually. So here is an excerpt from the AnyClick(
) method of the main form:
else if (sender == mnuDataEntryBrokerFormWithSqlBroker)
{
Command Cmd = Registry.MainProcessor["Customer"];
DataEntryForm.Show(Cmd);
}
The above creates an instance of the DataEntryBrokerFormWithSqlBroker
which derives from the DataEntryBrokerForm
.
When the associated menu item is clicked, we get the Customer Command from the Registry, and then we call the static Show()
of the DataEntryForm
class. That's all we have to do. An instance of the DataEntryBrokerFormWithSqlBroker
class and an instance of the SqlBroker
class are created and connected, and then the form displays itself. It is a data entry form that reads and writes its data to a relational database.
We can use the SQL Monitor menu item in order to display the SqlMonitorForm
before displaying the data form, just to inspect the SQL executed by Tripous while browsing or inserting or searching.
Since this is not a full Tripous application, not all form's functionality is available. For example, reports are not supported. Reports, syntax highlighting, scripting, RAPI, etc., are supported using external plug-in modules that require an ApplicationManager
class to load them. Nevertheless, almost all of the functionality is there. You can even execute a parameterized select. Here is the criteria part of the customer form:
In a normal Tripous application, the edit part of a master form hosts mostly Tripous controls that provide great flexibility in data binding. Those controls require full descriptor information. This sample uses standard .NET controls, and the binding is done by the code in the form.
protected override void BindControls()
{
base.BindControls();
/* If you use the standard TextBox and not the TextBoxEx provided by Tripous,
then you have to manually bind that text box as following */
if (bsBrowser != null)
{
textBox1.DataBindings.Add(Ui.Bind("Text", bsItem, "Code"));
textBox2.DataBindings.Add(Ui.Bind("Text", bsItem, "Name"));
}
}
A master form with a custom Broker
In this form case, we use the DataEntryBrokerFormWithCustomBroker
derived from DataEntryBrokerForm
again. A DataEntryBrokerForm
requires a Broker. And we are going to provide a custom Broker to that form. A Broker that has no idea about SQL data and databases.
Creating a custom Broker class
The Tripous.BusinessModel.Broker
class is the base class for all brokers. It provides just empty virtual methods that may be used in inserting, editing, or deleting data. Here is the interface of the Broker class.
public class Broker : IBroker
{
static public readonly string BrowserPostfix = "__BROWSER";
/* protected */
protected Command processor = new Command();
protected object owner;
protected DataSet dataSet;
protected Variables variables;
protected Tables tables;
protected CodeProducer codeProducer;
protected DataMode state = DataMode.None;
protected object lastEditedId;
protected object lastCommitedId;
protected object lastDeletedId;
protected MemTable _tblBrowser; // ref
protected MemTable _tblItem; // ref
protected MemTable _tblLines; // ref
protected MemTable _tblSubLines; // ref
protected MemTable _tblBackup; // ref
/* flags */
protected bool initialized;
protected bool initializing;
protected bool batchMode;
protected bool browserSelected;
protected bool selectBrowserOnce;
protected bool browserSetupDone;
protected bool isListBroker;
/* initialization */
public virtual void Initialize(bool AsListBroker);
protected virtual void DoInitializeBefore();
protected virtual void DoInitialize();
protected virtual void DoInitializeAfter();
/* browser */
public void BrowserSelect();
public void BrowserCommit();
public void BrowserCancel();
/* browser DoXXX methods */
protected virtual void DoBrowserSelect();
protected virtual void DoBrowserCommit();
protected virtual void DoBrowserSetup();
/* browser checks */
protected virtual void BrowserCheckCanCommit();
/* browser miscs */
protected void BrowserSetup();
protected void BrowserAcceptChanges();
protected void BrowserRejectChanges();
protected virtual void DoBrowserAcceptChanges();
protected virtual void DoBrowserRejectChanges();
/* item */
public void Insert();
public void Edit(object RowId);
public void Delete(object RowId);
public object Commit(bool Reselect);
public void Cancel();
/* item DoXXX before and after methods */
protected virtual void DoInsertBefore();
protected virtual void DoInsert();
protected virtual void DoInsertAfter();
protected virtual void DoEditBefore(object RowId);
protected virtual void DoEdit(object RowId);
protected virtual void DoEditAfter(object RowId);
protected virtual void DoDeleteBefore(object RowId);
protected virtual void DoDelete(object RowId);
protected virtual void DoDeleteAfter(object RowId);
protected virtual void DoCommitBefore(bool Reselect);
protected virtual object DoCommit(bool Reselect);
protected virtual void DoCommitAfter(bool Reselect);
protected virtual void DoCancelBefore();
protected virtual void DoCancel();
protected virtual void DoCancelAfter();
/* item checks */
protected virtual void CheckCanInsert();
protected virtual void CheckCanEdit(object RowId);
protected virtual void CheckCanDelete(object RowId);
protected virtual void CheckCanCommit(bool Reselect);
/* item miscs */
protected void AcceptChanges();
protected void RejectChanges();
protected virtual void DoAcceptChanges();
protected virtual void DoRejectChanges();
/* sending-posting data-related messages through Broadcaster */
protected void Send(string EventName);
protected void Send(string EventName, string[] Names, object[] Values);
protected void Post(string EventName);
protected void Post(string EventName, string[] Names, object[] Values);
/* constructors */
public Broker();
/* static */
static public Broker Create(BrokerDescriptor BrokerDes,
bool Initialized, bool AsListBroker);
static public Broker Create(BrokerDescriptor BrokerDes, bool Initialized);
static public Broker Create(string Name, bool Initialized, bool AsListBroker);
static public Broker Create(string Name, bool Initialized) ;
/* local reports */
public virtual void ReportsShowAdminDialog();
public virtual void ReportsShowMenuDialog();
public virtual bool ReportsExists(string Name);
public virtual bool ReportsExecute(string Name);
/* copy-paste */
public virtual bool Copy();
public virtual bool Paste();
public virtual bool CanPaste();
/* miscs */
public virtual string CurrentItemCodeName(int RowIndex);
public virtual DataRow Locate(string FieldName, object Value);
public DataRow Locate(object Value);
public virtual void BackUp();
public virtual TableDescriptor TableDescriptorOf(string TableName);
public virtual FieldDescriptor FieldDescriptorOf(string TableName,
string FieldName);
public virtual List<datarow> PickRows(string TargetTable,
string SourceSqlText, string[] VisibleColumns,
string TargetKeyName, string SourceKeyName);
/* properties */
public bool Initializing { get { return initializing; } }
public bool Initialized { get { return initialized; } }
public virtual int RowCount { get { return _tblBrowser == null ? 0 :
_tblBrowser.Rows.Count; } }
public virtual bool BrowserIsEmpty { get { return RowCount == 0; } }
public virtual bool IsBrowserModified { get { return _tblBrowser == null ?
false : _tblBrowser.GetChanges() != null; } }
public bool BatchMode { get; set; }
public DataSet DataSet { get ; }
public MemTable tblBrowser { get ; }
public MemTable tblItem { get ; }
public MemTable tblLines { get ; }
public MemTable tblSubLines { get ; }
public MemTable tblBackup { get ; }
public Tables Tables { get ; }
public CodeProducer CodeProducer { get ; }
public Variables Variables { get ; }
public Command Processor { get ; }
public virtual bool IsListBroker { get ; }
public virtual bool IsMasterBroker { get ; }
public object Owner { get; set; }
public DataMode State { get ; }
public virtual object LastEditedId { get ; }
public virtual object LastCommitedId { get ; }
public virtual object LastDeletedId { get ; }
public virtual string LinesTableName { get ; }
public virtual string SubLinesTableName { get ; }
public virtual string BackupTableName { get ; }
}
A broker, similar to a form class, divides its logic into two parts. A browse (or list) part and an edit (or item) part. Methods that start or contain the word "Browser" serve that browse part. Methods such as Commit()
, Insert()
, Delete()
, Edit()
, or DoCommit()
, DoInsert()
, DoDelete()
, and DoEdit()
serve the edit part of the broker. The "Do" prefix indicates a virtual method that is called by a method with the same name but without that "Do" prefix. Here is the Commit()
method of the Broker
class.
public object Commit(bool Reselect)
{
DataMode OldState = State;
try
{
DoCommitBefore(Reselect);
CheckCanCommit(Reselect);
lastCommitedId = DoCommit(Reselect);
DoCommitAfter(Reselect);
state = DataMode.Edit;
return lastCommitedId;
}
catch
{
state = OldState;
throw;
}
}
If you check the code of similar Broker methods, you'll see that they follow that same pattern.
try
{
DoXXXXBefore();
CheckCanXXXX();
DoXXXX();
DoXXXXAfter();
...
}
catch
{
...
throw;
}
The Tripous.BusinessModel.SqlBroker
derives from the Broker
class. A SqlBroker
uses a BrokerDescriptor
; it knows how to handle SQL data, and it provides suitable implementation for all those virtual methods of its Broker
parent class. Eventually, the SqlBroker
is able to handle many data scenarios without an extra line of code. But in this form case, we do not use a SqlBroker
.
Instead, this sample provides a direct Broker
derived class. The Broker
class does not have a BrokerDescriptor
. It relies on code. Take a look.
/// A custom Broker. It is not a SqlBroker. It just uses
/// a plain xml file to save its data.
/// There is the tblSource DataTable that acts as the data container.
/// That tblSource is not part of the Broker.Tables. It is just
/// a static field used as a helper.
public class CustomBroker : Broker
{
/* these static fields play the role of the source data */
static string fileName = Path.GetFullPath(@"..\..\Customers2.XML");
static DataSet DS = Db.CreateDataset();
static MemTable tblSource = new MemTable("Customers");
/* private */
private void BrowserSelectInternal()
{
tblSource.CopyTo(_tblBrowser);
}
/* overrides */
/// Initializes the broker
protected override void DoInitialize()
{
base.DoInitialize();
/* By convention the browser table is named after the main (top) table
of the broker, plus the BrowserPostfix */
_tblBrowser = tables.Add("Customers" + BrowserPostfix);
/* add schema to the browser table and copy data to it */
tblSource.CopyStructureTo(_tblBrowser);
tblSource.CopyTo(_tblBrowser);
/* create the top table and add schema to it */
_tblItem = tables.Add("Customers");
tblSource.CopyStructureTo(_tblItem);
}
/// Called by the Broker.BrowserSelect method
/// and it actually selects the browser.
protected override void DoBrowserSelect()
{
BrowserSelectInternal();
}
/// Called by theInsert to actually start an insert operation.
protected override void DoInsert()
{
tblItem.Rows.Clear();
DataRow Row = tblItem.NewRow();
tblItem.Rows.Add(Row);
}
/// Called by the Edit to actually starts an edit operation.
protected override void DoEdit(object RowId)
{
DataRow SourceRow = null;
if (!tblBrowser.Locate("Id", new object[] { RowId },
LocateOptions.None, out SourceRow))
Sys.Error("Can not edit a Customer. Row not found");
tblItem.Rows.Clear();
DataRow Row = tblItem.NewRow();
tblItem.Rows.Add(Row);
SourceRow.CopyTo(Row);
}
/// Called by the Commit and throws an exception if, for some reason, commiting
/// item is considered invalid.
protected override void CheckCanCommit(bool Reselect)
{
base.CheckCanCommit(Reselect);
string S = tblItem.Rows[0].AsString("Code");
if (string.IsNullOrEmpty(S))
Sys.Error("No Code provided");
}
/// Called by the Commit() to actually commit changes made by the Insert or Edit
/// methods, to the underlying table tree (database).
/// Returns the row id of the tblItem commited row.
protected override object DoCommit(bool Reselect)
{
DataRow Row = tblItem.Rows[0];
string Id = Row.AsString("Id");
/* commit to our "datastore" */
DataRow SourceRow = null;
if (!tblSource.Locate("Id", new object[] { Id },
LocateOptions.None, out SourceRow))
{
SourceRow = tblSource.NewRow();
tblSource.Rows.Add(SourceRow);
}
Row.CopyTo(SourceRow);
/* save to file */
tblSource.WriteXml(fileName, XmlWriteMode.WriteSchema);
return Id;
}
/// Called by the Delete to actually delete
/// a row to the underlying table tree (database).
protected override void DoDelete(object RowId)
{
DataRow SourceRow = null;
if (tblSource.Locate("Id", new object[] { RowId },
LocateOptions.None, out SourceRow))
{
tblSource.Rows.Remove(SourceRow);
tblSource.WriteXml(fileName, XmlWriteMode.WriteSchema);
}
}
/* construction */
/// Static constructor
static CustomBroker()
{
DS.Tables.Add(tblSource);
if (File.Exists(fileName))
{
tblSource.ReadXml(fileName);
}
else
{
DataColumn Column = tblSource.Columns.Add("Id", typeof(System.String));
tblSource.Columns.Add("Code", typeof(System.String));
tblSource.Columns.Add("Name", typeof(System.String));
}
}
/// Constructor
public CustomBroker()
{
}
/* public overrides */
/// Locates a row in the tblBrowser and, if needed
/// in the database by loading the row to the tblBrowser.
/// The search is always performed to the tblBrowser or the main table of the broker.
/// FieldName is the column name to search and Value the value to locate
public override DataRow Locate(string FieldName, object Value)
{
DataRow Result = base.Locate(FieldName, Value);
if (Result == null)
{
BrowserSelectInternal();
Result = _tblBrowser.Locate(FieldName, new object[] { Value }, LocateOptions.None);
}
return Result;
}
}
Creating the DataEntryBrokerForm with the custom broker
Here is what happens when the associated menu item is clicked. There is no command object here.
else if (sender == mnuDataEntryBrokerFormWithACustomBroker)
{
DataEntryBrokerFormWithCustomBroker Form =
new DataEntryBrokerFormWithCustomBroker();
Form.MdiParent = this;
Form.Show();
}
There is not a big difference from the previous form case where we had a standard SqlBroker
. Here is the code of the form.
/// This is a form with a broker.
/// A DataEntryBrokerForm class used with a custom Broker descendant.
/// Since this form is not created by a Command we have to pass the proper
/// information to its InitInfo property.
public partial class DataEntryBrokerFormWithCustomBroker : DataEntryBrokerForm
{
/* overrides */
/// The virtual BindControls() is called by the form initialization sequence
/// and it just binds controls to data.
protected override void BindControls()
{
base.BindControls();
/* If you use the standard TextBox and not the TextBoxEx provided by Tripous,
then you have to manually bind that text box as following */
if (bsItem != null)
{
textBox1.DataBindings.Add(Ui.Bind("Text", bsItem, "Code"));
textBox2.DataBindings.Add(Ui.Bind("Text", bsItem, "Name"));
}
}
/* construction */
/// Constructor
public DataEntryBrokerFormWithCustomBroker()
{
InitializeComponent();
if (!DesignMode)
{
/* InitInfo is a property of the BaseForm class. It is used to convey
initialization information to the initialization sequence of the form.
When a form is created by a Command class
with its Kind set to CommandKind.Mdi or Modal
the Command prepares that InitInfo properly.
Here there is not a Command, so we just pass the minimum information. */
InitInfo = new ArgList();
InitInfo["Broker"] = new CustomBroker();
InitInfo["DefaultTitle"] = "DataEntryBrokerForm with a custom-made Broker";
}
}
}
The most critical information we provide to the InitInfo
property is the "Broker
" variable. We just create an instance of our CustomBroker
class and pass that object. The InitializeBroker()
our form inherits from DataEntryBrokerForm
extracts that Broker
instance from InitInfo
and assigns a member field.
protected virtual void InitializeBroker()
{
if (initInfo != null)
{
broker = initInfo.ValueOf("Broker", broker);
if (broker != null)
{
broker.Initialize(IsListForm);
}
}
}
A master form bound to a single DataTable
The associated menu displays the form. Again, no Command object.
else if (sender == mnuDataEntryFormWithASingleDataTable)
{
DataEntryFormWithASingleDataTable Form = new DataEntryFormWithASingleDataTable();
Form.MdiParent = this;
Form.Show();
}
Here is the code of the form:
/// This form uses no broker at all.
/// A DataEntryForm class with a single table
/// used both as the browser and as the top table.
/// Table data is saved to a plain xml file.
///
/// Since this form is not created by a Command we have to pass the proper
/// information to its InitInfo property.
public partial class DataEntryFormWithASingleDataTable : Tripous.Forms.DataEntryForm
{
string fileName = Path.GetFullPath(@"..\..\Customers.XML");
DataSet DS = Db.CreateDataset();
MemTable tblCustomers = new MemTable("Customers");
/* private */
void TablesSetup()
{
DS.Tables.Add(tblCustomers);
if (File.Exists(fileName))
{
tblCustomers.ReadXml(fileName);
}
else
{
/* add schema to the table */
DataColumn Column = tblCustomers.Columns.Add("Id", typeof(System.Int32));
Column.AutoIncrement = true;
Column.AutoIncrementSeed = 1;
tblCustomers.Columns.Add("Code", typeof(System.String));
tblCustomers.Columns.Add("Name", typeof(System.String));
}
}
/* overrides */
/// The virtual BindControls() is called by the form initialization sequence
/// and it just binds controls to data.
protected override void BindControls()
{
base.BindControls();
/* Since there is no broker in this form and subsequentially no descriptors of any
type, we have to manually bind our controls */
if (bsBrowser != null)
{
textBox1.DataBindings.Add(Ui.Bind("Text", bsBrowser, "Code"));
textBox2.DataBindings.Add(Ui.Bind("Text", bsBrowser, "Name"));
}
}
/// Processes the Cmd. Cmd must be a "close" Cmd, that is OK, Cancel or Close.
protected override void ProcessClose(StdCmd Cmd)
{
base.ProcessClose(Cmd);
if (Bf.Member(ClosingStdCmd, StdCmd.OK | StdCmd.Close))
tblCustomers.WriteXml(fileName, XmlWriteMode.WriteSchema);
}
/* construction */
/// Constructor
public DataEntryFormWithASingleDataTable()
{
InitializeComponent();
if (!DesignMode)
{
TablesSetup();
/* InitInfo is a property of the BaseForm class. It is used to convey
initialization information to the initialization sequence of the form.
When a form is created by a Command class
with its Kind set to CommandKind.Mdi or Modal
the Command prepares that InitInfo properly.
Here there is not a Command, so we just pass the minimum information. */
InitInfo = new ArgList();
InitInfo.Add("tblBrowser", tblCustomers);
InitInfo.Add("tblItem", tblCustomers);
InitInfo["DefaultTitle"] = "DataEntryForm with a single DataTable";
}
}
}
There is just a single DataTable
, the tblCustomers
, which plays the role of both the browser and the item table.
Our form is not a Broker form. It inherits from Tripous.Forms.DataEntryForm
, which has no idea about brokers. So, since there is not a Broker in this case, we pass the same tblCustomers
as the tblBrowser
and tblItem
to the InitInfo
property of the form. The schema of tblCustomers
is created by the TablesSetup()
method. When the form closes, the ProcessClose()
method saves tblCustomers
to a plain XML file.
A master form bound to a generic BindingList
Nothing special in showing the form.
else if (sender == mnuDataEntryFormWithAGenericBindingList)
{
DataEntryFormWithBindingList Form = new DataEntryFormWithBindingList();
Form.MdiParent = this;
Form.Show();
}
In this form case, we use a BindingListEx
instance as our data store. BindingListEx
inherits from the generic BindingList
and provides a few extra capabilities. The type argument in that generic list is a humble Person
class. Nothing special at all, but enough for this sample that tries to describe how to bind a Tripous DataEntryForm
to a generic list.
public class Person
{
public Person()
{
}
public string Code { get; set; }
public string Name { get; set; }
}
Here is the code of the form.
/// This form uses no broker at all.
///
/// A DataEntryForm class with no DataTable at all.
/// It uses a BindingList, actually a BindingListEx which is a Tripous class.
/// BindindListEx is a bindable, sortable and searchable
/// generic BindingList with overridable methods.
public partial class DataEntryFormWithBindingList : DataEntryForm
{
string fileName = Path.GetFullPath(@"..\..\Persons.XML");
BindingListEx<Person> persons = new BindingListEx<Person>();
/* overrides */
/// The virtual BindControls() is called by the form initialization sequence
/// and it just binds controls to data.
protected override void BindControls()
{
base.BindControls();
/* Since there is no broker in this form and subsequentially no descriptors of any
type, we have to manually bind our controls */
if (bsBrowser != null)
{
textBox1.DataBindings.Add(Ui.Bind("Text", bsBrowser, "Code"));
textBox2.DataBindings.Add(Ui.Bind("Text", bsBrowser, "Name"));
}
}
/// Processes the Cmd. Cmd must be a "close" Cmd, that is OK, Cancel or Close.
protected override void ProcessClose(StdCmd Cmd)
{
base.ProcessClose(Cmd);
/* Pass the persons BindingListEx to the XmlPersistor and ask it to save it */
if (Bf.Member(ClosingStdCmd, StdCmd.OK | StdCmd.Close))
XmlPersistor.SaveToFile(persons, fileName);
}
/* construction */
/// Constructor
public DataEntryFormWithBindingList()
{
InitializeComponent();
if (!DesignMode)
{
/* prepare column information for the browser grid */
ColumnInfoList BrowserColumnList = new ColumnInfoList();
BrowserColumnList.Add(new ColumnInfo("Code"));
BrowserColumnList.Add(new ColumnInfo("Name"));
persons.IsBound = true;
InitInfo = new ArgList();
InitInfo.Add("DataList", persons);
InitInfo["BrowserColumnList"] = BrowserColumnList;
InitInfo["DefaultTitle"] = "DataEntryForm and a generic BindingList";
/* load the persons BindingListEx using the XmlPersistor */
XmlPersistor.LoadFromFile(persons, fileName);
}
}
}
Again, no Broker, no Command. Everything is coded. Our form inherits DataEntryForm
, which has no idea about brokers.
In fact, since there is no DataTable
, we have to provide information regarding the columns of the browse part of the form, the read-only DataGridView
. We do that by creating a Tripous.Data.ColumnInfoList
instance and some ColumnInfo
objects. We pass that ColumnInfoList
as the "BrowserColumnList
" variable of the InitInfo
property. The DoBrowserSetupGridColumns()
method, our form inherits from its parent DataEntryForm
class, has enough information now to do its job and create columns to the DataGridView
.
Since there is no Broker or a DataTable
to pass to the InitInfo
, we just assign the "DataList
" variable, passing the person's BindingListEx
object. The DataEntryForm
will use that list object as its data store.
The static Tripous.XmlPersistor
class is used in serializing and de-serializing that list to an XML file. XmlPersistor
and Tripous XML persisting capabilities deserve a separate tutorial.
Conclusion
Tripous DataEntryForm
and DataEntryBrokerForm
classes are quite flexible, and can be used as data entry forms with a variety of data sources: SqlBroker
, a custom made broker, DataTable
s, or even generic lists.