Custom grids and data views
Sometimes a form needs a list that Epicor doesn’t have: open follow-up items for the job on screen, a checklist, a set of values to confirm before posting. In a Classic customization you can build that list yourself. The pattern has four parts:
- A
DataTableyou define in code. - An
EpiDataViewwrapped around it and registered withoTrans, so controls can bind to it. - A grid bound to that view.
- Code that fills the table (usually from a BAQ) and writes changes back (usually to a UD table).
1 and 2: define the table and register the view
Section titled “1 and 2: define the table and register the view”private DataTable dtItems;private EpiDataView edvItems;
private void CreateItemsView(){ dtItems = new DataTable("XX_Items"); dtItems.Columns.Add("Selected", typeof(bool)); dtItems.Columns.Add("ItemID", typeof(string)); dtItems.Columns.Add("Description", typeof(string)); dtItems.Columns.Add("Qty", typeof(decimal)); dtItems.Columns.Add("Done", typeof(bool)); dtItems.Columns.Add("OrigDone", typeof(bool)); // lets us spot what the user changed
edvItems = new EpiDataView(); edvItems.dataView = new DataView(dtItems); oTrans.Add("XX_Items", edvItems);}
private void RemoveItemsView(){ oTrans.EpiDataViews.Remove("XX_Items"); dtItems.Dispose(); dtItems = null;}Call CreateItemsView() from InitializeCustomCode() and RemoveItemsView() from DestroyCustomCode(). Register the view before anything that binds to it, including row rules.
3: bind a grid
Section titled “3: bind a grid”Add an EpiUltraGrid with the ToolBox and set its EpiBinding to XX_Items. The grid picks up the columns from the table. Hide helper columns such as OrigDone in the grid’s properties, or with code after the first load.
To make a column read-only in every control bound to it, set it on the view:
edvItems.SetCurrentRowPropertyManually("Description", SettingStyle.ReadOnly);4a: fill it from a BAQ
Section titled “4a: fill it from a BAQ”Use the BAQ pattern from Adapters, BAQs and searches, then copy the results into your table. Pause notifications while you load so the grid doesn’t redraw for every row:
private void LoadItems(string jobNum){ oTrans.SuspendNotifications(); dtItems.Rows.Clear();
if (jobNum.Length > 0) { foreach (DataRow src in RunItemsQuery(jobNum).Rows) // BAQ XX_JobItems, parameter JobNum { DataRow row = dtItems.NewRow(); row["Selected"] = false; row["ItemID"] = src["UD01_Key3"]; row["Description"] = src["UD01_Character01"]; row["Qty"] = src["UD01_Number01"]; row["Done"] = src["UD01_CheckBox01"]; row["OrigDone"] = row["Done"]; dtItems.Rows.Add(row); } }
oTrans.ResumeNotifications(); oTrans.NotifyAll();}4b: save to a UD table
Section titled “4b: save to a UD table”UD tables (UD01–UD40 and the child UD100-series) give you somewhere to keep custom records without a schema change. UD01 has five keys; a common convention is Key1 = record type, Key2 = the parent record (job number), Key3 = the item.
private void SaveItem(string jobNum, string itemID, string desc, decimal qty, bool done){ UD01Adapter ud = new UD01Adapter(oTrans); try { ud.BOConnect(); bool exists; try { exists = ud.GetByID("XX_JobItem", jobNum, itemID, "", ""); } catch { exists = false; } // a missing record may throw rather than return false
if (!exists) { ud.ClearData(); ud.GetaNewUD01(); ud.UD01Data.UD01[0].Key1 = "XX_JobItem"; ud.UD01Data.UD01[0].Key2 = jobNum; ud.UD01Data.UD01[0].Key3 = itemID; }
ud.UD01Data.UD01[0].Character01 = desc; ud.UD01Data.UD01[0].Number01 = qty; ud.UD01Data.UD01[0].CheckBox01 = done; ud.Update(); } finally { ud.Dispose(); }}Deleting is one call: ud.DeleteByID("XX_JobItem", jobNum, itemID, "", "");.
Saving what the user ticked in the grid
Section titled “Saving what the user ticked in the grid”When a user ticks a checkbox in the grid, the view raises EpiViewNotification. Compare the current value with the copy you stored at load time and save only real changes:
private void edvItems_EpiViewNotification(EpiDataView view, EpiNotifyArgs args){ if (args.NotifyType == EpiTransaction.NotifyType.Initialize && args.Row > -1) { DataRowView r = view.dataView[args.Row]; if ((bool)r["Done"] != (bool)r["OrigDone"]) { SaveItem(txtJob.Text.Trim(), r["ItemID"].ToString(), r["Description"].ToString(), (decimal)r["Qty"], (bool)r["Done"]); r["OrigDone"] = r["Done"]; } }}For “delete selected rows”, call dtItems.AcceptChanges() so pending edits are committed to the table, Select("Selected = true"), confirm with the user, delete each by ID, then reload.
Styling the rows
Section titled “Styling the rows”Colour cells after a load, or add row rules to the custom view comparing its own columns; both are covered in Controls and styling and Row rules.