Is there anyway to set a value of a hidden control? - c#

I have a hidden control which contains a textbox control and I want to set the text property of the textbox but i get an NullReferenceException. However if I show the control, set the value and then hide it then i get no exception.
miStatus1.Show();
miStatus1.ioItem1.popIoItem(caseState);
miStatus1.Hide();
However this feels like a really unclean and not very elegant way to do it. And i'm seeing some flickering because i have to do this to 4 controls with up to 8 textboxes on each.
Is there any way to set the text propery of the textboxes while the control is hidden? Or is it perhaps a better idea to populate my textboxes when showing the control? And will this slow down my application as it needs to populate everytime the control is shown?
popIoItem - code
public void popIoItem(object obj){
if (ioType == 1)
{
tb.Text = (string)obj;
}
}
My interface
I'm trying to create the menu to the right and on each pressing of the categories the menus slide up/down and i hide/show the proper user control with the textboxes and other io-elements.
More code
When one of the boxes to the left is click'ed the following method is run:
public void openMenu(int caseNum)
{
caseDB.casesDataTable chosenCase;
chosenCase = _casesAdapter.GetDataByID(caseNum);
string caseName = "";
int caseOwner = -1;
DateTime caseDate = DateTime.Today;
string caseDesc = "";
int caseState = -1;
foreach (caseDB.casesRow casesRow in chosenCase)
{
if (!casesRow.IscaseNameNull())
caseName = casesRow.caseName;
if (!casesRow.IscaseCreatedByNull())
caseOwner = casesRow.caseCreatedBy;
if (!casesRow.IscaseCreatedNull())
caseDate = casesRow.caseCreated;
if (!casesRow.IscaseDescNull())
caseDesc = casesRow.caseDesc;
if (!casesRow.IscaseStateNull())
caseState = casesRow.caseState;
}
int caseJobs = (int)_jobsAdapter.JobCount(caseNum);
string caseStateStr = Enum.GetName(typeof(caseState), caseState);
caseInfoMenu1.popMenu(caseName, caseOwner, caseDate, caseDesc,caseJobs,caseStateStr);
}
The caseInfoMenu is the right menu. It consists of some drawing and mouse logic that draws the menu and handles hit-detection. Besides this it contains 4 user controls, one for each of the vertical tabs.
public void popMenu(string caseName, int caseOwner ,DateTime caseDate, string caseDesc, int caseJobs, string caseState)
{
marked = 0;
miGeneral1.Show();
miEconomy1.Hide();
miStatus1.Hide();
miHistory1.Hide();
miGeneral1.ioItem1.popIoItem(caseName);
miGeneral1.ioItem2.popIoItem(caseOwner.ToString());
miGeneral1.ioItem3.popIoItem(caseDate.ToShortDateString());
miGeneral1.ioItem4.popIoItem(caseJobs.ToString());
miGeneral1.ioItem5.popIoItem(caseDesc.ToString());
//miStatus1.ioItem1.popIoItem(caseState);
//This is commented out because it makes the application crash. However if I show miStatus1, set the value and hide it, it does not crash.
this.Invalidate();
}
Inside each of these user controls I have io-items user controls which essentially draws a blue box and puts a control in front of if ie. the textbox.
public partial class ioItem : UserControl
{
public int ioType { get; set; }
public int ioPadding { get; set; }
RichTextBox tb;
public ioItem()
{
InitializeComponent();
}
public void popIoItem(object obj){
if (ioType == 1)
{
tb.Text = (string)obj;
}
}
private void ioItem_Load(object sender, EventArgs e)
{
switch (ioType)
{
case 1:
tb = new RichTextBox();
tb.Location = new System.Drawing.Point(ioPadding, ioPadding);
tb.Name = "textbox";
tb.Size = new Size(this.Size.Width - (ioPadding * 2), this.Size.Height - (ioPadding * 2));
tb.BorderStyle = BorderStyle.None;
tb.Visible = true;
tb.BackColor = Color.FromArgb(255, 184, 198, 208);
tb.Font = new Font("Microsoft Sans Serif", 7);
this.Controls.Add(tb);
break;
case 2:
historyCtrl hiCtrl = new historyCtrl();
hiCtrl.Location = new Point(0,0);
hiCtrl.Size = new Size(this.Width, this.Height);
hiCtrl.Name = "history";
hiCtrl.Visible = true;
hiCtrl.BackColor = Color.FromArgb(255, 184, 198, 208);
this.Controls.Add(hiCtrl);
break;
default:
goto case 1;
}
}
}

Try checking the code with debugger..May be there is something other going on there? NullReference means that you try to do something with object that doesn't exist. Show/Hide just set Visible property of the control to true/false in normal situation(without custom overload/changes of Control class).

So i figured out what was wrong. The problem was something else than what I initially expected. The reason why it didn't work was because I created my textbox controls in the Load_event. When I tried to set the value of the text property of the textbox controls they hadn't been created yet. The reason why I had created the user controls containing the textboxes this way was in order to make it easy to drag them into the screen in the Designer. I found this discussion 'UserControl' constructor with parameters in C#, which showed me another way of doing it and now it works.

Related

Groupbox created in code doesn't dock correctly to parent TableLayoutPanel

In my C# WinForm application I have a tableLayoutPanel with only one row and two columns. The left column contains a TreeView, the right one another tableLayoutPanel which was set to Dock: Fill in the designer. I want to create several GroupBoxes programmatically that should use the full width of this second column also when the form is resized.
In the designer of Visual Studio it looks like this:
But when the application runs, the GroupBoxes don't use the complete width and it looks like this:
I'm using two methods to create the GroupBoxes (you can ignore the int values):
private void addDashboardRow(int i, int s)
{
// Add one row to dashboardTableLayoutPanel
sensorTableLayoutPanel.RowCount++;
// Maybe this one is wrong, but SizeType.AutoSize doesn't work either
sensorTableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Absolute, 75f));
sensorTableLayoutPanel.Controls.Add(createSensorGroupbox(i, s));
}
private GroupBox createSensorGroupbox(int i, int sen)
{
GroupBox g = new GroupBox();
//g.Width = 500; // I don't want fixed sizes, height of 75px is sufficient
//g.Height = 75;
g.Dock = DockStyle.Fill;
g.Text = "Sensor " + sen.ToString();
// just one label as example...
Label temp = new Label();
temp.Text = "Temperature: ";
temp.Location = new Point(5, 20);
Label tempVal = new Label();
tempVal.Text = "°C";
tempVal.Location = new Point(5, 50);
tempVal.Name = "Sensor" + sen.ToString() + "_temp";
//[... more labels]
g.Controls.Add(temp);
g.Controls.Add(tempVal);
return g;
}
My questions are:
How can I fit the GroupBox into the complete width of the parent container?
How can I delete all rows? E.g. when looking up the sensors during run time all GroupBoxes are dublicated, but everything should be created newly.
Layout of the question: how should I format terms like TableLayoutPanel correctly in this forum? It's my second question here and I haven't found out, yet.
For the second question. Currently I'm using this method that doesn't do what I'm expecting:
private void clearDashboardTable()
{
if (sensorTableLayoutPanel.Controls.Count == sensorList.Count) {
//MessageBox.Show(sensorTableLayoutPanel.Controls.Count.ToString());
foreach (Control con in sensorTableLayoutPanel.Controls)
{
sensorTableLayoutPanel.Controls.Remove(con);
con.Dispose();
}
//sensorTableLayoutPanel.Dispose();
sensorList.Clear();
}
}
I commented out what I've tried so far in the above code or added additional comments. I used this topic to try to understand the basics of dynamic groupbox creation: Add Row Dynamically in TableLayoutPanel
One way to achieve the outcome you describe is to make a custom UserControl containing a docked GroupBox that contains a docked TableLayoutPanel.
To adjust the width when the container changes, attach to the SizeChanged event of the parent.
public partial class CustomGroupBox : UserControl
{
public CustomGroupBox() => InitializeComponent();
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if((Parent != null) &&!DesignMode)
{
Parent.SizeChanged += onParentSizeChanged;
}
}
private void onParentSizeChanged(object? sender, EventArgs e)
{
if(sender is FlowLayoutPanel flowLayoutPanel)
{
Debug.WriteLine($"{flowLayoutPanel.Width}");
Width =
flowLayoutPanel.Width -
SystemInformation.VerticalScrollBarWidth;
}
}
public new string Text
{
get=>groupBox.Text;
set=>groupBox.Text = value;
}
}
Main Form
The main form is laid out similar to your image except to substitute a FlowLayoutPanel on the right side.
Now test it out with this minimal code in the method that loads the Main Form:
public partial class MainForm : Form
{
int _id = 0;
public MainForm()
{
InitializeComponent();
flowLayoutPanel.AutoScroll= true;
for (int i = 0; i < 5; i++)
{
var customGroupBox = new CustomGroupBox
{
Text = $"Sensor {++_id}",
Width = flowLayoutPanel.Width - SystemInformation.VerticalScrollBarWidth,
Padding = new Padding(),
Margin = new Padding(),
};
flowLayoutPanel.Controls.Add(customGroupBox);
}
}
}
Clear
An example of clearing the sensors would be to add a button and set a handler in the same Load method:
buttonClear.Click += (sender, e) => flowLayoutPanel.Controls.Clear();

Programmatically handle a dynamic range of C# UI form elements

I want to optimize my C# code for dynamically showing 1 to 10 elements in a form. The way I currently have it implemented is clumpsy and not very maintenance-friendly, so I did hope it will be possible to rewrite this, to a function that can handle e.g. one element at a time.
I have this below UI form which consists of 10 textBox and 10 pictureBox:
Depending on a showElements number I have, then it should show 1 to 10 of these boxes (both the textbox and the imagebox). My current code is this:
// How many boxes to show
int showElements = 4;
// Show X elements
for (int i = 1; i <= showElements; i++)
{
// Show specific element
switch (i)
{
case 1:
textBox1.Width = width - widthSubstract;
pictureBox1.Width = width - widthSubstract;
break;
case 2:
textBox2.Width = width - widthSubstract;
pictureBox2.Width = width - widthSubstract;
break;
... CUT but similar code up to 10
// Hide remaining elements
for (int i = showlements; i <= 10; i++)
switch (i)
{
case 1:
textBox1.Visible = false;
pictureBox1.Visible = false;
break;
case 2:
textBox2.Visible = false;
pictureBox2.Visible = false;
break;
... CUT but similar code up to 10
.. you get the trivial point for the rest. I have more code in each case but they all relate to the specific textBox or imageBox that should be shown.
I cannot figure out how I can optimize this? :-)
You can add UI elements on demand, instead of pre-defining them within your application.
for(int i = 0; i < 10; i++)
{
TextBox textBox = new TextBox();
textBox.Text = "Hello";
textBox.Tag = i;
this.Controls.Add(textBox);
}
Then re-access them via the Control property.
(this.Controls[i] as TextBox).Text += " World!";
In your particular case, you could define a custom control, containing a textBox and a PictureBox. Then adding that custom control as a child.
I do it like this :
namespace WindowsFormsApplication72
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
PictureText[] pictureTexts = new PictureText[] {
new PictureText { textbox = textBox1, picturebox = pictureBox1},
new PictureText { textbox = textBox2, picturebox = pictureBox2},
new PictureText { textbox = textBox3, picturebox = pictureBox3},
new PictureText { textbox = textBox4, picturebox = pictureBox4},
new PictureText { textbox = textBox5, picturebox = pictureBox5},
new PictureText { textbox = textBox6, picturebox = pictureBox6},
new PictureText { textbox = textBox7, picturebox = pictureBox7},
new PictureText { textbox = textBox8, picturebox = pictureBox8},
new PictureText { textbox = textBox9, picturebox = pictureBox9},
new PictureText { textbox = textBox10, picturebox = pictureBox10}
};
}
}
public class PictureText
{
public TextBox textbox { get; set; }
public PictureBox picturebox { get; set; }
}

Extra rectangle gets drawn after label

I want to include the colored panel below into my form:
For this I have created custom panel which will change Border color based on Radio Button selection. My panel code is
InfoPanel.cs
class InfoPanel : Panel
{
private Color colorBorder = Color.Transparent;
public InfoPanel()
: base()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.DrawRectangle(
new Pen(
new SolidBrush(colorBorder), 2),
e.ClipRectangle);
e.Graphics.DrawLine(new Pen(new SolidBrush(colorBorder), 0), 50, 0, 50, 50); //drawing a line to split the child & parent info panel
}
public Color BorderColor
{
get
{
return colorBorder;
}
set
{
colorBorder = value;
}
}
}
In my form,
1. created one parent Info Panel
2. created one child panel with Picture box
3. One label in parent info panel to show the information
Now for the parent panel I am changing the colors [back, border] & text based on user selection & for child panel I am not changing border color but updating back color based on user selection.
Below is the code for changing the panel colors, image, text update:
private void rbIPAddress_CheckedChanged(object sender, EventArgs e)
{
if (rbIPAddress.Checked)
{
ParentInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#FFFFEE");
ParentInfoPanel.BorderColor = System.Drawing.ColorTranslator.FromHtml("#DADA85");
ChildInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#F6F6D8");
InfoPanelPictureBox.Image = Template.InfoPanelInfoImage;
Infolabel.Text = "IP Address is already configured. You can switch to Forward Lookup Zone by choosing other configuration. *IP Address \ncan be either LB IP Address.";
txtBoxIPAddress.Enabled = true;
textBoxPort.Enabled = true;
}
else
{
Infolabel.Text = "";
txtBoxIPAddress.Text = "";
txtBoxIPAddress.Enabled = false;
textBoxPort.Enabled = false;
}
}
private void rbForwardLookupZone_CheckedChanged(object sender, EventArgs e)
{
if (rbForwardLookupZone.Checked)
{
ParentInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#FFFFEE");
ParentInfoPanel.BorderColor = System.Drawing.ColorTranslator.FromHtml("#DADA85");
ChildInfoPanel.BackColor = System.Drawing.ColorTranslator.FromHtml("#F6F6D8");
InfoPanelPictureBox.Image = Template.InfoPanelInfoImage;
Infolabel.Text = "Forward Lookup Zone is already configured. You can switch to IP Address by choosing other configuration and \nchanging port number will affect Firewall rules.";
textBoxControlPlane.Enabled = true;
if (string.IsNullOrEmpty(textBoxControlPlane.Text))
{
textBoxControlPlane.Text = Constants.DefaultControlPlaneDomain;
}
}
else
{
Infolabel.Text = "";
textBoxControlPlane.Text = "";
textBoxControlPlane.Enabled = false;
}
}
Note: used next line character to display label text in multiple line
Output: Everything is ok but in the end of label text I am getting another rectangle box. I'm wondering why is showing like this? Am I doing wrong? Please help me on this.
The issue is that you're using e.ClipRectangle. It informs you which portion of the control needs to be redrawn. This is sometimes only a small part of the control rather than the whole thing (in your case the area of the extra rectangle). Always draw the control's full rectangle instead.
Also, you must dispose of both the Pen and SolidBrush. Failing to do so causes memory leaks. Utilize the using statement.
using(SolidBrush brush = new SolidBrush(colorBorder))
using(Pen pen = new Pen(brush, 2))
{
e.Graphics.DrawRectangle(pen, new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1));
e.Graphics.DrawLine(pen, 50, 0, 50, 50);
}

C# Winforms trouble centering a form over a control, can center it over other controls

I have an application which has multiple controls on multiple pages. I'm using the Telerik Winforms controls. One of those pages has a RadGridView in a UserControl, which is on a RadPageViewPage in a RadPageView, which in turn is nested in another RadPageViewPage in another RadPageView. The following code is basically just to handle a Loading spinner that is housed in its own transparent Form. It is always called on its own thread, of course.
private static void RunWaiting(Control c, string text)
{
wf = new WaitingForm();
wf.drwbieSpinnerFrame.Text = text;
wf.ShowInTaskbar = false;
wf.Left = c.Left + (c.Width / 2);
wf.Top = c.Top + (c.Height / 2);
wf.Width = c.Width;
wf.Height = c.Height;
wf.FormBorderStyle = FormBorderStyle.None;
wf.ControlBox = false;
wf.TopMost = true;
wf.StartPosition = FormStartPosition.Manual;
Application.Run(wf);
}
Clearly, I want the spinner (WaitForm) to appear over the center of a control on-demand. It's fine if I pass it the main UserControl that houses the RadGridView, and I can also pass it the parent of that control and center on the RadPageViewPage. If I pass this method the RadGridView, the spinner doesn't appear at all, even though the code is called and the attributes of "wf" are still set. Can anyone tell me what I'm missing?
Unfortunately, the time-consuming task itself is updating the UI and must exist on the UI thread for other reasons, but I was able to put together a solution by using a recursive class that got me the appropriate (x,y) coordinates to display a form in the middle of the control.
private static void RunWaiting(Control c, string text)
{
wf = new WaitingForm();
wf.drwbieSpinnerFrame.Text = text;
wf.ShowInTaskbar = false;
int[] tl = GetTopLefts(c);
wf.Top = (tl[0] + (c.Height / 2)) - (wf.Height / 2);
wf.Left = (tl[1] + (c.Width / 2)) - (wf.Width / 2);
wf.FormBorderStyle = FormBorderStyle.None;
wf.ControlBox = false;
wf.TopMost = true;
wf.StartPosition = FormStartPosition.Manual;
IsHolding = true;
Application.Run(wf);
}
And the method it calls to get the position data:
private static int[] GetTopLefts(Control c)
{
int top, left;
top = c.Top;
left = c.Left;
if (c.Parent != null)
{
int[] parentPoint = GetTopLefts(c.Parent);
top += parentPoint[0];
left += parentPoint[1];
}
return new int[] { top, left };
}
You could probably get through this just fine by making the final output of GetTopLefts() a Point, but something about this way felt arbitrarily more reliable.

Windows Forms Separator Control

Where in VS2010 can I find a horizontal separator control, as can be found in Outlook settings (screenshots below)?
https://jira.atlassian.com/secure/attachment/14933/outlook+settings.jpg
http://www.keithfimreite.com/Images/OutlookSettings3.gif
Note: VB.NET preferred, but C# answers okay.
If I'm not mistaken, that's just a Line control, but I don't think that control exists anymore. Here is a workaround.
label1.AutoSize = False
label1.Height = 2
label1.BorderStyle = BorderStyle.Fixed3D
Even though this has been answered, I found the following to be what I need based partly on smoore's answer.
Create a new control. Edit the code to be the following:
public partial class Line : Label
{
public override bool AutoSize
{
get
{
return false;
}
}
public override Size MaximumSize
{
get
{
return new Size(int.MaxValue, 2);
}
}
public override Size MinimumSize
{
get
{
return new Size(1, 2);
}
}
public override string Text
{
get
{
return "";
}
}
public Line()
{
InitializeComponent();
this.AutoSize = false;
this.Height = 2;
this.BorderStyle = BorderStyle.Fixed3D;
}
}
Replace Line with the control's class name you want. This will put a separator that will allow you to resize in the designer and disables adding text, changing the autosize forces the size's height to be 2 and width to be whatever you want, and disables adding text.
It's not actually included in the standard set of controls (pretty sure it used to be back in the day!) but you can easily create your own or cheat by using a GroupBox with no text and a height of 1px.
UserControl to provide the same thing: (Not written by me, source: http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/0d4b986e-3ed0-4933-a15d-4b42e02005a7/)
public partial class LineSeparator:UserControl
{
public LineSeparator()
{
InitializeComponent();
this.Paint += new PaintEventHandler(LineSeparator_Paint);
this.MaximumSize = new Size(2000, 2);
this.MinimumSize = new Size(0, 2);
this.Width = 350;
}
private void LineSeparator_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawLine(Pens.DarkGray, new Point(0, 0), new Point(this.Width, 0));
g.DrawLine(Pens.White, new Point(0, 1), new Point(this.Width, 1));
}
}
I wrote a custom control just for this purpose. It supports both vertical and horizontal modes. Just install my [small] control suite and drag the separator control onto the form and place it anywhere you want.
Install-Package ALMSTWKND -Version 1.0.0
The controls will show up in the Toolbox pane after installation.

Categories

Resources