I'm trying to implement grid level validation in a WPF datagrid. All the examples I can find relate to row and cell level validation.
The rules will apply to a subset of rows per ID where the rows have From and To dates
Only 1 row may have To Date Null
The current row From Date must not be between any other From / To date pair for this ID
Where the To Date is not null then it must not be between any other From / To date pair for this ID.
Having considered this issue for a few days, the solution will be along these lines:
private void unitDataGridEditEnding(object sender, DataGridRowEditEndingEventArgs e)
{
CUnitMasterBase workingRow = (CUnitMasterBase)e.Row.Item;
CSharedVariables.m_FromToIssuesFlag dateIssues = CSharedVariables.m_FromToIssuesFlag.NONE;
IEnumerable<CDates> unitDates = new List<CDates>(this.ViewModel.Units
.Where(i => i.p_Unit_ID == workingRow.p_Unit_ID)
.Select(d => new CDates ( d.p_Valid_From, d.p_Valid_To )));
if (unitDates.Count() <= 1)
{
return;
}
dateIssues = sharedFunctions.Verify_Dates(unitDates, workingRow.p_Valid_From, workingRow.p_Valid_To);
if (dateIssues != CSharedVariables.m_FromToIssuesFlag.NONE)
{
unitDataGrid.CancelEdit();
}
}
Related
I have a DataGridView in a Winform desktop project in net 6.0
This DataGridView's generic row is composed by a DataGridViewCheckBoxCell in the first column, and another 6 DataGridViewTextBoxCells in the subsequent columns : the first one of the DataGridViewTextBOxCell (column 1) is permanently ReadOnly.
What I want to do is make the Cells from the second TextBox to the last one (that is columns 2 to 6) ReadOnly when the CheckBox is unchecked, and restore them to ReadWrite when the checkbox is checked.
What I do is override the dataGridViev.CellClick event, and when the sender is the element in column 0 (the checkbox) I get the row index and I iterate on the cells of the row from index 2 to 6 and set the ReadOnly property to true or false depending on the state of the checkobx itself.
The mechanism basically works fine, I read the event on the exact column/row, I correctly get the state of the checkobox (maybe in a not-so-smart way...) : but when it comes to setting the ReadOnly property I can see that ,in spite of iterating on the 2 to 6 column, it skips the cell in column 2 and sets the one in column 1 instead: all the settings on the other columns work correctly. I thought it was a matter of configuring the columns in the designer but all the columns from 2 to 6 are set identically.
My event handler is the following :
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0) // if checkbox
{
int row = e.RowIndex;
bool setState = true;
DataGridViewCheckBoxCell check = (DataGridViewCheckBoxCell)(dataGridView1.Rows[row].Cells[0]);
if (check.Value != null && check.Value == "true")
{
setState = true;
check.Value = "false";// CheckState.Unchecked;
}
else
{
setState = false;
check.Value = "true";// CheckState.Checked;
}
for (int i = 2; i < dataGridView1.Rows[row].Cells.Count; i++)
{
DataGridViewTextBoxCell txt = (DataGridViewTextBoxCell)(dataGridView1.Rows[row].Cells[i]);
txt.ReadOnly = setState;
if (setState) txt.Style.BackColor = Color.Gray;
else txt.Style.BackColor = Color.White;
}
}
}
I have requirement to sort group summary field.
Ex. I have 3 columns in the grid.
Step 1 : I have group by Id by dragging the Id column in Group by area.
Step 2: Add Sum,Count,Average on column.
Now i want to sort sum or count or average by clicking on that ,so that the whole grouped is sorted by sum like 100,200,300.
please help
The sort order is controlled by the GroupByComparer of the FieldSettings class and this can be accomplished by creating a custom IComparer for the field that is grouped. Note that grouping is actually also a sort so I am going to assume that you still want the default sort to happen when the column is first grouped.
In the following example group by records can be sorted by a single summary result when it is clicked on. This was accomplished by using a custom IComparer for the groups that sorts by the value of the tag if it is set and if not set falls back to the value of the group by record:
public class SummarySortComparer : IComparer
{
public int Compare(object x, object y)
{
GroupByRecord xRecord = x as GroupByRecord;
GroupByRecord yRecord = y as GroupByRecord;
IComparable xValue = xRecord.Value as IComparable;
object yValue = yRecord.Value;
if (xRecord.Tag != null)
{
xValue = xRecord.Tag as IComparable;
yValue = yRecord.Tag;
}
return xValue.CompareTo(yValue);
}
}
This is set on the grid using the following:
this.XamDataGrid1.FieldSettings.GroupByComparer = new SummarySortComparer();
Use the PreviewMouseLeftButtonDown of the grid to get the summary that was clicked on if there was one and set the tag of the group by records to be the value of that summary and refresh the sort of the grid:
void XamDataGrid1_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
SummaryResultPresenter summaryResultPresenter =
Utilities.GetAncestorFromType(e.OriginalSource as DependencyObject, typeof (SummaryResultPresenter), false) as
SummaryResultPresenter;
if (summaryResultPresenter != null)
{
GroupBySummariesPresenter groupBySummariesPresenter =
Utilities.GetAncestorFromType(summaryResultPresenter,
typeof(GroupBySummariesPresenter), false) as GroupBySummariesPresenter;
if (groupBySummariesPresenter != null)
{
SummaryResult summaryResult = summaryResultPresenter.SummaryResult;
int summaryResultIndex = summaryResult.ParentCollection.IndexOf(summaryResult);
foreach (GroupByRecord groupRecord in groupBySummariesPresenter.GroupByRecord.ParentCollection)
{
groupRecord.Tag = groupRecord.ChildRecords.SummaryResults[summaryResultIndex].Value;
}
this.XamDataGrid1.Records.RefreshSort();
}
}
}
Note that there are a few limitations in this example in that I haven't implemented any way to clear what summary is sorted so that is something that if desired would still need to be implemented by you. I also didn't include logic to change the sort direction and used the direction that the field is currently sorted by so if you also want to update the direction this will need to be added as well.
I work on windows form with entity framework 5.
I have a datagridview to record new sales and a save button to save changes on my entities.
But before saving the entities, I create some new stock objects. I have another gridview that looks at my stock but which is readonly.
My issues are the followings:
The sales and stock are updated in my database but while the gridview for sales show the new sales, the gridview for stock doesn't update to show the new stock
If there was an error on adding a new sale (say a foreign key error when inserting into the database) then after correcting the error and re-clicking on save, the sale is inserted once but the stock twice (or as many times as I had en error message)
I tried to create my stock through the createobject method rather than just with the new keywork but the problems remain.
Here is my saveButton click code
private void SaveButton_Click(object sender, EventArgs e)
{
try
{
var txnsGr = (IEnumerable<TransactionsGroupee>)this.achatsGroupeeBindingSource.DataSource;
foreach (var txnGr in txnsGr)
if (txnGr.EntityState == EntityState.Detached)
ge.TransactionsGroupees.AddObject(txnGr);
var localTransactions = ge.ObjectStateManager.GetObjectStateEntries(EntityState.Added).Where(ent => ent.Entity is Transaction).Select(ent => ent.Entity as Transaction);
var achats = localTransactions.Where(x => x.TransactionsGroupee.sens == (int)Sens.Achat);
foreach (Transaction txn in achats)
{
for (int i = 0; i < txn.quantite; i++)
{
Stock stock = ge.CreateObject<Stock>();
stock.Agence1 = agence;
stock.Transaction = txn;
ge.Stocks.AddObject(stock);
}
}
ge.SaveChanges();
}
catch (Exception ex)
{
StringBuilder err = new StringBuilder(ex.Message);
if (ex.InnerException != null)
err.AppendLine().Append(ex.InnerException.Message);
MessageBox.Show(err.ToString());
}
}
Note that my sales (from two gridviews in a master/details configuration actually) where somehow detached and I have to attach them back first but hopefully this is not related to the issues described above.
I bound a DataTable to a datagrid in wpf. The first column has unique values. Now I would like to autoselect a cell in the second column whose first column cell has the given value. How should I achieve that? For example,here is my datagrid:
Name | Age
cat | 2
dog | 3
When user input 'dog', I will need the '3' to be selected.
I tried the method show here:
How to select a row or a cell in WPF DataGrid programmatically?
However, I cannot figure out the displaying row number. Even though I know the row number of the dataTable, the display number can be different since I allow users to sort the table.Thanks a lot.
Set your grid's SelectionUnit property to "Cell", and assuming you feed the DataGrid with the table's DefaultView:
private void button1_Click(object sender, RoutedEventArgs e)
{
// Search for the source-row.
var Element = MyDataTable.AsEnumerable()
.FirstOrDefault(x => x.Field<string>("Name") == "horse");
if (Element == null) return;
// Found the row number in the DataGrid
var RowOnGrid = MyGrid.Items.OfType<DataRowView>()
.Select((a, Index) => new { data=a.Row, index = Index })
.Where(x=> x.data == Element)
.Select(x => x.index)
.FirstOrDefault();
// Assuming the desired column is the second one.
MyGrid.SelectedCells.Clear();
MyGrid.SelectedCells.Add(new DataGridCellInfo(MyGrid.Items[RowOnGrid], MyGrid.Columns[1]));
}
It should work even if you re-sort the rows.
In WPF, I can group, but the default is group with ascending. One of my need to to be able to control the sorting of the group (Ascending or descending). For example:
group 1
item 1.1
item 1.2
item 1.3
group 2
item 2.1
item 2.2
and also be able to switch to:
group 2
item 2.1
item 2.2
group 1
item 1.1
item 1.2
item 1.3
//Here is the function to setup a group for a particular column:
private void SetupGrouping(DataGrid parentGrid, DataGridColumn col)
{
if (parentGrid == null || col == null || string.IsNullOrEmpty(col.SortMemberPath))
return;
ICollectionView vw = GetDefaultView();
if (vw != null && vw.CanGroup)
{
if (vw.GroupDescriptions.Count != 0)
{
vw.GroupDescriptions.Clear();
}
PropertyGroupDescription gd = new PropertyGroupDescription(col.SortMemberPath);
// Check to see if the column is Priority, if it is
// then do the grouping with high priority (3) on top.
// The order should be High(3), Normal (2), Low(1)
DataGridColumn priCol = GetColumnByID(ColumnFlags.Priority);
if(col == priCol)
{
// Attempted to change the direction of the sort added by adding group.
// However, it has error complaining SortDescription is sealed
// and can't be changed.
//if (vw.SortDescriptions != null && vw.SortDescriptions.Count > 0)
//{
// SortDescription sd = vw.SortDescriptions[0];
// if (sd.PropertyName == col.SortMemberPath)
// {
// sd.Direction = ListSortDirection.Descending;
// }
//}
}
// Info: when we add a new GroupDescription to GroupDescriptions list,
// guest what? a new SortDescription is also added to the
// SortDescriptions list.
vw.GroupDescriptions.Add(gd);
}
// Save off the column for later use
GroupedColumn = col;
// Set the DataGrid's Tag so that the GroupSyle can get the column name
parentGrid.Tag = DispatchAttachedProperties.GetColumnHeader(col);
}
You had the right idea. Add a SortDescription to the ICollectionView based on the same property you are grouping on. If you want to change sort directions, you have to clear the existing one and add a new for the opposite direction. You can't change it once its created as you discovered.