Dynamically created trackbars and Labels and linking them together in C# - c#

I am trying to create trackbars and labels dynamically. In which if a user inputs a number like 4, it creates 4 trackbars and 4 labels. Then if the user moves any of the dynamically created trackbar moves it and updates its associated label. Then adds the numbers in all the labels and stores it in another label call total label.
Here is another way of explaining it. The user enters the number 3. The system creates 3 trackbars and 3 labels (next to the trackbars). The user moves first track bar to 5, the first label is automatically updated with 5. The user moves the second track bar to 3, the second label is automatically updated with 3. finally the user moves the third track bar to position 9 and the label is automatically updated with 9.
On the right side there is another label that shows 17 = (5+3+9).
I found a few websites ons creating dynamically controls but I don't know how to link a dynamically created trackbar to the dynamically created label. Then adding those dynamically added labels.
ALL this in C# on a windows form.
I did something very simliar to the below when creating my labels and trackbars.
for (int i = 0; i < 10; i++)
{
Label label = new Label();
label.Text = i.ToString();
PlaceHolder1.Controls.Add(label);
}
Thanks in advances
------------------------Update-----------------
void CreateLabeledTrackBars(Control host, int n)
how do I use this, I was hoping that when I start a new form
that all I would have to is this..that way the form already has the in n, but it seems not to work..i am confused on how the control works. can you please explain
namespace WindowsFormsApplication1
{
public partial class Form3 : Form
{
public static Form2 myNewForm = new Form2();
private TrackBar[] _trackBars;
private Label[] _labels;
public Form3(int n)
{
CreateLabeledTrackBars(new Label (), n);
}
//Then the rest of the code

You need to handle ValueChanged event of each TrackBar you create. To calculate sum of all values, store created controls in arrays.
private TrackBar[] _trackBars;
private Label[] _labels;
void CreateLabeledTrackBars(Control host, int n)
{
const int trackBarWidth = 150;
const int minValue = 0;
const int maxValue = 10;
const int defaultValue = 0;
_trackBars = new TrackBar[n];
_labels = new Label[n];
int y = 0;
for(int i = 0; i < n; ++i)
{
var label = new Label()
{
Top = y,
Left = trackBarWidth,
Text = defaultValue.ToString(),
Parent = host,
};
var trackBar = new TrackBar()
{
Top = y,
Width = trackBarWidth,
// save associated label
Tag = label,
Minimum = minValue,
Maximum = maxValue,
Value = defaultValue,
Parent = host,
};
// handle ValueChangedEvent
trackBar.ValueChanged += OnTrackBarValueChanged;
// apply vertical offset for next trackbar
y += trackBar.Height;
_trackBars[i] = trackBar;
_labels[i] = label;
}
}
void OnTrackBarValueChanged(object sender, EventArgs e)
{
// get trackbar, which generated event
var trackBar = (TrackBar)sender;
// get associated label
var associatedLabel = (Label)trackBar.Tag;
associatedLabel.Text = trackBar.Value.ToString();
// recalculate sum of all values and update other label here
}
Now, when you have an array of trackbars, getting sum of all values should be trivial.

Related

Creating a new control property for certain amount of time in C# Winform

Say if I want to create a multiple button on my form based on a loop value of 3 then 3 buttons should be created into that form, in my case I have this textbox input that should determine the loop value on button click. What I've tried:
void Button4Click(object sender, EventArgs e)
{
int get_col_range = Convert.ToInt32(textBox3.Text);
for(int i=0; i<get_col_range; i++) {
Button btn = new Button();
btn.Text = Convert.ToString(i);
this.Controls.Add(btn);
}
}
I put on value of 2 on the TextBox input as test value but turned out that nothing happened why?.
Try the following on a form with no controls in a button click event.
int top = 10;
int heightPadding = 30;
for (int index = 0; index < 3; index++)
{
var button = new Button()
{
Text = $"Button {index}",
Name = $"Button{index}",
Location = new Point(10, top)
};
Controls.Add(button);
top += heightPadding;
}

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; }
}

Dynamically created labels and text boxes not overwriting

I am creating some labels and text boxes when a combo box changes. I am using a tableFlowContainer with 2 columns and 5 rows. This works fine the first time the selection is changed but if I make another selection the labels and textboxes are not honoring the assigned location but seem to be inserting at 1 element increments. what should be {0,0} is {0,1} and {0,1} goes to {1,0} and so on.
Is there a way of clearing any labels and text boxes before rewritting the?
Here is my code.
private void cbPI_SelectedIndexChanged(object sender, EventArgs e)
{
//TextBox[] txtBox = new TextBox[5];
// Label[] lbls = new Label[5];
for (int x = 0; x <= 4; x++)
{
txtBoxs[x] = new TextBox();
this.txtBoxs[x].Text = "Text"+x;
this.txtBoxs[x].Name = "txtBoxName"+x;
this.txtBoxs[x].Anchor = System.Windows.Forms.AnchorStyles.Top;
this.Controls.Add(txtBoxs[x]);
lbls[x] = new Label();
this.lbls[x].Text = "label"+x;
this.lbls[x].Name = "lblBoxName1"+x;
this.lbls[x].Anchor = System.Windows.Forms.AnchorStyles.Right;
this.Controls.Add(lbls[x]);
tableLayoutPanel1.Controls.Add((lbls[x]), 0, x);
tableLayoutPanel1.Controls.Add(txtBoxs[(x)], 1, x);
}
}
By breaking at the controls the x variable is correct put the placement in the display is not.

How can I stop a Slider from changing its value when the user wants to drag it?

Take this scenario:
I have 5 sliders (slider 1, slider 2, slider 3, slider 4, and slider 5).
I can give each slider a value from 0 to 100, but...
They must share up to a maximum sum of 100 if you add all of their values together.
IE if slider 1 is 50 and slider 2 is 50, all other sliders must be 0.
IE if slider 1 is 100, all other sliders must be 0.
IE if slider 5 is 90, slider 2 is 5, and slider 3 is 5, all other sliders must be 0.
I want to make it so that if I increase a slider, I cannot go past the maximum if the sum of all the sliders is at the maximum, but I would also like it to not be able to actually process an event in that case. Specifically, if I try to drag a Slider, I do not want it to actually be able to change that value under those circumstances. Right now, I have the following code, but this will actually decrement the value if it goes over, which is not the same as constricting it from changing the value when the user tries to drag it:
public class MaximumSliderGroup
{
List<Slider> m_sliders = new List<Slider>();
Grid m_containerGrid;
int m_startColumn;
int m_startRow;
int m_startColumnSpan;
int m_startRowSpan;
double m_maximum;
public MaximumSliderGroup(Grid containerGrid, int startColumn, int startRow, int startColumnSpan, int startRowSpan, double maximum)
{
m_containerGrid = containerGrid;
m_startColumn = startColumn;
m_startRow = startRow;
m_startColumnSpan = startColumnSpan;
m_startRowSpan = startRowSpan;
m_maximum = maximum;
}
public void AddNewSlider(bool createNewRow)
{
Slider slider = new Slider();
slider.Maximum = m_maximum;
slider.Minimum = 0;
Grid.SetRow(slider, m_startRow + m_sliders.Count);
Grid.SetColumn(slider, m_startColumn + m_sliders.Count);
Grid.SetColumnSpan(slider, m_startColumnSpan);
Grid.SetRowSpan(slider, m_startRowSpan);
if (createNewRow)
{
RowDefinition rowDefinition = new RowDefinition();
rowDefinition.Height = GridLength.Auto;
m_containerGrid.RowDefinitions.Add(rowDefinition);
}
m_containerGrid.Children.Add(slider);
slider.ValueChanged += slider_ValueChanged;
m_sliders.Add(slider);
}
void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
Slider senderAsSlider = (Slider)sender;
double count = 0;
foreach (Slider slider in m_sliders)
{
count += slider.Value;
}
if (count > m_maximum)
{
senderAsSlider.Value--;
}
}
}
Edit: Just figured out a workaround that looks acceptable by my standards. It may not stop the event from happening, but it doesn't flicker the value when you keep trying to drag it due to the rounding.
public class MaximumSliderGroup
{
List<Slider> m_sliders = new List<Slider>();
List<Label> m_labels = new List<Label>();
Grid m_containerGrid;
Grid m_subContainerGrid;
int m_startColumn;
int m_startRow;
int m_startColumnSpan;
int m_startRowSpan;
double m_maximum;
public MaximumSliderGroup(Grid containerGrid, int startColumn, int startRow, int startColumnSpan, int startRowSpan, double maximum)
{
// Set the properties.
m_containerGrid = containerGrid;
m_startColumn = startColumn;
m_startRow = startRow;
m_startColumnSpan = startColumnSpan;
m_startRowSpan = startRowSpan;
m_maximum = maximum;
// Create a new sub-grid for sliders and labels at the specified location within the main grid.
m_subContainerGrid = new Grid();
Grid.SetRow(m_subContainerGrid, m_startRow);
Grid.SetColumn(m_subContainerGrid, m_startColumn);
Grid.SetColumnSpan(m_subContainerGrid, m_startColumnSpan);
Grid.SetRowSpan(m_subContainerGrid, m_startRowSpan);
ColumnDefinition sliderColumn = new ColumnDefinition();
sliderColumn.Width = new GridLength(1, GridUnitType.Star);
m_subContainerGrid.ColumnDefinitions.Add(sliderColumn);
ColumnDefinition textBoxColumn = new ColumnDefinition();
textBoxColumn.Width = GridLength.Auto;
m_subContainerGrid.ColumnDefinitions.Add(textBoxColumn);
m_containerGrid.Children.Add(m_subContainerGrid);
}
public void AddNewSlider(bool createNewRow)
{
// Create a new slider, and add it to the sub-grid.
Slider slider = new Slider();
slider.VerticalAlignment = VerticalAlignment.Center;
slider.VerticalContentAlignment = VerticalAlignment.Center;
slider.Maximum = m_maximum;
slider.Minimum = 0;
if (createNewRow)
{
RowDefinition rowDefinition = new RowDefinition();
rowDefinition.Height = GridLength.Auto;
m_subContainerGrid.RowDefinitions.Add(rowDefinition);
}
Grid.SetRow(slider, m_sliders.Count);
Grid.SetColumn(slider, 0);
slider.ValueChanged += slider_ValueChanged;
m_sliders.Add(slider);
m_subContainerGrid.Children.Add(slider);
// Create a new label, and add it to the sub-grid.
Label label = new Label();
label.Content = "0";
label.FontSize = 20;
label.VerticalAlignment = VerticalAlignment.Center;
label.VerticalContentAlignment = VerticalAlignment.Center;
Grid.SetRow(label, m_labels.Count);
Grid.SetColumn(label, 1);
m_labels.Add(label);
m_subContainerGrid.Children.Add(label);
}
void slider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
// Round the slider value.
Slider senderAsSlider = (Slider)sender;
double count = 0;
foreach (Slider slider in m_sliders)
{
count += slider.Value;
}
if (count > m_maximum)
{
senderAsSlider.Value = Math.Round(senderAsSlider.Value - 1);
}
else
{
if (senderAsSlider.Value != 0)
{
senderAsSlider.Value = Math.Round(senderAsSlider.Value);
}
}
// Update the relevant label.
foreach (Label label in m_labels)
{
if (Grid.GetRow(label) == Grid.GetRow(senderAsSlider))
{
label.Content = senderAsSlider.Value;
}
}
}
}
Ok, so this my friend is why they created coersion.
So, what you want to do is create a multibinding from a value you control to each of the sliders. Then you want to coerce on those values to control their values.
Step 1:
Rip out all the UI code from your class.
Step 2:
Create a data object that will store off values.
Step 3:
Multibind those values to a single total value.
Step 4:
Coerce the individual values whenever the multibinding changes.
Step 5:
Create the UI class
Step 6:
Every time you add a slider to the UI class, add a value to the data object and bind to it.

How to automaticly locate buttons on panel c#

I have a few buttons to add on the form. In the code I'm setting up some button properties:
class DigitButton : Button
{
private static int digitBtnTag;
public DigitButton()
: base()
{
this.Size = new Size(30, 30);
this.Tag = digitBtnTag;
this.Text = (this.Tag).ToString();
this.Margin = new Padding(2);
this.Padding = new Padding(2);
digitBtnTag++;
}
}
In the MainForm.cs I have
for (int i = 0; i < dgtBtns.Length; i++)
{
dgtBtns[i] = new DigitButton();
dgtBtns[i].Click += new EventHandler(this.digitButtonClick);
digitPanel.Controls.Add(dgtBtns[i]);
}
So when I launch a program I see all my buttons in the one place: (0;0) on digitPanel despite property Margin. So why don't all these buttons automaticly "push" each other in the different directions? And how to make it?
Have you tried using a FlowLayout Panel ?
Also, this video might help:
Windows Forms Controls Lesson 5: How to use the FlowLayout Panel
that's not the way controls works in c#. i'm guessing you programed at java a bit because the layout in jave works that whay, but in c# just do
for (int i = 0; i < dgtBtns.Length; i++)
{
dgtBtns[i] = new DigitButton();
dgtBtns[i].Location = new Point(50, 50 * i); // Multiplying by i makes the location shift in every loop
dgtBtns[i].Click += new EventHandler(this.digitButtonClick);
digitPanel.Controls.Add(dgtBtns[i]);
}
you'll have to figure out the location parameters by trying and see
You need to define Left and Top then add the button height or width each time you loop to position your buttons correctly i.e.
int bTop=0;
int bLeft=0;
for (int i = 0; i < dgtBtns.Length; i++)
{
dgtBtns[i] = new DigitButton();
dgtBtns[i].Click += new EventHandler(this.digitButtonClick);
dgtBtns[i].Top = bTop;
bTop += dgtBtns[i].Height;
digitPanel.Controls.Add(dgtBtns[i]);
}

Categories

Resources