DataGridColumns DisplayIndex Binding和DataContext更改

时间:2015-02-18 11:44:35

标签: c# wpf xaml binding datagrid

我将DataGridCoulmun的DisplayIndex绑定到我的ViewModel。由于DataGridColumns不属于DataGrid的可视或树状树,因此我必须做一些技巧来实现该绑定,但它可以工作。

我的问题是:当DataContext被更改时(如果我们有更多ViewModels),DataGridColumns会获得新的DisplayIndexes。不幸的是,这种行为很奇怪,在更改之后,列顺序或多或少是随机的。 你知道如何处理这个问题或至少是什么原因?

这是一个例子:

在初始化datagrid之前,我将DataContext设置为ViewModel的新实例,它可以正常工作。之后,我重新排序列,它仍然有效,并且更改正确传播到ViewModel。最后,我单击按钮,将DataContext设置为ViewModel的新实例,因此点击后的列应该从开头开始排序。

Column reordering and DataContext change

这是一个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/

1 个答案:

答案 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的值。