I have 100 sequential buttons and checkboxes showed in a Windows Forms application, and a database where some numbers are saved.
My aim is to hide the buttons and checkboxes according to the number saved in the database.
For example, in my database I have 4 numbers: 2, 4, 9, and 10. So I want to hide button2, checkbox2, button4, checkbox4, button9, checkbox9, button10, checkbox10.
Here's what I tried:
SqlCeCommand cmnd = con.CreateCommand();
cmnd.CommandText = "SELECT * FROM register_db WHERE semester = #s AND department = #d AND course = #c";
cmnd.Parameters.AddWithValue("#s", semester);
cmnd.Parameters.AddWithValue("#d", department);
cmnd.Parameters.AddWithValue("#c", course);
SqlCeDataReader rd = cmnd.ExecuteReader();
while (rd.Read())
{
string number = rd[0];
button[number].hide();
checkbox[number].hide();
// these are the main things that I didn't know how to do
}
Assuming your controls are named like that, you can access them through the form’s Controls collection:
string number = rd[0];
this.Controls["button" + number].Hide();
this.Controls["checkbox" + number].Hide();
But you should really put them in a separate list, and probably group them into panels in a StackedPanel, or a CheckedListBox.
All Windows Forms controls have a .Hide() method. The code is case-sensitive, so you're not calling it correctly. It needs to be capitalized:
button[number].Hide();
checkbox[number].Hide();
Alternatively, you can set their .Visible property:
button[number].Visible = false;
checkbox[number].Visible = false;
Or are you having trouble with the array of controls? Names like button1, button2, etc. aren't particularly meaningful in most cases. But if your controls are indeed meant to be part of an ordered collection of controls, you can probably just create a collection on your form to represent them:
protected IList<Button> Buttons
{
get
{
return new List<Button>
{
button1, button2, button3; // etc.
};
}
}
Accessing the numeric values in the names themselves would otherwise be a job for reflection, which in many cases isn't really the direction you want to go. It's better to build a structure which meets your needs than to circumvent a structure which doesn't.
With this you can access the controls as an array:
Buttons[number].Hide();
Checkboxes[number].Hide();
You can take it a step further and combine the two, since they pair together. Something like this:
private class ControlGroup
{
public Button Button { get; set; }
public CheckBox CheckBox { get; set; }
public void Hide()
{
this.Button.Hide();
this.CheckBox.Hide();
}
}
(You can add further error checking within that class to guard against nulls, etc. Probably give the class a more meaningful name, too.)
Then your collection becomes:
protected IList<ControlGroup> ControlGroups
{
get
{
return new List<ControlGroup>
{
new ControlGroup { Button = button1, CheckBox = checkbox1 },
new ControlGroup { Button = button2, CheckBox = checkbox2 },
new ControlGroup { Button = button3, CheckBox = checkbox3 }
// etc.
};
}
}
This keeps things logically grouped together where appropriate into a smarter data structure, which makes the calling code easier:
ControlGroups[number].Hide();
Related
This is my Form 1
When I click on the last row it shows data on the second grid control, Now I Want to Show this Data on the following Form (form 2 (Form with Purchase Written on Orange Color)) datagridview How can I do this.
table.Columns.Add("Item Name", Type.GetType("System.String"));
table.Columns.Add("Main Qty", Type.GetType("System.Decimal"));
table.Columns.Add("Price", Type.GetType("System.Decimal"));
table.Columns.Add("Per", Type.GetType("System.String"));
table.Columns.Add("Basic Amount", Type.GetType("System.Decimal"));
table.Columns.Add("Dis Amount", Type.GetType("System.Decimal"));
table.Columns.Add("Dis Percentage", Type.GetType("System.Decimal"));
table.Columns.Add("Tax Amount", Type.GetType("System.Decimal"));
table.Columns.Add("Net Value", Type.GetType("System.Decimal"));
dataGridView1.DataSource = table;
Above is the Form Load of (form 2)
And Below is the RowClick of Form 1
private void gridView1_RowClick(object sender, DevExpress.XtraGrid.Views.Grid.RowClickEventArgs e)
{
try
{
FRM_Purchase frm = new FRM_Purchase();
var ctx = new BizPlusEntities();
int GettingIdForShowing = (int)gridView1.GetRowCellValue(e.RowHandle, "PurchaseID");
var GettinginToDatabase = ctx.Purchases.Where(x => x.PurchaseID == GettingIdForShowing).ToList();
foreach (var item in GettinginToDatabase)
{
frm.txtPartyName.Text = item.PartyName;
frm.txtDate.Text = item.Date.ToString();
frm.txtTerms.Text = item.Terms;
frm.txtSeries.Text = item.Series;
frm.txtDueDate.Text = item.DueDate.ToString();
frm.txtPinvoice.Text = item.Pinvoice.ToString();
UniqueIdentifier = item.UniquePurchaseNumber;
string SelectingUniqueIdentfier = ctx.Purchases.SingleOrDefault(x => x.PurchaseID == item.PurchaseID)?.UniquePurchaseNumber ?? "Nulled";
var GettingInItems = ctx.ItemPurchaseDatas.Where(x => x.UniquePurchaseNumber == SelectingUniqueIdentfier).ToList();
foreach (var Sam in GettingInItems)
{
TItemName = Sam.ItemName;
TMainQty = Sam.MainQty ?? 0;
TPrice = Sam.Price ?? 0;
TPer = Sam.Per;
TBasicAmount = Sam.BasicAmount ?? 0;
TDisAmt = Sam.DisAmount ?? 0;
TDisP = Sam.DecimalPercentage ?? 0;
TTaxAmount = Sam.Gst ?? 0;
TTotalAmount = Sam.TotalAmount ?? 0;
}
frm.Show();
}
}
catch (Exception)
{
throw;
}
}
The problem is When I do frm.Table.Rows.Add(TItem,TMainQty...) on Form1
it shows input array is longer than the number of columns in this table
and when I create a new column it says the column already exists.
My advice would be to separate your data from the way that the data is displayed. Apart from that this makes it easier to unit test your data handling, it gives the displayer of the data the freedom to change how this data is being displayed.
In WPF this separation of model and view is almost forced, in Winforms you really have to pay attention otherwise you mix your data handling with the way that it is displayed, making it hard to change this.
In your case: should Form1 care about how the data is displayed in Form2, should it know that Form2 uses a DataGridView? Or should Form1 only care about what data is displayed in Form2, not in what format?
A proper interface with Form2 would be, that other Forms tell what data should be displayed, and if the data can be changed, that the other Form can ask afterwards the value of the data. Something like this:
private void ShowForm2()
{
var dataToShow = this.FetchDataToShow();
using (var dlg = new Form2())
{
dlg.Data = dataToShow;
var dlgResult = dlg.ShowDialog(this);
if (dlgResult == DialogResult.OK)
{
var dataToProcess = dlg.Data;
this.ProcessData(dataToProcess);
}
}
}
This way, you only tell Form2 what data to show, other forms don't really care about how Form2 shows its data. This gives Form2 the freedom to change how the data is displayed. Every user of this Form will have the same human interface.
By the way: did you notice that I also separated where Form1 gets the data for Form2 from and where it stores the results? This procedure also does not care about how the data is displayed in Form1, and gives you the freedom to change Form1, without having to change this procedure.
Use Databinding
It is usually way easier to use DataBinding to handle the rows in a DataGridView than to access the rows and the cells of the DataGridView directly.
To use databinding, your columns need to know which property of your Class should be displayed in this column. This is usually done in visual studio designer.
In your case, it seems that the DataGridView of Form2 needs to show ItemPurchaseDatas: every Row in the DataGridView will show several properties of one ItemPurchaseData. Using visual studio designer you will have added columns, and in every column you select the name of the property that needs to be displayed in that column:
DataGridView dataGridView1 = new DataGridView();
DataGridViewColumn columnName = new DataGridViewColumn();
columName.HeaderText = "Item Name";
columName.DataPropertyName = nameof(ItemPurchaseData.Name);
...
DataGridViewColumn columnPrice = new DataGridViewColumn();
columnPrice.HeaderText = "Price";
columnPrice.DataPropertyName = nameof(ItemPurchaseData.Price);
...
We earlier saw that the dialog had a property Data, that contains the data to be shown.
The form needs a method to extract the ItemPurchaseDatas that must be shown in the DataGridView:
public IEnumerable<ItemPurchaseData> GetInitialItemPurchaseDatas()
{
// TODO: use property Data to extract the ItemPurchaseDatas that must be shown
// in the DataGridView
}
Now all you have to do is on the event handler of FormLoad, get the data and put it in the DataSource of dataGridView1:
private void OnFormLoading(object sender, ...)
{
List<ItemPurchaseData> itemPurchaseDatas = GetInitialItemPurchaseDatas().ToList();
this.dataGridView1.DataSource = itemPurchaseDatas;
}
This is enough to show the data. However, it will be readonly: any changes that the operator makes: edits, addition of rows, removal of rows etc, are not reflected in itemPurchaseDatas. If you want that, you need an object that implements IBindingList like BindingList<T>.
If you want to know the changes that the operator made, it is usually wise to add the following methods:
private BindingList<ItemPurchaseData> DisplayedData
{
get => (BindingList<ItemPurchaseData>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
Now every change that the operator makes to the displayed data: add / remove rows, change cells, etc are reflected in property DisplayedData. Again, the display of the data is separated from the data itself: If the operator changes the looks of how the data is displayed, sorting the rows, rearranging the columns has no influence on the DisplayedData.
If you regularly have to handle SelectedRows, consider to add the following properties:
private ItemPurchaseData CurrentItemPurchaseData =>
(ItemPurchaseData)this.dataGridView1.CurrentRow?.DataBoundItem;
private IEnumerable<ItemPurchaseData> SelectedItemPurchaseData =>
this.dataGridView1.DataSource.SelectedRows.Cast<DataGridViewRow>()
.Select(row => row.DataBoundItem)
.Cast<ItemPurchaseData>();
Usage: on form loading displaying the data in the DataGridView and after a button press process the edited data:
private void OnFormLoading(object sender, ...)
{
IEnumerable<ItemPurchaseData> itemPurchaseDatas = GetInitialItemPurchaseDatas();
this.DisplayedData = new BindingList<ItemPurchaseData>(itemPurchaseDatas.ToList());
}
private void OnButtonOk_Clicked(object sender, ...)
{
ICollection<ItemPurchaseData> editedData = this.DisplayedData;
// if needed: check which items are changed
this.ProcessChangedData(editedData);
}
Again: due to the separation of view and model, the code in the view are one-liners
If you only want to Display the data, it is
So I do have a WinForm in my Programm, which contains a series of each a ComboBox and two TextBoxs. There are atm 8 Lines, but this will increase to a total of at least 32, therefore I would like to work with an Array or similar. How do I do that?
My current working, method is that a create a new array of TextBoxes/ComboBoxes which I assign the designated Elemt of the WinForm, manually. Therefore I have a list like this:
tbGU[0] = tbGU1;
tbGO[0] = tbGO1;
cbS[0] = cbS1;
Of course, this looks awful and isn't great if it's copied many times. Anyone got a Solution to my Problem?
I need to access the SelectedIndex of the ComboBox and the Text of the TextBoxes.
I was hoping that I could avoid having to create all the Elements manually by code.
One simple solution is to use the array initializer syntax:
ComboBox[] cbS = new[] { cbS1, cbS2, cbS3 ... };
Another way of doing this would be to get rid of the variables cbS1, cbS2 ... cBSn altogether and create the controls in a for loop.
ComboxBox[] cbS = new ComboBox[32];
// declare the text box arrays here as well
for (int i = 0 ; i < cbS.Length ; i++) {
cbS[i] = new ComboBox();
cbS[i].Location = ... // use "i" to help you position the control
// configure your combo box ...
this.Controls.Add(cbS[i]);
// do the same for the text boxes.
}
A third way is to create a custom control:
// name this properly!
public class MyControl: UserControl {
public ComboBox CbS { get; }
public TextBox TbGU { get; }
public TextBox TbGO { get; }
public MyControl() {
// create and configure the combo box and text boxes here ...
}
}
Then you can use a for loop to create lots of MyControls.
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;
To expand upon the title, I must create pairs of textboxes which together specify a numerical range (such as, say, 5 to 10, or -17 to -17). These textboxes must be such that the textbox which specifies the lower bound must have a lesser numerical value than the textbox which specifies the upper bound. One obvious solution come to mind: Ad hoc code in the parent form that handles the update event by checking their values. While this gets the job done, it strikes me as extremely inelegant and icky.
I suspect there must be a solid, OO-solution to this issue, but I'm not certain what that would be. How should I go about doing this?
Here's a thought - create a class called "TextBoxManager":
public class TextBoxManager
{
public List<Tuple<TextBox, TextBox>> LowerHigherPairs { get; set; }
public TextBoxManager()
{
LowerHigherPairs = new List<Tuple<TextBox, TextBox>>();
}
public void RegisterTextBoxes(TextBox lower, TextBox higher)
{
lower.Leave += TextBoxFocusLost;
higher.Leave += TextBoxFocusLost;
LowerHigherPairs.Add(new Tuple<TextBox, TextBox>(lower, higher));
}
public void TextBoxFocusLost(object sender, EventArgs e)
{
TextBox senderBox = sender as TextBox;
Tuple<TextBox, TextBox> matchingPair = LowerHigherPairs.Find(x => x.Item1 == senderBox || x.Item2 == senderBox);
if (matchingPair != null)
{
if (matchingPair.Item1 == senderBox)
{
//We know we should compare with the value in Item2.Text
}
else
{
//We know we should compare with the value in Item1.Text
}
}
}
}
In your form, declare this as a class level variable:
TextBoxManager higherLowerManager = new TextBoxManager();
Then, in your form OnLoad event, just register the pair of textboxes you want to manage:
higherLowerManager.RegisterTextBoxes(lowerEntryTextBox, higherEntryTextBox);
As you can see this class will then pair the two and subscribe them to a common event where we can work out which was which and perform the appropriate logic.
The other way to do this is to use a UserControl - this loses flexibility in terms of dynamic layout, but neatly encapsulates the entire interaction. Also, from a UI perspective, if the controls influence each other, then they should be close together anyway.
in my Win Forms app I create an array of dynamic custom controls inside a loop. These, lets call them 'boxes', are like my basic pieces of information. I also create string arrays in other parts of the code that contain the information of this 'boxes', so that for example string[3] is a variable of box[3] and so does stringa[3], stringb[3], stringc[3]... all the arrays with the same index are related to the box with that index. Hope I make myself clear.
Only 2 of this strings are shown in 2 labels inside each custom control 'box' in the array, but the others are there because I want to make something so that when the user clicks one of these controls the other strings can be shown in another control. Sort of something like "More Information...". All the 'boxes' in the array need to have the same event handler because I create +100.
To put it more into context, each custom control 'box' in the array shows the Symbol and the Price of a stock and I want that when the user clicks on each stock more quote information is shown on another special control which is like a placeholder for "More info".
I am thinking of 2 ways to do it:
If I could "detect" the index of the clicked control (which is the same in the strings related to it), I could just set this to an int j and all I have to do is show all the strings a,b,c... with index j. Unfortunately I cannot find a way to do this, maybe it is not even possible.
The other way I have thought is to create some properties for my custom control which "store" this variables, and in my app instead of assigning strings I would set properties for each control, which I could later retrieve when the control is clicked. I haven't tryed this because I don't know exactly how to do it.
What do you think? Do you know how can I achieve this or do you have a different idea that will work? Please help! Thanks in advance.
It's kind of a broad implementation question since there are countless ways you could implement something like this.
If you are creating two collections, one with the buttons and one with the information, you potentially could just assign each of the buttons 'Tag' properties to point to the corresponding info and assign a generic OnClick event handler that displays the info.. something like:
infoControl.text = ((InfoClass)((Button)Sender.Tag)).pieceOfInformation;
But again there are many ways to do this, and the choice comes down to how you store your information.
For your first method, you could have a property of your custom control that is the index.
public class Box : Control
{
// ...existing code
private int index;
public int Index
{
get
{
return index;
}
set
{
index = value;
}
}
}
OR
For your second method, you could have a property of your custom control that is the additional info string.
public class Box : Control
{
// ...existing code
private string extraInfo;
public string ExtraInfo
{
get
{
return extraInfo;
}
set
{
extraInfo = value;
}
}
}
In either case, you could then access the proper information right in your click handler for the "box".
i don't know about the first way - got to noodle around more, but in the second way you can extended your custom or built-in control: for example:
public class ExtendedLabel: Label
{
public string[] MoreInfo { get; set; }
}
and initialize it
public TestForm()
{
InitializeComponent();
ExtendedLabel label = new ExtendedLabel();
label.MoreInfo = new string[] { "test" };
this.Controls.Add(label);
label.AutoSize = true;
label.Location = new System.Drawing.Point(120, 87);
label.Name = "label1";
label.Size = new System.Drawing.Size(35, 13);
label.TabIndex = 0;
label.Text = label.MoreInfo[0];
}
And later in your event handler you can use the inside information