I have a winform application that allows a user(customer) to place their order. In order to so,when the user clicks "place your order" button then another form is shown up, which includes a number of different controls (numericUpDown, TextBox, Combobox, Calendar and CheckBoxes). The user then has to fill all the fields and then confirm the process.
Now this scenario is just for placing a single order, what if want to allow the user to place a number of orders at once?
The scenario I want to apply is that the user firstly has to determine how many orders they want, then based on that number another form is created and filled by controls that are created dynamically according to the number of order specified.
I mean, the user wants 8 orders, so eight controls of each type is dynamically created.
Actually, till this point, I've managed to do so, but what I'm stuck on is that how could I get the data from the controls?
Back to my example:
If the customer wanted 8 orders, then I shall have:
8 textboxes
8 comboboxes
8 calendars
and so on
To be more clear, Now I want to get the inserted data from textbox1, calender1, combobox1 .... and so on, then store them in a temp list to be stored later into the database,
Anyone could give me a clue?
thanks in advance
I would recommend you to make your own user control for each order (each line).
Pick Add->User control... in project context menu then enter control name "MyOrderControl" and put all your order's controls on it.
Also you will need to create spectial class to hold order's data (if ouy have no such class). And add to MyOrderControl special method to return filled instance of that class.
public partial class MyOrderControl : UserControl
{
public MyOrderControl()
{
InitializeComponent();
}
public class Order
{
public string Name { get; set; }
public int Quantity { get; set; }
}
public Order GetFilledOrder()
{
//Validate data
return new Order()
{
Name = textBox1.Text,
Quantity = (int)numericUpDown1.Value
};
}
}
On form initialization you can create any amount of your control instances. You can use FlowLayoutPanel to place them automaticaly.
public Form1()
{
InitializeComponent();
const int nOrders = 10;
for (int n = 0; n < nOrders; n++)
{
flowLayoutPanel1.Controls.Add(new MyOrderControl());
}
}
And then add spectial method to retrieve all filled orders (using System.Collections.Generic, System.Linq):
public IEnumerable<MyOrderControl.Order> GetOrders()
{
foreach (var order in flowLayoutPanel1.Controls.OfType<MyOrderControl>())
{
yield return order.GetFilledOrder();
}
}
It will allow you to access all orders from parent form:
public void AcquireOrders()
{
var form = new Form1();
if (form.ShowDialog() != DialogResult.OK)
return;
foreach (var order in form.GetOrders())
{
System.Diagnostics.Debug.Print("Order: {0} Qty={1}",
order.Name, order.Quantity);
}
}
Related
I am trying to develop a program in which it could create forms and add controls to it at runtime.
It also should be able to save, (Open and Edit) the forms created with the new controls added it at Runtime.The Application starts In the Main form.
CODE BEHIND MAIN Form
private void Btn_CREATE_FORM_Click(object sender, EventArgs e)
{
Form_Properties fp = new Form_Properties();
fp.Show();
}
private void BTn_ADD_BTN_Click(object sender, EventArgs e)
{
/// WHAT CODE SHOULD I ENTER TO ADD BUTON TO NEW FORM
}
Basically the main form is used to create/open/save new forms and add controls to it.
When the user clicks on Create New Form button the user will be presented with the following form (FORM_PROPERTIES) in which the user can customize the name, width and height of the new form.
CODE BEHIND FORM_PROPERTIES Form
public partial class Form_Properties : Form
{
public Form_Properties()
{
InitializeComponent();
}
String form_name;
int form_width;
int form_height;
private void Btn_OK_Click(object sender, EventArgs e)
{
form_name = TBox_NAME.Text;
form_width = Convert.ToInt32(TBox_WIDTH.Text);
form_height = Convert.ToInt32(TBox_HEIGHT.Text);
New_Form nf = new New_Form();
nf.Text = form_name;
nf.Width = form_width;
nf.Height = form_height;
nf.Show();
}
}
The following image shows what happens at runtime based on the code I have written so far.
ISSUES
Need help to Write Code
To add controls to new form created.
To Save/Open/Edit Functionalities.
I also need to know the method to access properties of added controls at runtime.
eg: If the user adds a text box to the NEW FORM and decides to type some text in it, I need a method to save that text.
Is there a way for me to name the added controls?
It seems you want to build some kind of WinForms' form designer. Your program would be similar to Glade (though Glade is much more powerful).
I'm afraid the question is too broad, though. There are many questions to answer, for example, how do you describe the created interface.
While Glade uses XML, you can choose another format, such as JSON. Let's say that you have a TextBox with the word "example" inside it.
{ type:"textbox" text:"example" }
It seems you want to add your components to the form as in a stack. Maybe you could add its position. For example, a form containing a label
("data"), a textbox ("example"), and a button ("ok"), would be:
{
{ pos:0, type:"label", text:"data" },
{ pos:1, type:"textbox", text:"example" },
{ pos:2, type:"button", text:"ok" },
}
But this is just a representation. You need to a) store this when the form is saved, and b) load it back when the form is loaded.
For that, you will need a class representing the components, such as:
public class Component {
public override string ToString()
{
return string.Format( "position:{0}, text:{1}", this.Position, this.Text );
}
public int Position { get; set; }
public string Text { get; set; }
}
public class TextBoxComponent: Component {
public override string ToString()
{
return base.ToString() + "type:\"textbox\"";
}
}
...and so on. This is a big task, I'm afraid, with no simple answer.
I am creating a WinForms application (for an instrumentation system) where the main form displays a list of equipment in a tableLayoutPanel wrapped in a groupBox. Each item has an associated label and comboBox. The application must populate the list of equipment at runtime, and then the user will select a value for each comboBox, which the application will fetch to run tests.
Here is a partial screenshot of what I'm describing:
I would like to make a class to hold the list of items, the possible options for the associated comboBox, and the user's selection and then data bind it such that changes to the class cause the correct controls to be created and populated. But I'm not sure if this is possible, and if it is, how I'd set up the binding sources?
Per this example, this is the class I think I should start with:
public class TestEquipment
{
public string Name { get; set; }
public List<string> Options { get; set; }
public string Selection { get; set; }
public TestEquipment(string name, string selection)
{
Name = name;
Selection = selection;
}
}
I'm working on a project that involves simply filling out a string of textboxes and clicking a button that will add the values in the textboxes to their respective variables in a new instance of a class, and save this instance of the class in a database.
I created the database first of all and used Entity Frameworks to create the class from it, so I know they are linked to eachother.
The general code for my class is shown below:
public partial class AnaestheticRecord
{
public int PatientID { get; set; }
public System.DateTime Date { get; set; }
public string Owner_Name { get; set; }
//25 other attributes of class
...
//methods------------------------------------------------
//get number of instances of class (objects + 1 creates new PatientID)
private static int objects = 0;
public AnaestheticRecord()
{
++objects; //add one to count
}
~AnaestheticRecord()
{
--objects; //remove one from count
}
public static int getPatientID()
{
objects += 1; //add 1 more to create previously unused value
return objects; //return new value
}
So, when it came time to add the code to make the application function, I started by declaring new instances of the class displayed above, and of the Entities class that should allow access to the database...
public partial class Form1 : Form
{
//empty instance of class, each property takes data through console input
AnaestheticRecord aRecord = new AnaestheticRecord(); //create new instance of anaesthetic record class ready for data input
List<AnaestheticRecord> recordCollection = new List<AnaestheticRecord>(); //create list to store instances of anaesthetic records (display-record purposes)
BlueBookDBEntities db = new BlueBookDBEntities(); //create entities to run database
This all works fine, but once the series of textboxes has been filled out and the button is clicked to add this record to the datase, the following code is run and an error is produced:
private void btnSaveRecord_Click(object sender, EventArgs e)
{
//transfer and convert values from input to respective field in record
addToClass();
recordCollection.Add(aRecord); //add completed instance of class to list
db.TblAnaestheticRecords.Add(aRecord); //add completed anaesthetic record to database (ERROR)
db.SaveChanges(); //save new input to database
}
At this point, the line 'db.TblAnaestheticRecords.Add(aRecord)' throws an error stating that "Entity type AnaestheticRecord is not part of model for current context".
I'm a little stuck here, as I can't see what I've done wrong. I've done this kind of thing before using MVC where slightly more of the groundwork is done for you, but this is my first time using a database linked to a Windows Form Application.
If anyone could point out to me where I've gone wrong, and maybe point me in the right direction as to how to get past this error, I would really appreciate it.
Thanks,
Mark
In the context you need to insert also the AnaestheticRecord DbSet (all the related tables, 1 DbSet for relational table)
The data binding works as it I intend, kind of... The real issue I'm running into now is what I believe to be 2 different instances of my User Control, but only the original, debug list I implemented is showing.
In short, I am building 2 lists that are technically bound to the data grid, the default debugging list I created in the default constructor and then the real list I created to bind to the data grid.
Every time I click on the user control with the data grid, the default constructor adds another line to my debugging list and displays it on the screen.
Every time I click the button that builds a list of selected options on a separate user control I can see my the options add on to the list of options I had been creating and technically set it to the data context of the data grid, the same way the default debug list does, except when I click back over to the data grid user control, the default constructor runs again, ads another line to my debug list, and displays the debug list that is being built.
Here's a copy of the class with a couple lines I added to help debug the problem.
public partial class QuotePreview : UserControl
{
private SelectionList _selectionList;
private SelectionList temp;
public QuotePreview()
{
InitializeComponent();
_selectionList = (SelectionList)this.DataContext;
}
private void QuotePreview_Loaded(object sender, RoutedEventArgs e)
{
//Adds item to Debugging list
_selectionList.SelectedOptions.Add(
new Selection
{
ModelNumber = "this",
Description = "really",
Price = "sucks"
});
}
public QuotePreview(SelectionList selectedOptions)
{
InitializeComponent();
_selectionList = (SelectionList)this.DataContext;
temp = selectedOptions;
//The list I am actually trying to display
_selectionList.AddRange(selectedOptions);
QuotePreview_Loaded();
}
private void QuotePreview_Loaded()
{
foreach (var options in temp.SelectedOptions)
{
_selectionList.SelectedOptions.Add(options);
}
QuotePreviewDataGrid.ItemsSource = _selectionList.SelectedOptions;
}
}
The implementation of the default constructor, is called every time the user control / tab, is clicked on. When that happens, _selectionList is set to the data context of the user control, followed by the Loaded Event which adds a line to my data grid.
In another user control where I select the options I want to add to my data grid user control, I click a button that creates a list of the options I want to be added and calls the custom constructor I wrote. Once the constructor finishes, it calls a custom Loaded Event method that I created for shits and giggles, that adds the selected options to my _selectionList.
Now once I click on the data grid user control again, it goes through the whole default process, and adds another default line.
If I go back a tab and say I want these options again and go back to the data grid, it again goes through the default process and adds another default line.
Whats most intriguing though is that I can see both of the selectionLists build since I dont clear the in between processes. I see a list build of the options i want to display and a list build of the default options build...
Oh, also, SelectionList does implement ObservableCollection
i don't follow exactly what you are asking but loaded event will fire whenever load is required and in your case you are switching between the views , TabControl will not render its content until it is required !
bool _isDefaultItemAdded = false
private void QuotePreview_Loaded(object sender, RoutedEventArgs e)
{
if(!_isDefaultItemAdded)
{
//Adds item to Debugging list
_selectionList.SelectedOptions.Add(
new Selection
{
ModelNumber = "this",
Description = "really",
Price = "sucks"
});
_isDefaultItemAdded = true;
}
}
I finally came up with a solution to the problem.
public static class QuotePreview
{
public static ObservableCollection<PurchasableItem> LineItems { get; private set; }
static QuotePreview()
{
LineItems = new ObservableCollection<PurchasableItem>();
}
public static void Add(List<PurchasableItems> selections)
{
foreach (var selection in selections)
{
LineItems.Add(selection);
}
}
public static void Clear()
{
LineItems.Clear();
}
}
public class QuoteTab : TabItem
{
public ObservableCollection<PurchasableItem> PreviewItems { get; private set; }
public QuoteTab()
{
Initialize()
PreviewItems = QuotePreview.LineItems;
DataGrid.ItemSource = PreviewItems
}
}
I now have two list box, lstStock and lstSupply. lstSupply consists of
-Pen
-Pencil
-Staple
-Stapler
-Folder
-Notebook
-Eraser
-Marker
and i have two button, one button named btnAdd and another btnRemove.
when i click on btnAdd i want the selected item in lstSupply to be added into lstStock which i have done by
lstStock.Item.Add(lstSupply.SelectedItem);
if i select the same item in lstSupply twice, I want it to be consolidated in lstStock.
for instance, if i select Pen twice, the list box shall give me "Pen x 2" instead of Pen in a line and another Pen in another line.
I got the feeling this is dealing with foreach but i dont really know how to use that.
Okay, so first of all, you're going to need to store something else in lstStock. I'd suggest something like this:
public class StockItem {
public string Name { get; set; }
public int Count { get; set; }
}
Next, I'd suggest that you don't use the .Items field as the actual container where you're storing your stock items. Instead, I'd have a List<StockItem> that you use to populate lstSupply when you refresh.
private List<StockItem> _items = new List<StockItem>();
Then, when you click "add", you do two things:
Iterate through _items to see if there is already a stock item in there of the matching supply. Create one, and add it, if it does not exist.
Find the matching stock item in _items and increment its Count.
To get it to display the way you'd like, we can override ToString() in our StockItem class.:
public class StockItem {
public string Name { get; set; }
public int Count { get; set; }
public override string ToString() {
if (Count > 1)
return Name + " x" + Count;
return Name;
}
}
Finally, whenever any changes are made, you simply re-populate lstStock:
private void RefreshGui() {
lstStock.Items.Clear();
lstStock.Items.AddRange(_items.ToArray());
}
There are plenty of other ways of going about this, but for this simple exercise, this is how I think I would do it.
If you want the code to work without extra coding, you could simply do this:
if (lstSupply.SelectedIndex > -1 && !lstStock.Items.Contains(lstSupply.SelectedItem))
lstStock.Items.Add(lstSupply.SelectedItem);
this way you are sure that you don't add null items and add each item once max.