How to change DataGridCell value programatically - wpf

I have a DataGrid with some rows.Now I want change some cell style, eg:
|red| Hel |/red| |blue| lo |/blue| Everybody.
Here is my GetCell function.
public static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
var v = (Visual) VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
public static DataGridRow GetSelectedRow(this DataGrid grid)
{
return (DataGridRow) grid.ItemContainerGenerator.ContainerFromItem(grid.SelectedItem);
}
public static DataGridRow GetRow(this DataGrid grid, int index)
{
var row = (DataGridRow) grid.ItemContainerGenerator.ContainerFromIndex(index);
if (row != null) return row;
// May be virtualized, bring into view and try again.
grid.UpdateLayout();
grid.ScrollIntoView(grid.Items[index]);
row = (DataGridRow) grid.ItemContainerGenerator.ContainerFromIndex(index);
return row;
}
public static DataGridCell GetCell(this DataGrid grid, DataGridRow row, int column)
{
if (row == null) return null;
var presenter = GetVisualChild<DataGridCellsPresenter>(row);
if (presenter == null)
{
grid.ScrollIntoView(row, grid.Columns[column]);
presenter = GetVisualChild<DataGridCellsPresenter>(row);
}
var cell = (DataGridCell) presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
grid.ScrollIntoView(row, grid.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
public static DataGridCell GetCell(this DataGrid grid, int row, int column)
{
DataGridRow rowContainer = grid.GetRow(row);
return grid.GetCell(rowContainer, column);
}
When I set DataGridCell content with some value by grid.GetCell(1, 0).Content = "PpppPppp", and then scroll DataGrid, the value is gone!
Why?
Even the value jumps to another cell.So strange.
I want to set the cell with style, so not use the funtion about:
(this.dataGrid1.Items[0] as DataRowView)[0] = "new value";
But:
private void SetBlockStyle(TextBlock block, Dictionary<int, int> section)
{
Brush foreground = Brushes.Red;
int start = 0;
string text = block.Text;
block.Text = "";
foreach (int i in section.Keys)
{
if (text.Length <= i)
{
break;
}
block.Inlines.Add(text.Substring(start, i - start));
start = i;
block.Inlines.Add(new Run(text.Substring(i, section[i]))
{
Foreground = foreground,
FontWeight = FontWeights.Bold
});
start += section[i];
}
if (start < text.Length)
{
block.Inlines.Add(text.Substring(start, text.Length - start));
}
}
Can you give me some advice?

Related

Unblock row editing on WPF DataGrid

I have validation in DataGrid. I am copying and trying to paste multiple rows. But if there is a validation error in one of the rows on insertion, the validation will will be called in and disable editing of the other rows, therefore next rows will not be inserted.
My ValidationRule:
public class MyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
MyModel myModel = (value as BindingGroup).Items[0] as MyModel ;
if (myModel.Top > myModel.Bottom)
{
return new ValidationResult(false, "Error!");
}
return new ValidationResult(true,null);
}
}
XAML:
<DataGrid.RowValidationRules>
<MyValidationRule ValidationStep="UpdatedValue"/>
<DataGrid.RowValidationRules>
The paste code:
protected virtual void OnExecutedPaste(object target, ExecutedRoutedEventArgs args)
{
if (ExecutePasteEvent != null)
{
ExecutePasteEvent(target, args);
if (args.Handled)
{
return;
}
}
List<string[]> clipboardData = Clipboard
Helper2.ParseClipboardData();
int minRowIndex = Items.Count;
int maxRowIndex = Items.Count;
int minColumn = 0;
if (SelectedCells.Any())
{
minRowIndex = SelectedCells.Min(g=> Items.IndexOf(g.Item));
maxRowIndex = Items.Count;
minColumn = SelectedCells.Min(g=> g.Column.DisplayIndex);
}
int minColumnDisplayIndex = (SelectionUnit != DataGridSelectionUnit.FullRow) ? minColumn : 0;
int maxColumnDisplayIndex = Columns.Count - 1;
int rowDataIndex = 0;
List<Tuple<int, int>> cellsToSelect = new List<Tuple<int, int>>();
for (int i = minRowIndex; i <= maxRowIndex && rowDataIndex < clipboardData.Count; i++, rowDataIndex++)
{
if (i == maxRowIndex)
{
if (!CanUserPasteToNewRows)
continue;
ICollectionView cv = CollectionViewSource.GetDefaultView(Items);
IEditableCollectionView iecv = cv as IEditableCollectionView;
if (iecv != null)
{
if (minRowIndex + clipboardData.Count > Items.Count)
{
iecv.AddNew();
if (rowDataIndex < clipboardData.Count)
{
maxRowIndex = Items.Count;
}
}
}
}
if (i < Items.Count)
{
CurrentItem = Items[i];
BeginEditCommand.Execute(null, this);
int clipboardColumnIndex = 0;
for (int j = minColumnDisplayIndex; clipboardColumnIndex < clipboardData[rowDataIndex].Length; j++, clipboardColumnIndex++)
{
DataGridColumn column = null;
foreach (DataGridColumn columnIter in this.Columns)
{
if (columnIter.DisplayIndex == j)
{
column = columnIter;
break;
}
}
column?.OnPastingCellClipboardContent(Items[i], clipboardData[rowDataIndex][clipboardColumnIndex]);
cellsToSelect.Add(new Tuple<int, int>(i, j));
}
CommitEditCommand.Execute(this, this);
}
}
UnselectAll();
UnselectAllCells();
if (Items.Count < 1)
return;
CurrentItem = Items[minRowIndex];
if (SelectionUnit == DataGridSelectionUnit.FullRow)
{
SelectedItem = Items[minRowIndex];
}
else if (SelectionUnit == DataGridSelectionUnit.CellOrRowHeader || SelectionUnit == DataGridSelectionUnit.Cell)
{
foreach(var c in cellsToSelect)
{
if(Columns.Count < c.Item1 && Columns.Count < c.Item2)
SelectedCells.Add(new DataGridCellInfo(Items[c.Item1], Columns[c.Item2]));
}
}
}
And ClipboardHelper.ParseClipboardData(); will return a list of strings for example: {"row1", "value1", "value2"},{"row2", "value1", "value2"}.
How to paste all copied rows?
And I need to allow editing of all rows with errors, and not the last one.

Why doesn't start MeasureOverride?

I have two custom controls for displaying collections of comments and signatures. Each of these controls has a button for changing display style - show entire collection or just first 4 elements.
When I put these controls in a default WrapPanel - they work fine: expand or collapse depending on user's actions.
In my own custom WrapPanel with new ArrangeOverride function they also work fine.
Now I need WrapPanel with option "LastChildFill" (I found example here) and with custom "WrapDirection" (similar to FlowDirection). First or last element in WrapPanel have to fill all remaining space while it's more than MinWidth of the element.
So I've created third WrapPanel with custom MeasureOverride and ArrangeOverride. And it works as I want, but when I change control display style a collection inside a control changes, but MeasureOverride in my WrapPanel doesn't start until I change window size.
Update: MeasureOverride doesn't start for expanding first or last element which fills remaining space. Collapsing for this element works fine.
I don't understand what thing I have broken with custom MeasureOverride?
Update 2:
I fugured that my problem is similar to this: MeasureOverride not always called on children's property changes.
And attach MeasureOverride code.
public class WrapPanelFlowDirection : WrapPanel
{
public bool LastChildFill
{
get { return (bool)GetValue(LastChildFillProperty); }
set { SetValue(LastChildFillProperty, value); }
}
public static readonly DependencyProperty LastChildFillProperty =
DependencyProperty.Register("LastChildFill", typeof(bool), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsMeasure));
public WrapDirection WrapDirection
{
get { return (WrapDirection)GetValue(WrapDirectionProperty); }
set { SetValue(WrapDirectionProperty, value); }
}
public static readonly DependencyProperty WrapDirectionProperty =
DependencyProperty.Register("WrapDirection", typeof(WrapDirection), typeof(WrapPanelFlowDirection), new FrameworkPropertyMetadata(WrapDirection.LeftToRight, FrameworkPropertyMetadataOptions.AffectsMeasure));
#region Measure Override
protected override Size MeasureOverride(Size availableSize)
{
var panelSize = new Size(availableSize.Width, 0);
double minWidth = 0;
foreach (FrameworkElement child in this.InternalChildren)
{
child.Measure(availableSize);
minWidth += child.DesiredSize.Width;
}
int firstChildInLine = 0;
double currLineHeight = 0;
double currLineWidth = 0;
UIElementCollection children = this.InternalChildren;
for (int i = 0; i < children.Count; i++)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (currLineWidth + w <= availableSize.Width)
{
currLineWidth += w;
currLineHeight = Math.Max(currLineHeight, child.DesiredSize.Height);
}
else
{
MeasureOneLine(firstChildInLine, i - 1, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
currLineWidth = w;
currLineHeight = child.DesiredSize.Height;
if (w > availableSize.Width)
{
MeasureOneLine(i, i, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
currLineHeight = currLineWidth = 0;
firstChildInLine = i + 1;
}
else
{
firstChildInLine = i;
}
}
}
if (firstChildInLine < children.Count)
{
MeasureOneLine(firstChildInLine, children.Count - 1, ref currLineHeight, availableSize.Width);
panelSize.Height += currLineHeight;
}
if (double.IsInfinity(panelSize.Width))
panelSize.Width = minWidth;
return panelSize;
}
private void MeasureOneLine(int start, int end, ref double height, double totalWidth)
{
UIElementCollection children = this.InternalChildren;
if (WrapDirection == WrapDirection.LeftToRight)
{
for (int i = start; i < end; i++)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (i < end)
{
child.Measure(new Size(w, height));
totalWidth -= w;
}
else
{
child.Measure(new Size(totalWidth, double.PositiveInfinity));
height = Math.Max(height, child.DesiredSize.Height);
}
}
}
else
{
for (int i = end; i >= start; i--)
{
FrameworkElement child = children[i] as FrameworkElement;
if (child == null)
continue;
double w = 0;
if (child.MinWidth == 0)
w = child.DesiredSize.Width;
else
w = child.MinWidth;
if (i > start)
{
child.Measure(new Size(w, height));
totalWidth -= w;
}
else
{
child.Measure(new Size(totalWidth, double.PositiveInfinity));
height = Math.Max(height, child.DesiredSize.Height);
}
}
}
}
#endregion
I found solution here : How does a container know when a child has called InvalidateArrange?
And it worked for me.
public static void OnPropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
UIElement panel= VisualTreeHelper.GetParent(source) as UIElement;
if(panel != null)
{
panel.InvalidateMeasure();
panel.InvalidateArrange();
}
}

DataGridComboboxColumn - Get cell value

The task is the following: there is a DataGrid with ComboboxColumns. If user changes cell[2,3] and selected value!=0 then disable cell[3,2]. I wrote the following Handler:
private void grAssessment_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
int x = e.Column.DisplayIndex;
int y = e.Row.GetIndex();
grAssessment.GetCell(x, y).IsEnabled = false;
grAssessment.GetCell(x, y).Background = Brushes.LightGray;
}
But it disables appropriate cell in anyway. How to add selected value!=0 condition to this code?
Thanks in advance.
Try to check this out:
1. DataGridComboBoxColumn ItemsSource (for current version only, use your own):
private ObservableCollection<ScoreData> _scores = new ObservableCollection<ScoreData>(
new List<ScoreData>
{
new ScoreData{Score = 1, ScoreVerbal = "the same"},
new ScoreData{Score = 3, ScoreVerbal = "moderate superiority"},
new ScoreData{Score = 5, ScoreVerbal = "strong superiority"},
new ScoreData{Score = 7, ScoreVerbal = "the samvery strong superioritye"},
new ScoreData{Score = 9, ScoreVerbal = "extremely superiority"},
} );
2. Handler code (the handler defined on the data grid):
private void SelectDataGrid_OnCellEditEnding(object sender,
DataGridCellEditEndingEventArgs e)
{
var editingElement = e.EditingElement as Selector;
if(editingElement == null) return;
var selectedData = editingElement.SelectedItem as ScoreData;
if(selectedData == null || selectedData.Score > 1) return;
var dataGridCell = editingElement.GetVisualParentOfType<DataGridCell>();
if(dataGridCell == null) return;
dataGridCell.IsEnabled = false;
}
3. GetVisualParentOfType code (as part of extension class):
public static class VisualTreeExtensions
{
public static T GetVisualParentOfType<T>(this DependencyObject child)
where T : DependencyObject
{
if (child is T)
return child as T;
var parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
var parent = parentObject as T;
return parent ?? GetVisualParentOfType<T>(parentObject);
}
public static T GetChildOfType<T>(this DependencyObject depObj)
where T : DependencyObject
{
if (depObj == null) return null;
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
var child = VisualTreeHelper.GetChild(depObj, i);
var result = (child as T) ?? GetChildOfType<T>(child);
if (result != null) return result;
}
return null;
}
}
regards,
I found the following solution
private void grAssessment_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
int x = e.Column.DisplayIndex;
int y = e.Row.GetIndex();
DataGridCell cell = grAssessment.GetCell(y, x);
if (((System.Windows.Controls.ComboBox)(cell.Content)).Text != "")
{
grAssessment.GetCell(x, y).IsEnabled = false;
grAssessment.GetCell(x, y).Background = Brushes.LightGray;
}
else
{
grAssessment.GetCell(x, y).IsEnabled = true;
grAssessment.GetCell(x, y).Background = Brushes.White;
}
}

How to get count of only visible colums in a WPF datagrid

How can I get count of only visible columns in a WPF datagrid. I am using the count to navigate through the colums present in the datagrid.
if (getSelectedRow().IsEditing)
{
if (dataGrid.SelectedCells.Count != 0)
{
int columnDisplayIndex = dataGrid.CurrentCell.Column.DisplayIndex + 1;
if (columnDisplayIndex <= dataGrid.Columns.Count)
{
if (columnDisplayIndex == dataGrid.Columns.Count)
{
row.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
nextColumn = dataGrid.ColumnFromDisplayIndex(nextCol(1) - 1);
dataGrid.CurrentCell = new DataGridCellInfo(dataGrid.SelectedItem, nextColumn);
}
else
{
e.Handled = true;
nextColumn = dataGrid.ColumnFromDisplayIndex(nextCol(columnDisplayIndex));
dataGrid.CurrentCell = new DataGridCellInfo(dataGrid.SelectedItem, nextColumn);
}
}
}
if (isNotComboBox())
{
dataGrid.CommitEdit();
}
}

BeginEdit of a specific Cell from code behind

Is possibile begin edit of a specific cell from code behind with DataGrid control (WPF Toolkit)?
I have to enable the celledittemplate of first cell of selected row after a button action...how can I do?
pls, try putting the code below in your button's on click event handler:
DataGridCell cell = GetCell(1, 0);
if (cell != null)
{
cell.Focus();
yourDataGrid.BeginEdit();
}
below is implementation for the GetCell method taken from here Grabing controls from a DataGrid
public DataGridCell GetCell(int row, int column)
{
DataGridRow rowContainer = GetRow(row);
if (rowContainer != null)
{
DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(rowContainer);
// try to get the cell but it may possibly be virtualized
DataGridCell cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
// now try to bring into view and retreive the cell
gridPersons.ScrollIntoView(rowContainer, gridPersons.Columns[column]);
cell = (DataGridCell)presenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
return null;
}
public DataGridRow GetRow(int index)
{
DataGridRow row = (DataGridRow)gridPersons.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
// may be virtualized, bring into view and try again
gridPersons.ScrollIntoView(gridPersons.Items[index]);
row = (DataGridRow)gridPersons.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
static T GetVisualChild<T>(Visual parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
hope this helps, regards

Resources