Screen scaffolding: Special Filter/Find Controls

From OpenPetra Wiki
Jump to navigation Jump to search

Overview

A Filter/Find panel is part of many of the screens in Open Petra. It is a user control that is docked to the left of the screen beside the Grid that shows the data table content. The user control contains all the base functionality of creating its content and responding to user events. You configure the content of the user control by means of the YAML file for the screen. In many cases there will be no need for any manual code because all the code for Filter/Find can be auto-generated. However if you need to handle additional events or construct special controls, some manual code will be necessary.

The Filter/Find panel consists of one or two tabs - a Filter tab and, optionally, a separate Find tab. The Filter tab allows the user to filter the rows in the grid by some combination of column values. The Find tab allows the user to skip forward or back to the next row that matches a given set of one or more column values. The Find tab is simpler to describe because it has fewer features than the Filter tab.

The Filter/Find panel is normally initially hidden by virtue of being collapsed to zero-width on the left side of the grid. Furthermore the controls on the panel are dynamically created the first time that the panel is shown. So there is no time penalty in terms of loading a screen that has a Filter/Find panel. When the user clicks the Filter button on the Buttons panel the user control is displayed for the first time. Likewise the panel can be collapsed by clicking the Filter button again or by clicking on the close box of the panel itself. This typical behaviour can be over-ridden by specifying that the panel is to be shown as soon as the screen loads.

Normally when the panel is collapsed (hidden) the filter is not applied - in other words when the filter button is clicked to close the panel the grid reverts to showing all rows. However this behaviour can be over-ridden as well so that the filter remains active when the panel is hidden.

The Filter panel can have one or two sets of column filter controls. This permits the developer to configure one set to be always on and the other set to be on when the panel is shown and off when the panel is hidden. The first set is referred to as the standard filter and the second set as the extra filter. The most common implementations will not need an extra filter.

Screenshots

The screen below shows all the currency rows that have been filtered down to those with a K in the currency and an I in the country code Currency Filter I And K.jpg

This screen shows the row that has been found that has 'son' in the Reciprocal Description column. Note that it is not case-sensitive. Find Son.jpg

Coding the YAML File

The special panel - pnlFilterAndFind

If you want your screen to include a Filter/Find panel you simply include pnlFilterAndFind as the first control on the pnlGrid. Here is a typical example.

   Controls:
       pnlContent:
           Controls: [pnlGrid, pnlDetails]
           Dock: Fill
       pnlGrid:
           Dock: Fill
           Controls: [pnlFilterAndFind, pnlButtons, grdDetails]
       pnlFilterAndFind:
           ExpandedWidth: 175
           FilterControls: [txtDetailBusinessCode, txtDetailBusinessDescription, chkDetailDeletable]
           FindControls: [txtDetailBusinessCode, txtDetailBusinessDescription, chkDetailDeletable]
       pnlButtons:
           Dock: Bottom
           Controls: [btnNew, btnDelete]
           ControlsOrientation: horizontal
       btnNew:
           Action: actNew
           Width: 80
       btnDelete:
           Action: actDelete
           Width: 80
       grdDetails:
           Dock: Fill
           Columns: [DetailBusinessCode, DetailBusinessDescription, DetailDeletable]
           ActionFocusRow: FocusedRowChanged
       pnlDetails:
           Dock: Bottom
           Controls:
               Row0: [txtDetailBusinessCode, txtDetailBusinessDescription]
               Row1: [chkDetailDeletable]

This screen has a content panel consisting of a grid panel and a details panel. The grid panel has the Filter/Find panel, a buttons panel and the grid itself. The buttons panel has two buttons - New and Delete. The pnlGrid is set to Dock: Fill. The pnlButtons is set to Dock: Bottom and grdDetails is set to Dock: Fill.

The first important point to note is that there is no declaration of btnFilter. The code generation automatically includes both the filter button and the record counter on the right hand end of the buttons panel.

The pnlFilterAndFind element will typically have the following sub-elements.

  • ExpandedWidth: The width of the panel when it is not collapsed to the side. The default is 150 but you may need to increase this to 175 or 180.
  • InitiallyExpanded: Set this to true if you want the Filter/Find panel to be visible as soon as the screen loads. You should only do this if you have a filter component that is always on.
  • FilterControls: Usually you will include a list of control names from the details panel. You can also refer to a column name directly in the situation where there is no control for that column in the details panel. This is useful for columns that have internally generated values such as Batch Number. Finally there is the option to create a custom filter control from scratch. All these methods are described in more detail below.
  • ExtraFilterControls: Control definitions for the Extra Filter Panel. This is the optional additional panel that can have its own set of controls that apply in a different context from the Filter Panel.
  • FindControls: The control definitions for the Find Panel.

There are other elements and attributes that you might set and these are fully described below. However many screens will be similar to the code example above - only requiring three elements to obtain the full functionality.

Naming Conventions

There are a few caveats about the naming of controls (which apply throughout Open Petra). If you do not conform to these naming conventions the auto-generated code is unlikely to compile.

When you specify a control name such as txtDetailColumnName you are doing several things at once.

  • You are specifying that the control is a TextBox
  • You are setting the control's Name property.
  • You are specifying that the .NET version of the database column name is ColumnName - which implies that the underlying database column name is something like pt_column_name_c. This full name can be discovered from .NET code by invoking the method .GetColumnNameDBName()
  • You may or may not choose to include the Detail in txtDetailColumnName. So txtColumnName and txtDetailColumnName work the same.

So the control name definition specifies to the code generator important information about both the control and the database column to which it refers.

What does the Filter/Find Panel Generator Do?

When you specify a control in the details panel the generator creates a 'Shallow Clone' of the original. (You definitely do not want to reference the details panel control directly). In fact all the controls in the filter/find panels are always cloned from something. If there is no control in the details panel to clone from, the code will create an in-memory control and clone from that. In fact it makes a clone of both the control and its associated label. However the clone is not a full clone - for example, if the details control is disabled the new clone will always be enabled. If the details control is one of our special ComboBoxes containing items from a database list, the new clone will usually be a basic auto-complete ComboBox that accepts new values - but it will contain the same drop-down data as the original (but no side label).

You can clone a radio button group to a ComboBox. This saves a lot of space.

If you specify a Filter/Find control by means of the table column name, the generator will create an in-memory label and control for you. The label text will be constructed from the column name and the control will either be a CheckBox if the data is a Boolean or a TextBox otherwise.

The generator will automatically hook up the change events for the Filter/Find control, so that as you type text into a control a new filter string can be constructed and applied dynamically as a RowFilter to the data set.

Filter/Find YAML Definition Reference

This section lists all the options available for the definition of the Filter/Find Panel.

Creating the Panel

You include pnlFilterAndFind as the first element inside pnlGrid. Then you declare a pnlFilterAndFind control in the root Controls element.

Controls in the Filter/Find panel are laid out vertically. Typically each control has a label above it and usually a Clear button beside it on the right. However check-boxes usually have no label but the label text is applied as the CheckBox text.

The Clear Button

By default all the controls that you clone will have a small Clear Button (marked with an X) on the right hand side of the control. The user can click this button to set the control to its cleared state, which implies 'Do not include this column in the overall filter'. Normally this is the correct behaviour. The cleared state of a TextBox or ComboBox is empty text. The cleared state of a CheckBox is the 'indeterminate' state. If a radio button group is cloned and the clone is to have a Clear button, the clone will have an additional option (All) automatically added unless it already has an All option. The cleared state is then the All button.

If the control associated with the clear button is disabled (through code) then clicking the clear button will have no effect - although the button itself will not be disabled.

Elements and Attributes of pnlFilterAndFind

  • ExpandedWidth: The width of the Filter/Find panel when it is visible (expanded).
  • InitiallyExpanded: Set this to true if you want the panel visible when the screen loads.
  • ShowApplyFilterButton: The default is not to show the Apply Filter button but you can change this behaviour by setting one of the following contexts: FilterContext.StandardFilterOnly, FilterContext.ExtraFilterOnly, FilterContext.StandardAndExtraFilter (or FilterContext.None).
  • ShowKeepFilterTurnedOnButton: The default is not to show the Keep Filter Turned On button. Modify this behaviour by specifying the contexts in which the button should be shown (see the item above).
  • ShowFilterIsAlwaysOnLabel: If you need the filter to always be on and the user to not have the option to turn it off, then show the Always On label. As with the previous two items, specify the filter contexts in which this label is to be displayed.
  • FilterButton: The text and status bar help message is, in most cases, handled automatically. However there are a few screens that have two grids and two filter panels. In that case you will want to specify a different button text and help for each one. (The button text contains the ampersand (&) that specifies the shortcut key). Set the value of this attribute to a two-part string separated by a colon. The first part is the button text. This can be empty to retain the standard text. The second part is the help text. You will need this for both filters. Use a variant of the examples below in order to keep the text consistent throughout the application.
FilterButton=;Click to show or hide the Analysis Types filter panel 
FilterButton=F&ilter;Click to show or hide the Analysis Values filter panel 
  • FilterControls: A list of controls to be created on the (standard) Filter panel. See below for the options to specify the controls themselves.
  • ExtraFilterControls: A list of controls to be created on the Extra Filter panel. See below for the options to specify the controls themselves.
  • FindControls: A list of controls to be created on the Find panel. See below for the options to specify the controls themselves.
  • ControlAttributes: This element is used to specify additional attributes that apply to the cloned controls - for example to shorten the label text or to change the width if a cloned control compared to the cloned-from control.
  • Panels: You will need this element if you create a user defined set of Filter Panel controls.

Specifying Filter/Find Controls by Cloning from the Details Panel

To include filtering based on a details panel control simply add the details panel control name to the list of controls in the FilterControls or ExtraFilterControls or FindControls.

The generator will clone both the control and its label (if it has one). By default, check boxes will have no label but will have text for the box itself.

The generator will clone the following controls:

  • Label
  • TextBox
  • Date Picker text box
  • CheckBox
  • Panels or GroupBoxes containing any of the above or RadioButtons
  • Any type of ComboBox including auto-populated ones. See more information about cloning ComboBoxes here.

Any control that you clone automatically has the SuppressChangeDetection attribute set.

  • If you do not want the cloned control to have a Clear button then you should add the attribute ClearButton=false to the attribute list for the control beneath the ControlAttributes element.

Here is an example of a filter/find panel that is simply created by cloning existing controls.

       pnlFilterAndFind:
           ExpandedWidth: 175
           FilterControls: [txtDetailFeeCode, txtDetailFeeDescription, cmbDetailCostCentreCode, cmbDetailAccountCode, cmbDetailDrAccountCode]
           FindControls: [txtDetailFeeCode, txtDetailFeeDescription]
           ControlAttributes:
               cmbDetailCostCentreCode: {Label=CR Cost Centre}
               cmbDetailAccountCode: {Label=CR Account}
               cmbDetailDrAccountCode: {Label=DR Account}

This is an example of a screen where the labels needed to be modified slightly to make their meaning clear. Most screens will have no need of a ControlAttributes section at all.

Specifying Filter/Find Controls from the Column Name

You can specify a control using this syntax:

 Column: [TableName.]ColumnName

where ColumnName is the .NET name for the column and TableName is the .NET name for the table (e.g. ACurrencyTable). Normally it is not necessary to specify the TableName - the code will use the DetailTable specified in the YAML file. Be sure to get the upper/lower case correct for the column definition. What you type will become part of the program code GetColumnNameDBName().

When you specify the control by means of the column name the generator will use the column name as the basis for the control name and label text. So, if the column name is ColumnName, the label will be Column Name. You can change the label, or indeed any other property of the label or control in the ControlAttributes element (see below).

Here is an example of a screen that uses a column definition - in this case referencing the column s_retired_l

       pnlFilterAndFind:
           ExpandedWidth: 175
           FilterControls: [txtDetailUserId, txtDetailFirstName, txtDetailLastName, Column:Retired]
           FindControls: [txtDetailUserId, txtDetailFirstName, txtDetailLastName]
           ControlAttributes:
               chkRetired: {Text=User Retired}

Notice that if you need to refer to a control that was created from a Column Name that the name of the control will be 'ControlType + ColumnName' - in this case 'chkRetired'. This example also provides a reminder that the check box text is set with the Text property and not the Label property as explained below in the section #Standard Attributes that can be Applied to all Filter/Find Panels.

Multiple Clones

You can clone a column or details control more than once and in this way create a filter/find range. For example you can filter between two dates by cloning a dtp control twice - the first to be the 'from' date and the second to be the 'to' date. To make multiple clones add '-1' and '-2' to the end of the column/control name:

  ExtraFilterControls: [dtpDetailGlEffectiveDate-1, dtpDetailGlEffectiveDate-2]

Then combine this with attributes in the ControlAttributes section:

  dtpDetailGlEffectiveDate-1: {Comparison=gte, Label=From GL Date}
  dtpDetailGlEffectiveDate-2: {Comparison=lte, Label=To GL Date}

Specifying Filter/Find Controls as a User-Defined Panel

This is the most complex method for creating Filter/Find controls and it will involve additional manual code, in particular to handle resulting events. But on some screens there is no alternative to a user-defined panel. You can have multiple user-defined panels if necessary. Here is an example of a user-defined panel from the UC_GLBatches.yaml file

       pnlFilterAndFind:
           ExpandedWidth: 180
           InitiallyExpanded: true     
           ShowApplyFilterButton: FilterContext.ExtraFilterOnly
           ShowFilterIsAlwaysOnLabel: FilterContext.StandardFilterOnly
           ShowKeepFilterTurnedOnButton: FilterContext.ExtraFilterOnly
           FilterControls: [pnlBatchFilter]
           ExtraFilterControls: [Column:BatchNumber, txtDetailBatchDescription, txtDetailBatchControlTotal]
           FindControls: [Column:BatchNumber, txtDetailBatchDescription]
           Panels:
               pnlBatchFilter:
                   Controls: [rgrShowBatches, cmbYearFilter, cmbPeriodFilter]
                   
                   rgrShowBatches: 
                       Label: Show batches for
                       ClearButton: false
                       OptionalValues: [Posting, =Editing, All]
                       
                   cmbYearFilter: {OnChange=RefreshPeriods, ClearButton=false, Width=100}
                   cmbPeriodFilter: {OnChange=RefreshFilter, ClearButton=false, Width=250}
                   rbtPosting: {OnChange=RefreshFilter}
                   rbtEditing: {OnChange=RefreshFilter}
                   rbtAll: {OnChange=RefreshFilter}


To use such a panel you simply add a Panel control that is not part of the main screen control list to the relevant list of controls. In the example above, this is pnlBatchFilter. Then you define pnlBatchFilter beneath the Panels: element.

Each special Panel needs

  • A Controls element containing a list of controls
  • An element for each of the specified controls. These controls will require attributes to set their properties and to hook up to events. See the standard properties in the next section.
    • You can define any of the cloneable controls listed above - a Panel or GroupBox, or a TextBox, CheckBox or ComboBox.

Notice in the above example that you can specify an attribute for a radio button by using its implied name - e.g. rbtPosting for an OptionalValue of Posting - even though you have not explicitly declared an rbtPosting control.

Events Applied to a User-Defined Panel

You should not connect an existing event that is fired from the main part of the screen to a control on the filter panel. This is likely to give rise to enormous complexity and the event firing multiple times in different circumstances. In our experience user-defined panels should not implement any change events directly. Rather, the best solution is to centralise the filtering code for the whole screen inside a manual implementation of ApplyFilterManual(). This becomes the single place where all filtering is done, whatever the individual control change that invokes it. For examples see one or all of these:

  • Ict/Petra/Client/MFinance/Gui/Gift/UC_GiftBatches.LoadAndFilter.ManualCode.cs,
  • Ict/Petra/Client/MFinance/Gui/GL/UC_GLJournals.LoadAndFilter.ManualCode.cs or
  • Ict/Petra/Client/MFinance/Gui/Setup/SetupDailyExchangeRate.ManualCode.cs

Standard Attributes that can be Applied to all Filter/Find Panels

These are the attributes that can be applied to all Filter/Find panels. They will appear beneath the ControlAttributes element.

  • Label - the text for the label. Note that the text (label) of a CheckBox is set by its 'Text' attribute - see below
  • NoLabel - set this to true to have no label. There is no need to do this explicitly for CheckBoxes. If the cloned-from check box has a label and no Text, the label text is applied as the control text, but if you specify label and control text they will both be preserved.
  • Text - the text for a control - usually a CheckBox
  • Width - the width of the control. The width cannot end up larger than the ExpandedWidth property of the Filter/Find panel. If you set it to a larger number it will display with the maximum width possible.
  • ClearButton - set this to false if you do not want a Clear Button
  • ClearValue - set this to a non-standard value if you want the clear button to have a different behaviour from blanking text or setting check boxes to an indeterminate state.
  • OnChange - specify an event handler in the manual code to handle this event
  • OptionalValues - a list for each radio button in a group
  • Comparison - one of: gt, gte, lt, lte, eq. These have the meaning 'greater than', 'greater than or equal', 'less than', 'less than or equal' and 'equal'. If you do not specify a comparison the default text comparison is 'LIKE'. This attribute applies to the Filter panel.
  • FindComparison - this attribute belongs to the Find panel (so you can have a different comparison rule for the filter and find panels). In addition to the attributes associated with Comparison above, you can also include 'StartsWith', 'EndsWith', or 'Contains' for the Find Panel.
  • CloneToComboBox - set this to true if you want to clone a radio button group (rgr) to a ComboBox containing the radio button items.
  • List - sets the 'ListTable' property of the combo box to 'TCmbAutoPopulated.TListTableEnum.attributevalue'.
  • In principle you can add any other property that is recognised by .NET. If the attribute value looks like a number, or 'true' or 'false' or contains a dot so is a .NET property then the attribute value will be used directly. Otherwise it will be interpreted as a string value and placed in quotes.
  • You do NOT need to specify a SuppressChangeDetection attribute for your controls. All Filter/Find panel controls always have this attribute set automatically.

You set these attributes for a particular control by writing an element for the control name either beneath a ControlAttributes: section when using a clone from the details panel or beneath the specific panel element when creating a dynamic panel. See the examples above. For clones from the details panel note that

  • The control name when cloned from a column is either txtColumnName or chkColumnName because columns are always cloned to a TextBox or CheckBox
  • The control names for multiple clones (see above) have the '-1' or '-2' appended.

Using ComboBoxes on the Filter/Find Panel

Open Petra uses a variety of ComboBox styles on its GUI screens.

  • TCmbAutoComplete is derived from a standard Windows ComboBox, but it adds the functionality to display the full text of an entry (and select it) as the user starts to type. This is a very nice capability. This style of ComboBox can also accept new values that are not included in the list. It supports a DataSource containing a ValueMember and a DisplayMember (so each item could for example have a numeric value and a corresponding non-numeric text display).
  • TCmbVersatile derives from TCmbAutoComplete. It adds up to 4 more columns so that it becomes possible to display a descriptive label along with the drop-down text and value options. Crucially this style does not allow new values although of course it can include a 'blank' entry.
  • TCmbLabelled is a user control that contains a TCmbVersatile box and additional controls to show label text etc.
  • TCmbAutoPopulated is a further derivative of TCmbLabelled so that it can be populated automatically by referring to a database specific table.

You can see that each style becomes progressively more complex and by the time we get to the auto-populated Combo there are many more features than we need for the Filter/Find panel.

For the Filter/Find panel we always use a TCmbAutoComplete for the following reasons

  • The auto-complete feature is nice
  • It can accept a 'blank' entry which has the meaning: 'Do not filter on this column'.
  • It accepts text that is the start of an entry or entries, or even text that is in the middle of an entry or entries. This means that, whereas an auto-completed entry might only return one row, a part entry can return several, depending on the list content.
  • It supports content that is either simple strings or content from a DataSource that contains value and display members.

In all cases where we are cloning from the details panel controls this gives a good solution. If you are using a ComboBox on a user-defined panel (such as the example above) you may have to write special code to populate the box or adapt some existing code that expects a different type of ComboBox.

If a Filter/Find panel ComboBox has no clear button then it does NOT accept new or blank entries and it behaves like a fixed drop-down list. This is likely the bahviour that you want on a filter panel where the filter is always on. Generally speaking dynamically created filter panels will not have clear buttons on their controls.

Manual Code and the Filter/Find Panel

The simplest screens will need no manual code at all. Beyond that

  • you can take advantage of a few optional manual methods that the automatic code will call if they are present in the -Manual.cs file.
  • you can access the individual controls on the panel(s) and modify their properties directly.
  • you will need to implement event handlers for any OnChange attributes you have created for a user-defined panel.

Most of the filter/find code is contained within a FilterFind object that exposes an interface IFilterAndFind that is implemented by the auto-generated client code. If you need to see the low level filter/find implementation it is contained in

  • Ict.Petra.Client.CommonControl.Gui
    • FilterFindCloneExtensions.cs
    • FilterFindHelper.cs
    • ucoFilterAndFind.cs
  • Ict.Petra.Client.CommonForms
    • FilterAndFindPanel.cs (this is the file that contains the interface)

New Form-Level Variables

A screen with a Filter/Find panel has one new Form-Level variable - FFilterAndFindObject. You use this to access all the filter/find properties and methods. The most useful properties return a reference to the internal filter/find objects themselves:

  • FilterFindPanel: This is the filter/find panel object itself. In actual fact it will be rare for you to need direct access to this object because other variables are more relevant to the needs of the manual programmer.
  • FilterAndFindParameters: this structure contains useful information about the initial set up of the panel, including its expanded width and the contexts in which the additional buttons are displayed.
  • FilterPanelControls and FindPanelControls: These two members hold references to all the labels and filter/find controls on the relevant panel. You use these form level variables to access a particular control by one of a variety of methods.

These four variables should give you access to everything you need to manually program the panel behaviour.

The FilterPanelControls object has an important method that you will need to set/use on complex screens with user-defined panels

  • SetBaseFilter(string ABaseFilter, bool ABaseFilterShowsAllRecords)

If you have code that can affect the default RowFilter you need to call this method so that the Filter panel can include your manual filter with the filter that the user is specifying through the dynamic panel. The Boolean parameter is true if the filter you are specifying would, on its own, show all the records that the user would consider to see. Call the method with 'false' if your filter only shows a sub-set of all the records. For example, on the finance screens there is sometimes a radio button group for 'Posted', 'Editing' and 'All'. You would set the All' option to pass 'true' but the other two cases to pass 'false' because they only show a sub-set of the records for the given year.

Names of Controls

Internally all controls on the filter panel have a name ending with "_filter". All controls on the find panel have a name ending with "_find". This is to ensure that they are uniquely identified and are different from the original control they were cloned from. Even controls that are created for a user-defined panel are cloned from a prototype that you specified. When you want to locate a control on a filter or find panel you access it using its 'original' cloned-from name, so you never need to be aware of the internal name.

Methods to Work With a Panel Control

The FilterPanelControls variable has two sub-members:

  • FilterPanelControls.FStandardFilterPanels
  • FilterPanelControls.FExtraFilterPanels

FindPanelControls has just one sub-member:

  • FindPanelControls.FFindPanels

The FilterPanelControls and FindPanelControls classes both have methods that return an individual control or label. All these methods search on both the standard panel and the extended panel (if it exists). They take one of these forms

  • FindControlByName(string AControlName) returns a Control that has the specified name
  • FindControlByClonedFrom(Control AFromControl) returns a control that was cloned from the specified control

When you have a reference to the control you are interested in you can cast it to its actual type and work with its properties, methods or events.

Or you can get a complete Panel object using one of these:

  • FindPanelByColumnName(string AColumnName) returns the filter panel that relates to the specified column name. The column name is the full database column name (like a_currency_name_c). When you have the Panel object you have access to its individual controls (see below).
  • FindPanelByClonedFrom(Control AFromControl) returns the panel that contains the control that was cloned from the specified control. When you have the Panel object you have access to its individual controls (see below).

The TIndividualFilterFindPanel class has these properties

  • PanelLabel as a Label (may be null)
  • PanelControl as a Control
  • DBColumnName as a string (may be null)
  • DBColumnDataType as a string (e.g. bit, integer, number, varchar) (may be null)
  • FilterComparison as a string (may be null)
  • FindComparison as a string (may be null)
  • HasClearButton as a Boolean

Examples

This example changes the label text to something shorter than the text that it was cloned from

 FFilterAndFindObject.FilterPanelControls.FindControlByClonedFrom(lblDetailEmailAddress).Text = "Email address";

Notice that although there was no lblDetailEmailAddress specified on the original details panel the label control takes its name from the txtDetailEmailAddress TextBox that is specified in the YAML. Note too that this is an unlikely example because you can more easily specify the Label text in the original YAML than in your manual code.

This example sets the checked state of a radio button

 ((RadioButton)FFilterAndFindObject.FilterPanelControls.FindControlByName("rbtEditing")).Checked = true;

Manual Code Placeholders

Use one or more of the following placeholders for your manual code.

To add manual code after the automatic code has generated the Filter/Find panels from the YAML

 private void CreateFilterFindPanelsManual()

To add manual code in response to the filter panel being toggled on/off. The parameter value represents the new state of the panel.

 private void FilterToggledManual(bool AIsCollapsed)

To modify the filter immediately before it gets applied. The parameter contains the filter that is about to be applied.

 private void ApplyFilterManual(ref AFilterString)

To modify the behaviour of the Find. The parameter is the row to test for a match. Return true if you want the row to be selected.

 private bool IsMatchingRowManual(DataRow ARowToMatch)

Setting the DefaultView RowFilter Manually

When you are coding complex screens you will very often respond to some user input by needing to modify the DefaultView RowFilter. You need to understand that the manual code no longer 'owns' this RowFilter, because the user can interact with the RowFilter through the GUI and all of this is handled automatically in auto-generated code or the user control.

If you need to work with a filtered DataView in manual code the first thing to ask is: Do I need to use the DefaultView for this purpose? Sometimes you just need a filtered view in order to get at a specific data item and you can achieve this by creating a new DataView, finding out what you need and then letting the DataView go out-of-scope. But if you actually do want to modify the grid's content, you must now take the following steps:

  • Construct your string which is the RowFilter that you want to apply (ignoring any additional filtering that the user has enetered in the GUI).
  • Call the method
    • FFilterAndFindObject.FilterPanelControls.SetBaseFilter(string AFilter, bool AFilterShowsAllRecords)
      • where AFilter is your filter string and AFilterShowsAllRecords set to true if this filter is showing all possible records for this screen or set to false if the filter is showing a limited subset of all records that the screen might show. If you set false the record counter will always be in italic font even if the user does not enter any additional filtering using the GUI.
  • Now call the method
    • FFilterAndFindObject.ApplyFilter() The filter is now applied along with any additional user-defined filtering.
      • The ApplyFilter call also includes calls to the following methods but you may have occasions when you need to call them explicitly:
        • UpdateRecordNumberDisplay(). This will update the record count.
        • SetRecordNumberDisplayProperties(). This ensures that the font is correct, so call this if there may have been a change to whether the grid is showing all/some records.

Clearing the Filter from Manual Code

FFilterAndFindObject.FilterPanelControls has a method for clearing all the discretionary (user-defined) filters. This is effectively the same as clicking all the individual clear buttons.

  • FFilterAndFindObject.FilterPanelControls.ClearDiscretionaryFilters()
    • When you call this method on a filter panel that has no Apply Now button the filter will automatically be applied due to the standard events being fired. However if the panel does have an Apply Now button, you will have to call ApplyFilter() after the call to clear the discretionary filters.

Using the Filter or Find Functionality

A final note about the use of filter and find. How does the user interact with the GUI?

  • Check boxes are tri-state, so when unchecked they match false, when checked they match true and in the indeterminate state they match either.
  • Text boxes usually match the specified sequence of characters anywhere in the string. The match is not case-sensitive. If the data is numeric the match depends on the comparison specified in the YAML. The default uses 'LIKE', so matches the specified sequence of numbers anywhere in the data - so 21 matches 21, 121, 210, 211, 521 and so on. However the YAML may specify, for example, 'greater than or equals' and in that case that specific comparison is applied.
  • ComboBoxes (with a clear button) behave like text boxes - it is the text that matters. If you start typing into a ComboBox it will auto-complete your text to the first matching item in its list - but the remaining characters will be 'selected' (highlighted in blue) so you can press the delete key and now you may well match more rows in the grid. A ComboBox containing no text matches all rows.