I have a simple database with 2 tables (product and category) and the code below is to create a new product in the product table under one of the categories selected in the combo box.
public partial class frmAddProduct : Form
{
public frmAddProduct()
{
InitializeComponent();
db = new DatabaseEntities();
}
DatabaseEntities db;
string category = "";
Product _product;
private void frmAddProduct_Load(object sender, EventArgs e)
{
categoryBindingSource.DataSource = db.Categories.ToList();
CategoryList();
}
private void CategoryList()
{
var list = db.Categories.ToList();
cboCategory.DataSource = list;
cboCategory.DisplayMember = "CategoryName";
cboCategory.ValueMember = "CategoryID";
if (cboCategory.Items.Count > 1)
cboCategory.SelectedIndex = -1;
}
private void btnNew_Click_1(object sender, EventArgs e)
{
_product = new Product();
int id = int.Parse(txtID.Text);
decimal price = decimal.Parse(txtPrice.Text);
int qty = int.Parse(txtPrice.Text);
_product.ProductID = id;
_product.ProductName = txtName.Text;
_product.UnitPrice = price;
_product.UnitsInStock = qty;
_product.CategoryID = int.Parse(category);
db.Products.Add(_product);
db.SaveChanges();
}
private void cboCategory_SelectedIndexChanged(object sender, EventArgs e)
{
category = cboCategory.SelectedValue.ToString();
}
}
When I run the form an error appears saying "Additional information: Object reference not set to an instance of an object". The error refers to the next line in the code:
category = cboCategory.SelectedValue.ToString();
Does anyone know what is the problem here?
One note: CategoryID is an integer field in the database.
It looks like your setting of the SelectedIndex to -1 is triggering SelectedIndexChanged event with nothing in SelectedValue which you are trying to read in the handler. You might be missing check for null in the handler.
Related
I am currently creating a winform program where account holders are able to purchase specific products/services.
The current problem I am having is that when I select an account holders name on the combobox, all the account holders details is supposed to be shown on the multiline textbox, however all im getting so far is the account holders name.
Here is the relevant code...
public Form1()
{
InitializeComponent();
mAccHolder[0] = new Customer("Rich", "Bronze Account", 11);
mAccHolder[1] = new Customer("Patrick", "Silver Account", 21);
mAccHolder[2] = new Customer("Steve", "Gold Account", 12);
mAccHolder[3] = new Customer("Kevin", "Platinum Account", 25);
foreach(Customer r in mAccHolder)
{
comboBox1.Items.Add(r.GetName());
}
}
and the code which connects the combobox and textbox together...
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == -1)
{
cstTxtBox.Text = string.Empty;
}
else
{
cstTxtBox.Text = comboBox1.SelectedItem.ToString();
}
You are only getting the account holders name because that is all you are giving the combobox. It does not know anything about a Customer. It only knows that it was supplied a string value, and that is all. You can pass it the name, this is fine, but you need to print the information corresponding with what that combobox item represent. You can do this by using an index. Since we know they are being supplied in the combobox in order, the index's will match. However, unless you override the ToString, you are just going to get the object name, e.g. "Customer.Object[]".
I threw an example together for you.
private Customer[] customers = new Customer[3];
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
customers[0] = new Customer("Bob", "Bronze", 22);
customers[1] = new Customer("Jane", "Silver", 32);
customers[2] = new Customer("Jordan", "Gold", 26);
foreach(var cust in customers)
{
comboBox1.Items.Add(cust.Name);
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
textBox1.Text = customers[comboBox1.SelectedIndex].ToString();
}
Also, in your 'Customer' class you may want to override the "ToString()" to make this nicer.
public override string ToString()
{
return Name + "\r\n" + Membership + "\r\n" + Age;
}
Using your example with an array of “Customer” objects, when the combo box's selected value changes, you need to get the selected “Customer” Object from the array mAccHolder. Once you have this customer either you can output the “Customer” values individually to the text box or you could make a “ToString” method for the “Customer” to output the customer info to your specifications, which is what the code below does.
The changes below to your current code appear to work as described. I added a blank value to the combo box so the user can de-select any currently selected customer. The “GetCustomer” method simply loops through the array mAccHolder to get the selected customer in the combo box. Hope this helps.
Customer Class
class Customer {
public string Name { get; set; }
public string AccountType { get; set; }
public int ID { get; set; }
public Customer(string inName, string actType, int inID) {
Name = inName;
AccountType = actType;
ID = inID;
}
public string GetName() {
return Name;
}
public override string ToString() {
return "Name: " + Name + " AccountType: " + AccountType + " ID: " + ID;
}
}
Updated code to use the Customer Class above.
Customer[] mAccHolder = new Customer[10];
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
mAccHolder[0] = new Customer("Rich", "Bronze Account", 11);
mAccHolder[1] = new Customer("Patrick", "Silver Account", 21);
mAccHolder[2] = new Customer("Steve", "Gold Account", 12);
mAccHolder[3] = new Customer("Kevin", "Platinum Account", 25);
comboBox1.Items.Add(""); // <- add a blank selection so the user can select NO customer
foreach (Customer r in mAccHolder) {
comboBox1.Items.Add(r.GetName());
}
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) {
if (comboBox1.SelectedIndex == -1) {
cstTxtBox.Text = string.Empty;
} else {
if (comboBox1.SelectedItem.ToString() != "") {
Customer selectedCustomer = GetCustomer(comboBox1.SelectedItem.ToString());
if (selectedCustomer != null)
cstTxtBox.Text = selectedCustomer.ToString();
else
cstTxtBox.Text = "Customer not found!";
}
else {
cstTxtBox.Text = "No Customer selected";
}
}
}
private Customer GetCustomer(string targetName) {
foreach (Customer curCustomer in mAccHolder) {
if (curCustomer.Name.Equals(targetName)) {
return curCustomer;
}
}
return null;
}
Easy add directly Customer to combobox. With this way you can use it anywhere any time. like this.
public Form1()
{
InitializeComponent();
mAccHolder[0] = new Customer("Rich", "Bronze Account", 11);
mAccHolder[1] = new Customer("Patrick", "Silver Account", 21);
mAccHolder[2] = new Customer("Steve", "Gold Account", 12);
mAccHolder[3] = new Customer("Kevin", "Platinum Account", 25);
foreach(Customer r in mAccHolder)
{
ComboboxItem item = new ComboboxItem();
item.Text = r.GetName();
item.Value = r;
comboBox1.Items.Add(item);
}
}
And use Customer where you want...
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == -1)
{
cstTxtBox.Text = string.Empty;
}
else
{
Customer c=(Customer) comboBox1.SelectedValue;
cstTxtBox.Text = c.whatyouwant....
}
You can use foreach,
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == -1)
{
cstTxtBox.Text = string.Empty;
}
else
{
foreach (var item in comboBox1.Items)
{
cstTxtBox.Text = cstTxtBox.Text + item.ToString();
}
}
}
I am trying to do an Ordering System where you can put many Products in one order. I have very little knowledge about this and this is where i am now
There are 3 tables, Product table,Order table and the Order-products table.
I really don't know if this is right as i am beginner especially on foreign keys.
What I want to achieve is you can order many products and put that products into one "OrderID" like this example in pic below.
This are my only codes. Sorry but i am really lost at this.
public Form1()
{
InitializeComponent();
fillCart();
}
private void fillCart()
{
dgvCart.ColumnCount = 3;
dgvCart.Columns[0].Name = "ProductID";
dgvCart.Columns[1].Name = "ProductName";
dgvCart.Columns[2].Name = "Quantity";
}
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
//dgvproducts
}
private void Form1_Load(object sender, EventArgs e)
{
crud.FillDataGrid("Select * from Products", ref dgvProducts);
crud.FillDataGrid("Select * from Orders", ref dgvOrder);
crud.FillDataGrid("Select * from Orderproducts", ref dgvOrderview);
lbldate.Text = DateTime.Now.ToShortDateString();
}
private void button2_Click(object sender, EventArgs e)
{
//button add to cart
addData(dgvProducts.CurrentRow.Cells[0].Value.ToString(), dgvProducts.CurrentRow.Cells[1].Value.ToString(), txtqty.Text);
}
private void addData(string p1, string p2, string p3)
{
String[] row = { p1, p2, p3 };
dgvCart.Rows.Add(row);
}
private void button1_Click(object sender, EventArgs e)
{
//button insert
}
Thank you very much and i hope someone can help me with my problem.
Method use for filling datagridview from SQLserver 2008:
public crud()
{
cnString = "Data Source=DESKTOP-MQKIBSK\\SQLEXPRESS;Initial Catalog=MARISCHELLdatabase;Integrated Security=True";
cn = new SqlConnection(cnString);
}
public void FillDataGrid(string sql, ref ns1.BunifuCustomDataGrid dg)
{
try
{
DataSet ds = new DataSet();
cn.Open();
cmd = new SqlCommand(sql, cn);
adptr = new SqlDataAdapter(cmd);
adptr.Fill(ds);
dg.DataSource = "";
dg.DataSource = ds.Tables[0];
}
catch (Exception e)
{
MessageBox.Show("" + e.Message);
}
cn.Close();
}
How Linq 2 SQL dataclasses look for me:
The code that goes with it:
//I have 2 columns in my dataGridView, Id 1st amount 2nd
//I added 3 items for testing
List<Tuple<int, int>> cart = new List<Tuple<int,int>>();
foreach (DataGridViewRow row in dataGridView1.Rows)
{
if (row.Cells[0].Value != null && row.Cells[1].Value != null)
{
cart.Add(new Tuple<int, int>(Convert.ToInt32(row.Cells[0].Value.ToString()),Convert.ToInt32(row.Cells[1].Value.ToString())));
//Now each list item will have .Item1 (productId) and .Item2 (amount)
}
}
using (DataClasses1DataContext dataContext = new DataClasses1DataContext())
{
//The tables you add in the dataContext are accessible by name
Order order = new Order();
dataContext.Orders.InsertOnSubmit(order);
dataContext.SubmitChanges(); // Submit once so we get an orderId
foreach (Tuple<int, int> product in cart)
{
OrderProduct orderProduct = new OrderProduct();
orderProduct.OrderId = order.OrderID;
orderProduct.ProductId = product.Item1;
orderProduct.Amount = product.Item2;
dataContext.OrderProducts.InsertOnSubmit(orderProduct);
}
dataContext.SubmitChanges();
}
Create foreign key relationship between Order - OrderProduct over OrderID and Product - OrderProduct over Product ID.
For each Product in order insert a row into OrderProduct with OrderId
This may not answer your question completely but consider an object orientated approach to this. It's always better in my opinion to have a strongly typed method of accessing values returned from a database, although others may disagree. Here is some pseudo code to get you started and is by no means the entire solution but should encourage you to think how you can make your code more object orientated and strongly typed. Use the same methodology to save and update tables in your database.
example
//Business layer
public class Product
{
public string ProductName {get;set;}
public int Quantity {get;set;}
public string Unit {get;set;}
public decimal Price {get;set;}
public long Total {get;set;}
public Product(){}
public Product(string productName, int quantity, string unit, decimal price, long total)
{
ProductName = productName;
Quantity = quantity;
Unit = unit;
Price = price;
Total = total;
}
public List<Product> GetProductList()
{
//get the list of products from the data access layer
ProductDal dal = new ProductDal();
return dal.GetProductList();
}
}
//Data layer
public class ProductDal
{
public List<Product> GetProductList()
{
List<Product> lstProducts = new List<Product>();
//connect to your database code here
//fill your list with records from your Sql query
//inside your DataReader while loop you can add a new Product object to your list for each record
//assuming your database field names match the Product class's proeprties you would do this
lstProducts.Add(new Product((string)reader["ProductName"],
(int)reader["Quantity"],
(string)reader["Unit"],
decimal)reader["Price"],
(long)reader["Total"]));
return lstProducts;
}
}
//front end code behind page
private void button2_Click(object sender, EventArgs e)
{
Product product = new Product();
dgvCart.DataScource = product.GetProductList();
dgvCart.DataBind();
}
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 am having problems in loading a datagridview with 2 columns.
Form with a Datagridview and 1 button.
Department (text)
EmployeesByDpt( Combo)
I have a form with a datagridview and a Button (Load),when I press load the datagridview should be populated.
clicking on the Employee Combo should display all the employees that belong to a particular department.
I cannot seem to get it to work ,below is what I have done,
Any suggestions?At the moment nothing shows.Thanks
Code (for semplicity I have put all together)
public partial class Form2 : Form
{
Repository repository;
readonly DataGridViewTextBoxColumn colDepartment=new DataGridViewTextBoxColumn();
readonly DataGridViewComboBoxColumn colComboEmployeesByDpt = new DataGridViewComboBoxColumn();
public Form2()
{
InitializeComponent();
repository = new Repository();
SetupDataGridView();
}
private void SetupDataGridView()
{
dataGridView1.EditingControlShowing += OnEditingControlShowing;
dataGridView1.CellValueChanged += OnCellsValueChanged;
dataGridView1.AutoGenerateColumns = false;
colDepartment.DataPropertyName = "Name";
colDepartment.HeaderText = "Department Name";
colComboEmployeesByDpt.DataPropertyName = "Employees";
colComboEmployeesByDpt.HeaderText = "Employees";
colComboEmployeesByDpt.DisplayMember = "FullName";
//colComboEmployeesByDpt.DataSource = "FullName";
dataGridView1.Columns.AddRange(new DataGridViewColumn[] { colDepartment ,colComboEmployeesByDpt});
}
private void OnCellsValueChanged(object sender, DataGridViewCellEventArgs e)
{
}
private void OnEditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if (dataGridView1.CurrentCell.ColumnIndex == colDepartment.Index)
{
var control = e.Control as DataGridViewComboBoxEditingControl;
if (control != null)
{
var bs = control.DataSource as BindingSource;
if (bs != null)
{
var comboBox = e.Control as ComboBox;
BindingList<Employee> employees = repository.GetEmployeeByDepartments(control.Text);
comboBox.DataSource = employees;
object employeeValue = dataGridView1.Rows[dataGridView1.CurrentCell.RowIndex].Cells[colComboEmployeesByDpt.Index].Value;
if (employeeValue == DBNull.Value || employeeValue == null)
if (dataGridView1.CurrentCell.Value != DBNull.Value && dataGridView1.CurrentCell.Value != null)
{
control.SelectedValue = dataGridView1.CurrentCell.Value;
}
}
}
}
}
private void btnLoad_Click(object sender, EventArgs e)
{
BindingList<Department> departments = repository.GetDepartments();
dataGridView1.DataSource = departments;
dataGridView1.Refresh();
}
}
public class Department
{
public Department()
{
Employees=new BindingList<Employee>();
}
public string Name { get; set; }
public BindingList<Employee> Employees { get; set; }
}
public class Employee
{
public string FullName { get; set; }
}
public class Repository
{
public BindingList<Department> GetDepartments()
{
var departments=new BindingList<Department>();
departments.Add(new Department{Name = "Food"});
departments.Add(new Department{Name = "Travel"});
departments.Add(new Department{Name = "Beauty"});
return departments;
}
public BindingList<Employee> GetEmployeeByDepartments(string name)
{
var employees = new BindingList<Employee>();
switch (name)
{
case "Food":
employees.Add(new Employee { FullName = "Jim Bloggs1" });
employees.Add(new Employee { FullName = "Jim Bloggs2" });
break;
case "Travel":
employees.Add(new Employee { FullName = "Marc Smith1" });
employees.Add(new Employee { FullName = "Marc Smith2" });
break;
case "Beauty":
employees.Add(new Employee { FullName = "Mario XXX1" });
employees.Add(new Employee { FullName = "Mario XXX2" });
break;
}
return employees;
}
}
Running your exact code, I ran into several problems. The following explanation will show you what I did to fix each subsequent problem but with a warning: In the end I couldn't make a selection from the ComboBox without changing the binding method between the DataGridView and the Departments and dropping some provided code.
ArgumentException thrown for each row
Each row was throwing the exception: "DataGridViewComboBoxCell value is not valid." Changing the following line fixed this:
colComboEmployeesByDpt.DataPropertyName = "Employees";
to
colComboEmployeesByDpt.DataPropertyName = "Employee";
Empty ComboBoxes
Now you'll notice the ComboBoxes are all empty. In the event handler OnEditingControlShowing the first if statement should check against colComboEmployeesByDpt.Index instead of colDepartment.Index. But that's not enough because if (bs != null) will always be false. Even fixing that check, control.Text is always empty. Instead, try:
BindingList<Employee> employees = repository.GetEmployeeByDepartments(this.dataGridView1.CurrentRow.Cells[colDepartment.Index].Value.ToString());
With this, you'll see each ComboBox has the correct list of employee names. However, the ArgumentException has returned. Try as I might, I couldn't fix it this time. (I suspect the ComboBox items lists were always empty, so the selected value was "invalid".)
Answer - Restructure
To get it to work I made several core changes. I completely dropped the following:
colComboEmployeesByDpt.DisplayMember = "FullName";
private void OnEditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
...
}
Then I added a public property to remember the Departments, manually bound each row's employees, and hooked-up OnCellsValueChanged to refresh the lists when the Department Name changed:
BindingList<Department> Departments { get; set; }
private void OnCellsValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == colDepartment.Index)
{
this.Departments[e.RowIndex].Employees = repository.GetEmployeeByDepartments(this.dataGridView1.CurrentCell.EditedFormattedValue.ToString());
DataGridViewComboBoxCell cell = (DataGridViewComboBoxCell)this.dataGridView1.CurrentRow.Cells[colComboEmployeesByDpt.Index];
cell.DataSource = this.Departments[e.RowIndex].Employees;
}
}
private void btnLoad_Click(object sender, EventArgs e)
{
//this.dataGridView1.Rows.Clear(); // Needed if the button can be clicked repeatedly.
this.Departments = repository.GetDepartments();
foreach (Department department in this.Departments)
{
department.Employees = repository.GetEmployeeByDepartments(department.Name);
DataGridViewRow row = (DataGridViewRow)(dataGridView1.Rows[0].Clone());
DataGridViewTextBoxCell textCell = (DataGridViewTextBoxCell)(row.Cells[0]);
textCell.Value = department.Name;
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)(row.Cells[1]);
comboCell.DataSource = department.Employees;
comboCell.DisplayMember = "FullName";
dataGridView1.Rows.Add(row);
}
}
This solution worked for me. When I have free time I will continue to look into fixing your original solution from the point that stumped me. Hope this helps for now.
Here are my requirements:
I have a dropdown list and text box (appName and profile).
I want to take the values from the dropdown and text box and add them to a table (or a control like gridview that renders into a
table)
At some point I want to be able to loop through the table and submit the values to a db.
My problem:
The postback caused by the onClick even is casing the table to only show the last value entered, and doesn't retain any of the previous
values.
Notes:
I tried to work arond this using a datalist bound to a datagrid, but no luck.
Code:
protected void addAppButton_Click(object sender, EventArgs e)
{
DropDownList appList = (DropDownList)newEntryFV.FindControl("appList");
TextBox profileTextBox = (TextBox)newEntryFV.FindControl("profileTextBox");
addAppsToTable(appList.SelectedValue.ToString(), profileTextBox.Text.ToString());
}
private void addAppsToTable(string appName, string profileName)
{
Table appsTable = (Table)newEntryFV.FindControl("appTable");
TableRow newRow = new TableRow();
TableCell appNameCell = new TableCell();
TableCell profileCell = new TableCell();
appNameCell.Text = appName;
profileCell.Text = profileName;
newRow.Cells.Add(appNameCell);
newRow.Cells.Add(profileCell);
appsTable.Rows.Add(newRow);
}
Code that solved my problem:
[Serializable]
public class securityApps
{
public string secAppID { get; set; }
public string secAppName { get; set; }
public string secProfile { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
BindApps();
}
protected void addAppButton_Click(object sender, EventArgs e)
{
DropDownList appList = (DropDownList)newEntryFV.FindControl("appList");
TextBox profileTextBox = (TextBox)newEntryFV.FindControl("profileTextBox");
addAppsToListVS(appList.SelectedValue.ToString(), appList.SelectedItem.Text.ToString(), profileTextBox.Text.ToString());
BindApps();
}
private void addAppsToListVS(string appID, string appName, string profile)
{
securityApps secApp = new securityApps();
secApp.secAppID = appID;
secApp.secAppName = appName;
secApp.secProfile = profile;
((List<securityApps>)ViewState["appsListVS"]).Add(secApp);
}
// Binds apps to Grid View
private void BindApps()
{
GridView appsListGV = (GridView)newEntryFV.FindControl("appsListGV");
if (ViewState["appsListVS"] != null)
{
appsListGV.DataSource = (List<securityApps>)ViewState["appsListVS"];
appsListGV.DataBind();
}
else
{
List<securityApps> appsListVS = new List<securityApps>();
ViewState["appsListVS"] = appsListVS;
}
}
How about storing a List of objects (they could even be simple key value pairs) in the ViewState. You can use that data as the DataSource for a GridView. I think that's the simplest way to go. If you need more details, let me know.
Edits-- Your solution above looks good-- I might just make it a little easier by setting up a property for your ViewState values..
List<securityApps> AppsListVS{
get
{
if(ViewState["AppListVS"] == null
this.AppListVS = new List(securityApps)();
return (List<securityApps>)ViewState["AppListVS"];
}
set
{
ViewState["AppListVS"] = value;
}
}