Good afternoon my fellow propeller heads; I come to you with a very strange issue today.
In my C# Winforms application, I have a form with a Tab Control. The Tab Control has 4 Tab Pages which I added during design. When my application runs, I am adding x amount of Tab Pages dynamically.
My issue lies not with any of the Tab Pages I added at design time, but only with the dynamic Tab Pages. The dynamic Tab Pages have buttons, which fire fine when clicked. However, when I iterate through the Tab Control’s Tab Pages, only the static Tab Pages are present, none of the dynamic pages are in the control.
I have a class that creates my Tab Pages and it all works fine and adds the Tab Pages as it should. The class name is TabTree and the method name is Add_Tree_View_Tab. "test tab" is the text being displayed on the tab. The null parameter is irrelevant at this time as it is input to another control docked on the Tab Page.
tabRecords.TabPages.Add(TabTree.Add_Tree_View_Tab("test tab", null));
I have also added just a Tab Page to see if I could find it within the Tab Control, but still no luck.
tabRecords.TabPages.Add("this page", "this page");
Trying to find either of my dynamic Tab Pages yield no result?
internal void Expand_Treeview(object sender, EventArgs e)
{
try
{
Button button = (Button)sender;
foreach (TabPage tab_page in tabRecords.TabPages)
{
if (tab_page.Name == "tab_" + button.Tag.ToString())
{
foreach (Control control in tab_page.Controls)
{
if (control is TreeView && control.Name == "treeview_" + button.Tag.ToString())
{
TreeView Tree_view = (TreeView)control;
}
}
}
}
}
catch (Exception ex)
{ }
}
What am I doing wrong or what am I missing? I have spent a good couple of ours trying to figure this out with no solution as yet…
Thanks for the help from everyone, you definitely put me on the right track. It was a case of different instances and destroyed objects.
There are two classes that create the dynamic tabs. The first class is instantiated, but also inherits from the second class. Also, the second class inherits from the actual form, in order to be able to add the dynamic buttons event handler that exist on the tab page. The tabs are created but as soon as the first instantiated class is destroyed, all reference to the dynamic tabs are lost. Which makes perfect sense. I didn't pick up on it as it is code I inherited.
Once again thanks for everyone's input to get me sorted.
Related
I am facing an issue while running through all the User Controls in my Windows form.
I am creating a Windows Form that has the following features:
The Main form has 3 User Controls embedded in it
The Main form also has a combo box. Selecting a particular value in the Combo box will bring the corresponding User Control to the front.
Each User Control has two Check boxes as well as two Combo boxes.
The User can summon each User Control through the Main Form's combo box and check the check boxes and/or modify the combo boxes inside each User Control
Once this is done, there is a button, which on being pressed, executes the following code. This code is supposed to check which check boxes have been checked from every User Control, and execute some functionality :
private void button1_Click(object sender, EventArgs e)
{
foreach (Control c in this.Controls)
{
if (c is UserControl)
{
foreach (Control ctl in c.Controls)
{
if (ctl is CheckBox && (ctl as CheckBox).Checked)
{
Indicator.Text = "It's in";
}
}
}
}
//Some other code after this
}
Here, I have included a Text Box called "Indicator" that shows whether the compiler has entered a particular "for" loop or "if" block. And I'm observing that the innermost "if" alone is not getting executed.
Could someone point out why exactly this is happening?
You need a recursive algorithm,
void ProcessControls(Control ctrlContainer)
{
foreach (Control ctrl in ctrlContainer.Controls)
{
if (ctrl is CheckBox && (ctrl as CheckBox).Checked)
{
Indicator.Text = "It's in";
}
if (ctrl.HasChildren)
ProcessControls(ctrl);
}
}
I do think you might be better off adding some functionality to your user control so it can describe the state of its own checkboxes rather than going digging inside it to find it and do logic. Generally in OO programming, when we encapsulate things within a class, we also provide general purpose accessors "visible to the outside" to describe the internal state of affairs, rather than letting external code interests go poking around inside class to find out what they want
At some point in time you've added these usercontrols to the form either directly in the designer, or programmatically. In the first case they will have their own name:
var u1 = usercontrol1.GetCheckboxStateArray();
var u2 = usercontrol2.GetCheckboxStateArray();
Etc
Or maybe you added them programmatically, in which case it would make sense to keep track of them in a list as you're adding them:
protected List<UserControl> _ucList = new List<UserControl>();
...
foreach(var result in somedatabasequery){
var uc = new UserControl(result.Whatever);
this.Controls.Add(uc);
_ucList.Add(uc);
}
Then this list can be iterated. Sure you could argue that "well .Controls is a collection too, so why add them to another list when they're already in an accessible collection" - for the reasons you're here; .Controls is a general purpose description of the hierarchy of all controls on a form, it contains stuff we don't want and is hard to iterate. This List is purely and simply all and only the stuff we're interested in
As an aside, the UI you have described is atypical. The more usual way of hiding and showing controls under the selection of something that holds a bit of text would be a TabControl. It might be easier to loop through too, if you will persist with this "search for UserControls in a collection of controls" method - tabcontrols have tabpages, tabpages would probably have a .Controls that just contains your UserControl. The tabpage intrinsically takes care of showing and hiding controls as pages are clicked on which could simplify your code
Thanks to everyone for the answers. As it happens, the issue was hiding in plain sight, right under my nose. In each of the User Controls, I had placed the Checkboxes and Combo Boxes inside a Group Box. It completely slipped my mind, so much so that I didn't even mention them in my question.
Thus, as #Caius had suggested in the comments, the code wasn't functioning because I had not addressed the Group Box Container holding these Controls. Once I removed the Group Boxes (used only for aesthetic purpose), the code started functioning properly.
What would be the "best" (or a better) way to do the following?
In a carousel page (say 6 content pages), I click a button, part of the action changes the text on that button, but it also has to change for all the other content pages.
I currently have this happening in the carousel pages OnCurrentPageChanged(), where I call a function and pass in "this". helpers.ChangeAll(this);
public void ChangeAll(CarouselSwipePage page)
{
foreach (SwipePageContent v in page.Children)
{
Button b = v.Content.FindByName<Button>("pause");
if (GlobalSettings.Settings.Default.CarouselCountEnabled) //this is set elsewhere and is used to determine whether the carousel is changing automatically, if it is then set the text to pause
{
b.Text = FontAwesomeFont.PauseCircleO;
}
else
{
b.Text = FontAwesomeFont.PlayCircleO;
}
}
}
This works ok on android but on ios when the user swipes to the next content page after clicking the button, the button text is momentarily the old value before changing to the new value, due to the function being called OnCurrentPageChanged().
Apart from that I'm sure there must be a better way to do this, it looks rubbish.
What about creating a style for the buttons, and just change the text value of the style?
So, using binding properties or dynamic resources, when you change the value, it is going to change all the buttons of your application that use this style. I think this approach is pretty much better and simpler than a loop.
It is a well known error if you try to add the RadAjaxManager to your page twice:
Only one instance of a RadAjaxManager can be added to the page
Telerik explains how you can solve this for design-time issues with a proxy control.
For most of the controls we use on pages, this error does not fire, even though each of these controls have a RadAjaxManager on them, sometimes even inside a repeater (accidentally, but still, the error doesn't throw). However, with one such control (a dynamic button) we have added it to several places on the page with no problem, possibly because this was all the same control, but nested in another control we receive the error above again, as soon as we add it to the page.
I have tried to solve it by:
adding the control dynamically to the page, but because control events fire before the page events, this leads to some dynamic behavior to not occur anymore.
adding the RadAjaxManager dynamically only once to the control, with built-in extra checks, like so:
private RadAjaxManager GetAjaxManager()
{
var ctl = this.FindControl("ajaxManager");
if (ctl != null)
{
return (RadAjaxManager)ctl;
}
// alternative method
var mgr = RadAjaxManager.GetCurrent(this.Page);
if (mgr != null)
{
return mgr;
}
// control is never found, always returns null
return null;
}
protected override void OnInit(EventArgs e)
{
if (this.GetAjaxManager() == null)
{
// ajax mgr is never found, and this always throws
// "cannot add multiple times" error
var ajaxManager = new RadAjaxManager();
ajaxManager.ID = "ajaxManager";
this.Controls.Add(ajaxManager);
}
}
Several variants of the above
The result is either: the control is never found and is therefore added more than once, resulting in the above error, or the control is added but too late in the process for the other controls in the usercontrol, resulting in several AJAX events not happening.
How should I add the RadAjaxManager to a user-control that is itself used inside several other user-controls, such that the manager only occurs once on the page and/or such that RadAjaxManager.GetCurrent actually works?
Plane A - add the RadAjaxManager to the page level, not to the user controls. Thus, the user controls can have RadAjaxManagerProxy controls, the static GetCurrent() method will work.
Plan B - use RadAjaxPanel controls if you want self-contained user controls. They have an ajaxRequest() client-side method and a server side event for that, and since user controls are usually smallish, you will likely be able to get away with a single panel for them.
Plan C - leave AJAX setup to the parent page. If a parent user control is already AJAX-enabled, its entire content will travel with the postback, so neesting more AJAX settings on inner user controls may not bring you a performance benefit.
Plan D - use only asp:UpdatePanel controls with UpdateMode=Conditional so you will have extremely fine-grained control of your partial rendering.
I have a page that dynamically creates multiple usercontrols on the page_init event, and adds it to a placeholder on the page.
The usercontrols themselves databind to a repeater on page_init to a collection of about 10 strings, which outputs a div for each item.
There's also a "view more" link button on the user control. When I click the "view more" button it databinds another collection to a second repeater, with even more divs.
The problem: After clicking "view more" on one of the usercontrols, if I click "view more" on another usercontrol, the "view more" data is lost on the first usercontrol. I suspect it's because I'm not re-adding the controls, so viewstate isn't re-loaded.
Anyone have any ideas or am I just way off on this one? Thank you.
Problem is you need to re-create the dynamic controls on each postback and recreate their viewstate. Take a look at this article Dynamic Web Controls, Postbacks, and View State
Stan is right.
When you click in the link a postback occurs and you lost everything
I ran across the same problem, my aproach was recreate the dinamics UserControls on every postback.
this article http://www.codeproject.com/KB/user-controls/DynamicUC.aspx shows a example, but i implement a diferent code like this:
my page have the following method which dinammicaly add the controls to an PlaceHolder.
private void AdicionarControlesDinamicamente(int idPergunta)
{
if (idPergunta > 0)
{
this.IdPerguntaAtual = idPergunta;
PerguntaAtual = new Pergunta(this.IdPerguntaAtual);
UserControl uc = LoadControl(PerguntaAtual.TipoResposta.CaminhoUserControl, PerguntaAtual.IdPergunta);
phResposta.Controls.Add(uc);
ViewState["ControlesDinamicosPerguntaCarregados"] = true;
}
}
note this line of code ViewState["ControlesDinamicosPerguntaCarregados"] = true;
i store an information tha says that the controls already have been added to page.
then a ovveride the CreateChildControls to recreate the controls
protected override void CreateChildControls()
{
base.CreateChildControls();
// CHeck if the controls have been added to page, case true, i call IncluirControlesDinamicamente() again
// The Asp.Net will look into viewstate and wil find my controls there, so "he" will recreate their for me
if (ViewState["ControlesDinamicosPerguntaCarregados"] != null)
if (Page.IsPostBack)
AdicionarControlesDinamicamente(this.IdPerguntaAtual);
}
I think this help you.
PS: Sorry my english.
I'm rewriting an old application and use this as a good opportunity to try out C# and .NET development (I usually do a lot of plug-in stuff in C).
The application is basically a timer collecting data. It has a start view with a button to start the measurement. During the measurement the app has five different views depending on what information the user wants to see.
What is the best practice to switch between the views?
From start to running?
Between the running views?
Ideas:
Use one form and hide and show controls
Use one start form and then a form with a TabControl
Use six separate forms
Creating a bunch of overlaid panels is a design-time nightmare.
I would suggest using a tab control with each "view" on a separate tab, and then picking the correct tab at runtime. You can avoid showing the tab headers by putting something like this in your form's Load event:
tabControl1.Top = tabControl1.Top - tabControl1.ItemSize.Height;
tabControl1.Height = tabControl1.Height + tabControl1.ItemSize.Height;
tabControl1.Region = new Region(new RectangleF(tabPage1.Left, tabPage1.Top, tabPage1.Width, tabPage1.Height + tabControl1.ItemSize.Height));
What I do is to have a Panel where your different views will sit on the main form.
then create user controls for your different views.
Then when I want to switch between a'view' you dock it to Panel on the main form.. code looks a little like this.
i preffer this because you can then reuse your views, like if you want to open up a view in a tab you can dock your user controls inside tab pages.. or even inherit from
tabpage instead of usercontrol to make things a bit more generic
public partial class MainForm : Form
{
public enum FormViews
{
A, B
}
private MyViewA viewA; //user control with view a on it
private MyViewB viewB; //user control with view b on it
private FormViews _formView;
public FormViews FormView
{
get
{
return _formView;
}
set
{
_formView = value;
OnFormViewChanged(_formView);
}
}
protected virtual void OnFormViewChanged(FormViews view)
{
//contentPanel is just a System.Windows.Forms.Panel docked to fill the form
switch (view)
{
case FormViews.A:
if (viewA != null) viewA = new MyViewA();
//extension method, you could use a static function.
this.contentPanel.DockControl(viewA);
break;
case FormViews.B:
if (viewB != null) viewB = new MyViewB();
this.contentPanel.DockControl(viewB);
break;
}
}
public MainForm()
{
InitializeComponent();
FormView = FormViews.A; //simply change views like this
}
}
public static class PanelExtensions
{
public static void DockControl(this Panel thisControl, Control controlToDock)
{
thisControl.Controls.Clear();
thisControl.Controls.Add(controlToDock);
controlToDock.Dock = DockStyle.Fill;
}
}
Tabbed forms are usually good... but only if you want the user to be able to see any view at any time... and it sounds like you might not.
Separate forms definitely works, but you need to make sure that the switch is seemless...if you make sure the new form appears the same exact size and location of the old form, it will look like it thew same for with changing controls.
The method I often use is actually to pre-setup all my controls on individual "Panel" controls and then show and hide these panels as I need them. The "Panel" control is basically a control container... you can move the panel and all controls on it move relative. And if you show or hide the panel, the controls on it do the same. They are great for situations like this.
The method I often use is actually to
pre-setup all my controls on
individual "Panel" controls and then
show and hide these panels as I need
them.
Instead of making each view a panel within a single form you could make each view a UserControl. Then create a single form and write code to create and display the correct UserControl in the Form and to switch from one to the next. This would be easier to maintain because you will have a separate class for each view instead of a single Form class with 6 panels each with their own controls -- that seems difficult and error prone to maintain.
I would also check out Composite Application Guidance for WPF or Smart Client Software Factory