Disable row based on cell value - wpf

Im trying to disable a RadGridView row based on a value in that row?
I have tried the below code in the RowLoaded event but it seems to disable all rows.
var isEnrolled = (from c in masterList
where c.StudentId == StudentID
where c.Rolls[0].RollId == item.RollId.ToString()
select c.IsEnrolled).FirstOrDefault();
if (isEnrolled)
e.Row.IsEnabled = false;
Can it be done in same event, so if a row contains the word "Confirmed" the row is disabled?
foreach(DataGridRow item in RollListGrid.Items)
{
//Disable row if it contains "Confirmed"
}
And one more, how can i check a checkbox in a row if the query returns true?
var isProspectiveStudent = (from c in masterList
where c.StudentId == StudentID
where c.Rolls[0].RollId == item.RollId.ToString()
select c.IsProspectiveStudent).FirstOrDefault();
if (isProspectiveStudent){
//Check the relevant checkbox
}
Thanks!!

I managed to achieve what I wanted to with the below code.
foreach (var x in e.Row.Cells)
{
if (((GridViewCell)x).Value != null && ((GridViewCell)x).Value.ToString() == "Confirmed" && x.Column.UniqueName.IndexOf("5") != -1)
{
e.Row.IsEnabled = false;
break;
}
}

Related

XAMDATAGRID: Enable first row first cell in XAML or by Code

I am working with XamDataGrid where in i have to disable the Row:0 Cell:1 enabled all the times.
to achieve this i Set AllowEdit for the field to false and then dynamically onClick of the cell for selection: i am trying to get rowindex and cell index and see if they are for first row i set enable. But that enables entire column all rows which i dont want.
DataRecord selectedRecord = (DataRecord)frameSpacingDataGrid.ActiveRecord;
Cell selectedCell = frameSpacingDataGrid.ActiveCell;
CellCollection cellCollection = selectedRecord.Cells;
int indxSelectedCell = cellCollection.IndexOf(selectedCell);
var rowIndex = selectedRecord.Index;
if (rowIndex == 0 && indxSelectedCell == 0)
{
selectedCell.DataPresenter.IsEnabled= true;
}
Above code is written in SelectedItemsChanged event.
Also, when user deletes rows: new row:0 and cell:0 should be editable.
How do i achieve this Via Triggers/XAML/ Codebehind.
I have found a work around as stated here:
What i did is to set cancel for the edit event.
But surely this is not a proper solution.

wpf render gui async

i got an ItemsControl with an Expander in it and in the Expander are a view ProgressBars inclued. My problem is when i am loading the data (which is not my performance problem) and then i update PropertyChanged of my ItemSource my gui freezes for a long time cuz it needs so long to render.
Is there a way i can redner the gui elements async so that my gui doesnt freeze???
I already searched a bit, though im not sure if my search results solve my problem.
So i am asking here, hoping for a nice solution.
They gui does look something like this.. though there are usually more elements
you all can imageine the xaml code behind...
private void RefreshOverview(){
...
foreach (Characteristic c in characteristics)
{
Area a = c.Area;
Characteristic c1 = c;
foreach (Line l in lines.Where(l => l.Product.Id == c1.Product.Id))
{
List<IMeasurementSchedule> measurementSchedules;
// take DefaultMeasurementSchedules if exists
if (c.DefaultMeasurementSchedules == null || c.DefaultMeasurementSchedules.Count == 0)
measurementSchedules = new List<IMeasurementSchedule>(l.LineMeasurementSchedules.ToArray());
else
measurementSchedules = new List<IMeasurementSchedule>(c.DefaultMeasurementSchedules.ToArray());
foreach (IMeasurementSchedule ms in measurementSchedules)
{
MeasureCharacteristic mc;
if (a.PeripheryEnabled)
{
Line l1 = l;
foreach (AreaItem ai in areaitems.Where(x => x.AreaId == a.Id && x.LineId == l1.Id))
{
mc = (from cm in _context.CharacteristicMeasures.Local
where cm.Charge == null &&
cm.Characteristic.Id == c.Id &&
cm.Line.Id == l.Id &&
cm.ShiftIndex.Id == actualShiftIndex.Id &&
cm.AreaItem != null &&
cm.AreaItem.Id == ai.Id &&
cm.MeasureScheduleId == ms.Id
select cm).FirstOrDefault() ??
new MeasureCharacteristic
{
Characteristic = c,
Line = l,
ShiftIndex = actualShiftIndex,
AreaItem = ai
};
mc.MeasureSchedule = ms;
characteristicsMeasures.Add(AddMeasures(mc));
}
}
else
{
mc = (from cm in _context.CharacteristicMeasures.Local
where cm.Charge == null &&
cm.Characteristic.Id == c.Id &&
cm.Line.Id == l.Id &&
cm.ShiftIndex.Id == actualShiftIndex.Id &&
cm.MeasureScheduleId == ms.Id
select cm).FirstOrDefault() ??
new MeasureCharacteristic {Characteristic = c, Line = l, ShiftIndex = actualShiftIndex};
mc.MeasureSchedule = ms;
characteristicsMeasures.Add(AddMeasures(mc));
}
}
}
}
MeasureCharacteristics = characteristicsMeasures;
MeasureCharacteristicsByType =
CharacteristicMeasureGroupedByType.GetExpanderViewProductItems(characteristicsMeasures);
}
thats my code ;) MeasureCharacteristicsByType is a IEnumerable<CharacteristicMeasureGroupedByType> i do bind my itemsource to. if you need more information just ask!!!
UPDATE
here is my link to my xaml code..
http://pastebin.com/UA777LjW
You are mixing logic to build your data and rendering operations.
Isolate the one from the other to identify which is taking time.
For example don't affect MeasureCharacteristicsByType in the same for loop as the linq queries.
Then measure times with a StopWatch instance.
If the rendering takes the most time, insert the items in the MeasureCharacteristicsByType one by one (not at the same time), with this kind of instruction to render them one after the other :
foreach(var charMeasureByType in CharacteristicMeasureGroupedByType.GetExpanderViewProductItems(characteristicsMeasures))
{
Dispatcher.BeginInvoke(new Action<OneTypeHere>((OneTypeHere item) =>
{
MeasureCharacteristicsByType.Add(item)
}), DispatcherPriority.Background, charMeasureByType);
}
Edit : OneTypeHere is the type of charMeasureByType.

Dynamically setting DataGridViewComboBoxCell's DataSource to filtered DataView based off of other cell selection

I have been looking high and low for a way to do the following, but to no avail. I came up with a solution that works, but am wondering if there is a better way of handling it.
The Problem:
I am using a DataGridView that has two DataGridViewComboBoxColumn, col1 and col2.
col1 has had its DataSource set to a DataTable. Based off of the selection from a given cell in col1, I would like to have the respective col2 cell in the same row have its DataSource set to be a filtered DataView of col2's DataSource.
The abbreviated code from my current implementation might better help describe what I am trying to do:
DataGridView dg = new DataGridView();
dg.DataSource = new BindingSource() { DataSource = _myDataTable }; //DataTable with Foreign Keys for ID1 and ID2
dg.CellValueChanged += new DataGridViewCellEventHandler(dg_CellValueChanged);
DataGridViewComboBoxColumn col1 = new DataGridViewComboBoxColumn();
col1.DataPropertyName = "ID1";
col1.DisplayMember = "Display1";
col1.ValueMember = "ID1";
col1.DataSource = dataTable1;
col1.ValueType = typeof(Int32);
DataGridViewComboBoxColumn col2 = new DataGridViewComboBoxColumn();
col2.DataPropertyName = "ID2";
col2.DisplayMember = "Display2";
col2.ValueMember = "ID2";
col2.DataSource = dataTable2;
col2.ValueType = typeof(Int32);
dg.Columns.Add(col1);
dg.Columns.Add(col2);
Then I define the event handler as:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
if (dgv == null)
return;
int selectedID;
if (!int.TryParse(dgv[e.ColumnIndex, e.RowIndex].Value.ToString(), out selectedID))
return;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
var col = dgv.Columns[e.ColumnIndex + 1] as DataGridViewComboBoxColumn;
if(col == null)
return;
var dt = col.DataSource as DataTable; // The macro-DataTable containing all possible values
if(dt == null)
return;
DataView dv = new DataView(dt, "ID1 = " + selectedID, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
//This is the part that I am wondering if there is a better way of handling
cell.DataSource = dt; //Set the data source to the macro-DataTable so that when we set the Value to something in the new DataView it will not throw an exception
cell.DisplayMember = "Display2"; // Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.Value = dv[0]["ID2"]; // Set the value to the first option in the new DataView to avoid an exception being thrown when setting the dv as the DataSource
cell.DataSource = dv;
}
}
The last part is my concern.
When dynamically switching the DataSource of cell2, if cell2's current selection doesn't appear in the new DataView, then an exception will be thrown:
System.ArgumentException: DataGridViewComboBoxCell value is not valid.
To avoid that, I am setting the datasource to the macro-DataTable (containing all of the values that could be shown based off of cell1's selection), changing the selected value of cell2 to be the first result in the DataView and then setting cell2's DataSource to be said DataView. This all ensures that the cell is never going to have an invalid selection and it works as expected.
My question is, is there a better/simpler way of doing this? For my use, this code is only activated when creating new rows, so it isn't being done more than a few times in the given form. However, if there is a better way of doing it or some suggestions on making it better, I would appreciate it!
(first question here, so I am also open to suggestions for posting ... apologies for any missteps)
EDIT (providing table structure - also changed "BoundID" to be "ID1" to avoid confusion):
DataGrid's table structure would be:
MainTableID int
ID1 int --This is a foreign key to col1
ID2 int --This is a foreign key to col2
Column 1's table structure:
ID1 int
Display1 varchar(50)
Column 2's table structure:
ID2 int
Display2 varchar(50)
ID1 int --This is a foreign key to col1
Updated:
I have redefined the CellValueChanged event handler per Mohsen's suggestions:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
DataView dv = new DataView( ((DataTable)((DataGridViewComboBoxColumn)dgv.Columns[e.ColumnIndex + 1]).DataSource), "ID1 = " + dgv.CurrentCell.Value, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
cell.DisplayMember = "Display2"; // Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.DataSource = dv;
}
}
And I have added in an event handler for the DataError like he suggested:
void dg_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
if(e.ColumnIndex != 1)
{
//Alert the user for any other DataError's outside of the column I care about
MessageBox.Show("The following exception was encountered: " + e.Exception);
}
}
This seems to work perfectly.
Your code can be simplified to this with no exception throwing:
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
cell.DataSource = ((DataTable)((DataGridViewComboBoxColumn)dgv.Columns[e.ColumnIndex + 1]).DataSource).Select("BoundID = " + dgv.CurrentCell.Value);
}
Updated
When you change an already set item in first combo box since the filtered dataview in the second combobox have different ID1, an exception is thrown with "DataGridViewComboBoxCell value is not valid.". In order to catch this exception register datagridview DataError event with no code in the registered method. Then when you change the already set combbox, the corresponding combobox will be filled with correct items.
This is the quick stop shop for the solution that worked for me, but I still wanted to give Mohsen the credit. Duplicated this in the original question.
I have redefined the CellValueChanged event handler per Mohsen's suggestions:
private void dg_CellValueChanged(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex > -1)
{
var dgv = sender as DataGridView;
var cell = dgv[e.ColumnIndex + 1, e.RowIndex] as DataGridViewComboBoxCell;
if (cell == null)
return;
DataView dv = new DataView( ((DataTable)((DataGridViewComboBoxColumn)dgv.Columns[e.ColumnIndex + 1]).DataSource), "ID1 = " + dgv.CurrentCell.Value, "DisplayOrder", DataViewRowState.CurrentRows);
if(dv.Count == 0)
return;
cell.DisplayMember = "Display2"; // Have to redefine the Display/Value members
cell.ValueMember = "ID2";
cell.DataSource = dv;
}
}
And I have added in an event handler for the DataError like he suggested:
void dg_DataError(object sender, DataGridViewDataErrorEventArgs e)
{
if(e.ColumnIndex != 1)
{
//Alert the user for any other DataError's outside of the column I care about
MessageBox.Show("The following exception was encountered: " + e.Exception);
}
}
This seems to work perfectly.

Is it possible to add CheckBoxcolumn only at certain rows in a datagridview

I will have my data grid view as follows
RecordTypeCode Content
FileHeader 1111111111111
BatchHeader 5666666666666
EntryDetail 656546545644545
BatchControl 8654654564564
FileControl 945645
I would like to have check boxes only at BatchHeader and EntryDetail is it possible. This is the way i am binding data to data grid view
if (line.StartsWith("1"))
{
dcID = new DataColumn("RecordTypeCode");
dt.Columns.Add(dcID);
DataColumn dcSomeText = new DataColumn("Content");
dt.Columns.Add(dcSomeText);
dr = dt.NewRow();
dr["RecordTypeCode"] = filecontrolvariables.rectype[line.Substring(0, 1)].ToString();
dr["Content"] = line;
dt.Rows.Add(dr);
}
if (line.StartsWith("5"))
{
dr = dt.NewRow();
dr = dt.NewRow();
dr["RecordTypeCode"] = filecontrolvariables.rectype[line.Substring(0, 1)].ToString();
dr["Content"] = line;
dt.Rows.Add(dr);
}
I would like to add check boxes as per needed can any one help me
I tried this
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
{
if (dataGridView2.Rows[e.RowIndex].Cells["RecordTypeCode"].Value.ToString() == "BatchHeader")
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
}
Sample image when my data binded
I tried this
int columnIndex = -1;
int rowIndex = -1;
if (dataGridView2.CurrentCell != null)
{
columnIndex = dataGridView2.CurrentCell.ColumnIndex;
rowIndex = dataGridView2.CurrentCell.RowIndex;
}
if (columnIndex == 0)
{
if (e.RowIndex >= 0)
{
if (dataGridView2.Rows[e.RowIndex].Cells["RecordTypeCode"].Value.ToString() == "Batch Header")
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
}
}
But my corresponding row is showing empty when i check it only for BatchHeader
You need to use the cellpainting event to not display the checkbox at all. I'm not sure if this would have other consquences during data binding but it did not display the checkbox for the rows that I wanted.
I have updated the method for what you would need.
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.RowIndex >= 0 && e.ColumnIndex >= 0)
{
//if (e.ColumnIndex == <index of your checkbox column> && (e.RowIndex == 1 || e.RowIndex == 2))
if (dataGridView2.Rows[e.RowIndex].Cells["RecordTypeCode"].Value.ToString() == "Batch Header" && e.ColumnIndex == <index of your checkbox column should be 0 sicne its the first column>)
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
}
}
The condition for e.RowIndex == 1 || e.RowIndex == 2 is based on the fact that batchheader and entrydetail wont change their position. But if that is dynamic, then you can just check the text in the recordtype column to match BatchHeader and EntryDetail.
The if statement is the most important, it checks which particular cell (row, column) is the one you dont want any checkbox in. They must be used with an AND (&&) as you want one particular cell. You can use the code I have written as is if what you have explained is what you need. You will probably need to add one more check in the if statement for entry detail which needs to be done with an OR (||) with the batch header check. Exactly how I have in the commented part of the code, taking notice to the brackets especially.
I saw the solution at this link which worked when I tried it. There are others mentioned incase this isn't suitable for what you want.

grouping with order?

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.

Resources