DataGridColumns DisplayIndex 绑定和 DataContext 更改
DataGridColumns DisplayIndex Binding and DataContext change
我正在将 DataGridCoulmun 的 DisplayIndex 绑定到我的 ViewModel。由于 DataGridColumns 不属于 DataGrid 的可视树或 lgical 树,因此我不得不采取一些技巧来实现该绑定,但它确实有效。
我的问题是:当 DataContext 更改时(如果我们有更多 ViewModel),DataGridColumns 应该获得新的 DisplayIndexes。不幸的是,行为很奇怪,更改后,列顺序或多或少是随机的。
您是否知道如何处理此问题或至少是什么原因?
这是一个例子:
在初始化数据网格之前,我将 DataContext 设置为 ViewModel 的新实例,它可以正常工作。之后我重新排序列并且它仍然有效并且更改正确传播到 ViewModel。最后我单击按钮,将 DataContext 设置为 ViewModel 的新实例,因此单击后的列应按开头排序。
这是一个XAML代码:
<Window x:Class="TestDataGridBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataGridBinding"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn
Header="A"
Binding="{Binding A}"
DisplayIndex="{Binding Path=Data.ADisplayIndex, FallbackValue=0, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="B"
Binding="{Binding B}"
DisplayIndex="{Binding Path=Data.BDisplayIndex, FallbackValue=1, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="C"
Binding="{Binding C}"
DisplayIndex="{Binding Path=Data.CDisplayIndex, FallbackValue=2, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="D"
Binding="{Binding D}"
DisplayIndex="{Binding Path=Data.DDisplayIndex, FallbackValue=3, Mode=TwoWay, Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
<Button Click="Button_Click" Width="70" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" Content="Click me"/>
</Grid>
</Window>
这是代码隐藏
public partial class MainWindow : Window
{
ViewModel _model;
public MainWindow()
{
_model = new ViewModel();
_model.Items.Add(new Item("x","y","z","zz"));
DataContext = _model;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_model = new ViewModel();
_model.Items.Add(new Item("xx", "y", "zz", "zzz"));
DataContext = _model;
}
}
[Serializable]
public class ViewModel : INotifyPropertyChanged
{
#region notifications
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
#region private and default values
private int _a = 3;
private int _b = 2;
private int _c = 1;
private int _d = 0;
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
#endregion
#region public
public int ADisplayIndex { get { return _a; } set { _a = value; NotifyPropertyChanged("ADisplayIndex"); } }
public int BDisplayIndex { get { return _b; } set { _b = value; NotifyPropertyChanged("BDisplayIndex"); } }
public int CDisplayIndex { get { return _c; } set { _c = value; NotifyPropertyChanged("CDisplayIndex"); } }
public int DDisplayIndex { get { return _d; } set { _d = value; NotifyPropertyChanged("DDisplayIndex"); } }
public ObservableCollection<Item> Items
{ get { return _items; } set { _items = value; NotifyPropertyChanged("Items"); } }
#endregion
}
public class Item
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string D { get; set; }
public Item(string a, string b, string c, string d)
{
A = a; B = b; C = c; D = d;
}
}
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
当您调整第一列时,DataGrid 会自动重新编号所有其他列的 DisplayIndex。
例如如果您将第二列设置为 DisplayIndex = 0,它会给第一列 DisplayIndex = 1.
中的 UpdateDisplayIndexForChangedColumn()
这将对您的绑定造成严重破坏,除非您按升序更改它们。
您需要某种自定义 NotifyPropertyChanged 逻辑,它首先在您的模型上设置所有新的所需索引值,而不引发 NotifyPropertyChanged,然后按数字升序引发 NotifyPropertyChanged 事件。
例如。
_aDisplayIndex = 2;
_bDisplayIndex = 1;
_cDisplayIndex = 0;
_dDisplayIndex = 3;
NotifyPropertyChanged("CDisplayIndex");
NotifyPropertyChanged("BDisplayIndex");
NotifyPropertyChanged("ADisplayIndex");
NotifyPropertyChanged("DDisplayIndex");
还要注意双向绑定,在这种情况下,您可能希望在每次 NotifyPropertyChanged 调用之间重写 _*DisplayIndex 的值。
我正在将 DataGridCoulmun 的 DisplayIndex 绑定到我的 ViewModel。由于 DataGridColumns 不属于 DataGrid 的可视树或 lgical 树,因此我不得不采取一些技巧来实现该绑定,但它确实有效。
我的问题是:当 DataContext 更改时(如果我们有更多 ViewModel),DataGridColumns 应该获得新的 DisplayIndexes。不幸的是,行为很奇怪,更改后,列顺序或多或少是随机的。 您是否知道如何处理此问题或至少是什么原因?
这是一个例子:
在初始化数据网格之前,我将 DataContext 设置为 ViewModel 的新实例,它可以正常工作。之后我重新排序列并且它仍然有效并且更改正确传播到 ViewModel。最后我单击按钮,将 DataContext 设置为 ViewModel 的新实例,因此单击后的列应按开头排序。
这是一个XAML代码:
<Window x:Class="TestDataGridBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestDataGridBinding"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn
Header="A"
Binding="{Binding A}"
DisplayIndex="{Binding Path=Data.ADisplayIndex, FallbackValue=0, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="B"
Binding="{Binding B}"
DisplayIndex="{Binding Path=Data.BDisplayIndex, FallbackValue=1, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="C"
Binding="{Binding C}"
DisplayIndex="{Binding Path=Data.CDisplayIndex, FallbackValue=2, Mode=TwoWay, Source={StaticResource proxy}}"/>
<DataGridTextColumn
Header="D"
Binding="{Binding D}"
DisplayIndex="{Binding Path=Data.DDisplayIndex, FallbackValue=3, Mode=TwoWay, Source={StaticResource proxy}}"/>
</DataGrid.Columns>
</DataGrid>
<Button Click="Button_Click" Width="70" Height="30" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="10" Content="Click me"/>
</Grid>
</Window>
这是代码隐藏
public partial class MainWindow : Window
{
ViewModel _model;
public MainWindow()
{
_model = new ViewModel();
_model.Items.Add(new Item("x","y","z","zz"));
DataContext = _model;
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_model = new ViewModel();
_model.Items.Add(new Item("xx", "y", "zz", "zzz"));
DataContext = _model;
}
}
[Serializable]
public class ViewModel : INotifyPropertyChanged
{
#region notifications
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
#endregion
#region private and default values
private int _a = 3;
private int _b = 2;
private int _c = 1;
private int _d = 0;
private ObservableCollection<Item> _items = new ObservableCollection<Item>();
#endregion
#region public
public int ADisplayIndex { get { return _a; } set { _a = value; NotifyPropertyChanged("ADisplayIndex"); } }
public int BDisplayIndex { get { return _b; } set { _b = value; NotifyPropertyChanged("BDisplayIndex"); } }
public int CDisplayIndex { get { return _c; } set { _c = value; NotifyPropertyChanged("CDisplayIndex"); } }
public int DDisplayIndex { get { return _d; } set { _d = value; NotifyPropertyChanged("DDisplayIndex"); } }
public ObservableCollection<Item> Items
{ get { return _items; } set { _items = value; NotifyPropertyChanged("Items"); } }
#endregion
}
public class Item
{
public string A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string D { get; set; }
public Item(string a, string b, string c, string d)
{
A = a; B = b; C = c; D = d;
}
}
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
当您调整第一列时,DataGrid 会自动重新编号所有其他列的 DisplayIndex。
例如如果您将第二列设置为 DisplayIndex = 0,它会给第一列 DisplayIndex = 1.
中的 UpdateDisplayIndexForChangedColumn()这将对您的绑定造成严重破坏,除非您按升序更改它们。 您需要某种自定义 NotifyPropertyChanged 逻辑,它首先在您的模型上设置所有新的所需索引值,而不引发 NotifyPropertyChanged,然后按数字升序引发 NotifyPropertyChanged 事件。
例如。
_aDisplayIndex = 2;
_bDisplayIndex = 1;
_cDisplayIndex = 0;
_dDisplayIndex = 3;
NotifyPropertyChanged("CDisplayIndex");
NotifyPropertyChanged("BDisplayIndex");
NotifyPropertyChanged("ADisplayIndex");
NotifyPropertyChanged("DDisplayIndex");
还要注意双向绑定,在这种情况下,您可能希望在每次 NotifyPropertyChanged 调用之间重写 _*DisplayIndex 的值。