我将DataGridCoulmun的DisplayIndex绑定到我的ViewModel。由于DataGridColumns不属于DataGrid的可视或树状树,因此我必须做一些技巧来实现该绑定,但它可以工作。
我的问题是:当DataContext被更改时(如果我们有更多ViewModels),DataGridColumns会获得新的DisplayIndexes。不幸的是,这种行为很奇怪,在更改之后,列顺序或多或少是随机的。 你知道如何处理这个问题或至少是什么原因?
这是一个例子:
在初始化datagrid之前,我将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));
}
在这里找到了代理欺骗: http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
答案 0 :(得分:0)
在调整第一列时,DataGrid会自动重新编号所有其他列的DisplayIndex。
例如如果将第二列设置为DisplayIndex = 0,则第一列将为DisplayIndex = 1。
请参见https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Controls/DataGridColumnCollection.cs中的UpdateDisplayIndexForChangedColumn()
除非您按升序更改绑定,否则这将对绑定造成严重破坏。 您需要某种自定义的NotifyPropertyChanged逻辑,该逻辑首先在模型上设置所有新的所需索引值,而无需引发NotifyPropertyChanged,然后按数字升序引发NotifyPropertyChanged事件。
例如
_aDisplayIndex = 2;
_bDisplayIndex = 1;
_cDisplayIndex = 0;
_dDisplayIndex = 3;
NotifyPropertyChanged("CDisplayIndex");
NotifyPropertyChanged("BDisplayIndex");
NotifyPropertyChanged("ADisplayIndex");
NotifyPropertyChanged("DDisplayIndex");
还要注意双向绑定,在这种情况下,您可能希望在每个NotifyPropertyChanged调用之间重写_ * DisplayIndex的值。