add controls (Picturebox) in loop - c#

What i have: Big PictureBox (lets call it Pic_Map) on the form. A class Ore.cs, A List<Ore> ores; and a database that pulls the data and places it into the ores list.
Functionality: So, The functionality of this is i have a TextBox/Combobox and a Button. When i press the Button, it will loop through the ores list and Dynamically add PictureBoxes ontop of the Pic_Map based on TexBox/ComboBox being equal to the data (in this case Ore_Name).
Problem: This all works fine, but the problem is that when i add the PictureBoxes Dynamically, it only seems to add the last value on the ores list (Red circles on Pic_Map). so, it ends up showing only 1 PictureBox instead of lets say 3, since i have 3 value Names that match with the TextBox/ComboBox.
Question: How to get it to work like when i write/choose "Flame Stone" it looks on all the data that has "Flame Stone" in its name and add it (Instead of it adding only the last value from the list).
Code:
private void PopulateComboBoxByName()
{
PictureBox ore_Area = new PictureBox();
db.GetOre(); //Getting data and putting it into "ores" list
foreach (Ore ore in db.ores)
{
if (CBOX_Filter.SelectedItem.ToString() == ore.Ore_Name)
{
int area_Width = Convert.ToInt32(ore.Area_Width);
int area_Height = Convert.ToInt32(ore.Area_Height);
int ore_Width = Convert.ToInt32(ore.Ore_Width);
int ore_Height = Convert.ToInt32(ore.Ore_Height) - area_Height / 2;
ore_Area.Name = "ore_Area";
ore_Area.ImageLocation = #"Data\Images\Circle.png";
ore_Area.SizeMode = PictureBoxSizeMode.StretchImage;
ore_Area.Size = new Size(Convert.ToInt32(area_Width), Convert.ToInt32(area_Height));
ore_Area.Location = new Point(Convert.ToInt32(ore_Width), Convert.ToInt32(ore_Height));
ore_Area.BackColor = Color.Transparent;
this.Controls.Add(ore_Area);
}
}
ore_Area.Parent = PIC_Map;
}
Images:
Data:
Map:

Thanks to LarsTech, it has been fixed.
Making the picturebox is moved inside the loop, this.controls.add is changed to PIC_Map.controls.add and Ore_Area.Parent = PIC_Map; is deleted.

Related

Dynamically Generate Groupboxes

I'm working on an inventory program and have finished the main functionality as a command line console app. I am now working on a version for winforms. I want to enable it to dynamically generate a Groupbox that holds some textboxes. I'd rather not design 50+ lines of multiple textboxes. Keep in mind I'm rather new to programming, having started with C# a year ago. I know next to nothing on Winforms.
I've tried to use dynamic item = new Groupbox();as a similar method allowed generation of objects at runtime. In the command line app, the way it works is that based on information given, a certain amount of objects are passed into the list _AllItems. I was thinking of generating the Groupboxes by using:
private void InitializeGroupBox()
{
foreach (Product product in Product._AllItems)
{
dynamic Item = new GroupBox();
}
}
But I have the feeling I'm nowhere near the correct method. Thanks to anybody who helps.
You will need to learn a bit more, but here is what I usually do to achieve what you asked.
internal class DynamicForm : Form
{
private FlowLayoutPanel mFlowLayoutPanel;
public DynamicForm()
{
mFlowLayoutPanel = new FlowLayoutPanel();
mFlowLayoutPanel.Dock = DockStyle.Fill;
// Add to this Form
this.Controls.Add(mFlowLayoutPanel);
InitializeGroupBox();
}
private void InitializeGroupBox()
{
mFlowLayoutPanel.SuspendLayout(); // Performance
for (int i = 1; i <= 20; i++) {
var groupBox = new GroupBox();
groupBox.Text = "GroupBox #" + i;
groupBox.Size = new Size(200, 50);
var textBox = new TextBox();
textBox.Dock = DockStyle.Fill;
// Add the TextBox to GroupBox
groupBox.Controls.Add(textBox);
// Add to this Form
mFlowLayoutPanel.Controls.Add(groupBox);
}
mFlowLayoutPanel.ResumeLayout(); // after suspend, resume!
}
}

Alpha background replaced by white background in a dataGridView (first data filling)

I'm using a dataGridView to show a list of task and a delete button in an image column. My problem is that my image as an alpha background (.png) and the first data filling creates a white background to my images, anyone knows how to fix it?
My project is a C# Windows form in Visual Studio Code 2019. When I call the filling function again it removes the white background.. Also I'm using the same code to fill it the first time and all the other times after.
Here's some printscreen:
I've opened my program, then added 2 new task and the one that was loaded (task1) as a white bg while the two others got an alpha bg:
https://i.imgur.com/3DGAicO.jpg
I've opened my program with these 3 tasks:
https://i.imgur.com/fOkOXrT.jpg
I've loaded an Empty list then loaded a list of 3 tasks
https://i.imgur.com/i1GV89z.jpg
More info: When I load a new list that contains X Tasks, it won't replace the bg of the X first tasks, seems like the dataGridView needs to be empty in the first place?
Init:
public myApp()
{
InitializeComponent();
ComboList(1); // Select my list of task
int ID = Int32.Parse(comboBox_Lists.SelectedValue.ToString()); // Get the ID of this list
RefreshData(ID); // Fill the DataGridView with all the tasks linked to the selected list
}
public void RefreshThingsList(int ID)
{
using (MySqlConnection cnn2 = new MySqlConnection(Globals.connectionString.ToString()))
{
using (MySqlDataAdapter commandTasks = new MySqlDataAdapter("Select Item from Tasks where ID_List = " + ID, cnn2))
{
// Fill the dataFridView with the selected list of tasks
DataTable dataSrc = new DataTable();
commandTasks.Fill(dataSrc);
dataGridViewListThings.DataSource = dataSrc;
// Resizing my columns: img = 32px / tasks = remaining width + some marging
DataGridViewColumn column0 = dataGridViewListThings.Columns[0];
column0.Width = 32;
DataGridViewColumn column1 = dataGridViewListThings.Columns[1];
column1.Width = dataGridViewListThings.Width - 41;
}
}
}
I expect to have a 100% of the time alpha background for my image column...
Any ideas?

Programmatically added LinkLabel not visible

I am encountering a strange phenomenon: I have a WinForms application with four GroupBoxes, all four initially empty. I use this to track new followers/unfollowers on Twitter, planning on expanding its use once this functions properly.
It does work properly for new followers. For these I have a GroupBox called grpFollow, to which I add LinkLabels with the ScreenNames of my new followers like this:
var folTop = new Point(grpFollow.Left + 5, grpFollow.Top + 5);
lblFollowers.Text = Properties.Settings.Default.FollowersNow.Count.ToString();
lblFriends.Text = Properties.Settings.Default.FriendsNow.Count.ToString();
var ctr = 1;
foreach (var fol in newFollowers)
{
var kvp = LookupUser(fol);
if (string.IsNullOrEmpty(kvp.Key)) continue;
var linklabel = new LinkLabel()
{
Text = kvp.Value,
Width = 200,
Height = 15,
Location = folTop,
Visible = true,
Name = $"follbl{ctr}"
};
ctr++;
linklabel.Links.Add(0, linklabel.Width-1, $"https://twitter.com/{kvp.Key}");
linklabel.Click += Linklabel_Click;
grpFollow.Controls.Add(linklabel);
folTop.Y += 25;
}
LookupUser is just a function that passes the user id to the Twitter API and returns the name & screen_name of that user. Works fine, no problem. LinkLabels added nicely, no problem there either.
The trouble is with the other group boxes, e.g. the one for new friends:
folTop = new Point(grpFriends.Left + 15, grpFriends.Top + 15);
ctr = 1;
foreach (var fol in newFriends)
{
var kvp = LookupUser(fol);
if (string.IsNullOrEmpty(kvp.Key)) continue;
var llabel = new LinkLabel()
{
Text = kvp.Value,
Width = 200,
Height = 15,
Location = folTop,
Visible = true,
Name = $"frdlbl{ctr}"
};
ctr++;
llabel.Links.Add(0, llabel.Width - 1, $"https://twitter.com/{kvp.Key}");
llabel.Click += Linklabel_Click;
grpFriends.Controls.Add(llabel);
folTop.Y += 25;
}
As you can see, the logic is identical (because I want to extract this part to a separate method to avoid repetition). The location is set relative to the grpFriends group box, everything else is the same. Yet, the LinkLabel does not show, i.e. the second group box remains (visually) empty!
I have set a breakpoint to check what might go wrong. I single stepped through: the correct screen name is being retrieved, the location is correct, the control is added - but nothing ever shows up.
P.S: This code is in the RunWorkerCompleted method of a background worker, no further code is executed after this point.
Any idea why the Label isn't displayed?
Edit: I'll be damned!
I just changed the location of the grpFriends LinkLabel to 10,10: it appears, juuust clipping the lower border of my friends' group.
Now here is where this gets weird for me:
As you can see, the group has a Y value of 351. Point (10,10) should not even be in the box. So, it seems that the location is the culprit and the original code created the label outside the form.
Replacing grpFriends.Top as Y value with grpFriends.Top - grpFriends.Height got me closer. The LinkLabel is farther down from the top than I'd like but that's not so bad.
Very strange.
Okay, thanks to #raBinn I figured it out. The mistake here was me assuming, the LinkLabels needed a location relative to the Form. However, when adding them to the Controls collection of a GroupBox, they automatically assume a location relative to the GroupBox, not the Form.
So, my code is now basically the same for all four group boxes and looks like this:
PopulateGroup(newFollowers, grpFollow);
PopulateGroup(unFollow, grpLost);
PopulateGroup(newFriends, grpFriends);
PopulateGroup(unFriend, grpDitched);
And:
private void PopulateGroup(List<string> collPeople, GroupBox groupBox)
{
var folTop = new Point(12, 25);
foreach (var fol in collPeople)
{
var kvp = LookupUser(fol);
if (string.IsNullOrEmpty(kvp.Key)) continue;
var linklabel = new LinkLabel()
{
Text = kvp.Value,
Width = 200,
Height = 15,
Location = folTop
};
ctr++;
linklabel.Links.Add(0, linklabel.Width - 1, $"https://twitter.com/{kvp.Key}");
linklabel.Click += Linklabel_Click;
groupBox.Controls.Add(linklabel);
folTop.Y += 25;
}
}
So it doesn't really matter whether the group box is on the left or right, top or bottom of the form...

retrieving data from database with dynamic controls

I have made a voting system for our school. I already made an Add, Update, Delete, and retrieving data from database. I have a problem making a dynamic control automatically with the exact amount of data i have in database. For example, I have added pictures to the two Candidates in the position of President, then, I want it to dynamically create a PictureBox in my new form and retrieve the pictures in a 2 pictureboxes and radiobutton for their names under it. Is it possible for me to do it in array? I'm new to programming so bear with me please.
I'm a little bit confused. Can you make an example of it, if possible please.. :)
This should get you going.
Add this code to your form. And you can use this code for any buttons or anything you want. But you should maybe read up on the FlowLayoutPanel or GroupBox to get this to work in reality.
Point _imagePos = new Point(10,10);
int _imageCounter = 1;
private void NewPictureBox(string pathToImg, string imageName)
{
var img = new PictureBox
{
Name = "imageBox" + _imageCounter,
ImageLocation = pathToImg,
Left = _imagePos.X,
Top = _imagePos.Y,
SizeMode = PictureBoxSizeMode.StretchImage,
Height = 50,
Width = 50
};
var txt = new TextBox
{
Text = imageName,
Left = _imagePos.X,
Top = img.Bottom + 10
};
this.Controls.Add(img);
this.Controls.Add(txt);
_imageCounter++;
_imagePos.Y += 10 + img.Height + txt.Height;
}
private void Form1_Load(object sender, EventArgs e)
{
NewPictureBox(#"C:\test\QuestionMark.jpg", "image1");
NewPictureBox(#"C:\test\QuestionMark.jpg", "image2");
}

Dynamic and Immutable UIElement Arrays

I have a WrapPanel that contains multiple Canvas of the same size. Each Canvas has some UIElements (i.e. TextBox, TextBlock, Buttons etc) as children. The creation of each Canvas (including its UIElement children) and the number of Canvas to be created are all done in run-time code behind (no XAML).
Initially I did the following, which works:
// declare as class properties, so all function can access them
WrapPanel wp = new WrapPanel();
Canvas[] cv = new Canvas[500];
TextBox[] tb = new TextBox[500];
// A function (e.g. a Button_Click event) that generates multiple Canvas in a WrapPanel
for (int i = 0; i<myInt; i++)
{
cv[i] = new Canvas();
tb[i] = new TextBox();
cv[i].Children.Add(tb[i]);
wp.Children.Add(cv[i]);
}
The above code is straight forwards works OK - Until I implement add, minus and destroy buttons where I could
1. Add an additional `Canvas` on a click event
2. Remove the last `Canvas` on a click event
3. Destroy a specific `Canvas` in the `WrapPanel` on a click event (may ba a little cross icon in each `Canvas`)
If I process some combination of the above 3 actions, I could easily create UIElements of the same index or create Canvas that goes out of the range of what it had been declared initially.
I looked into List however, each Canvas have different properties (each also has UIElement Children with different properties) and I can't figure out how List would solve it. A way for me to go around that is to declare a super large Array size for Canvas (e.g. Canvas[] cv = new Canvas[99999] but I though that's not very efficient.
Also, if I use List, how could I change properties of a specific UIElement after the they are generated? E.g. If i add 10 Canvas and add to List, and after they are all generated, I need to select the 5th Canvas and change a TextBox.Text, how do I access it like I did in an Array (i.e. tb[5].Text = "Hello")?
Can anyone show me some approaches to this problem?
Just a direct translation on how to do this with a list instead below. Given your code I don't know why you want to keep track of the canvas and textbox'es in a list - you can just access the children collection of the WrapPanel directly instead - let's assume you do need these separate collections for now.
WrapPanel wp = new WrapPanel();
List<Canvas> cvList = new List<Canvas>();
List<TextBox> tbList = new List<TextBox>();
public void Init()
{
int myInt = 500;
// in a function (e.g. a Button_Click event) to generate the multiple Canvas in a WrapPanel
for (int i = 0; i < myInt; i++)
{
Canvas cv = new Canvas();
TextBox tb = new TextBox();
cv.Children.Add(tb);
wp.Children.Add(cv);
cvList.Add(cv);
tbList.Add(tb);
}
}
public void AddCanvas()
{
Canvas cv = new Canvas();
TextBox tb = new TextBox();
cv.Children.Add(tb);
wp.Children.Add(cv);
cvList.Add(cv);
tbList.Add(tb);
}
public void RemoveCanvas()
{
wp.Children.RemoveAt(wp.Children.Count-1);
cvList.RemoveAt(cvList.Count - 1);
tbList.RemoveAt(cvList.Count - 1);
}
Edit for added comment:
E.g. If i add 10 Canvas, and after
they are all generated, I need to
select the 5th Canvas and change a
TextBox.Text, how do I access it like
I did in an Array (i.e. tb[5].Text =
"Hello")?
You can just access the children directly. You know you only added Canvas elements to your WrapPanel. So you could do (wp is the WrapPanel again):
TextBox textbox = (wp.Children[5] as Canvas).Children[0] as TextBox;
textbox.Text = "Hello";
Just operate directly on the WrapPanel's Children collection.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddCanvasToWrapPanel(this.TestWrapPanel);
RemoveLastCanvasFromWrapPanel(this.TestWrapPanel);
AddCanvasToWrapPanel(this.TestWrapPanel);
DestroyCanvasAtWrapPanelIndex(this.TestWrapPanel, 0);
}
private void AddCanvasToWrapPanel(WrapPanel wp)
{
TextBox t = new TextBox();
Canvas c = new Canvas();
c.Children.Add(t);
wp.Children.Add(c);
}
private void RemoveLastCanvasFromWrapPanel(WrapPanel wp)
{
wp.Children.RemoveAt(wp.Children.Count - 1);
}
private void DestroyCanvasAtWrapPanelIndex(WrapPanel wp, int index)
{
wp.Children.RemoveAt(index);
}
}
}

Categories

Resources