Scroll state causes RadDropDownListElement to show incorrect value

3 posts, 0 answers
  1. Adam
    Adam avatar
    38 posts
    Member since:
    Oct 2010

    Posted 13 Mar 2012 Link to this post

    I have a collection of custom visual items contained in a RadListView.  These visual items consist of several RadTextBoxElements and a RadDropDownListElement.  If a new item is added to the RadListView, say the drop down list item selected is different than the one above it, then the user scrolls up, the selected item of the drop down list for the next to last item becomes the selected item of the new item's drop down.  Doing this again propagates it up the visual items.

    I have debugged all code and determined that there are some inconsistencies in scrolling that seem to be making this happen.  I can get around it if the scrollbar does not start at the bottom by setting focus to the first textbox in the visual item.  This scrolls to the bottom and then I cannot reproduce.  

    What is happening here?  Any ideas?

    Below are my overridden CreateChileElements and SynchronizeProperties methods:

    base.CreateChildElements();
     
    //Setup the name label and field in a stack panel layout
    this.nameStackPanelElement = new StackLayoutElement();
    this.nameStackPanelElement.Orientation = Orientation.Horizontal;
    this.nameStackPanelElement.ElementSpacing = 17;
    this.nameStackPanelElement.StretchHorizontally = true;
    this.nameLabelElement = new RadLabelElement();
    this.nameLabelElement.Text = "Name";
    this.nameLabelElement.AutoSize = true;
    this.nameLabelElement.StretchHorizontally = false;
    this.nameTextBoxElement = new RadTextBoxElement();
    this.nameTextBoxElement.StretchHorizontally = true;
    this.nameTextBoxElement.MinSize = new Size(120, 0);
    this.nameTextBoxElement.TextBoxItem.MaxLength = 100;
    this.nameTextBoxElement.TextBoxItem.LostFocus += new EventHandler(NameTextBoxItem_LostFocus);
    this.nameTextBoxElement.TextChanged += new EventHandler(ImportantField_TextChanged);
    this.nameStackPanelElement.Children.Add(this.nameLabelElement);
    this.nameStackPanelElement.Children.Add(this.nameTextBoxElement);
     
    //Setup the script label and field in a stack panel layout
    this.scriptStackPanelElement = new StackLayoutElement();
    this.scriptStackPanelElement.Orientation = Orientation.Horizontal;
    this.scriptStackPanelElement.ElementSpacing = 18;
    this.scriptStackPanelElement.StretchHorizontally = true;
    this.scriptLabelElement = new RadLabelElement();
    this.scriptLabelElement.Text = "Script";
    this.scriptLabelElement.AutoSize = true;
    this.scriptLabelElement.StretchHorizontally = false;
    this.scriptTextBoxElement = new RadTextBoxElement();
    this.scriptTextBoxElement.StretchHorizontally = true;
    this.scriptTextBoxElement.MinSize = new Size(120, 0);
    this.scriptTextBoxElement.TextBoxItem.MaxLength = 30;
    this.scriptTextBoxElement.TextBoxItem.LostFocus += new EventHandler(ScriptTextBoxItem_LostFocus);
    this.scriptTextBoxElement.TextChanged += new EventHandler(ImportantField_TextChanged);
    this.scriptStackPanelElement.Children.Add(this.scriptLabelElement);
    this.scriptStackPanelElement.Children.Add(this.scriptTextBoxElement);
     
    //Setup the modifier label and drop down list in a stack panel layout
    this.modifierStackPanelElement = new StackLayoutElement();
    this.modifierStackPanelElement.Orientation = Orientation.Horizontal;
    this.modifierStackPanelElement.ElementSpacing = 4;
    this.modifierStackPanelElement.StretchHorizontally = true;
    this.modifierLabelElement = new RadLabelElement();
    this.modifierLabelElement.Text = "Modifier";
    this.modifierLabelElement.AutoSize = true;
    this.modifierLabelElement.StretchHorizontally = false;
    this.modifierDropDownListElement = new RadDropDownListElement();
    this.modifierDropDownListElement.StretchHorizontally = true;
    this.modifierDropDownListElement.MinSize = new Size(60, 0);
    this.modifierDropDownListElement.DropDownStyle = RadDropDownStyle.DropDownList;
    this.modifierDropDownListElement.SelectedIndexChanged +=
        new Telerik.WinControls.UI.Data.PositionChangedEventHandler(modifierDropDownListElement_SelectedIndexChanged);
    this.modifierStackPanelElement.Children.Add(this.modifierLabelElement);
    this.modifierStackPanelElement.Children.Add(this.modifierDropDownListElement);
     
    //Fill the modifier drop down list and select the zeroth element
    foreach (ModKeys mod in Enum.GetValues(typeof(ModKeys)))
    {
        this.modifierDropDownListElement.Items.Add(mod.ToString());
    }
     
    //Setup the key label and field in a stack panel layout
    this.keyStackPanelElement = new StackLayoutElement();
    this.keyStackPanelElement.Orientation = Orientation.Horizontal;
    this.keyStackPanelElement.ElementSpacing = 4;
    this.keyLabelElement = new RadLabelElement();
    this.keyLabelElement.Text = "Key";
    this.keyLabelElement.AutoSize = true;
    this.keyLabelElement.StretchHorizontally = false;
    this.keyTextBoxElement = new RadTextBoxElement();
    this.keyTextBoxElement.StretchHorizontally = true;
    this.keyTextBoxElement.MinSize = new Size(34, 0);
    this.keyTextBoxElement.TextBoxItem.MaxLength = 1;
    this.keyTextBoxElement.TextBoxItem.LostFocus += new EventHandler(KeyTextBoxItem_LostFocus);
    this.keyTextBoxElement.TextChanged += new EventHandler(ImportantField_TextChanged);
    this.keyStackPanelElement.Children.Add(this.keyLabelElement);
    this.keyStackPanelElement.Children.Add(this.keyTextBoxElement);
     
    //Combine the key and modifier panels into one stack panel layout
    this.modifierKeyStackPanelElement = new StackLayoutElement();
    this.modifierKeyStackPanelElement.Orientation = Orientation.Horizontal;
    this.modifierKeyStackPanelElement.ElementSpacing = 4;
    this.modifierKeyStackPanelElement.StretchHorizontally = true;
    this.modifierKeyStackPanelElement.Children.Add(this.modifierStackPanelElement);
    this.modifierKeyStackPanelElement.Children.Add(this.keyStackPanelElement);
     
    //Setup the save and cancel buttons in a stack panel layout
    this.newButtonsStackPanelElement = new StackLayoutElement();
    this.newButtonsStackPanelElement.Orientation = Orientation.Horizontal;
    this.newButtonsStackPanelElement.ElementSpacing = 4;
    this.newButtonsStackPanelElement.StretchHorizontally = true;
    Padding newButtonsStackMargin = this.newButtonsStackPanelElement.Margin;
    this.newButtonsStackPanelElement.Margin = new Padding(52, newButtonsStackMargin.Top,
        -12, newButtonsStackMargin.Bottom);
    this.saveButtonElement = new RadButtonElement("Save");
    this.saveButtonElement.Enabled = false;
    this.saveButtonElement.Click += new EventHandler(saveButtonElement_Click);
    this.cancelButtonElement = new RadButtonElement("Cancel");
    this.cancelButtonElement.Click += new EventHandler(deleteCancelButtonElement_Click);
    this.newButtonsStackPanelElement.Children.Add(this.saveButtonElement);
    this.newButtonsStackPanelElement.Children.Add(this.cancelButtonElement);
     
    //Setup the insert and delete buttons in a stack panel layout and set them
    //to collapsed initially.
    this.existingButtonsStackPanelElement = new StackLayoutElement();
    this.existingButtonsStackPanelElement.Orientation = Orientation.Horizontal;
    this.existingButtonsStackPanelElement.Visibility = ElementVisibility.Collapsed;
    this.existingButtonsStackPanelElement.ElementSpacing = 4;
    this.existingButtonsStackPanelElement.StretchHorizontally = true;
    Padding existingButtonsStackMargin = this.existingButtonsStackPanelElement.Margin;
    this.existingButtonsStackPanelElement.Margin = new Padding(52, existingButtonsStackMargin.Top - 4,
        -12, existingButtonsStackMargin.Bottom + 4);
    this.insertButtonElement = new RadButtonElement("Insert");
    this.insertButtonElement.Click += new EventHandler(insertButtonElement_Click);
    this.deleteButtonElement = new RadButtonElement("Delete");
    this.deleteButtonElement.Click += new EventHandler(deleteCancelButtonElement_Click);
    this.existingButtonsStackPanelElement.Children.Add(this.insertButtonElement);
    this.existingButtonsStackPanelElement.Children.Add(this.deleteButtonElement);
     
    //Setup the separator that will separate this item from the one below it
    this.itemSeparator = new SeparatorElement();
    this.itemSeparator.StretchHorizontally = true;
    this.itemSeparator.Line1.BackColor = Color.FromArgb(76, 76, 76);
    //Padding separatorMargin = itemSeparator.Margin;
    //this.itemSeparator.Margin = new Padding(separatorMargin.Left, separatorMargin.Top + 4, separatorMargin.Right, separatorMargin.Bottom);
     
    //Setup the outermost stack panel layout element and add all
    //of the other stack panel layout elements as its children.
    this.parentStackPanelElement = new StackLayoutElement();
    this.parentStackPanelElement.Orientation = Orientation.Vertical;
    this.parentStackPanelElement.ElementSpacing = 4;
    this.parentStackPanelElement.Margin = new Padding(4);
    this.parentStackPanelElement.StretchHorizontally = true;
    this.parentStackPanelElement.Children.Add(this.nameStackPanelElement);
    this.parentStackPanelElement.Children.Add(this.scriptStackPanelElement);
    this.parentStackPanelElement.Children.Add(this.modifierKeyStackPanelElement);
    this.parentStackPanelElement.Children.Add(this.newButtonsStackPanelElement);
    this.parentStackPanelElement.Children.Add(this.existingButtonsStackPanelElement);
    this.parentStackPanelElement.Children.Add(this.itemSeparator);
     
    //Finally, add the outermost stack panel layout element to the
    //SimpleListViewVisualItem's Children
    this.Children.Add(this.parentStackPanelElement);


    lock (Program.scriptsLockObject)
                {
                    base.SynchronizeProperties();
     
                    this.Text = "";
     
                    this.Script = this.Data.Value as Script;
     
                    if (this.Script != null)
                    {
                        if (this.Script.IsNew)
                        {
                            this.newButtonsStackPanelElement.Visibility = ElementVisibility.Visible;
                            this.existingButtonsStackPanelElement.Visibility = ElementVisibility.Collapsed;
     
                            //if (!this.IsElementVisible)
                            //{
     
                            //}
     
                            this.nameTextBoxElement.Focus();
                        }
                        else
                        {
                            this.newButtonsStackPanelElement.Visibility = ElementVisibility.Collapsed;
                            this.existingButtonsStackPanelElement.Visibility = ElementVisibility.Visible;
     
                            this.insertButtonElement.Enabled = !string.IsNullOrWhiteSpace(this.Script.ScriptText);
                        }
     
                        this.nameTextBoxElement.Text = this.Script.Name;
                        this.scriptTextBoxElement.Text = this.Script.ScriptText;
                        this.modifierDropDownListElement.SelectedIndex = (int)this.Script.KeyModifier;
                        this.keyTextBoxElement.Text = this.Script.Key;
                    }
                }
  2. Ivan Todorov
    Admin
    Ivan Todorov avatar
    688 posts

    Posted 16 Mar 2012 Link to this post

    Hello Adam,

    Thank you for contacting us.

    The reason for this behavior is the virtualization mechanism of RadListView. When you scroll up and down, RadListView reuses the visual items by attaching to them different data items. It is implemented this way in order to prevent initalization of visual elements which is usually a rather heavy operation.

    The proper way to implement this scenario is to save the currently selected index of the drop-down in the data item and restore the saved index on the SynchronizeProperties() method. This method of the visual items is called every time a different data item is being attached to them.

    I am sending you a small sample project which demonstrates how to achieve the desired scenario. I hope you find it useful.

    Do not hesitate to write back in case you have any additional questions.

    Greetings,
    Ivan Todorov
    the Telerik team
    RadControls for WinForms Q1'12 release is now live! Check out what's new or download a free trial >>
  3. UI for WinForms is Visual Studio 2017 Ready
  4. Adam
    Adam avatar
    38 posts
    Member since:
    Oct 2010

    Posted 16 Mar 2012 Link to this post

    Thanks for your reply.  I've found a work around though where I just have a small form pop up for new items.  The behavior seems to be deeper than what you're saying because my previous implementation was:

    • Add a new data object to the list view
    • Have the user enter values and click the save button
    • Resynchronize to make the visual item change its buttons from Save/Cancel to Insert/Delete

    The behavior only happened if the new item was added while the scroll bar was not at the bottom of the list, synchronized, and then the scroll bar was moved up slightly.  It seems more like an edge case in the virtualization because on debugging the SynchornizeProperties method and any other code that would touch the value of the drop down list, I determined that the value was not being changed in code.

    Either way I'm going to stick with my work around but hopefully this will help someone in the future.  Thanks again.
Back to Top