Unexpected index change when adding a control to a FlowLayoutPanel - c#

I have a place in my code where I am dynamically adding controls to a Top-Down arranged FlowLayoutPanel. I need the controls to appear in a certain order so what I am doing everytime is clearing the FlowLayoutPanel.Controls collection and then adding each child control in the order I want them to appear. My code does this:
private void arrangement1()
{
flowLayoutPanel1.Controls.Clear();
flowLayoutPanel1.Controls.Add(control1);
flowLayoutPanel1.Controls.Add(control2);
flowLayoutPanel1.Controls.Add(control3);
}
Most of the time this works great. However, there is one specific control that does not maintain it's position in the control collection when adding other controls after it. For instance in the following code segment:
private void arrangement2()
{
flowLayoutPanel1.Controls.Clear();
flowLayoutPanel1.Controls.Add(control1);
flowLayoutPanel1.Controls.Add(movingControl);
//movingControl current is at index = 1 in Controls.
flowLayoutPanel1.Controls.Add(control2);
//control2 is now at index = 1, movingControl got bumped to index = 2 in Controls.
flowLayoutPanel1.Controls.Add(control3);
//control3 is now at index =2, movingControl got bumped to index = 3 in Controls.
}
This only happens the first time movingControl is added to Controls. If I go back and call arrangement1 and then arrangement2 a second time. The controls will appear the in intended order of:
control1
movingControl
control2
control3
This seems to be a bug in the code for Controls.Add. Either that or the documentation for .Add's behaviour is incomplete as it doesn't always add to the end of the collection. Does anyone have any insight into why this occurs. The obvious "fix" is to just call:
arrangement2();
arrangement1();
arrangement2();
However, that seems like a very poor solution to some other underlying problem.
Thanks in advance for the help!
EDIT: Note that each of these controls is a member of a custom view class so they persist after the Controls collection is Cleared. However, these controls are not stored in any sort of ordered collection. They are just members of this custom class. The code shown above works correctly as shown. However, in the context of my GUI program it has the described erroneous behaviour. I would post more code if I had any idea what would be helpful but there is a lot of code that touches these peices. This is all of the code that executes for the described action.
What I'm really looking for is what possible scenarios cause a Controls.Add to Insert a control not at the last index of the collection. Specifically after a call to Clear() and with NO Remove() calls.

First running Arrangement1 gives me:
control1
control2
control3
Running arrangement2 gives me:
control1
movingcontrol
control2
control3
Your code, pure as it is posted, works for me.
However, recently i encountered a similar problem. I was holding the controls in a list, then itterating through this to add them to the flowlayout. Removing a single item from the list, then adding a new control would not insert the new control at the end, it would replace the vacant spot left by the deleted item.
So in summary, i suspect it's something you haven't posted, possibly where you store the controls themselves? do you store them in an array or a List?

Would it not be easier to just toggle the visibilty of movingControl?
But I guess that answer is based on your example such that if more rearranging is going on then this may not apply.
In conjunction with visibility toggling, you could also look into using ControlCollection.SetChildIndex() instead which seems more appropriate and seems more likely to produce a smoother re-ordering.

Related

In ASP.NET, how to store controls in a Dictionary

I am storing ASP.NET Controls (Tables, Buttons, TextBoxes) in a Dictionary in a webform application, so that they can be accessed directly from user controls, instead of having to do a recursive search for them from other user controls. When they are added to the dictionary, I can verify that the visible control on the .ascx control is identical to the object in the Dictionary: X == Y returns true. But later on, when I want to do something like changing a background color, or disabling a button, X == Y returns false. A change that I make to the object in the Dictionary (which is declared as static) is not reflected in the visible GUI control.
How do I correct this?
(it's difficult to give you a helpful answer without seeing any code, and without understanding what you're trying to achieve, but ...)
Whenever a Postback is made, a new instance of the page and all its controls is created. Therefore the control instances stored in your dictionary no longer match with the control instances on the current page instance. They are still the instances created when the page was requested for the first time.
But in any case, storing control instances in a static dictionary is a very bad idea (e.g. think about what happens when multiple users call your page in parallel).
Please explain what you want to achieve and why you think your dictionary is necessary. It will then probably be easier to help you.
The dictionary stores information about each control. However, the controls that the UI displays are separate. Every time you make a change to one element in the dictionary, you have to set the actual UI components to = their corresponding dictionary element. So
Dictionary["textBoxExampleKey"].Text = "blah";
won't change the actual textbox. To update the textbox to match the value in the dictionary, do`
textBoxExample.Text = Dictionary["textBoxExampleKey"].Text;
I recommend making a method that updates the entire UI to match the dictionary for the sake of simplicity. So this method would be something like
private void UpdateUI()
{
textBoxExample.Text = Dictionary["textBoxExampleKey"].Text;
labelExample.Text = Dictionary["labelExampleKey"].Text;
listBoxExample.DataSource = Dictionary["listBoxExampleKey"].DataSource;
Refresh();
}
The "Refresh()" method reloads your UI so that all changes are made visible, which could also be part of your problem.
TL;DR: Update your actual controls, refresh your UI.

C# ASP.NET - Dynamic MenuItems in a user control are randomly being duplicated outside of control creation

EDIT: I needed to skip control creation during post back -- see my answer below.
I'm working on a very basic front end to a simple tool and I wanted to present some data in a much more sorted and useful way, instead of making one huge wall of text. I found this tutorial on building a simple tabbed interface using MultiView, but have run into a bizarre problem. I can't use Ajax tabs because of legal hissy fits over 3rd party software.
My webpage is a basic ASP.NET page with a user control plopped in the middle of it. In this control's ascx file, I defined the Menu (empty) and the MultiView (also empty) so that I can dynamically populate the tabs with content driven from an external file.
When the default page's OnInitComplete function is called, I call through to the user control to load the data file, then build out the tabs and the view content based on the loaded data. I tried doing this from PageLoad, PreInit, and CreateChildControls, but I kept getting an errors saying that I was setting the the MultiView's active view index at an invalid time (and also that there were 0 views despite the fact I just added a bunch of them):
ActiveViewIndex is being set to '0'. It must be smaller than the
current number of View controls '0'. For dynamically added views, make
sure they are added before or in Page_PreInit event.
But OnInitComplete appears to work just fine, so I went with that.
I iterate over the loaded data (multiple lists of strings), and for each list, I add a MenuItem with the list's title to the Menu and a View to the MultiView. The View is populated with a table->row->cell as in the above tutorial. In the cell, I add the list title and a CheckBoxList data bound to the list of strings.
So far so good, but when I click on a tab (or one of the checkboxes, etc) and there is a postback or something like that (the screen flashes as the site redraws itself), there is now a duplicate set of MenuItems immediately after the original. Each time I click on a tab or checkbox, another set of menu items are added.
I clear the MenuItem's Items list prior to building the controls and I verify that the controls hierarchy is structurally as expected after the control construction. Yet when one of my callbacks is called, my MenuItem list magically has some items added to it. None of my other controls appear affected at all. As a hack, I can remove the duplicates manually in my menu's OnMenuItemClick event, but I'd have to do the same in any of the callbacks I receive. Obviously I'd rather prevent this from happening. This has me stumped and I haven't been able to find anything online about it. Why would one set of controls have some content duplicated, yet every other control maintain its state correctly? My code is really simple so there isn't a way to add additional menu items without also adding the views. Anyway, there are a correct number of items prior to clicking on the tab/checkbox, an additional set immediately following in the callback.
This is my first time using ASP.NET, so I'm learning as I go. :) Thanks!
My problem was that I was not testing for postback before creating the controls. The code below is working for me.
In my user control's code behind:
protected void OnInitComplete( EventArgs e )
{
if( !Page.IsPostBack )
{
CreateMyControls();
}
}

ObjectListView Not Showing Selection Color For Selected Item

I have an ObjectListView which is essentially a wrapper around the standard .NET ListView. My problem is that I cannot figure out the correct sequence of method calls to add a new object to the control, scroll the control to ensure that the object is visible, and select the object. Below, is my code to achieve this. Everything works but, for some reason the background color for the selected item/object does not show up until I click on or re-size one of the columns. I'm not sure if the control is not being focused or what.
// objectListViewItems is of type BrightIdeasSoftware.ObjectListViewItems
objectListViewItems.AddObject(e.InsertedItem);
objectListViewItems.Refresh();
objectListViewItems.Focus();
objectListViewItems.EnsureModelVisible(e.InsertedItem);
objectListViewItems.SelectedObject = e.InsertedItem;
objectListViewItems.Focus();
The code below updates an item in the ObjectListView and works just fine. Not sure what I'm doing wrong above...
objectListViewItems.RefreshObject(itemToEdit);
objectListViewItems.Focus();
objectListViewItems.SelectObject(itemToEdit);
This should work like you proposed (I did this on several occasions). However, calling Refresh() and the second Focus() is unnecessary. Also I would rather use SelectObject() than the SelectedObject property.
Like this:
objectListView.AddObject(newItem);
objectListView.Focus();
objectListView.EnsureModelVisible(newItem);
objectListView.SelectObject(newItem);
Also, make sure that there is no code executed afterwards, that may cause another control to get the focus.
To narrow down what's happening, you could try setting
objectListView.HideSelection = false;
As for the normal ListView, this ensures that the current selection stays visible (but "grayed" out), even if the control loses focus.
Please post the complete OLV configuration (from InitializeComponent()) if you used the designer. Maybe there is some weird constellation causing this.

User control keeps stretching once dropped on form

This may be a winforms newb mistake, but I've created a user control that has one component on it - a Telerik dropdownlist control. When I initially drop it on a form it looks correct. However, as soon as I run the form the control stretches off the form.
The dropdownlist on the user control has its anchor property set to top/left/right/bottom.
Before:
After:
What the heck am I doing wrong here?
EDIT: a bit more info: If I close the designer and open it back up, it immediately changes sizes. The code itself doesn't appear to cause the problem. In fact, I commented out all of the code (besides the InitializeComponent() call) and I still see the problem. The control itself still works - when I run the form it is populated with the data I'm expecting. It's just stretched.
I can "work around" the issue by setting the width of the dropdownlist about 35 pixels less than the size I actually want it to be, so when it resizes itself it's actually the correct width, but I really shouldn't have to.
You were probably on the right track. It looks as though it is anchoring to something that is way off the page. I would try removing the anchor right for starters to see what effect this has. If that doesn't work, perhaps try different combinations of the anchoring to see what effect it has on the control.
Make sure there are no docking settings enabled, as it would undo any anchoring you have on the control.
Check to see what the dropdownlist control is contained in. You said it was in a usercontrol, but is it within a grid in the UC or anything else like that? Also, ensure that the user control is sized properly (not outside the bounds of the page) -- would cause anchoring problem. Use a background color to test this.
Next, I would check the Maximum/Minimum size of the control (perhaps consider setting one?).
If all else fails, delete the dropdownlist and create a new one and see if you have the same problem. Sometimes the designer side of code gets glitchy and causes problems or you just accidentally change something you didn't mean to.
Finally, last resort, just use a normal drowndownlist ;)

How to find where I have a duplicate control ID (C#)

I am creating huge amounts of dynamic controls, and for everything that must have an ID assigned, I assign it using a guaranteed unique variable(unless 64bits overflows from just controls).
Now, I have a problem though. I have a duplicate control somewhere and I can not discover where it is added or anything because it doesn't happen until after Page_Load, which means, it is out of my own code when an exception is thrown(I think at like Render or some other internal function).
The control name is "ctlXXX" where XXX is a number(right now, always 244) this control ID is not being made by me. It is being made by ASP.Net automatically(as none of my IDs are prefixed with ctl). So how do I correct this error that I can not see? Can anyone suggest ways of finding my error(I really hope this isn't a bug in ASP.Net)?
Also, I got the error just recently by using a Copy function which will do a Memberwise copy on a custom control, and then it will reset any controls to have either a unique ID or to be null as this part happens before controls are loaded into the custom control, which is a descendant of Panel)
Can anyone give me advice on how to find this bug?
I think you want to look at the INamingContainer interface. I think it will solve your problem but you will have to derive a new control class to do it.

Categories

Resources