I found a problem with binding while making a simple color picker. After binding each cell to the indexer in Datagrid, if you click a specific cell 2 times in a row, the binding is removed. The images below are shown the state of Textblock's Visibility. The initial visibility is 'collapsed', but after the second click the visibility is changed to 'visible' as the binding is removed. It might be related to the selection of the row or cell, but I cannot find any clues.
Initial view.
After the first click the cell 0,0.
After the second click the cell 0,0.
The binding for the cell is removed after the second click.
<UserControl
x:Class="PracticeCode.TestColorPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PracticeCode"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Loaded="UserControl_Loaded">
<UserControl.Resources>
<DataTemplate x:Key="ColorCell">
<Grid Background="Red">
<Rectangle Panel.ZIndex="1" Width="30" Height="30"></Rectangle>
<TextBlock Panel.ZIndex="2" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="Black" FontWeight="Bold">v</TextBlock>
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<DataGrid
Name="dgColorPicker"
HeadersVisibility="None"
GridLinesVisibility="None"
AutoGenerateColumns="False">
</DataGrid>
</Grid>
</UserControl>
public partial class TestColorPicker : UserControl
{
public TestColorPicker()
{
InitializeComponent();
}
public List<ColorInfo> ColorInfoList { get; set; }
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
ColorInfoList = new List<ColorInfo>()
{
new ColorInfo(0),
new ColorInfo(1),
};
dgColorPicker.ItemsSource = ColorInfoList;
int colCnt = ColorInfoList.Max(info => info.ColCount);
var rowCnt = dgColorPicker.Items.Count;
for (int colIndex = 0; colIndex < colCnt; colIndex++)
{
DataGridTemplateColumn templateColumn = new DataGridTemplateColumn();
templateColumn.CellTemplate = (DataTemplate)Resources["ColorCell"];
dgColorPicker.Columns.Add(templateColumn);
for (int rowIndex = 0; rowIndex < rowCnt; rowIndex++)
{
var dgCell = UIHelper.GetDataGridCell(dgColorPicker, rowIndex, colIndex);
var textblock = UIHelper.GetVisualChild<TextBlock>(dgCell);
//
// Binding
//
Binding binding = new Binding();
binding.Path = new PropertyPath(string.Format("ColorCheckes[{0}]", colIndex));
binding.Source = ColorInfoList[rowIndex];
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(textblock, TextBlock.VisibilityProperty, binding);
//
// Events
//
dgCell.PreviewMouseLeftButtonDown += DgCell_MouseLeftButtonDown;
}
}
dgColorPicker.UpdateLayout();
}
private void DgCell_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// --------------------------------------------
// Check if the binding is removed.
// --------------------------------------------
var textblock = UIHelper.GetVisualChild<TextBlock>(UIHelper.GetDataGridCell(dgColorPicker, 0, 0));
BindingExpression be = BindingOperations.GetBindingExpression(textblock, TextBlock.VisibilityProperty);
Console.WriteLine(be == null ? "Binding is removed" : "Alive");
}
}
public class ColorInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); }
}
public Dictionary<int, Visibility> Visibilities = new Dictionary<int, Visibility>();
public int ColCount => Visibilities.Count();
[IndexerName("ColorCheckes")]
public Visibility this[int colIndex]
{
get
{
return Visibilities.ContainsKey(colIndex) ? Visibilities[colIndex] : Visibility.Collapsed;
}
set
{
if (Visibilities.ContainsKey(colIndex)) { Visibilities[colIndex] = value; }
else { Visibilities.Add(colIndex, value); }
NotifyPropertyChanged();
}
}
public ColorInfo(int rowIndex)
{
this[0] = Visibility.Collapsed;
this[1] = Visibility.Collapsed;
}
}
public static class UIHelper
{
/// <summary>
///
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <returns></returns>
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++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (v == null) { continue; }
else if (child == null) { child = GetVisualChild<T>(v); }
else if (child != null) { break; }
}
return child;
}
/// <summary>
/// https://www.aspforums.net/Threads/517789/Get-Cell-Value-of-DataGrid-using-Loop-in-WPF/
/// </summary>
/// <param name="row"></param>
/// <param name="column"></param>
/// <returns></returns>
public static DataGridCell GetDataGridCell(DataGrid dg, int row, int column)
{
DataGridRow rowData = GetRow(dg, row);
if (rowData != null)
{
DataGridCellsPresenter cellPresenter = GetVisualChild<DataGridCellsPresenter>(rowData);
DataGridCell cell = (DataGridCell)cellPresenter.ItemContainerGenerator.ContainerFromIndex(column);
if (cell == null)
{
dg.ScrollIntoView(rowData, dg.Columns[column]);
cell = (DataGridCell)cellPresenter.ItemContainerGenerator.ContainerFromIndex(column);
}
return cell;
}
return null;
}
public static DataGridRow GetRow(DataGrid dg, int index)
{
DataGridRow row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
if (row == null)
{
dg.UpdateLayout();
dg.ScrollIntoView(dg.Items[index]);
row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromIndex(index);
}
return row;
}
}