I have a form with DataGridViews that have the DataSource set to a DataContext of my database to auto pull in the info and allow it to be edited. But I need a color to display a Color from the color id that corresponds to a color in a colors table and I don't know how I can achieve this with also allowing it to auto edit and update the records.
private dbCounterTopsDataContext dbContext = new dbCounterTopsDataContext();
private void FrmCurrentInventory_Load(object sender, EventArgs e)
{
dataGridColors.DataSource = dbContext.Colors;
dataGridBarTops.DataSource = dbContext.BarTops;
dataGridKitchenTops.DataSource = dbContext.Kitchens;
dataGridVanityTops.DataSource = dbContext.Vanities;
}
I have also tried this and it worked with displaying but I could no longer update directly from the datagrid
private void FrmCurrentInventory_Load(object sender, EventArgs e)
{
dataGridColors.DataSource = dbContext.Colors.Select(o => new { ColorId = o.ColorID, Color = o.Color1 });
dataGridBarTops.DataSource = dbContext.BarTops.Select(o => new { Color = o.Color.Color1, Length = o.Length, Width = o.Width });
dataGridKitchenTops.DataSource = dbContext.Kitchens.Select(o => new { Color = o.Color.Color1, Length = o.Length }).ToList();
dataGridVanityTops.DataSource = dbContext.Vanities;
}
Some good tips in the comments from SSS, perhaps the easiest way to get this going is to use AutogenerateColumns to gen most of the stuff and then add a few combo box columns for your colors. It would probably go something like this:
dataGridColors.DataSource =
dataGridBarTops.DataSource = dbContext.BarTops;
dataGridBarTops.AutogenerateColumns = false;
var c = new DataGridViewComboBoxColumn();
c.HeaderText = "Color";
c.DisplayMember = "Color1"; //name of property that says Blue in color entity
c.ValueMember = "ColorID"; //name of property that says 5 in color/entity
c.DataPropertyName = "BarTopColorId"; //name of property that says 5 in BARTOPS table/entity
c.DataSource = dbContext.Colors; //personally I would use a bindingSource here
dataGridBarTops.Columns.Add(cb);
I'll leave removing the text box color is column as an exercise for you- maybe leave it in first to see how combo is working
The Colors datagridview is for editing the actual Colors definitions etc. The combo box in the bar top table is for choosing a different color is for a single bar top. If you edit the Colors table it will change what is in the list of the combo. If you pick a new combo value it will change the bartop's color id (whatever that property is called) value
Be careful not to edit away ids in Colors that are used in bartops - don't delete color Id 5 from the Colors table while there is still a bartop that uses it
I put a comment there about bindingsources. I've never bound a DGV straight to a LINQ entity list, nor probably would I (partly because it's downloading an entire table, which could be huge and partly because my winforms databinding is near exclusively with strongly typed datasets) but you find it to work (and maybe you need the whole table, no filtering). If, however you start to see strange effects like selecting different rows in the Colors datagridview causes all the combo boxes to change value, bind the combo through a bindingsource so they have something that dedicatedly tracks "the current item" for the combo
Related
In my winform app , there is a column called "Next_Calibration_On"(in the format "dd-MM-yyyy") with which i have to compare today date if it is less than it then i want highlight the cell in the datagrid view in red .
Fr this i have the below code :
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
DateTime currentToday = (DateTime)this.dataGridView1.Rows[e.RowIndex].Cells["Next_Calibration_On"].Value;
if (currentToday <= DateTime.Now.Date)
{
e.CellStyle.ForeColor = Color.Red; //Font Color
e.CellStyle.SelectionForeColor = Color.Red; //Selection Font color
}
However this is showing the error message as below:
System.ArgumentException: 'Column named Next_Calibration_On cannot be found.Parameter name: columnName'
But i do have column in my table ..how to solve this?
Don't edit your cells. Tell your DataGridView where to fetch its data
Don't put the data directly in the DataGridView, tell it where to fetch its data, and how to display the fetched data. This separates your data from how it is displayed. Whenever you decide to display it differently you don't have to change your original data.
You can see how this separation between data and display is done in a spreadsheet. The spreadsheet data can be shown as a collection of cells layed out in columns and rows, and displaying textual representation of the values in the cells.
Another method to show the same data would be in a graph: even though the display is entirely different than a collection of cells, the data behind is the same.
Same data, differently displayed.
In winforms you have the same separations with all Controls that show sequences of data: ListBox, ListView, DataGridView, Chart, etc.
All these classes offer the possibility to directly add the data to the component, but they also have a property DataSource.
If you assign a sequence to the DataSource, something like a List or Array, the Data is displayed. other properties decide how each element of the DataSource must be displayed.
In a DataGridView the DataGridViewColumns define which property of each row must be displayed, and how they must be displayed.
In its most easy form:
Suppose you have a class Customer:
class Customer
{
public int Id {get; set;}
public string Name {get; set;}
public Address Address {get; set;}
public string PhoneNr {get; set;}
};
Suppose you also have a DataGridView that shows only the Id and Name of Customers
DataGridViewColumn columnCustomerId = new DateGridViewColumn
{
DataPropertyName = nameof(Customer.Id),
ValueType = typeof(int),
};
DataGridViewColumn columnCustomerName = new DateGridViewColumn
{
DataPropertyName = nameof(Customer.Name),
ValueType = typeof(string),
};
DataGridView customersDataGridView = new DataGridView(...) {...}
customersDataGridView.Columns.Add(columnCustomerId);
customersDataGridView.Columns.Add(columnCustomerName);
Now to show Customers:
List<Customer> customers = FetchCustomers(...);
customersDataGridview.DataSource = customers;
This is enough to show the customer's Id and Name. If you also want to show the Telephone numbers, just add a Column. You don't have to change your original data.
Similarly if you want to format the cells of certain customers differently: this has no influence on your original customer collection. Only the Display changes.
Side Remark
Although the above works, you still have problems if operators edit the displayed data. If you want to automatically update changes made by operators, do not put the data in a List, but in a System.ComponentModel.BindingList:
BindingList<Customer> customers = new BindingList<Customer>(FetchCustomers(...));
customerDataGridView.DataSource = customers;
Now if the operator adds / removes / changes Customers from the DataGridView, they are automatically updated in the BindingList.
And a handy tip: if you want automatic sorting by clicking on the column header, and if you want easy filtering: "show only Customers who live in 'Amsterdam'" without having to change the Customers list, consider to use Nuget package BindingListView
BindingListView<Customer> customers = new BindingListView<Customer>(FetchCustomers(...));
customerDataGridView.DataSource = customers;
And presto: you've got sortable columns; editable customers. Filtering is easy:
// show only Amsterdam customers:
customers.ApplyFilter( customer => customer.Address.City == "Amsterdam");
Even though you don't show the City of the customer in the DataGridView, you can easily filter on it.
By the way: did you notice how easy I switched from DataSource without even changing my DataGridView? This is because I separated the display from the source data.
Back to your question
You want to change the layout of the cells in column NextCalibration. Every Cell with a value higher than xxx should be Red.
The layout of a cell is done in a DataGridViewCellStyle. If your Cell doesn't have its own CellStyle, it uses the CellStyle of the DataGridViewColumn.
You want to change the cell style whenever the value of the displayed property meets some requirement. In this case: foreground color red if it is less than today's date, otherwise the default value. Make a Clone of the default cell style of the column.
DataGridViewColumn columnNextCalibration = ...
DataGridViewCellStyle specialCellStyle = (DataGridViewCellStyle)columnNextCalibration
.CellTemplate
.CellStyle
.Clone();
If your column has no special style (it is null), use the InheritedStyle.
// adjust the CellStyle to the style it should have when the value meets the requirements
specialCellStyle.ForeColor = Color.Red;
// if desired: change the Font, the fontSize, or the BackColor, the frame whatever!
You want to select either the default CellStyle (null) or this special cell style, depending on the displayed DateTime value:
Now that you have a special CellStyle wait until the displayed value changes:
this.customersDataGridView.CellValueChanges += OnCellValueChanged;
private void OnCellValueChanged(object sender, DataGridViewCellEventArgs eventArgs)
{
DataGridView dgv = (DataGridView)sender;
// is the changed cell from my column in my DataGridView?
if (Object.ReferencEquals(sender, customersDataGridView)
&& e.ColumnIndex == columnNextCalibation.DisplayIndex)
{
DataGridViewCell changedCell = dgv.Rows[e.RowIndex].Column[e.ColumnIndex];
DateTime cellValue = (DateTime)cell.Value;
DateTime today = ...
if (cellValue < today)
{
// use the special CellStyle:
changedCell.CellStyle = specialCellStyle;
}
else
{
// use the default CellStyle of the column, not a special one for this cell
changedCell.CellStyle = null;
}
}
}
I'm trying to set DataSource to DataGridViewComboBoxColumn of my DataGridView. First I'm trying to bind a data source to my DataGridView, where bindingList is a List of my custom class Plugin with properties Name (string), Id (string) and Dependencies (List):
var bindingList = PluginsHandler.GetPlugins();
var source = new BindingSource(bindingList, null);
pluginsDataGridView.AutoGenerateColumns = false;
pluginsDataGridView.DataSource = source;
pluginsDataGridView.Columns["pluginName"].DataPropertyName = "Name";
pluginsDataGridView.Columns["pluginID"].DataPropertyName = "Id";
So I can set my first two columns, but now I want to bind data to a third column of type DataGridViewComboBoxColumn. I try to do it on DataBindingComplete event:
private void pluginsDataGridView_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
for (int i = 0; i < pluginsDataGridView.Rows.Count; i++)
{
var comboCell = (DataGridViewComboBoxCell) pluginsDataGridView.Rows[i].Cells["pluginDependencies"];
var entry = pluginsDataGridView.Rows[i].DataBoundItem as IPlugin;
comboCell.DataSource = entry.Dependencies;
}
}
Sadly comboBox is empty. Funny thing happens when I incorrectly put these lines after the first block of code I posted:
var dependenciesColumn = (DataGridViewComboBoxColumn) pluginsDataGridView.Columns["pluginDependencies"];
dependenciesColumn.DataPropertyName = "Dependencies";
Then binding seem to start to work, as I can see that there are some entries in comboboxes, but when I try to hover mouse on combobox, I am getting an error that says DataGridViewComboBoxCell value is not valid).
How can I make it work?
Instead of assigning each ComboCell a data Source, set the DataSource of the column. I assume the Dependencies property of PlugIn class is a List<string>.
pluginsDataGridView.Columns["pluginDependencies"].DataSource = //list of dependencies
You have to set the DataPropertyName of the Dependencies ComboBoxColumn to get the initial values. If you don't set it you won't see any value in the column when the application is loaded.
pluginsDataGridView.Columns["pluginDependencies"].DataPropertyName = "Dependencies"
Edit:
You have a list of dependencies for a plug-in. i.e, more than one value. Usually, to select one value from list of values, you associate list with ComboBoxColumn. Achieving your requirement of multiple values from a list using standard ComboBoxColumn is difficult. Write a custom CheckedComboBoxColumn where you can display and select multiple values.
I have this problem for days and can't find solution for it. I tried all possible solutions i found on internet, but seems like none suits this one.
Thing is that i added repository item to gridControls (i added it through designer, not through code). Then, in code i added data source to that repository lookUpEdit and i have items in dropDown in that column. But when i select item in repository and click on other cell, Selected item in repository is cleared and repository shows null value again...
Any ideas what i did wrong ?
EDIT:
Also, when i click on any cell in my grid, i have delay of second or two, and after that delay clicked cell is focused... Any solutions for all of this?
EDIT:
Don't know what code to show You, because I did all in devExpress designer. Here is part of the code where I set data source to repository item, and i will give You code from designer of that repository item.
private void ConfigureRepositoryItems()
{
BetService.SportManagerClient dbSportManager = new BetService.SportManagerClient();
BetService.BLOddsControlSettings[] oddsControlSettings = dbSportManager.GetOddsControlSettings("", "");
repositoryOddsControlSettings1.DataSource = oddsControlSettings;
}
And here is code from designer:
//
// repositoryOddsCalculationSettings1
//
this.repositoryOddsCalculationSettings1.AutoHeight = false;
this.repositoryOddsCalculationSettings1.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] {
new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)});
this.repositoryOddsCalculationSettings1.Columns.AddRange(new DevExpress.XtraEditors.Controls.LookUpColumnInfo[] {
new DevExpress.XtraEditors.Controls.LookUpColumnInfo("ID", "ID", 20, DevExpress.Utils.FormatType.None, "", false, DevExpress.Utils.HorzAlignment.Default),
new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Name", "Name")});
this.repositoryOddsCalculationSettings1.DisplayMember = "Name";
this.repositoryOddsCalculationSettings1.Name = "repositoryOddsCalculationSettings1";
this.repositoryOddsCalculationSettings1.NullText = "Select Settings";
this.repositoryOddsCalculationSettings1.PopupSizeable = false;
this.repositoryOddsCalculationSettings1.ValueMember = "ID";
For starters check whether the column name in your Grid datasource and the column in your grid control match. The match is case sensitive so name and Name are not same and hence can cause this issue. Secondly make sure the Grid datasource column datatype matches the value type of the LookUpEdit. If the LookupEdit is returning int and the Grid datasource column datatype is string, this alone can cause lots of headaches.
I have a DataGridView where the units can be entered in a TextBox column.
How do I restrict the input length of this column to 6 characters?
Use the MaxInputLength property of the DataGridViewTextBoxColumn.
This property is available through the Designer or through code:
((DataGridViewTextBoxColumn)dataGridView1.Columns[yourColumn]).MaxInputLength = 6;
Please use CellValueChanged event of DataGridView.
In the handler of the event you can check ColumnIndex and RowIndex properties of DataGridViewCellEventArgs argument to identify that grid's field of interest is edited and then - take appropriate actions.
As stated in other answers - most natural way to restrict text lengths for DataGridView field is to modify respective grid column properties. Properties of grid columns can be altered on Edit Columns form that is invoked for grid control in form designer with right click menu item Edit Columns...:
You may have to play with cell-edit events.
http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.cellvaluechanged.aspx
You don't necessarily have the columns ready to manipulate if you're using data binding. For data binding, using the ColumnAdded listener can help:
public FormSingleValidation(BindingList<ValidateSingle> validateSingles)
{
InitializeComponent();
dataGridViewSingleValidation.ColumnAdded += ColumnAdded;
this.validateSingles = validateSingles;
var source = new BindingSource(validateSingles, null);
dataGridViewSingleValidation.DataSource = source;
}
private void ColumnAdded(object sender, DataGridViewColumnEventArgs e)
{
if(e.Column.GetType() == typeof(DataGridViewTextBoxColumn))
{
DataGridViewTextBoxColumn column = (DataGridViewTextBoxColumn) e.Column;
column.MaxInputLength = 6;
}
}
Caveats
Obviously this applies to all text columns without discrimination, you can add a conditional filter using the column's name if you only want specific columns to be effected.
I have a DataGridView that uses databinding, with manually created columns, and this works fine.
However I want the rows' BackColor to be databound as well, and so far my attempts have hit errors.
This is my latest attempt:
dataGridFileTransfer.RowHeadersVisible = false;
dataGridFileTransfer.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridFileTransfer.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridFileTransfer.MultiSelect = false;
dataGridFileTransfer.ReadOnly = true;
var files = GetReceivedFiles(false).Union(FileDetails.Select(FileStatus.FailedVerification)).ToList();
dataGridFileTransfer.AutoGenerateColumns = false;
string[] displayHeaders = new string[] { COL_NAME, COL_TYPE, COL_CREATED, COL_SIZE, COL_STATUS };
string[] displayProps = new string[] { "Filename", "FileTypeDisplayName", "Created", "Size", "FileStatus" };
for (int i = 0; i < displayHeaders.Length; i++)
{
DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn();
col.HeaderText = displayHeaders[i];
col.DataPropertyName = displayProps[i];
if (displayHeaders[i] == COL_CREATED)
col.DefaultCellStyle.Format = Constants.DDMMYYYHHMMSS;
dataGridFileTransfer.Columns.Add(col);
}
Binding bi = new Binding("DefaultCellStyle.BackColor", files, "DisplayColor");
dataGridFileTransfer.DataBindings.Add(bi);
dataGridFileTransfer.DataSource = files;
Which is generating an ArguementException:
"Cannot bind to the property
"DefaultCellStyle.BackColor' on the
target control. Parameter name:
PropertyName"
Is it the value of PropertyName that is wrong, or should I binding to an object other than the DataGridView? (i.e. a column?)
Or is the problem that PropertyName cannot be in the form X.Y? I thought I had seen/used this syntax before, but maybe it only works for DataMember?
Any help is much appreciated
I think the problem is files.DisplayColor. files is a collection an has no property DisplayColor but each item of the collection has. So you are trying to bind a not existing property. Further binding collection DataGridView.DataBindings allows you to data bind properties of the control, not of its rows. There is only one DataGridView.DefaultCellStyle.BackColor for all rows. So I believe you end up needing to bind the DefaultCellStyle of each row to the coresponding item from files and I am not sure if this is possible. It might be that the DataGridView creates and deletes rows as required - for example if you perform filtering - and this will destroy the data binding, too.
So, I am not sure if row coloring can be done with data binding, but I personaly doubt it. This would require some really smart logic recognicing 'bind the property DisplayColor of the object data bound to this row to the property DefaultCellStyle.BackColor of this row.'
You could surly implement such an smart data binding. While it would be a great thing, it will be quite complex, too. As a simple solution you could just use the RowPrepaint event to set the correct color for the row.