Add User Control to GridView Column

20 posts, 1 answers
  1. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 06 Oct 2011 Link to this post

    I want to add a column to the GridView that will allow me to assign and display multiple values for an item.

    For example, imagine you want to track the types (home, office, cell) of telephone numbers for users. Note, this is not my actual requirement.  A user can have multiple telephone number types. I don't want to display two separate rows in the GridView for the same user, though. I want to display one row per user, and have the telephone types column allow the display and assignment of multiple telephone number types (e.g., home, office, cell).

    A co-worker came up with the idea of creating a User Control that would contain a combobox control to allow the user to select the telephone number types in the combobox and display the selected values in a listbox below it. I specifically chose types for this example, because there's a limited, known set of them-- that's where the idea of the combobox control came in, that is, I need to choose from a limited set of choices.

    I looked at the Forums blog titled, "Possible to Add a User Control a A Grid Column," at the following url:
     http://www.telerik.com/community/forums/winforms/gridview/possible-to-add-a-user-control-to-a-grid-column.aspx 
     . . . and tried to implement the solution therein, but when I tried defining a variable of type GridViewDataColumn I got the following design-time error message:
     'New' cannot be used on a class that is declared 'MustInherit'.

    I also tried defining a variable of GridViewColumn, but got the same error message.

    Is there a way to add a User Control to a GridView?

    As a separate, but related issue, can I add a LinkLabel control to the GridView, and can the GridViewComboBoxColumn display multiple selections?

  2. Emanuel Varga
    Emanuel Varga avatar
    1336 posts
    Member since:
    May 2010

    Posted 07 Oct 2011 Link to this post

    Hello Hector,

    I've provided a solution like this in the past, but you don't really need a new column, you can simply define and use a custom editor for that kind of cells, please take a look at the example I've provided here and also this KB Article on how to add custom elements inside a cell when in design mode.

    Hope this helps, if you have any other questions or comments, please let me know,

    Best Regards,
    Emanuel Varga

    Telerik WinForms MVP
  3. UI for WinForms is Visual Studio 2017 Ready
  4. Jack
    Admin
    Jack avatar
    2335 posts

    Posted 07 Oct 2011 Link to this post

    Hi Hector,

    You can show multiple values in a cell by placing a list element inside. It will add an automatic scrolling when there are more items than the cell can show. Please consider the following sample:
    DataTable table = new DataTable();
    table.Columns.Add("ID", typeof(int));
    table.Columns.Add("Name", typeof(string));
    table.Columns.Add("Phones", typeof(string));
     
    table.Rows.Add(1, "John Smith", "431-235-235\n235-255-333\n233-323-323");
    table.Rows.Add(2, "Tomas Wright", "431-235-235\n235-255-333");
    table.Rows.Add(2, "Lewis Anderson", "");
     
    this.radGridView1.DataSource = table;
    this.radGridView1.TableElement.RowHeight = 80;
    this.radGridView1.AllowAddNewRow = false;
     
    this.radGridView1.Columns.Remove("Phones");
    this.radGridView1.Columns.Add(new ListBoxColumn("Phones"));
     
    this.radGridView1.Columns["Name"].Width = 90;
    this.radGridView1.Columns["Phones"].Width = 150;
     
    public class ListBoxColumn : GridViewDataColumn
    {
        public ListBoxColumn(string name)
            : base(name)
        {
        }
     
        public override Type GetCellType(GridViewRowInfo row)
        {
            if (row is GridViewDataRowInfo)
            {
                return typeof(ListBoxCell);
            }
            return base.GetCellType(row);
        }
    }
     
    public class ListBoxCell : GridDataCellElement
    {
        RadListElement listElement;
     
        public ListBoxCell(GridViewColumn column, GridRowElement row)
            : base(column, row)
        {
        }
     
        protected override void CreateChildElements()
        {
            base.CreateChildElements();
     
            listElement = new RadListElement();
            this.Children.Add(listElement);
        }
     
        protected override void SetContentCore(object value)
        {
            listElement.Items.Clear();
            if (value != null && value != DBNull.Value)
            {
                string[] phones = value.ToString().Split('\n');
                foreach (string phone in phones)
                {
                    listElement.Items.Add(new RadListDataItem(phone));
                }
            }
        }
     
        public override bool IsCompatible(GridViewColumn data, object context)
        {
            return data.Name == "Phones" && context is GridDataRowElement;
        }
    }

    I hope it helps. If you need further assistance with this task, we will be glad to help.
     
    Greetings,
    Jack
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  5. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 10 Oct 2011 Link to this post

    Firstly, let me thank both Emanuel and Jack. I think I need a combination of the two solutions, though. First, I need the listbox aspect that Jack provided for displaying the values, but then I also need the custom editor aspect that Emanual provided. I'm not sure, however, how to combine the two. I'm going to give it a try.

    In essence, though, I want to be able to display multiple values (listbox aspect). When I edit, I want to have a combobox control to choose one of several possible choices, when I choose the item in the combobox, that item gets added to the listbox for display.

    I also thought I would need two LinkLabel controls (one that displays Done, another displays Remove), perhaps located under the listbox. Clicking on Done would close editing, and leave the listbox as the display control. Clicking on Remove while editing, removes a selected item from the listbox.

    So to recap, a combobox (perhaps located at the top during editing) for adding items, listbox (located just below the combobox) for displaying items, and two link labels (Done and Remove).

    Thanks for your prompt reply,

    Hector
  6. Answer
    Jack
    Admin
    Jack avatar
    2335 posts

    Posted 11 Oct 2011 Link to this post

    Hello Hector,

    Thank you for these details. I modified my example to include link labels and a drop down list as you have required. Please consider the code below:
    public class LinkItem : LightVisualElement
    {
        static Font linkFont = new Font("Segoe UI", 9f, FontStyle.Underline);
     
        public LinkItem(string text)
        {
            this.Text = text;
        }
     
        protected override void InitializeFields()
        {
            base.InitializeFields();
     
            this.Font = linkFont;
            this.ForeColor = Color.Blue;
            this.StretchHorizontally = false;
            this.StretchVertically = false;
            this.Alignment = ContentAlignment.MiddleRight;
        }
    }
      
    public class ListBoxCell : GridDataCellElement
    {
        RadListElement listElement;
        LinkItem linkDone;
        LinkItem linkRemove;
        StackLayoutElement linksContainer;
        RadDropDownListElement dropDownListElement;
     
        public ListBoxCell(GridViewColumn column, GridRowElement row)
            : base(column, row)
        {
        }
      
        protected override void CreateChildElements()
        {
            base.CreateChildElements();
     
            StackLayoutElement stack = new StackLayoutElement();
            stack.Orientation = Orientation.Vertical;
            stack.StretchHorizontally = true;
            stack.StretchVertically = true;
            this.Children.Add(stack);
     
            dropDownListElement = new RadDropDownListElement();
            dropDownListElement.Visibility = ElementVisibility.Collapsed;
            dropDownListElement.StretchVertically = false;
            stack.Children.Add(dropDownListElement);
     
            listElement = new RadListElement();
            listElement.StretchVertically = true;
            listElement.StretchHorizontally = true;           
            stack.Children.Add(listElement);
     
            linksContainer = new StackLayoutElement();
            linksContainer.StretchVertically = false;
            linksContainer.StretchHorizontally = true;
            linksContainer.Visibility = ElementVisibility.Collapsed;
            stack.Children.Add(linksContainer);
     
            linkDone = new LinkItem("Done");
            linksContainer.Children.Add(linkDone);
     
            linkRemove = new LinkItem("Remove");
            linksContainer.Children.Add(linkRemove);
        }
     
        public override void UpdateInfo()
        {
            base.UpdateInfo();
            if (linksContainer != null)
            {
                linksContainer.Visibility = RowInfo.IsCurrent ? ElementVisibility.Visible : ElementVisibility.Collapsed;
                dropDownListElement.Visibility = RowInfo.IsCurrent ? ElementVisibility.Visible : ElementVisibility.Collapsed;
            }
        }
     
        protected override void SetContentCore(object value)
        {
            listElement.Items.Clear();
            if (value != null && value != DBNull.Value)
            {
                string[] phones = value.ToString().Split('\n');
                foreach (string phone in phones)
                {
                    listElement.Items.Add(new RadListDataItem(phone));
                }
            }
        }
     
        protected override SizeF MeasureOverride(SizeF availableSize)
        {
            if (linksContainer.Visibility == ElementVisibility.Visible)
            {
                listElement.MaxSize = new System.Drawing.Size(0, (int)availableSize.Height - 40);
            }
            else
            {
                listElement.MaxSize = Size.Empty;
            }
            return base.MeasureOverride(availableSize);
        }
     
        public override bool IsCompatible(GridViewColumn data, object context)
        {
            return data.Name == "Phones" && context is GridDataRowElement;
        }
    }

    Could you please describe in detail the exact behavior and look that you want to achieve. This will help me to better understand the scenario and to assist you further. 
     
    Best wishes,
    Jack
    the Telerik team

    Q2’11 SP1 of RadControls for WinForms is available for download (see what's new); also available is the Q3'11 Roadmap for Telerik Windows Forms controls.

  7. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 12 Oct 2011 Link to this post

    Jack,

    thanks for your prompt reply. I've implemented your example as you instructed to make sure I got the plumbing (or wiring) correct, before changing it to work with my actual data.

    I may leave it the way you designed it, but, just in case, can the ListBoxCell be made to expand, showing the RadDropDownListElement and LinkItems only when the cell is clicked?

    How can I populate the RadDropDownListElement with the list of possible choices?

    Follow-up:

    I presented the custom ListBoxCell to a user for review. Following is the user's commments:

    The drop-down blends in to the listbox, which appears just below it. How can I provide some spacing (padding) between the drop-down and the listbox?

    When the drop-down is selected, the list that drops down blends into the next data row's listbox values. Perhaps setting a different background color to the drop-down list during selection would help. What do you suggest?

    Can the Done link be aligned left, while the Remove link be aligned right?

    Thanks in advance,

    Hector

     

  8. Jack
    Admin
    Jack avatar
    2335 posts

    Posted 13 Oct 2011 Link to this post

    Hello Hector,

    Directly to your questions:

    Yes, you can "expand" the current row by handling the CurrentRowChanged event. Consider the following code:
    this.radGridView1.CurrentRowChanged += new CurrentRowChangedEventHandler(radGridView1_CurrentRowChanged);
    if (this.radGridView1.CurrentRow != null)
    {
        this.radGridView1.CurrentRow.Height = 100;
    }
     
    void radGridView1_CurrentRowChanged(object sender, CurrentRowChangedEventArgs e)
    {
        if (e.OldRow != null)
        {
            e.OldRow.Height = 23;
        }
        if (e.CurrentRow != null)
        {
            e.CurrentRow.Height = 100;
        }
    }

    In addition, I modified the UpdateInfo method to hide the cell content until the row becomes current:
    public override void UpdateInfo()
    {
        base.UpdateInfo();
        if (linksContainer != null)
        {
            linksContainer.Visibility = RowInfo.IsCurrent ? ElementVisibility.Visible : ElementVisibility.Collapsed;
            dropDownListElement.Visibility = RowInfo.IsCurrent ? ElementVisibility.Visible : ElementVisibility.Collapsed;
            this.Children[0].Visibility = RowInfo.IsCurrent ? ElementVisibility.Visible : ElementVisibility.Collapsed;
        }
    }

    You can populate the RadDropDownListElement  when handling the CreateChildItems event. Here is an example:
    DataTable table = new DataTable();
    table.Columns.Add("ID", typeof(int));
    table.Columns.Add("Name", typeof(string));
    table.Rows.Add(1, "one");
    table.Rows.Add(2, "two");
    table.Rows.Add(3, "three");
    dropDownListElement.DisplayMember = "Name";
    dropDownListElement.ValueMember = "ID";
    dropDownListElement.DataSource = table;

    To add spacing, you can use the  ElementSpacing property of StackLayoutElement:
    StackLayoutElement stack = new StackLayoutElement();
    //...
    stack.ElementSpacing = 3;

    or you can add Margin in RadDropDownListElement:
    dropDownListElement.Margin = new System.Windows.Forms.Padding(0, 0, 0, 4);

    You can customize almost every aspect of our controls by using the Visual Style Builder tool and modifying any of our existing themes. However, if you prefer to use code, you can customize the background color for the popup by using the following code:
    dropDownListElement.ListElement.BackColor = Color.LightGray;
    dropDownListElement.ListElement.GradientStyle = GradientStyles.Solid;

    You can align the link buttons by adding a stretchable element between them, as demonstrated below:
    linkDone = new LinkItem("Done");
    linksContainer.Children.Add(linkDone);
     
    LightVisualElement stretchElement = new LightVisualElement();
    stretchElement.StretchHorizontally = true;
    linksContainer.Children.Add(stretchElement);
     
    linkRemove = new LinkItem("Remove");
    linksContainer.Children.Add(linkRemove);

    I attached the modified code here for your convenience. I hope it helps.

    Greetings,
    Jack
    the Telerik team

    Q2’11 SP1 of RadControls for WinForms is available for download (see what's new); also available is the Q3'11 Roadmap for Telerik Windows Forms controls.

  9. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 13 Oct 2011 Link to this post

    Jack,

    thanks for your prompt reply. Regarding expanding the row, I only want to expand it when the specific cell column is selected (that is, I'm editing the cell). In our example, it would be the "Phones" column.

    By the way, FYI . . . I'm developing in VB.NET, so the code won't exactly help. But don't worry about converting it to VB.NET, I'm doing it while I read you code snippets.

    About the Visual Style Builder (VSB) is that a separate tool (I guess I can research this on my own)? Considering we're creating the control programmatically, can I still incorporate the VSB?

    Thanks in advance for your reply,

    Hector
  10. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 13 Oct 2011 Link to this post

    Jack,

    I took a look at the modified code because I wanted to find out where you placed the code that performs the following:

    if(this.radGridView1.CurrentRow != null) {...}

    However, I did not find that (nor any of the other new code snippets) in the attached file.

    Thnx,

    Hector
  11. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 13 Oct 2011 Link to this post

    After reviewing the new updates (spacing, background color, etc.), the user requested that i get rid of the Remove link, replacing it by adding a column to the listbox with an "x" that can be clicked on to remove the item.

    I figured the solution would be to use a GridView (instead of adding a column to the listbox), so I tried adding a RadGridViewElement instead of a RadListElement. Unfortunately, I couldn't figure out how to define the columns for the RadGridViewElement. Perhaps I don't understand the relation between the RadGridViewElement and the RadGridView.

    Can you help?

  12. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 13 Oct 2011 Link to this post

    I changed my application to use the actual data.

    My first issue appeared when I changed the way I populated the dropdownlistelement. Initially, I was populating it by hardcoding the data using Columns.Add. Then I changed it to call a subroutine that calls a stored procedure in the database using a DataReader. I called the subroutine from the CreateChildElements method. Using this approach, I got the following error message: 'Invalid attempt to call FieldCount when reader is closed.'

    I'm writing this from home right now so I don't have access to the code.

    Any suggestions would be greatly appreciated.

    Regards,

    Hector

  13. Jack
    Admin
    Jack avatar
    2335 posts

    Posted 14 Oct 2011 Link to this post

    Hello Hector,

    Regarding your questions:

    If you want to expand the row only when clicking on a specific cell, you have to use the CurrentCellChanged event instead of CurrentRowChanged event. Here is a sample:
    Private Sub radGridView1_CurrentCellChanged(sender As Object, e As CurrentCellChangedEventArgs) Handles RadGridView1.CurrentCellChanged
        If e.NewCell IsNot Nothing Then
            If e.CurrentCell IsNot Nothing AndAlso Not e.CurrentCell.RowInfo.Equals(e.NewCell.RowInfo) Then
                e.CurrentCell.RowInfo.Height = 23
            End If
            If e.NewCell.ColumnInfo.Name = "Phones" Then
                e.NewCell.RowInfo.Height = 100
            Else
                e.NewCell.RowInfo.Height = 23
            End If
        ElseIf e.CurrentCell IsNot Nothing Then
            e.CurrentCell.RowInfo.Height = 23
        End If
    End Sub

    Yes, Visual Style Builder is a separate tool used to create or modify new themes. You cannot use it directly from your application, however you can create a theme. Please consider the following help article.

    I added the code which checks whether the CurrentRow is Nothing in Form's OnLoad event after setting the data source. This code ensures that the CurrentRow will have the correct size in all cases.

    You cannot use RadGridViewElement in the same way you are using RadListElement. The best option in this case is to use custom visual elements in the list box. Here is the code:
    Public Class CustomListElement
        Inherits RadListVisualItem
        Private buttonX As RadButtonElement
     
        Protected Overrides ReadOnly Property ThemeEffectiveType() As Type
            Get
                Return GetType(RadListVisualItem)
            End Get
        End Property
     
        Protected Overrides Sub CreateChildElements()
            MyBase.CreateChildElements()
            buttonX = New RadButtonElement()
            buttonX.Alignment = ContentAlignment.MiddleRight
            buttonX.StretchHorizontally = False
            buttonX.StretchVertically = False
            buttonX.Text = "x"
            AddHandler buttonX.Click, AddressOf buttonX_Click
            Me.Children.Add(buttonX)
        End Sub
     
        Private Sub buttonX_Click(sender As Object, e As EventArgs)
            Dim listElement As RadListElement = Me.FindAncestor(Of RadListElement)()
            listElement.Items.Remove(Data)
        End Sub
    End Class

    You should handle also the CreatingVisualItem event to replace the default visual item:
    AddHandler listElement.CreatingVisualItem, AddressOf listElement_CreatingVisualItem
    Private Sub listElement_CreatingVisualItem(sender As Object, args As CreatingVisualListItemEventArgs)
         args.VisualItem = New CustomListElement()
    End Sub

    Regarding the last issue, I am not sure what causes it. Could you please send me your application. This will help me to locate the issue and provide you with accurate support.

    Greetings, Jack
    the Telerik team

    Q2’11 SP1 of RadControls for WinForms is available for download (see what's new); also available is the Q3'11 Roadmap for Telerik Windows Forms controls.

  14. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 14 Oct 2011 Link to this post

    Jack,

    I will try your last suggestions shortly. However, for now, here is some information on the exception I'm getting:

    InvalidOperationException was unhandled by user code
    View Detail:
    - InnerException: "Invalid attempt to call FieldCount when reader is closed."
      - StackTrace:
       at System.Data.SqlClient.SqlDataReader.get_FieldCount()
       at System.Data.Common.DbEnumerator.BuildSchemaInfo()
       at System.Data.Common.DbEnumerator.MoveNext()
       at Telerik.WinControls.Data.RadListSource`1.BindToEnumerable()
    ...

    Note: above is an excerpt of the StackTrace. I can provide the full StackTrace if you wish.

    ...

    My HandleException method, showed me that the statement that caused the exception to occur is the following (from PopulateDatabaseDependentControls() method in code, see code block, below):

      Me.dgBidItems.DataSource = bsBidItems

    Note, I change the following statement in the CreateChildElements method:
    From:
                    PopulateDropDownListElement__FromDatabase()
                    'PopulateDropDownListElement__UsingHardCodedValues()  ' COMMENTED OUT; NOT CURRENTLY USED
    To:
                    'PopulateDropDownListElement__FromDatabase()  ' COMMENTED OUT; NOT CURRENTLY USED
                    PopulateDropDownListElement__UsingHardCodedValues()

    ... and it works!

    FYI . . . Stored Procedure Returns data as such:

    BidItemID   CustomerDesc                               Plants                           DateCreated
    14              TestSaveItem201108121517a     PLAA, PLBB                      08/12/2011
    19              TestSaveItem201108121517b     PLAA, PLBB, PLCC             08/18/2011

    Code Excerpt:

        Private Sub InitializeBidItemsDataGrid()
            ' called by CustomInitialization after call to InitializeComponent() in New() constructor
      
            Try
                With Me.dgBidItems
      
                    ' Set Location, Size
      
                    .Left = 6 ' X
                    .Top = 22 ' Y: space for the Group Box Text
                    .Height = Me.gpItems.Height - 24
                    .Width = Me.gpItems.Width - 12
      
                    .TableElement.RowHeight = 60
      
                    ' Disable 'Click here to add a new row' row 
                    .AllowAddNewRow = False
                    ' Disable 'Drag a column here to group by that column' row
                    .ShowGroupPanel = False
      
                    Dim gvdCol As tlrkUI.GridViewDataColumn
      
                    gvdCol = New tlrkUI.GridViewCheckBoxColumn("colSelected")
                    gvdCol.HeaderText = ".."
                    gvdCol.Width = 20
                    .Columns.Add(gvdCol)
      
                    gvdCol = New tlrkUI.GridViewTextBoxColumn("BidItemID")
                    gvdCol.HeaderText = "BidItemID"
                    gvdCol.Width = 70
                    .Columns.Add(gvdCol)
      
                    gvdCol = New tlrkUI.GridViewTextBoxColumn("CustomerDesc")
                    gvdCol.HeaderText = "CustomerDesc"
                    gvdCol.Width = 140
                    .Columns.Add(gvdCol)
      
                    '
                    ' PlantsV9
                    '
                    '' comebackhere
                    .Columns.Remove("Plants")
                    gvdCol = New CustomGridViewColumn("Plants")
                    gvdCol.HeaderText = "Plants"
                    gvdCol.Width = 90
                    .Columns.Add(gvdCol)
      
                    gvdCol = New tlrkUI.GridViewTextBoxColumn("DateCreated")
                    gvdCol.HeaderText = "DateCreated"
                    gvdCol.Width = 70
                    .Columns.Add(gvdCol)
      
                End With
      
            Catch ex As Exception
                HandleException(ex)
            End Try
        End Sub
      
      
        Private Sub PopulateDatabaseDependentControls()
            ' called in response to handling User Control Load event
      
            If Me.iBidHeaderID <= 0 Then
                Exit Sub
            End If
      
            Dim startTime As DateTime = DateTime.Now
      
            Try
                Dim cmd As New SqlCommand
                Dim ds As DataSet = New DataSet()
                Dim da As SqlDataAdapter
                ' [sp_APP_BidItem_Get_ByBidHeaderID]
                Dim sSql As String = "dbo.sp_APP_GetBidItemsByBidHeaderID"
      
                Using dbconn As New SqlConnection(sMainConnectionString)
                    Try
                        dbconn.Open()
      
                        cmd.CommandType = CommandType.StoredProcedure
                        cmd.Connection = dbconn
                        cmd.CommandText = sSql
                        cmd.Parameters.Add("@BidHeaderID", System.Data.SqlDbType.Int).Value = Me.iBidHeaderID
      
                        da = New SqlDataAdapter(cmd)
                        da.Fill(ds, "BidItems")
      
                        If ds IsNot Nothing AndAlso ds.Tables IsNot Nothing AndAlso ds.Tables.Count > 0 Then
                            ' assign data binding data soures
                            bsBidItems = New BindingSource()
                            bsBidItems.DataSource = ds.Tables("BidItems").DefaultView()
      
    ' * * * * * NOTE: FOLLOWING IS THE STATEMENT THAT THE EXCEPTION OCCURS ON * * * * * * 
      
                            Me.dgBidItems.DataSource = bsBidItems  ' * * * * * NOTE: THIS IS THE STATEMENT THAT THE EXCEPTION OCCURS ON * * * * * * 
      
    ' * * * * * NOTE: ABOVE IS THE STATEMENT THAT THE EXCEPTION OCCURS ON * * * * * * 
                        End If
      
                    Catch sqlEx As SqlException
                        HandleException(sqlEx)
                    Finally
                    End Try
                End Using
      
            Catch ex As Exception
                HandleException(ex)
            End Try
      
        End Sub
      
      
      
    #Region "Assign Plants Using CustomGridViewCellElement"
      
        Public Class CustomGridViewCellElement
            Inherits tlrkUI.GridDataCellElement
      
            Private listElement As Telerik.WinControls.UI.RadListElement
            Private linkDone As CustomLinkItem
            Private linkRemove As CustomLinkItem
            Private dropDownListElement As Telerik.WinControls.UI.RadDropDownListElement
            Private linksContainer As Telerik.WinControls.UI.StackLayoutElement
      
            Public Sub New(ByVal column As tlrkUI.GridViewColumn, ByVal row As tlrkUI.GridRowElement)
                MyBase.New(column, row)
            End Sub
      
            Protected Overrides Sub CreateChildElements()
                MyBase.CreateChildElements()
      
                Try
      
                    Dim mainContainer As Telerik.WinControls.UI.StackLayoutElement = New Telerik.WinControls.UI.StackLayoutElement()
                    With mainContainer
                        .Orientation = Orientation.Vertical
                        .StretchHorizontally = True
                        .StretchVertically = True
                        .ElementSpacing = 3
                    End With
                    Me.Children.Add(mainContainer)
      
                    dropDownListElement = New Telerik.WinControls.UI.RadDropDownListElement()
                    With dropDownListElement
                        .Visibility = Telerik.WinControls.ElementVisibility.Collapsed
                        .StretchVertically = False
                        .ListElement.BackColor = Color.PaleGoldenrod
                        .ListElement.GradientStyle = Telerik.WinControls.GradientStyles.Solid
                    End With
                    mainContainer.Children.Add(dropDownListElement)
      
                    listElement = New Telerik.WinControls.UI.RadListElement
                    With listElement
                        .StretchHorizontally = True
                        .StretchVertically = True
                    End With
                    mainContainer.Children.Add(listElement)
      
                    linksContainer = New tlrkUI.StackLayoutElement()
                    With linksContainer
                        .StretchHorizontally = True
                        .StretchVertically = False
                    End With
                    linksContainer.Visibility = Telerik.WinControls.ElementVisibility.Collapsed
      
                    linkDone = New CustomLinkItem("Done")
                    linksContainer.Children.Add(linkDone)
      
                    linkRemove = New CustomLinkItem("Remove")
                    linksContainer.Children.Add(linkRemove)
      
                    mainContainer.Children.Add(linksContainer)
      
                    '
                    ' Populate DropDownListElement
                    '
                    PopulateDropDownListElement__FromDatabase()
                    'PopulateDropDownListElement__UsingHardCodedValues()
      
      
                Catch ex As Exception
                    HandleException(ex)
                End Try
      
            End Sub
      
            Public Overrides Sub UpdateInfo()
                MyBase.UpdateInfo()
                Try
                    If (linksContainer IsNot Nothing) Then
                        If RowInfo.IsCurrent Then
                            linksContainer.Visibility = Telerik.WinControls.ElementVisibility.Visible
                            dropDownListElement.Visibility = Telerik.WinControls.ElementVisibility.Visible
                        Else
                            linksContainer.Visibility = Telerik.WinControls.ElementVisibility.Collapsed
                            dropDownListElement.Visibility = Telerik.WinControls.ElementVisibility.Collapsed
                        End If
                    End If
                Catch ex As Exception
                    HandleException(ex)
                End Try
            End Sub
      
            Private Sub HandleException(ByVal ex As Exception, Optional ByVal bLogException As Boolean = True)
                If bLogException AndAlso sbLog IsNot Nothing Then
                    modAppMain.LogException(ex, sName & "." & sSourceMethod & "  Log: " & sbLog.ToString(), _
                                            "Exception", sName, True)
                End If
            End Sub
      
            Private Sub PopulateDropDownListElement__UsingHardCodedValues()
                dropDownListElement.Items.Add(New tlrkUI.RadListDataItem("PLAA", 1))
                dropDownListElement.Items.Add(New tlrkUI.RadListDataItem("PLBB", 2))
                dropDownListElement.Items.Add(New tlrkUI.RadListDataItem("PLCC", 3))
                dropDownListElement.Items.Add(New tlrkUI.RadListDataItem("PLDD", 4))
            End Sub
      
            Private Sub PopulateDropDownListElement__FromDatabase()
                sSourceMethod = "PopulateDropDownListElement__FromDatabase"
      
                Dim sbSQL As StringBuilder = New StringBuilder("select distinct plantid, plantcode")
                sbSQL.Append(" from Plant") ' NOT ACTUAL SQL
      
                Dim cmd As New SqlCommand
                Dim reader As SqlDataReader
      
                Try
                    Using dbconn As New SqlConnection(APP.My.Settings.dbConnOAM)
                        Try
                            dbconn.Open()
      
                            cmd.CommandType = CommandType.Text
                            cmd.Connection = dbconn
                            cmd.CommandText = sbSQL.ToString()
      
                            reader = cmd.ExecuteReader()
                            If reader.HasRows Then
                                dropDownListElement.DataSource = reader
                                dropDownListElement.DisplayMember = "plantcode"
                                dropDownListElement.ValueMember = "plantid"
                            End If
      
                            reader.Close()
      
                        Catch sqlEx As SqlException
                            HandleException(sqlEx)
                        Finally
                        End Try
                    End Using
                Catch ex As Exception
                    HandleException(ex)
                End Try
      
                dropDownListElement.Text = "Select a Plant"
            End Sub
      
            Protected Overrides Sub SetContentCore(ByVal value As Object)
                sSourceMethod = "SetContentCore"
      
                Try
      
                    listElement.Items.Clear()
      
                    If ((value IsNot Nothing) AndAlso (value IsNot DBNull.Value)) Then
                        Dim arValues() As String = value.ToString().Split(",")
                        For Each val As String In arValues
                            listElement.Items.Add(New Telerik.WinControls.UI.RadListDataItem(val))
                        Next
                    End If
                Catch ex As Exception
                    HandleException(ex)
                End Try
            End Sub
      
            Protected Overrides Function MeasureOverride(ByVal availableSize As System.Drawing.SizeF) As System.Drawing.SizeF
                sSourceMethod = "MeasureOverride"
      
                Try
      
                    If linksContainer.Visibility = Telerik.WinControls.ElementVisibility.Visible Then
                        listElement.MaxSize = New System.Drawing.Size(0, System.Convert.ToInt32(availableSize.Height - 40))
                    Else
                        listElement.MaxSize = Size.Empty
                    End If
      
                Catch ex As Exception
                    HandleException(ex)
                End Try
      
                Return MyBase.MeasureOverride(availableSize)
            End Function
      
            Public Overrides Function IsCompatible(ByVal data As Telerik.WinControls.UI.GridViewColumn, ByVal context As Object) As Boolean
                'Return MyBase.IsCompatible(data, context)
                sSourceMethod = "IsCompatible"
      
                Try
      
                    If ((data.Name = "Plants") AndAlso (TypeOf context Is Telerik.WinControls.UI.GridDataRowElement)) Then
                        Return True
                    End If
      
                Catch ex As Exception
                    HandleException(ex)
                End Try
      
                Return False
            End Function
        End Class
      
        Public Class CustomGridViewColumn
            Inherits tlrkUI.GridViewDataColumn
      
            Private sName As String = "ucBidItemsList"
            Private sSourceMethod As String
            Private sbLog As StringBuilder = New StringBuilder("ucBidItemsList.CustomGridViewColumn Log:" & vbCrLf)
      
            Public Sub New(ByVal sFieldName As String)
                MyBase.New(sFieldName)
                sSourceMethod = "CustomGridViewColumn.New"
                sbLog.AppendLine("Reached " & sName & "." & sSourceMethod)
                sbLog.AppendLine(" - sFieldName: " & sFieldName)
            End Sub
      
            Public Overrides Function GetCellType(ByVal row As Telerik.WinControls.UI.GridViewRowInfo) As System.Type
                sSourceMethod = "CustomGridViewColumn.GetCellType"
                sbLog.AppendLine("Reached " & sName & "." & sSourceMethod)
                sbLog.AppendLine(" - TypeOf row: " & row.GetType().ToString)
      
                Try
      
                    If (TypeOf row Is Telerik.WinControls.UI.GridViewDataRowInfo) Then
                        sbLog.AppendLine(" before Return GetType(CustomGridViewCellElement)")
                        Return GetType(CustomGridViewCellElement)
                    End If
      
                    If sbLog IsNot Nothing Then
                        modAppMain.LogMessage(sbLog.ToString(), sName)
                    End If
      
                Catch ex As Exception
                    HandleException(ex)
                End Try
      
                Return MyBase.GetCellType(row)
            End Function
      
            Private Sub HandleException(ByVal ex As Exception, Optional ByVal bLogException As Boolean = True)
                'If (ex.InnerException IsNot Nothing) Then
                '    MessageBox.Show(sSourceMethod & ". Exception: " & ex.Message & vbCrLf & _
                '                    " - - - - - - - - - - " & vbCrLf & _
                '                    "    Inner Exception: " & vbCrLf & ex.InnerException.Message)
                'Else
                '    MessageBox.Show(sSourceMethod & ". Exception: " & ex.Message)
                'End If
                If bLogException AndAlso sbLog IsNot Nothing Then
                    modAppMain.LogException(ex, sName & "." & sSourceMethod & "  Log: " & sbLog.ToString(), _
                                            "Exception", sName, True)
                End If
            End Sub
      
      
        End Class
      
      
        Private Sub dgBidItems_CurrentRowChanged(ByVal sender As Object, ByVal e As Telerik.WinControls.UI.CurrentRowChangedEventArgs) Handles dgBidItems.CurrentRowChanged
            If e.OldRow IsNot Nothing Then
                e.OldRow.Height = 40
            End If
            If e.CurrentRow IsNot Nothing Then
                e.CurrentRow.Height = 120
            End If
        End Sub
      
      
    #End Region


    Thanks in advance for your help,

    Hector
    P.S.  In a way I think you've answered my initial question, and the rest has been detail implementation issues. Should I mark one of the previous responses as an answer, or wait till the final solution?

  15. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 14 Oct 2011 Link to this post

    Jack,

    In Emanuel Varga's example, he wired up the Click event handler to the RadButtonElement in the CellBeginEdit event for the GridView.

    In your design where would I wire up the event handlers for the dropdownlistElement's SelectedIndexChanged event and the Done and Remove CustomLinkItem(s) Click events?

    Thanks in advance for your help,

    Hector
    Telerik WinControls Newbie, :-)


     

  16. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 14 Oct 2011 Link to this post

    I got rid of the CurrentRowChanged event handler, replacing it with the CurrentCellChanged event handler, and added the code which checks whether the CurrentRow is Nothing in a method called in response to the OnLoad event handler, setting the CurrentRow.Height to my default height as you suggested.

    However, when the form initially appears and the data grid displays, the custom cell column displays the dropDownListElement and the listbox (displaying only the first value) for the default selected row. All other rows display just the listElement. When I select a different row, the same thing happens. FYI, I don't know if this affecta anything, but I added the following statement to set the first selection in the dropDownListElement:

     

    dropDownListElement.Text = "Select a Plant"

    I want the custom cell element to expand showing all elements only when the user clicks in that specific row and column's cell (i.e., the custom cell element).

    How can I limit the height of the drop-down list aspect of the dropDownListElement when the user clicks on the drop-down arrow?

    How can I restore the custom cell element to display only the listElement when the user clicks or tabs away from the the custom cell element (e.g., it loses focus). This will allow me to get rid of the Done link altogether.

  17. Jack
    Admin
    Jack avatar
    2335 posts

    Posted 18 Oct 2011 Link to this post

    Hi Hector,

    Regarding the exception, it seems to be a generic data binding exception caused by the SqlDataReader used in your application. I found this thread which discusses the issue. 

    You have to change the UpdateInfo method in the custom cell element in order to hide the cell contents when the cell is not current:
    Public Overrides Sub UpdateInfo()
        MyBase.UpdateInfo()
        If linksContainer IsNot Nothing Then
            linksContainer.Visibility = If(RowInfo.IsCurrent, ElementVisibility.Visible, ElementVisibility.Collapsed)
            dropDownListElement.Visibility = If(RowInfo.IsCurrent, ElementVisibility.Visible, ElementVisibility.Collapsed)
            Me.Children(0).Visibility = If(IsCurrent, ElementVisibility.Visible, ElementVisibility.Collapsed)
        End If
    End Sub

    This code will display the list element only when the cell is current.

    To restrict the drop down height, you should set DropDownMinSize and DropDownMaxSize properties:
    dropDownListElement.DropDownMinSize = new Size(0, 150)
    dropDownListElement.DropDownMaxSize = new Size(0, 150)

    Feel free to mark this response as an answer when all your questions are answered. If you have any other questions, do not hesitate to open new threads.
     
    All the best,
    Jack
    the Telerik team

    Q2’11 SP1 of RadControls for WinForms is available for download (see what's new); also available is the Q3'11 Roadmap for Telerik Windows Forms controls.

  18. Hector
    Hector avatar
    25 posts
    Member since:
    Jul 2011

    Posted 18 Oct 2011 Link to this post

    Jack,

    thanks for the info on the SqlDataReader generic data binding exception. I will investigate that further at a later time.

    I've managed to implement a similar fix in UpdateInfo, with the following code:

    If (linksContainer IsNot Nothing) Then
       If RowInfo.IsCurrent AndAlso ColumnInfo.IsCurrent AndAlso (Not bChangingRowInfoHeight) Then
          linksContainer.Visibility = Telerik.WinControls.ElementVisibility.Visible
          dropDownListElement.Visibility = Telerik.WinControls.ElementVisibility.Visible
       Else
          linksContainer.Visibility = Telerik.WinControls.ElementVisibility.Collapsed
          dropDownListElement.Visibility = Telerik.WinControls.ElementVisibility.Collapsed
       End If
    End If

    I added the Boolean, bChangingRowInfoHeight, so I don't set properties to .Visible when I've set RowInfo.Height to default (minimized, only listElement Visible, height) in the the done link event handler (when I click on the Done linkItem). I'm considering whether I even need the Done link anymore.

    Thanks for the info on setting the drop-down height.

    Regards,

    Hector

    P.S.   Can I get the e-mail address of your supervisor(s), so I can tell them what a great job you've done.

  19. Jack
    Admin
    Jack avatar
    2335 posts

    Posted 20 Oct 2011 Link to this post

    Hello Hector,

    I am glad that I could help. Your code seems OK and will also does the job. I am not allowed to provide you with the e-mail of my supervisor here, because this is public forum thread. However, I made him aware of this thread and your feedback. If you still want to share something about our support services, please find my response in a ticket called "Add User Control to GridView Column Ticket" that you can find in Your Account.

    Please do not hesitate to contact me if you have any further questions.
     
    Greetings,
    Jack
    the Telerik team

    Q2’11 SP1 of RadControls for WinForms is available for download (see what's new); also available is the Q3'11 Roadmap for Telerik Windows Forms controls.

  20. Rawad
    Rawad avatar
    23 posts
    Member since:
    Jan 2012

    Posted 27 Jun 2014 in reply to Jack Link to this post

    Dear

    I have the following Code, that draw a RadDropDownListElement in second and third row.
    But when I execute, and I scrollbar the datagrid,  the RadDropDownListElement is displayed in others rows?
    Meanwhile if I make the grid bigger size, it's displayed normal.
    Why?  and how to solve it
    And how to not allow write or edit in the RadDropDownListElement,  but just select an item.


      Private Sub RadGridView1_CellFormatting(sender As System.Object, e As Telerik.WinControls.UI.CellFormattingEventArgs) Handles RadGridView1.CellFormatting
            If e.RowIndex <> -1 Then
                If e.ColumnIndex = 0 Then
                    If e.RowIndex = 2 Then
                        Dim vars As New ArrayList
                        vars.Add("AY@12@" & 2014)
                        vars.Add("Semester@12@" & 1)
                        Dim dt = Programs.ExecuteReturnProcedure(Programs.Company.ID, "GetStudents", vars).Tables(0)
                        Dim cc As New RadDropDownListElement()
                        cc.DataSource = dt
                        cc.DisplayMember = "Number"
                        cc.ValueMember = "Id"
                        e.CellElement.Children.Add(cc)
                    ElseIf e.RowIndex = 3 Then
                        Dim dt As New DataTable()
                        dt.Columns.Add("Number")
                        dt.Columns.Add("Id")
                        Dim dr = dt.NewRow()
                        dr("Number") = "One"
                        dr("Id") = "One"
                        dt.Rows.Add(dr)
                        dr = dt.NewRow()
                        dr("Number") = "two"
                        dr("Id") = "two"
                        dt.Rows.Add(dr)
                        dt.AcceptChanges()
                        Dim cc As New RadDropDownListElement()
                        cc.DataSource = dt
                        cc.DisplayMember = "Number"
                        cc.ValueMember = "Id"
                        e.CellElement.Children.Add(cc)
                    End If
                End If
            End If
        End Sub
  21. Dess
    Admin
    Dess avatar
    1601 posts

    Posted 02 Jul 2014 Link to this post

    Hello Rawad,

    Thank you for writing.

    Due to the UI virtualization in RadGridView, cell elements are created only for currently visible cells and are being reused during operations like scrolling, filtering, grouping and so on. That is why you may obtain unexpected behavior like undesired style for some cells. In addition, the CellFormatting event is appropriate for customizing cell's visual design (with the appropriate reset option), not for modifying the internal cell's structure, which may lead to performance issues as well. The recommended approach for achieving the described functionality is creating custom cell. Here is a sample approach:
    public Form1()
    {
        InitializeComponent();
     
        CustomColumn col = new CustomColumn("Column");
        this.radGridView1.Columns.Add(col);
     
        for (int i = 0; i < 20; i++)
        {
            this.radGridView1.Rows.Add("item1");
        }
     
        this.radGridView1.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill;
    }
     
    public class CustomCellElement : GridDataCellElement
    {
        public CustomCellElement(GridViewColumn column, GridRowElement row) : base(column, row)
        {
        }
     
        RadDropDownListElement dropDown;
     
        protected override void CreateChildElements()
        {
            base.CreateChildElements();
     
            dropDown = new RadDropDownListElement();
        dropDown.DropDownStyle = RadDropDownStyle.DropDownList;
            this.Children.Add(dropDown);
        }
         
        protected override void SetContentCore(object value)
        {
            dropDown.DataSource = new List<string>() { "item1", "item2", "item3" };
        }
     
        protected override Type ThemeEffectiveType
        {
            get
            {
                return typeof(GridDataCellElement);
            }
        }
     
        public override bool IsCompatible(GridViewColumn data, object context)
        {
            GridDataRowElement rowElement = context as GridDataRowElement;
            return data is CustomColumn && rowElement != null && (rowElement.RowInfo.Index % 2 == 0);
        }
    }
     
    public class CustomColumn : GridViewDataColumn
    {
        public CustomColumn(string fieldName) : base(fieldName)
        {
        }
     
        public override Type GetCellType(GridViewRowInfo row)
        {
            if (row is GridViewDataRowInfo)
            {
                if (row.Index % 2 == 0)
                {
                    return typeof(CustomCellElement);
                }
                else
                {
                    return typeof(GridDataCellElement);
                }
            }
            return base.GetCellType(row);
        }
    }

    However, note that our RadDropDownListElement hosts the Microsoft TextBox in its editable part when the DropDownStyle is DropDown. Using controls in grid cells may slow down the scrolling and will cause visual glitches (similar to the one demonstrated in your first screenshot). A better option would be using RadDropDownListEditor for specific cells, which can be specified in the EditorRequired event. 

    I hope this information helps. Should you have further questions, I would be glad to help.

    Regards,
    Desislava
    Telerik
     
    Check out Telerik Analytics, the service which allows developers to discover app usage patterns, analyze user data, log exceptions, solve problems and profile application performance at run time. Watch the videos and start improving your app based on facts, not hunches.
     
Back to Top
UI for WinForms is Visual Studio 2017 Ready