I have xml file with users with different types of tasks.
<Users>
<Instructor>
<ID>1</ID>
<Name>Jack</Name>
<Surname>C</Surname>
<Rank>r1</Rank>
</Instructor>
<Instructor>
<ID>2</ID>
<Name>Jim</Name>
<Surname>A</Surname>
<Rank>r1</Rank>
</Instructor>
<Pilot>
<ID>3</ID>
<Name>Tim</Name>
<Surname>T</Surname>
<Rank>r2</Rank>
</Pilot>
</Users>
I am passing the ids of the users who are in the instructor task to the combobox with the code below.
void InsGetCombo()
{
DataSet ds = new DataSet();
ds.ReadXml(path);
DataTable dt = ds.Tables["Instructor"];
comboBox1.DataSource = dt;
comboBox1.DisplayMember = "ID";
}
What I want to do here is to show the id, name, surname and rank values on label1, of the record with the id value I selected in the combobox.
I tried to get just Name with string id not with selectedIndex but could not get the value.
XmlDocument doc = new XmlDocument();
doc.Load(path);
string id = "2";
label1.Text = doc.SelectSingleNode($"/Users/Instructor[ID='{id}']").Attributes["Name"].Value.ToString();
To select the node whose ID element is in the id variable, you could use an XPath expression something like this: $"/Users/Instructor/ID[text()='{id}']/parent::*”. Since what you ultimately want is the value of that element’s Name element, you might use this instead, $"/Users/Instructor/ID[text()='{id}']/parent::*/Name/text()”. If you want need the value of other elements, remember that ID, Name, Surname and Rank are child elements of the Instructor node, not attributes of it.
Use data binding.
using System;
using System.Data;
using System.Windows.Forms;
namespace WinForm
{
public partial class Form1 : Form
{
ComboBox comboBox;
Label nameLabel;
Label surnameLabel;
Label rankLabel;
DataSet dataSet;
DataTable instructorsDataTable;
public Form1()
{
//InitializeComponent();
comboBox = new ComboBox { Parent = this, Dock = DockStyle.Left };
nameLabel = new Label { Parent = this, Left = 150 };
surnameLabel = new Label { Parent = this, Left = 150, Top = 30 };
rankLabel = new Label { Parent = this, Left = 150, Top = 60 };
this.Load += Form1_Load;
}
private void Form1_Load(object sender, EventArgs e)
{
dataSet = new DataSet();
dataSet.ReadXml("test.xml");
instructorsDataTable = dataSet.Tables["Instructor"];
comboBox.DisplayMember = "Id";
comboBox.DataSource = instructorsDataTable;
nameLabel.DataBindings.Add("Text", instructorsDataTable, "Name");
surnameLabel.DataBindings.Add("Text", instructorsDataTable, "Surname");
rankLabel.DataBindings.Add("Text", instructorsDataTable, "Rank");
}
}
}
Related
I have a text box which I manage its text changing event to filter the RadGrid:
private void txtJob_TextChanging(object sender, TextChangingEventArgs e)
{
this.gridCustomers.Columns["JobColumn"].FilterDescriptor = new FilterDescriptor
{
Operator = FilterOperator.Contains,
Value = txtJob.Text
};
}
I change JobColumn Text using CellFormatting Event:
private void gridCustomers_CellFormatting(object sender, CellFormattingEventArgs e)
{
if (e.Column.Name == "JobColumn")
e.CellElement.Text = db.tblJobs.First(x => x.JobID == Convert.ToInt32(e.Row.Cells[9].Value.ToString())).JobName;
}
I'm replacing Job ID with its Job Name in JobColumn, in that Text Box which I filtering RadGrid I'm searching for Job Name which is visible in RadGrid Now, but it will filter based on Job ID which is the default value before replacing.
So How can I filter a RadGrid Column based on its Text not Value?
For more information I'm binding a table like this to my girdview:
int JobID
nvarchar(10) Name
nvarchar(100) Address
.
.
.
And I have a table named Jobs like this:
int JobID
nvarchar(30) JobName
.
.
.
I need to get JobID from table one and in data binding (cell Formatting) replace the ID with its JobName in Jobs table.
Why I'm not selecting new and joining two table? because in that case I have not a grid view which can be edited easily, I must use Virtual Gird which is not my goal.
You are trying to hack the system. The correct approach in your case is to use GridVieWComboBoxColumn, which can be bound and DisplayMember and ValueMember to be specified. It also has FilterMode property to determine which field to use for filtering.
Read more GridViewComboBoxColumn | Telerik UI for WinForms Documentation
UPDATE
Here is a sample to get you started
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
DataTable mainTable = new DataTable();
mainTable.Columns.Add("JobID", typeof(int));
mainTable.Columns.Add("Name");
mainTable.Columns.Add("Address");
Random rand = new Random();
for (int i = 0; i < 10; i++)
{
mainTable.Rows.Add(rand.Next(1,4), "Name " + i, "Address " + i);
}
DataTable jobsTable = new DataTable();
jobsTable.Columns.Add("JobID", typeof(int));
jobsTable.Columns.Add("JobName");
jobsTable.Rows.Add(1, "ABC ");
jobsTable.Rows.Add(2, "DFG");
jobsTable.Rows.Add(3, "XCV");
radGridView1 = new RadGridView() { Dock = DockStyle.Fill, AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.Fill };
this.Controls.Add(radGridView1);
radGridView1.EnableFiltering = true;
radGridView1.DataSource = mainTable; //this will create all columns
radGridView1.Columns.Remove(radGridView1.Columns["JobId"]);
GridViewComboBoxColumn comboCol = new GridViewComboBoxColumn();
comboCol.DataSource = jobsTable;
comboCol.FieldName = "JobID"; //the name of the field in the main table to look for
comboCol.DisplayMember = "JobName"; //you want to see job names not ids
comboCol.ValueMember = "JobID";
comboCol.FilteringMode = GridViewFilteringMode.DisplayMember;
radGridView1.Columns.Insert(0, comboCol);
}
private void radButton1_Click(object sender, EventArgs e)
{
radGridView1.Columns["JobID"].FilterDescriptor = new FilterDescriptor
{
Operator = FilterOperator.Contains,
Value = "B"
};
}
My program displays a simple datagridview with some data:
The user (me) can change the data (notice row one):
When I look at the datasource in the program, I can see the changed value:
However, the DataRowState is Added instead of Modified. How can this be? The data has obviously changed, but the DataRowState is not reflecting this:
For the life of me, I can't figure out why the DataRowState is showing Added on every row in the datasource. Below is the full listing of my program:
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
namespace DataBindingTest
{
using System.ComponentModel;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Create some people and a list to hold them.
var personA = new Person { Name = "Kevin", Address = "140 Holiday Drive" };
var personB = new Person { Name = "Donna", Address = "123 Somestreet" };
var personC = new Person { Name = "Mark", Address = "145 Uptown Blvd" };
var personD = new Person { Name = "Bryce", Address = "504 Greymere Road" };
var personE = new Person { Name = "Parzival", Address = "99A Housing Brad St" };
var people = new List<Person> { personA, personB, personC, personD, personE };
//// Make a datatable to hold the people
var tblPeople = new DataTable();
tblPeople.Columns.Add("Name");
tblPeople.Columns.Add("Address");
foreach (var person in people)
{
tblPeople.Rows.Add(person.Name, person.Address);
}
// Set binding source to the table
bindingSource1 = new BindingSource();
bindingSource1.DataSource = tblPeople;
dataGridView1.DataSource = bindingSource1;
}
private void btnUpdate_Click(object sender, EventArgs e)
{
// Get only the changed rows
// (The line below is where the null reference error is occuring because rowstate never == Modified)
var changedRows = ((DataTable)bindingSource1.DataSource).GetChanges(DataRowState.Modified).Rows;
}
}
public class Person
{
public string Name { get; set; }
public string Address { get; set; }
}
}
As you have added the rows in your constructor, all rows have state 'Added'. When you modify the first one, it is still Added, that is normal. you only need to accept the changes in your data table an all is running:
public Form1()
{
InitializeComponent();
// Create some people and a list to hold them.
var personA = new Person { Name = "Kevin", Address = "140 Holiday Drive" };
var personB = new Person { Name = "Donna", Address = "123 Somestreet" };
var personC = new Person { Name = "Mark", Address = "145 Uptown Blvd" };
var personD = new Person { Name = "Bryce", Address = "504 Greymere Road" };
var personE = new Person { Name = "Parzival", Address = "99A Housing Brad St" };
var people = new List<Person> { personA, personB, personC, personD, personE };
//// Make a datatable to hold the people
var tblPeople = new DataTable();
tblPeople.Columns.Add("Name");
tblPeople.Columns.Add("Address");
foreach (var person in people)
{
tblPeople.Rows.Add(person.Name, person.Address);
}
// this line sets the state of the added rows to 'unchanged',
// so when you modify one row it becomes'modified'
tblPeople.AcceptChanges();
// Set binding source to the table
bindingSource1 = new BindingSource();
bindingSource1.DataSource = tblPeople;
dataGridView1.DataSource = bindingSource1;
}
I Hope it helps.
After adding the rows to your table you need to call
tblPeople.AcceptChanges();
When adding rows to a table, their RowState is "Added". When you call AcceptChanges(), their RowState is set to "UnChanged". If you then edit cells in the rows, the RowState is changed to "Modified" in the corresponding rows and those rows will show up in your "changedRows" in your event handler.
I am looking to format the datagridview so I can hide and format certain column headers. The problem is I cant use the built in winforms dgv formatting because I am filling the grid from a straight linq query and not binding the table with the built in wizard. Here is my code to fill the table. How can I format the headers on the grid? Thanks!
var search = from s in db.trips
orderby s.tripNo descending
select s;
dgvTripGrid.DataSource = search;
Create a dynamic entity with the column (header) names which should be displayed and bind it here is an example:
BindingSource bindingSource1= new BindingSource();
private void LoadGrid()
{
List<Data> dataListing = new List<Data>()
{
new Data() { Name = "Jabberwocky", Operation="Read", DateStart= DateTime.Now.AddDays(-2), DateEnd = DateTime.Now.AddDays(-2), Description="Process Started No errors"},
new Data() { Name = "Space", Operation="Write", DateStart= DateTime.Now.AddDays(-2), DateEnd = DateTime.Now.AddDays(-1), Description="Final process remote allocation of 3000 items to main buffer."},
new Data() { Name = "Stock Purchase", Operation="DataWarehousing", DateStart= DateTime.Now, DateEnd = DateTime.Now, Description="Shared data transport."}
};
var items = from dta in dataListing
select new
{
OperationName = dta.Name,
Start = dta.DateStart.ToShortDateString(),
End = dta.DateEnd.ToShortDateString(),
Operation = dta.Operation,
Description = dta.Description
};
bindingSource1.DataSource = items;
dataGridView1.DataSource = bindingSource1;
// Grid attributes
dataGridView1.BorderStyle = BorderStyle.Fixed3D;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
}
I show this example on my blog C# Linq How To Load a Winform Control Using Dynamic Linq entitites
i am using a datagridview in that i am using a datagridviewcomboboxcolumn, comboboxcolumn is displaying text but the problem is i want to select the first item of comboboxcolumn by default how can i do this
DataGridViewComboBoxColumn dgvcb = (DataGridViewComboBoxColumn)grvPackingList.Columns["PackingUnits"];
Globals.G_ProductUtility G_Utility = new Globals.G_ProductUtility();
G_Utility.addUnittoComboDGV(dgvcb);
DataSet _ds = iRawMaterialsRequest.SelectBMR(bmr_ID, branch_ID, "PACKING");
grvPackingList.DataSource = _ds.Tables[0];
int i = 0;
foreach (DataRow dgvr in _ds.Tables[0].Rows)
{
grvPackingList.Rows[i].Cells["Units"].Value = dgvr["Units"].ToString();
i++;
}
The values available in the combobox can be accessed via items property
row.Cells[col.Name].Value = (row.Cells[col.Name] as DataGridViewComboBoxCell).Items[0];
the best way to set the value of a datagridViewComboBoxCell is:
DataTable dt = new DataTable();
dt.Columns.Add("Item");
dt.Columns.Add("Value");
dt.Rows.Add("Item1", "0");
dt.Rows.Add("Item1", "1");
dt.Rows.Add("Item1", "2");
dt.Rows.Add("Item1", "3");
DataGridViewComboBoxColumn cmb = new DataGridViewComboBoxColumn();
cmb.DefaultCellStyle.Font = new Font("Tahoma", 8, FontStyle.Bold);
cmb.DefaultCellStyle.ForeColor = Color.BlueViolet;
cmb.FlatStyle = FlatStyle.Flat;
cmb.Name = "ComboColumnSample";
cmb.HeaderText = "ComboColumnSample";
cmb.DisplayMember = "Item";
cmb.ValueMember = "Value";
DatagridView dvg=new DataGridView();
dvg.Columns.Add(cmb);
cmb.DataSource = dt;
for (int i = 0; i < dvg.Rows.Count; i++)
{
dvg.Rows[i].Cells["ComboColumnSample"].Value = (cmb.Items[0] as
DataRowView).Row[1].ToString();
}
It worked with me very well
If I had known about doing it in this event, it would have saved me days of digging and
trial and errors trying to get it to set to the correct index inside the CellEnter event.
Setting the index of the DataGridViewComboBox is the solution I have been looking for.....THANKS!!!
In reviewing all the issues other coders have been experiencing with trying to set
the index inside of a DataGridViewComboBoxCell and also after looking over your code,
all that anyone really needs is:
1. Establish the event method to be used for the "EditingControlShowing" event.
2. Define the method whereby it will:
a. Cast the event control to a ComboBox.
b. set the "SelectedIndex" to the value you want.
In this example I simply set it to "0", but you'd probably want to apply so real life logic here.
Here's the code I used:
private void InitEvents()
{
dgv4.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler( dgv4EditingControlShowing );
}
private void dgv4EditingControlShowing( object sender, DataGridViewEditingControlShowingEventArgs e )
{
ComboBox ocmb = e.Control as ComboBox;
if ( ocmb != null )
{
ocmb.SelectedIndex = 0;
}
}
If DataGridViewComboBoxCell already exist:
DataTable dt = new DataTable();
dt.Columns.Add("Item");
dt.Columns.Add("Value");
dt.Rows.Add("Item 1", "0");
dt.Rows.Add("Item 2", "1");
dt.Rows.Add("Item 3", "2");
dt.Rows.Add("Item 4", "3");
for (int i = 0; i < dvg.Rows.Count; i++)
{
DataGridViewComboBoxCell comboCell = (DataGridViewComboBoxCell)dvg.Rows[i].Cells[1];
comboCell.DisplayMember = "Item";
comboCell.ValueMember = "Value";
comboCell.DataSource = dt;
};
I've had some real trouble with ComboBoxes in DataGridViews and did not find an elegant way to select the first value. However, here is what I ended up with:
public static void InitDGVComboBoxColumn<T>(DataGridViewComboBoxCell cbx, List<T> dataSource, String displayMember, String valueMember)
{
cbx.DisplayMember = displayMember;
cbx.ValueMember = valueMember;
cbx.DataSource = dataSource;
if (cbx.Value == null)
{
if(dataSource.Count > 0)
{
T m = (T)cbx.Items[0];
FieldInfo fi = m.GetType().GetField(valueMember, BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
cbx.Value = fi.GetValue(m);
}
}
}
It basically sets the .Display and .ValueMember properties of the DataGridViewComboBoxCell and uses a List as DataSource. It then takes the first item, and uses reflection to get the value of the member that was used as ValueMember and sets the selected value via .Value
Use it like this:
public class Customer
{
private String name;
public String Name
{
get {return this.name; }
set {this.name = value; }
}
private int id;
public int Id
{
get {return this.id; }
set {this.id = value; }
}
}
public class CustomerCbx
{
private String display;
public String Display
{
get {return this.display; }
set {this.display = value; }
}
private Customer value;
public Customer Value
{
get {return this.value; }
set {this.value = value; }
}
}
public class Form{
private void Form_OnLoad(object sender, EventArgs e)
{
//init first row in the dgv
if (this.dgv.RowCount > 0)
{
DataGridViewRow row = this.dgv.Rows[0];
DataGridViewComboBoxCell cbx = (DataGridViewComboBoxCell)row.Cells[0];
Customer c1 = new Customer(){ Name = "Max Muster", ID=1 };
Customer c2 = new Customer(){ Name = "Peter Parker", ID=2 };
List<CustomerCbx> custList = new List<CustomerCbx>()
{
new CustomerCbx{ Display = c1.Name, Value = c1},
new CustomerCbx{ Display = c2.Name, Value = c2},
}
InitDGVComboBoxColumn<CustomerCbx>(cbx, custList, "display", "value");
}
}
}
}
It seems pretty hacky to me, but I couldn't find any better way so far (that also works with complex objects other than just Strings). Hope that will save the search for some others ;)
You need to set the Items for the new cell. This must be auto done by the column when creating a new row from the UI.
var cell = new DataGridViewComboBoxCell() { Value = "SomeText" };
cell.Items.AddRange(new String[]{"SomeText", "Abcd", "123"});
something different worked for me what i did is to simply set the value of dtataGridComboBox when ever new record is added bu user with 'userAddedRow' event. For the first row I used the code in constructor.
public partial class pt_drug : PatientDatabase1_3._5.basic_templet
{
public pt_drug()
{
InitializeComponent();
dataGridView_drugsDM.Rows[0].Cells[0].Value = "Tablet";
}
private void dataGridView_drugsDM_UserAddedRow(object sender, DataGridViewRowEventArgs e)
{
dataGridView_drugsDM.Rows[dataGridView_drugsDM.RowCount - 1].Cells[0].Value = "Tablet";
}
}
Here the solution I have found : select the cell you are interested in so you can cast it to a combobox.
this.Invoke((MethodInvoker)delegate
{
this.dataGridView1.CurrentCell = dataGridView1.Rows[yourRowindex].Cells[yourColumnIndex];
this.dataGridView1.BeginEdit(true);
ComboBox comboBox = (ComboBox)this.dataGridView1.EditingControl;
comboBox.SelectedIndex += 1;
});
I seem to be running around in circles and have been doing so in the last hours.
I want to populate a datagridview from an array of strings. I've read its not possible directly, and that I need to create a custom type that holds the string as a public property. So I made a class:
public class FileName
{
private string _value;
public FileName(string pValue)
{
_value = pValue;
}
public string Value
{
get
{
return _value;
}
set { _value = value; }
}
}
this is the container class, and it simply has a property with the value of the string. All I want now is that string to appear in the datagridview, when I bind its datasource to a List.
Also I have this method, BindGrid() which I want to fill the datagridview with. Here it is:
private void BindGrid()
{
gvFilesOnServer.AutoGenerateColumns = false;
//create the column programatically
DataGridViewTextBoxColumn colFileName = new DataGridViewTextBoxColumn();
DataGridViewCell cell = new DataGridViewTextBoxCell();
colFileName.CellTemplate = cell; colFileName.Name = "Value";
colFileName.HeaderText = "File Name";
colFileName.ValueType = typeof(FileName);
//add the column to the datagridview
gvFilesOnServer.Columns.Add(colFileName);
//fill the string array
string[] filelist = GetFileListOnWebServer();
//try making a List<FileName> from that array
List<FileName> filenamesList = new List<FileName>(filelist.Length);
for (int i = 0; i < filelist.Length; i++)
{
filenamesList.Add(new FileName(filelist[i].ToString()));
}
//try making a bindingsource
BindingSource bs = new BindingSource();
bs.DataSource = typeof(FileName);
foreach (FileName fn in filenamesList)
{
bs.Add(fn);
}
gvFilesOnServer.DataSource = bs;
}
Finally, the problem: the string array fills ok, the list is created ok, but I get an empty column in the datagridview. I also tried datasource= list<> directly, instead of = bindingsource, still nothing.
I would really appreciate an advice, this has been driving me crazy.
Use a BindingList and set the DataPropertyName-Property of the column.
Try the following:
...
private void BindGrid()
{
gvFilesOnServer.AutoGenerateColumns = false;
//create the column programatically
DataGridViewCell cell = new DataGridViewTextBoxCell();
DataGridViewTextBoxColumn colFileName = new DataGridViewTextBoxColumn()
{
CellTemplate = cell,
Name = "Value",
HeaderText = "File Name",
DataPropertyName = "Value" // Tell the column which property of FileName it should use
};
gvFilesOnServer.Columns.Add(colFileName);
var filelist = GetFileListOnWebServer().ToList();
var filenamesList = new BindingList<FileName>(filelist); // <-- BindingList
//Bind BindingList directly to the DataGrid, no need of BindingSource
gvFilesOnServer.DataSource = filenamesList
}
may be little late but useful for future. if you don't require to set custom properties of cell and only concern with header text and cell value then this code will help you
public class FileName
{
[DisplayName("File Name")]
public string FileName {get;set;}
[DisplayName("Value")]
public string Value {get;set;}
}
and then you can bind List as datasource as
private void BindGrid()
{
var filelist = GetFileListOnWebServer().ToList();
gvFilesOnServer.DataSource = filelist.ToArray();
}
for further information you can visit this page Bind List of Class objects as Datasource to DataGridView
hope this will help you.
I know this is old, but this hung me up for awhile. The properties of the object in your list must be actual "properties", not just public members.
public class FileName
{
public string ThisFieldWorks {get;set;}
public string ThisFieldDoesNot;
}
Instead of create the new Container class you can use a dataTable.
DataTable dt = new DataTable();
dt.Columns.Add("My first column Name");
dt.Rows.Add(new object[] { "Item 1" });
dt.Rows.Add(new object[] { "Item number 2" });
dt.Rows.Add(new object[] { "Item number three" });
myDataGridView.DataSource = dt;
More about this problem you can find here: http://psworld.pl/Programming/BindingListOfString
Using DataTable is valid as user927524 stated.
You can also do it by adding rows manually, which will not require to add a specific wrapping class:
List<string> filenamesList = ...;
foreach(string filename in filenamesList)
gvFilesOnServer.Rows.Add(new object[]{filename});
In any case, thanks user927524 for clearing this weird behavior!!