Windows Forms is getting too large - c#

Im making a program with many many pages... and in my design, the buttons will eventually get stacked up, so its getting harder and harder to work the more there are.
This question is a clone of this topic.
However, i didn't really get the answer since they were talking about xaml and wpf.
I've also tried to make multiple windows forms, hide and show them to split it up.
But when i hide and show a wndow, its very very easily to see the GUI fading in and out which looks ugly.
I want an instant hide/show function so it looks like its just 1 program with 1 window and now switching.
So what are the tecniques to make a big windowsforms program more managable?

You may create a couple of UserControls, organize and separate your logic on them. Then you may use String Array to store your control names and iterate througth items to display apropriate view. The form may have Panel as container and Dock Style defined as Fill. The simplefied code will look like:
public class MyContainer:Control
{
public MyContainer(string szControlName, UserControl nControl)
{
UserControlName = szControlName; MyControl = nControl;
}
public string UserControlName { get; set; }
public UserControl MyControl { get; set; }
}
In main form:
public List<MyContainer> MyNavigationArray;
public void InitArray()
{
MyNavigationArray = new List<MyContainer>(MyFormPageCount);
for (int i = 0; i < MyFormPageCount; i++)
{
MyNavigationArray.Add(new MyContainer(TheNameOfUserControl, new PredefinedUserControl()));
}
}
private void NextButton_click(object sender, EventArgs e)
{
MyContainer mYcn = this.panel1.Controls[0] as MyContainer;
int nCurPos = MyNavigationArray.IndexOf(mYcn);
if (nCurPos < MyNavigationArray.Count)
{
panel1.Controls.Clear();
MyContainer c = MyNavigationArray[nCurPos + 1];
panel1.Controls.Add(c);
c.Dock = DockStyle.Fill;
}
}
All information stored inside user controls will be in your array and can be used later.
Hope this helped.

Related

How to modify control properties through variable reference

I've been working on making a project of mine more modular. Something I've wanted to do is have multiple buttons use the same function when they perform a similar action, but with different values. I've been stuck on trying to apply this to the following situation:
"When this button is clicked, have the user select an image, and then have a PictureBox display the selected image". Each button has its own PictureBox. All Controls have been created before runtime.
Hope that makes sense!
My last attempt can be seen in the code below- I have tried assigning the Controls(Button and PictureBox) to variables to be stored together in a class. There's 6 of these classes all included within a single List.
I've also tried to store only the Control Names and then using this.Controls.Find to retrieve the Controls.
I've tried quite a few smaller changes such as passing by reference, making the List static, and things such as that would (somehow)magically do the trick- I've gotten desperate.
public class score_control
{
public Button score_button;
public PictureBox score_picture;
public int picture_index;
}
public List<string> score_boxes_names = new List<string>();
public List<score_control> score_boxes = new List<score_control>();
public void add_score_control(Button button, PictureBox pictureBox)
{
score_control new_score = new score_control();
new_score.score_button = button;
new_score.score_picture = pictureBox;
new_score.picture_index = score_boxes.Count();
score_boxes.Add(new_score);
score_boxes_names.Add(button.Name);
}
public score_control find_score_control(string name)
{
int index = score_boxes_names.IndexOf(name);
return score_boxes[index];
}
public frm_settings()
{
InitializeComponent();
add_score_control(btn_score1_image1, pic_score1_image1);
add_score_control(btn_score1_image2, pic_score1_image2);
add_score_control(btn_score1_image3, pic_score1_image3);
add_score_control(btn_score2_image1, pic_score2_image1);
add_score_control(btn_score2_image2, pic_score2_image2);
add_score_control(btn_score2_image3, pic_score2_image3);
}
private void score_button_Click(object sender, EventArgs e)
{
Button image_button = (Button)sender;
if (ofd_png.ShowDialog() == DialogResult.OK)
{
score_control clicked_control = find_score_control(image_button.Name);
score_image[clicked_control.picture_index] = ofd_png.FileName;
clicked_control.score_picture.Image = Image.FromFile(ofd_png.FileName);
}
}
The problem seems centered around this line:
clicked_control.score_picture.Image = Image.FromFile(ofd_png.FileName);
The program throws a NullReferenceException , but clickedcontrol is being recognized in the Local Watch, as well as score_image being noted to be a PictureBox(as it should be).
When I instead held the Control Names in the class, I had broke this line down into multiple lines, but the following line produced a NullReferenceException:
Control[] find_control = this.Controls.Find(clicked_control.score_picture, true);
In this case, clicked_control.score_picture would be a string containing the PictureBox Name. Again, the Local Watch showed that it clicked_control was not null, and neither was score_picture.
Any help figuring out how to properly store a Control within a variable to later be used to modify that Control's properties would be greatly appreciated.
dontpanic was able to help me out with this one. The issue was actually outside of this code - it had to do with the line score_image[clicked_control.picture_index] = ofd_png.FileName;. The way score_image was initialized as an array was incorrect. Fixing that made everything work fine.

Responsive Design UI with DockPanel Suite

I have design 1 winform to look like the picture. But I want the highlighted yellow part to be dockable with dockpanel suite reference. Is that do-able or any other suggestion of better design?
Right now the treeview is on the dockpanel and the red box part is a usercontrol placed in the same dockpanel. I tried to put the redbox as another form but I can't place it as it is in the picture. Also, this winform is need to be responsive so I put in the redbox part in a table layout panel.winform design and not familiar actually with the dockpanel suite reference. If there is a beginner tutorial that I can refer to, it would be much appreciated.
Current design:
There are two approach to your problem. First is dirty one and second elegant one. By dirty and elegant i mean way they display. Method they work are both same.
I will explain to you how to do it on empty form and you just implement that in your populated one.
First create new form.
Add 2 or more GroupBoxes to it
Add some items inside them (just to see if it works)
At the top of the each boxes add Button which will toggle visibility
Our form now looks like this and let's look of code behind it.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Test
{
public partial class TestForm : Form
{
// This is property
bool ShowFirstGroupBox
{
get
{
// We let user get our property from private variable
return _ShowFirstGroupBox;
}
set
{
// When user change this property we do something based on that
switch(value)
{
case true:
groupBox1.Size = new Size(groupBox1.Width, FirstGroupBoxDefaultHeight);
break;
case false:
groupBox1.Size = new Size(groupBox1.Width, 55);
break;
}
_ShowFirstGroupBox = value;
}
}
bool ShowSecondGroupBox
{
get
{
return _ShowSecondGroupBox;
}
set
{
switch (value)
{
case true:
groupBox2.Size = new Size(groupBox1.Width, FirstGroupBoxDefaultHeight);
break;
case false:
groupBox2.Size = new Size(groupBox1.Width, 55);
break;
}
_ShowSecondGroupBox = value;
}
}
// We store our boxes current state ( TRUE = shown, FALSE = HIDDEN )
bool _ShowFirstGroupBox = true;
bool _ShowSecondGroupBox = true;
// We store our default height for groupboxes
int FirstGroupBoxDefaultHeight;
int SecondGroupBoxDefaultHeight;
public TestForm()
{
InitializeComponent();
// Assigning default height of our groupboxes
FirstGroupBoxDefaultHeight = groupBox1.Height;
SecondGroupBoxDefaultHeight = groupBox2.Height;
}
private void button1_Click_1(object sender, EventArgs e)
{
ShowFirstGroupBox = !(_ShowFirstGroupBox); // This sets our property value to opposite of this boolean
}
private void button1_Click_1(object sender, EventArgs e)
{
ShowSecondGroupBox = !(_ShowSecondGroupBox); // This sets our property value to opposite of this boolean
}
}
}
Now when we have code like this and press button it will collapse groupbox.
NOTE: Controls under groupbox are still on place but just hidden since they are child of groupbox and everything outside of bounds is not visible to user.
This is dirty way since i would like to display it much prettier with MINUS sign on the right side of the groupbox title so i do not have button inside it. To do this you would need to create custom control which inherits groupbox, add button to it and position it in title bar and create event for it. It is easy if you have ever tried creating custom controls but if you haven't and you think dirty approach is okay with you then do not try it.

How do I clear a user control from a winform?

This is probably a basic question, but I can't find answers because the terms are generic.
I am building a WinForm aplication. Its purpose is to set up memory in a certain chip. I think the best way to organize the application is to have a user control for each chip type, derived from a generic parent class. Think of the children as "iphone," "android" and "blackberry," derived from a parent class "phone".
VS2017 Designer has a Panel where I want the control to be. On startup, I generate an object of the base class and add it to the panel. When I press a button, the old object is deleted and replaced with a new one. Each class has just one control, a label with distinctive text.
The problem is, after I press the button, I see both texts. The panel's Controls collection has just one element, but I see the text from both objects. I have tried Refresh, Update and Invalidate withe the same results.
What do I have to do to make the old text "go away" so the only thing I see is the latest object?
private ChipMemBase ChipMemControl = new ChipMemBase();
public Form1()
{
InitializeComponent();
//tbFeedback.Text = string.Format(fmtString, 0, 1, 2, 3, 4, 5);
cbChipName.SelectedIndex = 0;
tbVersion.Text = Version;
OriginalWindowColor = tbFeedback.BackColor;
ShowChipMemControl();
PrintToFeedback(Version);
}
private void ShowChipMemControl()
{
var ctl = pnlChipMem.GetChildAtPoint(new Point(5,5));
if (null != ctl)
{
if (ctl != ChipMemControl)
{
pnlChipMem.Controls.Remove(ctl);
ctl.Dispose();
pnlChipMem.Update();
Refresh();
}
}
if (null != ChipMemControl)
{
pnlChipMem.Controls.Add(ChipMemControl);
}
}
private void btnMakeChipMemory_Click(object sender, EventArgs e)
{
ChipMemControl = new ChipMemGen2();
ShowChipMemControl();
}
Screenshots before and after clicking Create
Your ShowChipMemControl gets the control at point 5,5 and checks if it's a ChipMemControl then removes it.
I'm guessing that the reason it's not getting removed is that the control at point 5,5 is not a ChipMemControl.
You can use:
pnlChipMem.Controls.Clear()
to remove all the controls
Or:
ChipMemControl cmc = pnlChipMem.Controls.OfType<ChipMemBase>().FirstOrDefault();
if (cmc != null)
{
pnlChipMem.Controls.Remove(cmc);
cmc.Dispose();
}
To only remove the first instance of ChipMemBase on your pnlChipMem panel.
Got it. The problem was from inheritance, not window behavior. Control lblDefault in the base class, carrying the inconvenient text, was still present in the child class. I had to make it Public in the base class and remove it in the child class constructor:
InitializeComponent();
Controls.Remove(lblDefault);
lblDefault.Dispose();
lblDefault = null;
The clue was this article and project:
dynamically-and-remove-a-user-control

I need to know how to take the selected item of a comboBox and make it appear on a windows form application?

I have a windows form application with a ComboBox on it and I have some strings in the box. I need to know how when I select one of the strings and press my create button, how can i make that name show up on another windows form application in the panel I created.
Here is the code for adding a customer
public partial class AddOrderForm : Form
{
private SalesForm parent;
public AddOrderForm(SalesForm s)
{
InitializeComponent();
parent = s;
Customer[] allCusts = parent.data.getAllCustomers();
for (int i = 0; i < allCusts.Length; i++)
{
Text = allCusts[i].getName();
newCustomerDropDown.Items.Add(Text);
newCustomerDropDown.Text = Text;
newCustomerDropDown.SelectedIndex = 0;
}
now when i click the create order button I want the information above to be labeled on my other windows form application.
private void newOrderButton_Click(object sender, EventArgs e)
{
//get the info from the text boxes
int Index = newCustomerDropDown.SelectedIndex;
Customer newCustomer = parent.data.getCustomerAtIndex(Index);
//make a new order that holds that info
Order brandSpankingNewOrder = new Order(newCustomer);
//add the order to the data manager
parent.data.addOrder(brandSpankingNewOrder);
//tell daddy to reload his orders
parent.loadOrders();
//close myself
this.Dispose();
}
The context is not very clear to me, but if I got it right, you open an instance of AddOrderForm from an instance of SalesForm, and when you click newOrderButton you want to update something on SalesForm with data from AddOrderForm.
If this is the case, there are many ways to obtain it, but maybe the one that requires the fewer changes to your code is this one (even if I don't like it too much).
Make the controls you need to modify in SalesForm public or at least internal (look at the Modifiers property in the Design section of the properties for the controls). This will allow you to write something like this (supposing customerTxt is a TextBox in SalesForm):
parent.customerTxt.Text = newCustomerDropDown.SelectedItem.Text;

tabcontrol not refreshing

I have a TabControl that starts with three TabPages in it. On the first tab there is a NumericUpDown (spinner) which displays the number of tabs and allows a user to add up to 10 extra tabs. Once they add more than about 5 or 6 it goes beyond the width of the form and the rest of the tabs are accessible by a couple of left/right arrows at the top. When going all the way to the right and then using the spinner to go back down to 0 (removing all the extra tabs and leave the starting three) it removes all tabs from the top of the pane and only by setting the spinner back to 1 does it refresh and display all 4 (3 from the start plus the 1 from the spinner).
I have tried several commbinations of
Application.DoEvents()
this.Refresh()
this.Invalidate()
this.Update()
but nothing seems to work. can anybody suggest a reason why it is not updating/refreshing?
public partial class Form1 : Form
{
TabPage[] tabs;
public Form1()
{
InitializeComponent();
tabs = new TabPage[tabControl1.Controls.Count];
tabs[0] = tabPage1;
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
int numTabs = tabControl1.Controls.Count;
decimal spinnerValue = numericUpDown1.Value;
if (numTabs < spinnerValue) //add a tab
{
TabPage[] newTabs = new TabPage[(int)spinnerValue];
for (int i = 0; i < numTabs; i++)
{
newTabs[i] = tabs[i];
}
TabPage tab = new TabPage("Tab " + numTabs);
newTabs[(int)spinnerValue-1] = tab;
tabControl1.Controls.Add(tab);
tabs = newTabs;
}
else //remove a tab
{
TabPage[] newTabs = new TabPage[(int)spinnerValue];
for (int i = 0; i < spinnerValue; i++)
{
newTabs[i] = tabs[i];
}
tabControl1.Controls.Remove(tabs[(int)spinnerValue]);
tabs = newTabs;
}
}
}
Without seeing any code or knowing what type of project this is winforms, WPF, ASP.NET etc..
it's hard to give a definite answer, I am going to assume that this is WinForms
I'm not sure if you can. The following is a quote from MSDN:
"Controls contained in a TabPage are not created until the tab page is shown, and any data bindings in these controls are not activated until the tab page is shown."
However, instead of having the update code get the values from the controls directly, maybe you could create a class that could hold the Data you use to populate the controls and then when the update code is called it asks the class for the value and the class checks if the control is loaded and otherwise it gets the value from the Data instead.

Categories