I have a WinForms application with only one DevExpress GridControl inside.
This GridControl uses two GridViews and one relationship in Master-detail mode.
As dataSource for gridControl I am using following class:
public class DashboardParameter
{
public string Name { get; set; }
public int DataType { get; set; }
public int ValueType { get; set; }
public BindingList<DashboardParameterValue> Detail { get; set; }
public DashboardParameter()
{
Detail = new BindingList<DashboardParameterValue>();
}
}
public class DashboardParameterValue
{
public string Value { get; set; }
}
Here is the code for data loading:
private void MasterDetail_Load(object sender, EventArgs e)
{
data = new BindingList<DashboardParameter>();
var p1 = new DashboardParameter() { Name = "First", DataType = 1, ValueType = 1};
p1.Detail.Add(new DashboardParameterValue() { Value = "Value1" });
p1.Detail.Add(new DashboardParameterValue() { Value = "Value2" });
var p2 = new DashboardParameter() { Name = "Second", DataType = 1, ValueType = 1 };
p2.Detail.Add(new DashboardParameterValue() { Value = "Value3" });
p2.Detail.Add(new DashboardParameterValue() { Value = "Value4" });
data.Add(p1);
data.Add(p2);
gridControl.DataSource = data;
}
As I understand it, in this way my gridControl automatically finds master-detail relation and creating Columns for each field in class for DataSource (If AutoPopulateColumns property is true).
Trouble: I can not change ANYTHING in my detailView Columns. I dont know in wich time my dataView columns are being created. All of detailView properties are ignored.
For example, if i changing detailView.AutoPopulateColumn = false, Columns still are being created.
If I create custom GridColumn gridColumn1 and add there detailView.Columns.Add(gridColumn1), it will be ignored.
Only one thing i can do is using [DisplayAttribute] for changing DisplayName, Visible and so on.
Question: How must I change my code to be able to change my detailView.
For example, can I add Column in detailView after all auto-generated columns, or change Column type to ComboBox (using RepositoryItemComboBox).
You can customize a detail view in the GridView.MasterRowExpanded event handler. This event is raised when a master row is expanded and a corresponding detail view is created and configured. To access this view, use the GridView.GetDetailView method. Alternatively, you can handle the GridControl.ViewRegistered event.
One more solution is to create a pattern detail view and customize it at runtime or design time.
I suggest you to go through documentation for Working with Master-Detail Relationships in Code and Master-Detail Relationships.
You can create your views either based on your database structure or pragmatically by adding views and columns with their settings.
You can customize a detail view in the GridView.MasterRowExpanded event handler. Fires immediately after a particular detail clone has become visible. The MasterRowExpanded event fires when expanding a master row or when switching between details.
Example:
//Assign a CardView to the relationship
CardView cardView1 = new CardView(gridControl1);
gridControl1.LevelTree.Nodes.Add("CategoriesProducts", cardView1);
//Specify text to be displayed within detail tabs.
cardView1.ViewCaption = "Category Products";
//Hide the CategoryID column for the master View
gridView1.Columns["CategoryID"].VisibleIndex = -1;
//Present data in the Picture column as Images
RepositoryItemPictureEdit riPictureEdit = gridControl1.RepositoryItems.Add("PictureEdit") as RepositoryItemPictureEdit;
gridView1.Columns["Picture"].ColumnEdit = riPictureEdit;
//Stretch images within cells.
Related
I'm creating an app with two forms, one is for understanding which file I am choosing, and the second one is just to make a filter before, which means you can choose which properties you want to see in this file. I have a checkboxlist and class with my properties.
I also have a button in my first form where I have:
foreach (var item in ds)
{
DataGridViewRow row = new DataGridViewRow();
fileListDataGridView.Rows.Add(
item.Path,
item.PatientName,
item.PatientID);
}
I'm not sure that this is the correct way how to add data from the list in DataGridWiev, but right now I have this solution. Cause it's just adding a new item at the end of the list.
The problem is that I have checkedListBox in the second form and I need to bind it somehow with my properties.
Properties:
public string Path { get; set; }
public string PatientName { get; set; }
public string PatientID { get; set; }
When you click on checkbox with Patient Name that means you will get the information in your first form with only this property. I know that when we are making a checkedListBox, we also have an index there, but how I can get this index and bind it with my prop?
People who are new to programming a DataGridView tend to fiddle with the rows and cells of the DataGridView. This is cumbersome, hard to understand, hard to reuse and very difficult to unit test.
Using databinding is way more easy.
Apparently you want to show several properties of a Patient in your DataGridView.
class Patient
{
public int Id {get; set;}
public string Name {get; set;}
public string Path {get; set;}
... // other properties?
}
Using visual studio you have added a DataGridView and the columns that you want to show. In the constructor of your form:
public MyForm()
{
InitializeComponent();
// assign Patient properties to the columns:
this.columnId.DataPropertyName = nameof(Patient.Id);
this.columnName.DataPropertyName = nameof(Patient.Name);
this.columnPath.DataPropertyName = nameof(Patient.Path);
... // etc.
}
Somewhere in your form you have a method to fetch the Patients that must be displayed:
IEnumerable<Patient> FetchPatientsToDisplay()
{
... // TODO: implement; out-of-scope of this question
}
To display Patients we use a BindingList:
BindingList<Patient> DisplayedPatients
{
get => (BindingList<Patient>)this.dataGridView1.DataSource;
set => this.dataGridView1.DataSource = value;
}
Now to fill the DataGridView when the form is loaded:
void OnFormLoading(object sender, ...)
{
this.ShowPatients();
}
void ShowPatients()
{
this.DisplayedPatients = new BindingList<Patient>(this.FetchPatientsToDisplay().ToList());
}
That is all! The Patients are displayed; if allowed, the operator can add / remove / edit Patients. When finished editing, he notifies the program by pressing the OK or Apply Now button:
void ButtonOk_Clicked(object sender, ...)
{
// Fetch the edited Patients:
ICollection<Patient> editedPatients = this.DisplayedPatients;
// find out which patients are changed and process them
this.ProcessPatients(editedPatients);
}
So you don't need to Add / Remove rows by yourself. Usually the operator does this. If a default Patient is not enough for you to show, use event BindingList.AddningNew:
void OnAddingNewPatient(object sender, AddingNewEventArgs e)
{
// create a new Patient and give it the initial values that you want
e.NewObject = new Patient()
{
Id = 0, // zero: this Patient has no Id yet, because it is not added to the database yet
Name = String.Empty,
Path = String.Empty,
}
}
Maybe the following properties might be useful:
Patient CurrentPatient => (Patient) this.dataGridView1.CurrentRow?.DataBoundItem;
And if you allow multiple selection:
IEnumerable<Patient> SelectedPatients = this.dataGridView1.SelectedRows
.Cast(row => row.DataGridViewRow)
.Select(row => row.DataBoundItem)
.Cast<Patient>();
In words: interpret every selected rows in the datagridView as a DataGridViewRow. From every DataGridViewRow get the item that is DataBound to it. We know that this is a Patient, so we can cast it to a Patient.
I have a DataGridView in c# WinForms
In this form I fill a big DataGridView with data, the process behind takes long time.
anyway, my issue is that I want to update one cell in that DataGridView based on criteria
DataGridView has 4 columns, StNo, StName, StAge, StMark.
I want to search for StNo = 123 and update their StMark to be 68
I tried the following but does not work
grd1.Rows.Cast<DataGridViewRow>().Where(x => x.Cells["StNo"].Value.ToString() == "123")["StMark"] = 68;
How to do that?
DataGridView is a winforms control which responsible for displaying given records in UI and provide user's input to the underlying records via different events.
So instead of updating values via DataGridView - update underlying records.
public class Student
{
public int Number { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public int Mark { get; set; }
}
public class MyForm
{
public readonly BindingList<Student> _sutdents;
public MyForm()
{
_students = new BindingList<Student>();
myDataGridView.DataSource = _students;
}
private void AddStudentButton_Click(object sender, EventArgs args)
{
var student = new Student
{
Number = int.Parse(textboxNumber.Text),
Name = textboxName.Text,
Name = int.Parse(textboxAge.Text),
Name = int.Parse(textboxMark.Text),
};
_students.Add(student);
}
// After button click you should see that new student appears in the DataGridView
// Now you can update existing students from code without "touching" DataGridView
private void UpdateMarkButton_Click(object sender, EventArgs args)
{
var studentNumber = int.Parse(textboxNewMarkStudentNumber.Text);
var newMark = int.Parse(textboxNewMark.Text);
var student = _students.FirstOrDefault(s => s.Number = studentNumber);
if (student != null)
{
student.Mark = newMark;
}
}
}
Your life, made easy:
add a new DataSet file to your project, open it, name it StudentDataSet in the property grid
right click, add a new DataTable to the surface, name it Students
add columns of StNo (make it an int in the property grid), StName, StAge (int), StMark (int)
right click StNo and make it a primary key
save
switch to the form designer, open the Data Sources panel (view menu.. other windows)
drag the node representing the table, onto the form. A datagridview appears
Now the code you need for updating a Mark is:
var s = studentDataSet.Students.FindByStNo(123);
if(s!=null)
s.StMark = 99;
Yes, that's all you need (plus you need to load the table with student data)
If these students come out of a database, you can make your life easier in the second step by not adding a DataTable but instead adding a TableAdapter and setting up the db connection/query that pulls them out of the db..
Either I am doing something wrong or this is a bug.
I have an interface IPerson which has one property set to [Browsable(false)].
After I created a new UserControl (UC), dragged a DataGridView(DGV) into it and generated a BindingSource (BS) for the interface and assigned it to the DGV it renders totally fine and the property does not show up.
In the constructor of the UC i created a sample Person and assigned it as the source of the BS, just so i have at least a row in the DGV to look at.
I created a Form and dragged the new created UC onto it just to see that the evil property shows up as a Column.
WHY?
I tried a few things, from recompiling, to casting the concrete class to the interface but i still have the same issue. On the Form the Control suddenly creates a column for it, while the Control it self does a) not have it b) does not create it either.
// person interface
public interface IPerson : IEntity
{
string Surname { get; set; }
int Age { get; }
[DisplayName("Date of Birth")]
DateTime DateOfBirth { get; set; }
Gender Gender { get; set; }
Address Address { get; set; }
//THIS IS THE BAD BED :-P which should not show up
[DefaultValue(null), Browsable(false), ReadOnly(true)]
IBed Bed { get; set; }
}
// this is the UserCOntrol with a DGV in it, it displays it fine...
// and does not generate the column for the property
public partial class PersonControlView : UserControl
{
public PersonControlView()
{
InitializeComponent();
// just a temporary test ...
var l = new List<IPerson>
{
new Person
{
Address =
new Address
{
City = "Cologne",
Country = "Germany",
County = "***",
Number = "**",
Postcode = "*****",
Street = "**** Strasse"
},
DateOfBirth = new DateTime(1886, 32, 13),
Gender = Gender.Male,
Name = "***",
Surname = "***"
}
};
set(l);
}
// just a temporary test method...
public void set(IList<IPerson> persons)
{
iPersonBindingSource.DataSource = persons;
}
Some pictures
This is the view on the UserControl, it generated the columns correctly as shown on the picture
This is the control after i drag it from the toolbox onto the form ... the population happens in the constructor (see the test code above). The field should not be displayed - correct?
Facepalm OK, following happened:
I created the control and the DGV before I set the attribute on the property. This caused to actually create the column for the property.
Then I added the attribute to the propert, refreshed the datasource and the DGV in the control must have switched the column to visible=false.
So the column was always there, just didnt display in the UserControl
The question is, why did it suddenly in the form? (*)
(*) irrelevant since i fixed my problem. but still mysterious behaviour
I removed the DGV again from the control (which removed obviously all columns as well, re-created it, assigned the DataSource and now it behaves as expected.
I am developing a win form application and i am stuck in a situation with data grid view.
what i exactly want to do is, if i enter some bar code then according to the bar-code i want to get data and fill them in the the combo box of brand name and Item name and mrp and all.
Which event should i work on and how can i get data and fill in this data grid-view row.
You can do this in many ways, my experience with DataGridView I do as follows.
Suppose you have a model class for your dataGridView
public class MyBarcodeRow
{
private String barcode = String.Empty;
public String Barcode
{
get { return barcode; }
set {
barcode = value;
////TODO OPERATION ON OTHER FIELD
//FOR EXAMPLE GET DATA OF QUANTITY FROM DATABASE
this.Quantity = new Random().Next(Int32.MaxValue);
}
}
private int quantity = 0;
public int Quantity
{
get { return quantity; }
set { quantity = value; }
}
//AND OTHER FIELD
}
In your form drag a BindingSource and associated it
BindingSource.DataSource = MyBarcodeRow
Now when you insert the barcode and leave cells you can load other data values.
From experience I suggest you focus on the events of the BindingSource and the Get / Set methods of the class of model that uses the VIEW.
Then there are also many interesting events in the grid, however, these structures work best when used from this point of view
Use DataGridViewComboBoxColumn.DataSource.
For example you have a class of Brand
public class Brand
{
public int32 ID {get; set;}
public string Name {get; set;}
}
In the form create a list of Brands and fill it with data(from database I assume)
List<Brand> _brands;
this.dgvBrandNamesColumn.DataSource = _brands;
this.dgvBrandNamesColumn.DisplayMemeber = "Name";
this.dgvBrandNamesColumn.ValueMemeber = "Name";
After adding data to DataGridView
DataGridViewComboBoxColumn will select matched brand name from DataSource(_brands)
For more specific help, show code how fill datagridview with data and how/from where you get all brands names
I have two classes:
public class Equipment
{
public int EquipmentId { get; set; }
public string Name { get; set; }
public Nullable<int> EquipmentTypeId { get; set; }
}
public class EquipmentType
{
public int EquipmentTypeId { get; set; }
public string TypeName { get; set; }
}
I'm returning list of Equipment from database as a List<Equipment>.
Then I bind it to the Gridview through Gridview1.DataSource = equipmentList
That works fine, but what I need to do is I need to display EquipmentType Name instead of ID. And, in edit mode I need to display Dropdown list with all available EQuipmentTypeNames. Any suggestion on how to do that? what is the architecture?
For displaying in the GridView you have a couple of options, ranging from easiest to hardest.
Use a nested SqlDataSource for equipment type, and display in a disabled dropdownlist.
Load the equipment type with the equipment for data binding.
Programmatically load equipment type in the RowDataBound event.
Here is an example of how you could include the equipment type data:
var equips = GetEquipment();
var equipTypes = GetEquipmentTypes();
var data = from eq in equips
join et in equipTypes on eq.EquipmentTypeId equals et.EquipmentTypeId
select new { eq.EquipmentId, eq.Name, et.TypeName };
GridView.DataSource = data;
For editing in the GridView, it is simplest just to use a DropDownList with it's own SqlDataSource (or custom data source), which is bound to Equipment.EquipmentTypeId, something like this:
<EditItemTemplate>
<asp:DropDownList ID="EquipmentTypeDropDown" runat="server"
SelectedValue='<%#Bind("EquipmentTypeId") %>'
DataSource='<%#GetEquipmentTypes() %>'
DataTextField="TypeName" DataValueField="EquipmentTypeId">
</asp:DropDownList>
</EditItemTemplate>
Where GetEquipmentTypes() is just a method that returns List<EquipmentType> that I'm using for databinding. In this case, since I don't have a database, I'm just generating data in a stub object. You would replace this method with your BAL class that reads the data from the database (or ADO.Net code, or an ObjectDataSource, etc.).
protected List<EquipmentType> GetEquipmentTypes()
{
return new List<EquipmentType> {
new EquipmentType { EquipmentTypeId = 1, TypeName = "Baseball" },
new EquipmentType { EquipmentTypeId = 2, TypeName = "Football" },
new EquipmentType { EquipmentTypeId = 3, TypeName = "Soccer" },
};
}
I'd recommend extending your Equipment class out with a child class (let's call it EquipmentEx for now), and add on the EquipmentTypeName property. Instead of your stored proc returning just the Equipment columns, join in the EquipmentType table, and include the additional type name column.
Or create a view that does the join, and have your stored proc return the view contents instead of the table contents - it depends on how your app is generally organized.
Once you have an EquipmentEx object, then you've got your answer for displaying the type name instead of ID - it's already part of the object, so you can display it just like normal.
As far as putting the dropdown into the GridView, you'll probably be working with the EditItemTemplate tag inside your template field, but I really haven't worked enough with the GridView to know exactly what it will look like. I've always preferred to handle this kind of thing a little more manually, rather than trusting ASP.NET to do it for me. But since your dropdownlist's value will represent the EquipmentTypeID, you should be able to bind this value to the object when you save it, instead of a TextBox value.
First of all , if you want to bind it like that you need to make your own class that contains all informations , like that:
public class CompositeEquipment
{
public int EquipmentId { get; set; }
public string Name { get; set; }
public string EquipmentTypeName { get; set; }
}
And if you are using sql statements or stored procedures you can write the following :
SELECT Equipment.EquipmentID,Equipment.Name,EquipmentType.TypeName FROM Equipment
INNER JOIN EquipmentType ON Equipment.EquipmentTypeId = EquipmentType.EquipmentTypeId
and when reading SqlDataReader do the following :
List<Equipment> lstEquipment = new List<Equipment>();
while(reader.read())
{
Equipment eq = new Equipment();
eq.reader["EquipmentId"];
eq.reader["Name"];
eq.reader["TypeName "];
lstEquipment.Add(eq);
}
Gridview1.DataSource = lstEquipment;
and if you are using EntityFramework as Data Access Layer you can write the following :
using(YourOwnContext context = new YourOwnContext)
{
List<Equipment> lstEquipment = (from e in context.Equipments
select new Equipment
{
EquipmentId = e.EquipmentId,
Name = e.Name,
TypeName = e.EquipmentType.TypeName
}}).ToList();
Gridview1.DataSource = lstEquipment;
Hope this helps you.