I have a XtraGrid with one GridView, with a column with checkbox repository item. Now I am handling the CellValueChanging event because I want to only allow the user to check or uncheck based on calculations on other column values on the same row hence I need the e.RowHandle and e.Column of this event and this cannot be done on the EditValueChanging of the repository control.
Now somewhere my calculations say that user cannot check a particular cell to and I throw a message box and try Me.BandedGridView1.SetRowCellValue(e.RowHandle, e.Column, False) but unfortunately this does not set the value to false of that cell.
I need to do it here and here only because of the huge number of calculations based on other column values and I need to set value of the current cell whose event I'm handling right.
Please help.
I'm using DevExpress 9.2 (no chance of upgrading to higher version)
Try this code it's working perfectly !
private void GridView1_CellValueChanged(object sender, CellValueChangedEventArgs e)
{
if (e.Column.Caption != "yourColumnCaption") return;
GridView1.SetFocusedRowCellValue("yourColumnFieldName", 1);
}
You might want to prevent updates by handling ShowingEditor event.
class TestData
{
public TestData(string caption, bool check)
{
Caption = caption;
Check = check;
}
public string Caption { get; set; }
public bool Check { get; set; }
}
Initialize some test data:
BindingList<TestData> gridDataList = new BindingList<TestData>();
gridDataList.Add(new TestData("First row", true));
gridDataList.Add(new TestData("Second row", true));
gridControl.DataSource = gridDataList;
Handle ShowingEditor. Check if user is allowed to change chechbox. If not, cancel the event.
private void gridView1_ShowingEditor(object sender, CancelEventArgs e)
{
GridView view = sender as GridView;
// Decision to allow edit using view.FocusedRowHandle and view.FocusedColumn
if (view.FocusedColumn.FieldName == "Check")
{
// Allow edit of odd rows only
bool allowEdit = view.FocusedRowHandle % 2 == 1;
e.Cancel = !allowEdit;
}
}
Related
The following code returns -1 for each displayed column.
Anyone knows the answer? I tried to use ColumnDisplayIndexChanged event. But it did not show anything.
i.konuk
private void dg_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
string headername = e.Column.Header.ToString();
//Cancel the column you don't want to generate
if (headername == "Occupation")
{
e.Cancel = true;
}
//update column details when generating
if (headername == "FirstName")
{
e.Column.Header = "First Name";
}
//update column details when generating
if (headername == "LastName")
{
e.Column.Header = "Last Name";
}
int myin = e.Column.DisplayIndex;
System.Text.StringBuilder messageBoxCS = new System.Text.StringBuilder();
messageBoxCS.AppendFormat("{0} = {1}", "Column", myin);
messageBoxCS.AppendLine();
MessageBox.Show(messageBoxCS.ToString(), "DataGridAutoGeneratingColumnEvent");
}
AutoGeneratingColumn event occurs when an individual column is auto-generated in other words the event is called when columns are forming in the dataGrid. This means no columns are displayed yet.
DataGridColumn.DisplayIndex property displays the position of the column in the DataGrid. We have not yet displayed the columns in AutoGeneratingColumn event. The DisplayIndex property has a default value of -1 before it is added to the DataGrid.Columns collection.
Thats why you are getting the default value of -1.
ColumnDisplayIndexChanged Event is called when you have selected a particular column and changed the dispayed order in the DataGrid.
Hope I have answered your Question!
In my VS2015 Winform app, there is one DataGridView control bound to a BindingSource that is bound to a SQL database. The Grid has four columns: ID, URL, Name, Type. The URL column is DataGridViewLinkColumn whose ReadOnly property, by default, is set to False. I can edit the Name and Type columns but URL columns shows as ReadOnly. Why? How can I make URL column editable?
As Reza stated:
DataGridViewLinkColumn is not editable.
Therefore, to edit a cell in such a column you'll have to convert it to a DataGridViewTextBoxCell as needed. For instance, if I have subscribed to DataGridView.CellContentClick to handle clicking on a link, then I would handle CellDoubleClick for the cell conversion:
private void DataGridView1_CellDoubleClick(object sender, DataGridViewCellEventArgs e)
{
if (this.dataGridView1.Columns[e.ColumnIndex] == this.dataGridView1.Columns["URL"])
{
this.dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex] = new DataGridViewTextBoxCell();
this.dataGridView1.BeginEdit(true);
}
}
Once you've entered your value and left the cell, you should then use CellValidated to verify that the new value is a URI before converting the cell back to a DataGridViewLinkCell:
private void DataGridView1_CellValidated(object sender, DataGridViewCellEventArgs e)
{
if (this.dataGridView1.Columns[e.ColumnIndex] == this.dataGridView1.Columns["URL"])
{
DataGridViewCell cell = this.dataGridView1.Rows[e.RowIndex].Cells[e.ColumnIndex];
if (Uri.IsWellFormedUriString(cell.EditedFormattedValue.ToString(), UriKind.Absolute))
{
cell = new DataGridViewLinkCell();
}
}
}
Caveat:
This only worked for me when the data for the "URL" column were strings and thus after binding, the column defaulted to a DataGridViewTextBoxColumn - forcing a manual conversion to link cells to begin with:
private void DataGridView1_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
{
foreach (DataGridViewRow r in dataGridView1.Rows)
{
if (Uri.IsWellFormedUriString(r.Cells["URL"].Value.ToString(), UriKind.Absolute))
{
r.Cells["URL"] = new DataGridViewLinkCell();
}
}
}
Setting up the "URI" column as a DataGridViewLinkColumn from the beginning allowed for the conversion of cells to TextBox type successfully. But when converting back to link cells, debugging showed the conversion to happen, but the cell formatting and behavior failed.
I want to add a method to the WPF Calendar control to enable it to select many dates at once, and raise the SelectedDatesChangedEvent only once at the end.
The WPF Calendar control allows you to add only one date at a time (ranges are not useful to me). However I might need to add some 1000 dates and I don't want the SelectedDatesChangedEvent handler called 1000 times because in my case it's an expensive operation.
The WPF DataGrid has a very nice feature that allows for this to be done:
public class MyDataGrid : DataGrid
{
public void ClearAndSelectMany(IEnumerable<DateTime> datesToBeSelected)
{
this.BeginUpdateSelectedItems();
...
foreach (DateTime date in datesToBeSelected)
this.SelectedDates.Add(date);
...
this.EndUpdateSelectedItems();
}
}
However the Calendar doesn't have anything like DataGrid's BeginUpdateSelectedItems(), so I'm trying to create a workaround by preventing base.OnSelectedDatesChanged() being called until it's all done:
public class MyCalendar : Calendar
{
protected override void OnSelectedDatesChanged(SelectionChangedEventArgs e)
{
if (false == this.temporaryDontReportSelectionChanged)
base.OnSelectedDatesChanged(e); // this is where I get an exception
}
public void ClearAndSelectMany(IEnumerable<DateTime> datesToBeSelected)
{
this.temporaryDontReportSelectionChanged = true;
...
foreach (DateTime date in datesToBeSelected)
SelectedDates.Add(date);
...
this.temporaryDontReportSelectionChanged = false;
OnSelectedDatesChanged(
new SelectionChangedEventArgs(
MyCalendar.SelectedDatesChangedEvent,
removedDates.ToList(),
addedDates.ToList()));
}
}
Now my problem is that I'm getting an exception when calling base.OnSelectedDatesChanged():
Unable to cast object of type
'System.EventHandler`1[System.Windows.Controls.SelectionChangedEventArgs]'
to type
'System.Windows.Controls.SelectionChangedEventHandler'.
I suppose I didn't properly create the SelectionChangedEventArgs object near the end, but I have no idea how to do it right. Any help will be appreciated.
Update: Motivated by Jamleck's question, I recreated the problem in a new project, and now have a bit more information to provide. Here's the MyCalendar class:
public class MyCalendar : System.Windows.Controls.Calendar
{
private bool temporaryDontReportSelectionChanged;
public MyCalendar()
{
// removing this line below makes my problem go away and it works ok
this.SelectedDatesChanged += MyCalendar_SelectedDatesChanged;
}
void MyCalendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
}
protected override void OnSelectedDatesChanged(SelectionChangedEventArgs e)
{
if (!temporaryDontReportSelectionChanged)
base.OnSelectedDatesChanged(e);
}
public void ClearAndSelectMany(IEnumerable<DateTime> datesToBeSelected)
{
this.temporaryDontReportSelectionChanged = true;
...
foreach (DateTime date in datesToBeSelected)
SelectedDates.Add(date);
...
this.temporaryDontReportSelectionChanged = false;
OnSelectedDatesChanged(
new SelectionChangedEventArgs(
MyCalendar.SelectedDatesChangedEvent,
removedDates.ToList(),
addedDates.ToList()));
}
}
So if I don't add an event handler to the SelectedDatesChanged event handler, everything works great, but if I do add it, then I get the InvalidCastException described above.
I have a DataGrid showing some databases having quite some columns.
I would like that, when the user edit a new row, some values are set automatically.
With the windows form DataGrid that would be easy, since there's RowsAdded event handler.
But how could i handle this with the wpf DataGrid ??
Edit : my DataGrid is bound in Xaml to a public property which is an ITable. When user select a table in a ComboBox, the property is updated with corresponding table.
Yes there's autogenerating column, and the way the user can enter a new row is to edit the last blank row (default behaviour).
You can do this in the LoadingRow event. Try something like this:
private void myDataGrid_LoadingRow(object sender, System.Windows.Controls.DataGridRowEventArgs e)
{
MyObject myObject = e.Row.Item as MyObject;
if (myObject != null)
{
myObject.PropertyOne = "test";
myObject.PropertyTwo = 2;
}
}
Ok i think i got it.
When a DataTable is bound to a DataGrid, a CollectionView is created in order to see it. You can get it by using the (static/shared) CollectionView.GetDefaultView(ThePropertyThatIsBound) method.
Since it implements ICollectionChanged, you can add an event handler to the CollectionChangedEvent.
In the CollectionChanged event handler, if you have a new item (e.NewItems.Count>0) you must check it against System.Windows.Data.CollectionView.NewItemPlaceholder and if it is not a place holder, then it is a brand new item, for wich i can set all default values.
Assign a CollectionViewSource to your DataGrid then listen to the CollectionChanged event as following :
..
public CollectionViewSource ViewSource { get; set; }
..
this.ViewSource = new CollectionViewSource();
this.ViewSource.Source = new List<YourObjectType>();
this.ViewSource.View.CollectionChanged += View_CollectionChanged;
..
private void View_CollectionChanged(object sender,System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.NewItems.Count > 0)
{
YourObjectType myObject = e.NewItems[e.NewItems.Count-1] as YourObjectType;
if (myObject != null)
{
myObject.Property = TheValueYouWant;
..
}
}
}
..
<DataGrid ItemsSource="{Binding ViewSource.View}" ../>
I've been using WinForms databinding to display data from a database mapped with Fluent NHibernate, and that's been working great.
For example, I can just set a DataGridView's DataSource property from an entity's IList property, and voila - there's all the data!
But now I need to start adding and saving new data rows, and that's not going so well. I thought I'd be able to just enable the grid's AllowUserToAddRows property, and new rows would get added to the underlying IList in the entity, but that didn't work.
Then, after a little searching, I tried setting the DataSource property to a BindingList that was populated from the IList, but that's not being updated with new rows either.
During the course of my searches, I also came upon a few people reporting difficulty with WinForms and DataBinding in general, which makes me wonder if I should pursue that approach any further.
Is the DataBinding approach worth continuing? If so, can anyone suggest where I'm going wrong?
Or is it better to just handle all the DataGridView events associated with adding a new row, and writing my own code to add new objects to the IList property in my entity?
Other suggestions? (though I don't think switching to WPF is going to be an option, no matter how much better the databinding may be)
Can you load (or copy) your nHibernate entities into a generic List? If so, I have had good success in with two-way binding using a DataGridView bound to a generic List.
The key points are:
The generic list contains list objects where each is an instance of your custom class.
Your custom class must implement public properties for each of the fields to bind. Public fields didn't work for me.
Use a BindingSource to wrap the actual generic list.
The BindingSOurce allows you to set the AllowNew property to true. Binding directly to the List almost works, but the DataGridVieww does not display the "New row" line, even if AllowUsersToAddRows = true.
For example, add this code to a Form with a dataGridView1:
private List<MyObject> m_data = new List<MyObject>();
private BindingSource m_bs =new BindingSource();
private void Form1_Load(object sender, EventArgs e)
{
m_data.Add(new MyObject(0,"One",DateTime.Now));
m_data.Add(new MyObject(1, "Two", DateTime.Now));
m_data.Add(new MyObject(2, "Three", DateTime.Now));
m_bs.DataSource = m_data;
m_bs.AllowNew = true;
dataGridView1.DataSource = m_bs;
dataGridView1.AutoGenerateColumns = true;
dataGridView1.AllowUserToAddRows = true;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
for (int i = 0; i < m_data.Count ; i++)
{
Console.WriteLine(string.Format("{0} {1} {2}", m_data[i].ID, m_data[i].Name, m_data[i].DOB));
}
}
}
public class MyObject
{
// Default ctor, required for adding new rows in DataGridView
public MyObject()
{
}
public MyObject(int id, string name, DateTime dob)
{
ID = id;
Name = name;
DOB = dob;
}
private int m_id;
public int ID
{
get
{
return m_id;
}
set
{
m_id = value;
}
}
private string m_name;
public string Name
{
get
{
return m_name;
}
set
{
m_name = value;
}
}
private DateTime m_dob;
public DateTime DOB
{
get
{
return m_dob;
}
set
{
m_dob = value;
}
}
}
When the form closes, the contents of the bound List are printed to the Output window.