I am developing a restaurant application in which a new order will be placed. Itemtype will be in combobox. And based on the selection of combobox value, results should be displayed in DataGridView. For example, if I select "Biryani" Item in combobox, all the Biryani type items should be displayed in DataGridView.
As far as I understand your question, you could probably do this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApplication1 {
public partial class Form1 : Form {
public class Selection {
public enum eType { None, Indian, Chinese, Italian, British };
public eType Type { get; private set; }
public string Name { get; private set; }
public Selection(eType xType, string xName) {
Type = xType;
Name = xName;
} //
} // class
private List<Selection> _AllMeals = new List<Selection>();
public Form1() {
InitializeComponent();
comboBox1.DataSource = Enum.GetValues(typeof(Selection.eType)).Cast<Selection.eType>();
comboBox1.SelectedItem = Selection.eType.None;
Selection s1 = new Selection(Selection.eType.Chinese, "tasty Wan Tan soup");
Selection s2 = new Selection(Selection.eType.Chinese, "yummy Spring Rolls");
Selection s3 = new Selection(Selection.eType.Indian, "extreme spicy");
Selection s4 = new Selection(Selection.eType.Indian, "deadly spicy");
Selection s5 = new Selection(Selection.eType.Italian, "great Tortellini");
Selection s6 = new Selection(Selection.eType.Italian, "large Pizza");
Selection s7 = new Selection(Selection.eType.British, "fatty Fish and Chips");
_AllMeals.AddRange(new Selection[] { s1, s2, s3, s4, s5, s6, s7 });
dataGridView1.DataSource = _AllMeals;
} //
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
object o = comboBox1.SelectedItem;
Selection.eType lFilter = (Selection.eType)o;
var lOptions = (from x in _AllMeals
where x.Type == lFilter
select x).ToArray();
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = lOptions;
dataGridView1.Invalidate();
} //
} // class
} // namespace
Visit my blog at www.ohta.de
As i can read that you are talking about DAtaGridView and ComboBox, you must be using Windows Forms. So what you ca do is call SelectedIndexChanged event of ComboBox, then you can bind the DataGridView. e.g
private void ComboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
ComboBox combo = sender as ComboBox;
if(combo.SelectedIndex >=0)
{
int itemId=Convert.ToInt32(combo.SelectedValue);
datagridview1.DataSource = somFunction(itemId);
}
}
Related
I made a form that contains a datagridview, button, and a chart. The datagridview shows a Sales record of the business. The graph shows the top selling items from the datagridview. It is based from the quantity column (number of items sold) of the datagridview. What I want to happen is that when a user clicks the Generate button, a messagebox/ new form will appear.
It will contain a label (Top 3 Items) and under that label, there will be a ranking of the best selling items.
The question is how will I be able to create a ranking list in a messagebox/new form if the data is from a chart/datagridview?
Here's my current code for the form:
public partial class CFReport : Form
{
public CFReport()
{
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
label5.Text = DateTime.Now.ToLongTimeString();
timer1.Start();
}
private void CFReport_Load(object sender, EventArgs e)
{
// TODO: This line of code loads data into the 'casaFrancaDataSet.Sales' table. You can move, or remove it, as needed.
this.salesTableAdapter.Fill(this.casaFrancaDataSet.Sales);
timer1.Start();
label5.Text = DateTime.Now.ToLongTimeString();
label6.Text = DateTime.Now.ToLongDateString();
}
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("Top 3 items:" + Environment.NewLine + "1. " + Environment.NewLine + "2. " + Environment.NewLine + "3. ");
}
}
Try this if I got the question right. You can use LINQ for ordering your chart/datagridview.
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApp9
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
List<Product> products = new List<Product>(); // your chart/datagridview
products.Add(new Product { Name = "Banana", Price = 1 });
products.Add(new Product { Name = "Orange", Price = 10 });
products.Add(new Product { Name = "Apple", Price = 5 }); // your products
products = products.OrderByDescending(p => p.Price).ToList(); // ordering products by price
StringBuilder sb = new StringBuilder();
int i = 1;
foreach (var product in products)
{
sb.Append($"{i}. {product.Name} - {product.Price}\n");
i++;
}
MessageBox.Show(sb.ToString());
}
}
class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
}
Use data binding to separate your data (model) from how the data is displayed (view).
Your DataGridView has a DataSource. Use that to show the data.
class DisplayedProduct
{
public int Id {get; set;}
public string Name {get; set;}
public int Quantity {get; set;}
public decimal Price {get; set;}
}
Use visual studio designer to create the columns and tell which column should show which property, or do this programmatically.
public MyForm : Form
{
public MyForm()
{
this.InitializeComponent();
this.columnId.DataPropertyName = nameof(DisplayedProdut.Id);
this.columnName.DataPropertyName = nameof(DislayedProduct.Name);
...
}
I'll make a lot of small methods, so they will be easy to understand, easy to reuse, easy to unit test, debug and change:
Fetch the Products:
private IEnumerable<DisplayedProduct> FetchProductsToDisplay()
{
... // TODO: implement
}
Create a property to show the Products and fetch the displayed (and possibly edited) products:
private BindingList<DisplayedProduct> DisplayedProducts
{
get => (BindingList<DisplayedProduct>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
And show the products on form loading:
private void InitProductDisplay()
{
this.DisplayedProducts = new BindingList<DisplayedProduct>
this.FetchProductsToDisplay.ToList());
}
private void OnFormLoading(object sender, ...)
{
this.InitProductDisplay();
}
This is enough to show the products, and fetch them when needed. All changes made by the operator (add / remove rows, edit cells) are automatically reflected in the binding list.
private IEnumerable<DisplayedProduct> GetTopProducts()
{
return this.DisplayedProduts.OrderByDescending(product => product.Quantity);
}
private string CreateTextTopProducts(int count)
{
IEnumerable<DisplayedProduct> top3Products = GetTopProducts()
.Select((product, index) => new
{
Index = index + 1,
// select the properties that you want to show in the messagebox:
Name = product.Name,
...
})
.Take(count);
// use a string builder to efficiently add formatted lines:
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("Top 3 items:");
foreach (var product in top3Products)
{
stringBuilder.AppendFormat("{0}. {1}", product.Index, product.Name);
stringBuilder.AppendLine();
}
return stringBuilder.ToString();
}
Finally the methods to show the top3 products:
private void ShowTop3Products()
{
string displayText = this.CreateTextTop3Products(3);
MessageBox.Show(this, displayText, ...);
}
private void button2_Click(object sender, EventArgs e)
{
this.ShowTop3Products();
}
I have a comboBox where the user can select from a variety of drinks
comboBoxBeverage.Items.Add("");
comboBoxBeverage.Items.Add("Soda $1.95");
comboBoxBeverage.Items.Add("Tea $1.50");
comboBoxBeverage.Items.Add("Coffee $1.25");
comboBoxBeverage.Items.Add("Mineral Water $2.95");
comboBoxBeverage.DropDownStyle = ComboBoxStyle.DropDownList;
comboBoxBeverage.SelectedIndex = 0;
but I want to take the option selected in the combobox and split it so I can use the price.
I tried doing
double beverage;
beverage = double.Parse(comboBoxBeverage.Text.TrimStart(new[] { '$' }));
labelSubtotal.Text = beverage.ToString();
but it's giving me an error:
System.FormatException: 'Input string was not in a correct format.'
A more OOP approach to your question and also removing the problem of parsing the input text is the following:
First create a class Beverage
public class Beverage
{
public string Description {get;set;}
public decimal Price {get;set;}
public override string ToString()
{
return $"{this.Description} {(this.Price != 0 ? this.Price.ToString("C") : "")}";
}
}
now create a List<Beverage> with your data
List<Beverage> drinks = new List<Beverage>()
{
new Beverage {Description = "", Price = 0m},
new Beverage {Description = "Soda", Price = 1.95m},
new Beverage {Description = "Tea", Price = 1.50m},
new Beverage {Description = "Coffee", Price = 1.25m},
new Beverage {Description = "Mineral Water", Price = 2.95m}
};
comboBoxBeverage.DropDownStyle = ComboBoxStyle.DropDownList;
comboBoxBeverage.DataSource = drinks;
Now you can retrieve a Beverage instance from your combobox instead of a string and you get the Price directly from this Beverage instance
private void Button1_Click(object sender, EventArgs e)
{
if(comboBoxBeverage.SelectedItem != null)
{
Beverage b = comboBoxBeverage.SelectedItem as Beverage;
SubTotal += b.Price;
labelSubtotal.Text = SubTotal.ToString("C");
}
}
This solution works because the ComboBox calls ToString() for every item added to its list through the DataSource property unless you set the DisplayMember and ValueMember properties.
Also notice that when dealing with currency values you should use the decimal type, not the double type
You might give this a shot. It just takes the selected item, splits it into two elements where the $ is located, skips over the first element and assigns the second element's value to itemPrice:
using System;
using System.Linq;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public double SubTotal { get; set; }
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
comboBoxBeverage.Items.Add("");
comboBoxBeverage.Items.Add("Soda $1.95");
comboBoxBeverage.Items.Add("Tea $1.50");
comboBoxBeverage.Items.Add("Coffee $1.25");
comboBoxBeverage.Items.Add("Mineral Water $2.95");
comboBoxBeverage.DropDownStyle = ComboBoxStyle.DropDownList;
comboBoxBeverage.SelectedIndex = 0;
}
private void Button1_Click(object sender, EventArgs e)
{
var itemPrice = comboBoxBeverage.Text.Split('$').Skip(1).FirstOrDefault();
SubTotal += double.Parse(itemPrice);
labelSubtotal.Text = "$" + SubTotal;
}
}
}
I have a DataGridView whose DataSource is a DataTable with five columns. If I attempt to access a column's ReadOnly property, like so:
datagridview.Columns[1].ReadOnly = true;
It throws a NullReferenceExcpetion.
I understand this is due to how the framework manages its auto generated columns, as noted by the answer to this question.
My question is: How do I make a column(s) readonly when the data source is auto generated?
Can't really say why it's not working, but a simple test with this code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = GenerateData();
dataGridView1.Columns[0].ReadOnly = true;
}
private List<DataSourceTest> GenerateData()
{
return new List<DataSourceTest>()
{
new DataSourceTest(1, "A"),
new DataSourceTest(2, "B"),
new DataSourceTest(3, "C"),
new DataSourceTest(4, "D"),
new DataSourceTest(5, "E"),
new DataSourceTest(6, "F"),
};
}
}
public class DataSourceTest
{
public DataSourceTest(int id, string name) { ID = id; Name = name; }
public int ID { get; set; }
public string Name { get; set; }
}
and making the gridview EditMode set to EditOnEnter so we can easily check if it's readonly or not, shows that it does the job well.
But if you still have issues, the best bet is to use an event, and the closest event for your question is the DataBindingComplete that will fire after the binding is done, so on that time, you will have full access to all your columns as they already bind to the gridview object.
double click on the event in the GridView control and add your readonly setter:
private void dataGridView1_DataBindingComplete(
object sender, DataGridViewBindingCompleteEventArgs e)
{
dataGridView1.Columns[0].ReadOnly = true;
}
In true TEK fashion, I figured out a solution to my own question:
To do this, you need to make use of the ColumnAdded event
datagridview.ColumnAdded += dataGridView_ColumnAdded;
Then in the event, you can check a column by name:
private void dataGridView_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
{
if (e.Column is DataGridViewColumn)
{
DataGridViewColumn column = e.Column as DataGridViewColumn;
column.ReadOnly = true;
if (column.Name == "first_name")
{
column.ReadOnly = false;
}
}
}
Make column read-only when column has been generated
private void Form1_Load(object sender, EventArgs e)
{
List<Student> allStudent = new List<Student>();
for (int i = 0; i < 10; i++)
{
allStudent.Add(new Student { Name = "Student" + i, Roll = i + 1 });
}
dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = allStudent;
//Edited to show column count
MessageBox.Show("Column count is " + dataGridView1.Columns.Count);
foreach (DataGridViewColumn column in dataGridView1.Columns)
{
column.ReadOnly = true;
}
}
public partial class Student
{
public string Name { get; set; }
public int Roll { get; set; }
}
I have two comboboxes where the first one has categories (and I can fill that easily from the source file). The trick is having the second combobox show only the items that are associated with the chosen category from the first combobox. For example:
cb1 is populated from a sourcefile with the category values 1, 2, 3 & 4 and cb2 is populated with values A,B,C,D,E,F,G,H
What I am failing at doing is limiting what is seen in cb2. So when cb1's value is "1", I only want "A" and "B" to be visible in cb2, and if cb1 changes to "2" I only want "C" and "D" to be visible.
For winforms :
If you have a form with 2 combo boxes (cb1, cb2) you could use something like this? (obviously modified to support your data objects).
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
//create a list for data items
List<MyComboBoxItem> cb1Items = new List<MyComboBoxItem>();
//assign sub items
cb1Items.Add(new MyComboBoxItem("1")
{
SubItems = { new MyComboBoxItem("A"), new MyComboBoxItem("B") }
});
cb1Items.Add(new MyComboBoxItem("2")
{
SubItems = { new MyComboBoxItem("C"), new MyComboBoxItem("D") }
});
cb1Items.Add(new MyComboBoxItem("3")
{
SubItems = { new MyComboBoxItem("E"), new MyComboBoxItem("F") }
});
cb1Items.Add(new MyComboBoxItem("4")
{
SubItems = { new MyComboBoxItem("G"), new MyComboBoxItem("H") }
});
//load data items into combobox 1
cb1.Items.AddRange(cb1Items.ToArray());
}
private void cb1_SelectedIndexChanged(object sender, EventArgs e)
{
//get the combobox item
MyComboBoxItem item = (sender as ComboBox).SelectedItem as MyComboBoxItem;
//make sure no shinanigans are going on
if (item == null)
return;
//clear out combobox 2
cb2.Items.Clear();
//add sub items
cb2.Items.AddRange(item.SubItems.ToArray());
}
}
public class MyComboBoxItem
{
public string Name;
public List<MyComboBoxItem> SubItems = new List<MyComboBoxItem>();
public MyComboBoxItem(string name)
{
this.Name = name;
}
public override string ToString()
{
return Name;
}
}
I have a routine which opens a recordset and builds the Items collection for a combo box. After googling around I found the approach which uses the ComboboxItem class.
public class ComboboxItem
{
public string Text { get; set; }
public object Value { get; set; }
public override string ToString()
{
return Display;
}
}
My code uses this class to Add items to the ComboBox. When I run the app and click the Combobox the correct values are in the list... great! My problem is that when the form loads a record from the database, instead it looking up the appropriate list value which corresponds to the database value, it simply shows the value from the database: eg UK instead of United Kingdom. When I try to save the record, it tries to save "United Kingdom" instead of "UK". So I think the DisplayMember and ValueMember properties need assigning. I assumed that I would need to assign them as "Text" and "Value", but when I do this the Combobox displays a list of identical values. What am I doing wrong please?
Edit: This is a simplified version of what I have put into my ComboBox Class:
public class StandardComboBox : ComboBox
{
protected List<ComboboxItem> DataSourceList = new List<ComboboxItem>();
public bool SetRecordSource(string Criteria)
{
ADODB.Recordset RS = new ADODB.Recordset();
try
{
DataSourceList.Clear();
// Open ADDOB.Recordset RS with the records specified in Criteria
while (RS.EOF == false)
{
ComboboxItem MyComboboxItem = new ComboboxItem();
MyComboboxItem.Value = RS.Fields[0].Value.ToString();
MyComboboxItem.Display = RS.Fields[1].Value.ToString();
DataSourceList.Add(MyComboboxItem);
RS.MoveNext();
}
this.DataSource = DataSourceList;
this.ValueMember = "Value";
this.DisplayMember = "Display";
return true;
}
}
}
First, I think you should name your class better (eg. Country). Then set the name of the property too. Use string as your data type.
public class Country
{
public string ID { get; set; }
public string Name { get; set; }
}
Then you bind the combobox and set the DisplayMember and DisplayValue.
comboBox1.DataSource = listCountry;
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "ID";
If you want to take the value, just use SelectedValue.
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(comboBox1.SelectedValue.ToString());
}
Full source code.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
var listCountry = new List<Country>() {
new Country() {ID = "UK", Name = "United Kingdom"},
new Country() {ID = "US", Name = "United States of America"},
};
comboBox1.DataSource = listCountry;
comboBox1.DisplayMember = "Name";
comboBox1.ValueMember = "ID";
}
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show(comboBox1.SelectedValue.ToString());
}
}
public class Country
{
public string ID { get; set; }
public string Name { get; set; }
}
}
I've managed to work this out now. I'm from an MsAccess background where for example you just assign a combobox with 'UK' and the combobox displays 'United Kingdom'. In C# it's more complicated. When you want to assign a combobox with a value, you have to use the FindString() method to locate the value you want to display, then assign the SelectedIndex property in order to make it display that value. It's a ball ache, but I can build this logic into my class and not have to think about it again. Thanks for your input. –