Popup Container Editor

  • 10 minutes to read

DevExpress WinForms controls suite includes multiple look-up editors and editors with popup panels. All of these editors have a predefined layout. For instance, the TreeListLookUpEdit editor displays a TreeListControl in its popup window: you can only tweak this TreeList, but not replace it with another control. If you need a custom popup window layout, or the control embedded in a standard lookup editor's popup window cannot be edited in the way you need it, create a custom editor with the PopupContainerEdit / PopupContainerControl controls combo.

PopupEdit

PopupContainerEdit

PopupContainerControl

A textbox-like editor with a drop-down button. When a user clicks this button, a PopupContainerControl assigned to the editor's RepositoryItemPopupContainerEdit.PopupControl property is shown. If no panel was assigned to this property, the button does nothing.
image-pce

A panel available from the Visual Studio Toolbox as a separate control. Drop any controls onto this panel, then assign it to a RepositoryItemPopupContainerEdit.PopupControl property. To populate this panel in code, add controls to the panel Controls collection.
image-pcc

Example 1: Create a lookup editor with a RichTextBox within

The following example creates a PopupContainerControl with a RichTextBox inside, and assigns this control to a PopupContainerEdit editor.

  RichTextBox rtb = new RichTextBox();
  rtb.Dock = DockStyle.Fill;
  PopupContainerControl popupControl = new PopupContainerControl();
  popupControl.Controls.Add(rtb);

  PopupContainerEdit editor = new PopupContainerEdit();
  editor.Properties.PopupControl = popupControl;
  Controls.Add(editor);

EditValue and Display Text. Editor Events

As any other DevExpress Editor, the PopupContainerEdit has an EditValue property that stores the actual editor value, and the Text property - the currently displayed editor text. Since you can build any popup panel layout, the editor cannot automatically retrieve values for these two properties, and you have to handle events to provide them.

The following figure illustrates the Popup Container Edit demo available in the DevExpress Demo Center.

demo

The "popupContainerControlSample" is a popup panel for the "popupContainerEditSample" editor. The "popupContainerControlSample" contains three editors:

The "lbSample" label contains a sample text written with the font selected in the "popupContainerEditSample" editor. This is a two-way relation: when you open the editor popup window, font selectors focus values depending on the current label font. When you change font selector values, selected font settings are applied to the label.

Current font is stored in the helper "CurrentFont" class.

Font CurrentFont {
    get { return lbSample.Font; }
    set { lbSample.Font = value; }
}

string CurrentFontCaption {
    get { return string.Format("{0}, {1}, {2}", CurrentFont.Name, CurrentFont.Size.ToString(CultureInfo.InvariantCulture), CurrentFont.Style); }
}

string CurrentFontStyle {
    get { return CurrentFont.Style.ToString(); }
}

The list below illustrates main editor events that you should handle to provide a similar functionality.

  • PopupBaseEdit.QueryPopUp - fires when a user clicks the editor drop-down button. Allows you to perform specific actions or forcibly close the popup panel. In the demo module, the QueryPopUp event handler reads the current "lbSample" font and selects corresponding font selectors' values.
private void popupContainerEditSample_QueryPopUp(object sender, System.ComponentModel.CancelEventArgs e) {
    ilbFont.SelectedValue = CurrentFont.Name;
    seSize.Value = Convert.ToDecimal(CurrentFont.Size);
    foreach(CheckedListBoxItem item in clbStyle.Items)
        item.CheckState = (CurrentFontStyle.IndexOf(item.Value.ToString()) > -1) ? CheckState.Checked : CheckState.Unchecked;
}
  • RepositoryItemPopupContainerEdit.QueryResultValue - allows you to set the editor's EditValue. In the demo module, a new CurrentFont object is created from current font selector values. The custom "CurrentFontCaption" method assigns the string representation of this object to the EditValue.
private void popupContainerEditSample_QueryResultValue(object sender, DevExpress.XtraEditors.Controls.QueryResultValueEventArgs e) {
    CurrentFont = new Font(ilbFont.SelectedValue.ToString(), Convert.ToSingle(seSize.Value), GetFontStyleByValues(clbStyle));
    e.Value = CurrentFontCaption;
}
  • RepositoryItemPopupContainerEdit.QueryDisplayText - allows you to set a custom editor text. If you do not handle this event, the editor will try to convert its EditValue into a string, and display the result. In this demo, the QueryDisplayText event is not handled and the display text matches the EditValue. The code sample below illustrates how to invert this EditValue (e.g., if the EditValue is "Tahoma, 8.5, Bold" the editor display text is "Bold, 8.5, Tahoma").
private void PopupContainerEditSample_QueryDisplayText(object sender, QueryDisplayTextEventArgs e)
{
    string editValueString = e.EditValue as string;
    string[] sArray = editValueString.Split(',');
    if (sArray.Length > 1) e.DisplayText = sArray[2] + ", " + sArray[1] + ", " + sArray[0];
}
  • PopupBaseEdit.CloseUp - fires if a user closes the popup panel or presses the ESC key to discard all edits.

Add Buttons

If you need buttons that should apply and\or discard edits made in the popup window, handle their Click events and call the PopupBaseEdit.ClosePopup() and PopupBaseEdit.CancelPopup() methods.

The code below illustrates how to create an editor that shows a ListBox in its popup window. The PopupContainerControl panel has "OK" and "Cancel" buttons.

PopupContainerEdit_QueryResultValue_example

using DevExpress.XtraEditors.Controls;

public Form1() {
    InitializeComponent();
    //...
    popupContainerEdit1.EditValue = new ArrayList();
}

private void popupContainerEdit1_QueryResultValue(object sender, QueryResultValueEventArgs e) {
    //Get the list box displayed in the editor's dropdown
    ListBox listBox = findChildListBox((sender as PopupContainerEdit).Properties.PopupControl);
    //Add selected items to the array specified by the editor's edit value
    ArrayList ar = e.Value as ArrayList;
    ar.Clear();
    ar.AddRange(listBox.SelectedItems);
}

private void popupContainerEdit1_QueryDisplayText(object sender, QueryDisplayTextEventArgs e) {
    //Return the string representing the selected item count
    int itemCount = (e.EditValue as ArrayList).Count;
    e.DisplayText = itemCount.ToString() + " item(s) selected";
}

private void buttonOk_Click(object sender, System.EventArgs e) {
    Control button = sender as Control;
    //Close the dropdown accepting the user's choice
    (button.Parent as PopupContainerControl).OwnerEdit.ClosePopup();
}

private void buttonCancel_Click(object sender, System.EventArgs e) {
    Control button = sender as Control;
    //Close the dropdown discarding the user's choice
    (button.Parent as PopupContainerControl).OwnerEdit.CancelPopup();
}

private void popupContainerEdit1_CloseUp(object sender, CloseUpEventArgs e) {
    //Restore selected items if the user's choice should be discarded
    if (! e.AcceptValue) {                
        PopupContainerEdit popupContainerEditor = sender as PopupContainerEdit;
        //Get the list box displayed in the dropdown of the popup container editor
        ListBox listBox = findChildListBox(popupContainerEditor.Properties.PopupControl);
        if (listBox != null) {
            //Clear existing selection
            listBox.ClearSelected();
            //Restore selection from the editor's edit value
            foreach(object o in popupContainerEditor.EditValue as ArrayList)
                listBox.SetSelected(listBox.Items.IndexOf(o), true);
        }
    }
}

//Locates and retrieves a child ListBox control contained within the specified control
private ListBox findChildListBox(Control parent) {
    foreach(Control control in parent.Controls)
        if (control is ListBox) return (ListBox)control;
    return null;
}

Example 2: Create a GridLookUpEdit analogue that allows users to select multiple values

The GridLookUpEdit control is a lookup editor that displays a GridControl in its popup panel. This editor allows users to select only one Grid row at a time. If you need the multi-select functionality, create a custom PopupContainerEdit editor with a GridControl in its PopupContainerControl.

grid-multiselect

The code below illustrates how to retrieve selected Grid rows, convert them to comma-separated values (CSVs), and pass them to the editor's EditValue.

void ri_QueryResultValue(object sender, DevExpress.XtraEditors.Controls.QueryResultValueEventArgs e) {
    int[] selectedRows = popupGridView2.GetSelectedRows();
    //get selected rows' handles
    List<string> values = new List<string>();
    foreach(int rowHandle in selectedRows) {
        //get values of all cells that belong to selected rows and the "Name" column
        values.Add(popupGridView2.GetRowCellValue(rowHandle, "Name").ToString());
    }
    //combine retrieved cell values into a string
    string csv = String.Join(", ", values);
    //assign the string value to the EditValue property
    e.Value = csv;
}

Vice versa, when a user enters values into the editor's textbox, the Grid should select rows that match these values. The code below illustrates how to do this.

void Properties_EditValueChanged(object sender, EventArgs e) {
    PopupContainerEdit edit = sender as PopupContainerEdit;
    UpdateSelection(edit, popupGridView2);
}

//select Grid rows
private void UpdateSelection(PopupContainerEdit edit, GridView view) {
    view.BeginSelection();
    view.ClearSelection();
    if(edit != null)
        if(edit.EditValue != null) {
            edit.Focus();
            //retrieve handles for rows whose "Name" column cells match values entered by users
            List<int> selection = GetSelection(edit.EditValue.ToString()
              .Split(new string[] { ", " }, StringSplitOptions.None), "Name", view);
            foreach(int rowHandle in selection) {
                view.SelectRow(rowHandle);
            }
        }
    view.EndSelection();
}

//return handles of all rows whose cells store given strings under the required column
private List<int> GetSelection(string[] values, string fieldName, GridView view) {
    List<int> selection = new List<int>();
    foreach(string val in values) {
        for(int i = 0; i < view.RowCount; i++) {
            if(view.GetRowCellValue(i, fieldName).ToString() == val)
                selection.Add(i);
        }
    }
    return selection;
}

Complete sample on GitHub: How to provide the MultiSelect functionality for GridLookUpEdit

See also: How to emulate an editable GridLookUpEdit with PopupContainerEdit