使用绑定来控制 DataGrid 中的列顺序

发布于 2024-08-19 07:57:02 字数 3674 浏览 2 评论 0原文

问题

我有一个 WPF 工具包 DataGrid,并且我希望能够在多个预设列顺序之间切换。这是一个 MVVM 项目,因此列顺序存储在 ViewModel 中。问题是,我无法让绑定适用于 DisplayIndex 属性。无论我尝试什么,包括 这个 Josh Smith 教程,我得到:

标题为“ID”的 DataGridColumn 的 DisplayIndex 超出范围。 DisplayIndex 必须大于或等于 0 且小于 Columns.Count。参数名称:显示索引。实际值为-1。

有解决方法吗?

我在下面添加了我的测试代码。如果您发现任何问题,请告诉我。


ViewModel 代码

public class MainViewModel
{
    public List<Plan> Plans { get; set; }
    public int IdDisplayIndex { get; set; }
    public int NameDisplayIndex { get; set; }
    public int DescriptionDisplayIndex { get; set; }

    public MainViewModel()
    {
        Initialize();
    }

    private void Initialize()
    {
        IdDisplayIndex = 1;
        NameDisplayIndex = 2;
        DescriptionDisplayIndex = 0;
        Plans = new List<Plan>
        {
            new Plan { Id = 1, Name = "Primary", Description = "Likely to work." },
            new Plan { Id = 2, Name = "Plan B", Description = "Backup plan." },
            new Plan { Id = 3, Name = "Plan C", Description = "Last resort." }
        };
    }
}

计划类

public class Plan
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

窗口代码 - 这使用 Josh Smith 的 DataContextSpy

<Window
    x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    xmlns:mwc="http://schemas.microsoft.com/wpf/2008/toolkit"
    Title="Main Window" Height="300" Width="300">
    <Grid>
        <mwc:DataGrid ItemsSource="{Binding Plans}" AutoGenerateColumns="False">
            <mwc:DataGrid.Resources>
                <local:DataContextSpy x:Key="spy" />
            </mwc:DataGrid.Resources>
            <mwc:DataGrid.Columns>
                <mwc:DataGridTextColumn
                    Header="ID" 
                    Binding="{Binding Id}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.IdDisplayIndex}" />
                <mwc:DataGridTextColumn
                    Header="Name"
                    Binding="{Binding Name}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.NameDisplayIndex}" />
                <mwc:DataGridTextColumn
                    Header="Description"
                    Binding="{Binding Description}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.DescriptionDisplayIndex}" />
            </mwc:DataGrid.Columns>
        </mwc:DataGrid>
    </Grid>
</Window>

注意: 如果我只使用普通DisplayIndex 的数字,一切正常,因此问题肯定出在绑定上。


更新 5/1/2010

我只是对我的项目进行了一些维护,我注意到当我运行它时,我在这篇文章中讨论的问题又回来了。我知道上次运行它时它是有效的,因此我最终将问题范围缩小到我安装了较新版本的 WPF Toolkit(10 年 2 月)这一事实。当我恢复到 09 年 6 月版本时,一切又恢复正常了。因此,我现在正在做一些我应该首先做的事情:我将在我的解决方案文件夹中工作的 WPFToolkit.dll 包含在内,并将其签入版本控制中。但不幸的是,新的工具包发生了重大变化。

Problem

I have a WPF Toolkit DataGrid, and I'd like to be able to switch among several preset column orders. This is an MVVM project, so the column orders are stored in a ViewModel. The problem is, I can't get bindings to work for the DisplayIndex property. No matter what I try, including the sweet method in this Josh Smith tutorial, I get:

The DisplayIndex for the DataGridColumn with Header 'ID' is out of range. DisplayIndex must be greater than or equal to 0 and less than Columns.Count. Parameter name: displayIndex. Actual value was -1.

Is there any workaround for this?

I'm including my test code below. Please let me know if you see any problems with it.


ViewModel code

public class MainViewModel
{
    public List<Plan> Plans { get; set; }
    public int IdDisplayIndex { get; set; }
    public int NameDisplayIndex { get; set; }
    public int DescriptionDisplayIndex { get; set; }

    public MainViewModel()
    {
        Initialize();
    }

    private void Initialize()
    {
        IdDisplayIndex = 1;
        NameDisplayIndex = 2;
        DescriptionDisplayIndex = 0;
        Plans = new List<Plan>
        {
            new Plan { Id = 1, Name = "Primary", Description = "Likely to work." },
            new Plan { Id = 2, Name = "Plan B", Description = "Backup plan." },
            new Plan { Id = 3, Name = "Plan C", Description = "Last resort." }
        };
    }
}

Plan Class

public class Plan
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
}

Window code - this uses Josh Smith's DataContextSpy

<Window
    x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    xmlns:mwc="http://schemas.microsoft.com/wpf/2008/toolkit"
    Title="Main Window" Height="300" Width="300">
    <Grid>
        <mwc:DataGrid ItemsSource="{Binding Plans}" AutoGenerateColumns="False">
            <mwc:DataGrid.Resources>
                <local:DataContextSpy x:Key="spy" />
            </mwc:DataGrid.Resources>
            <mwc:DataGrid.Columns>
                <mwc:DataGridTextColumn
                    Header="ID" 
                    Binding="{Binding Id}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.IdDisplayIndex}" />
                <mwc:DataGridTextColumn
                    Header="Name"
                    Binding="{Binding Name}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.NameDisplayIndex}" />
                <mwc:DataGridTextColumn
                    Header="Description"
                    Binding="{Binding Description}"
                    DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.DescriptionDisplayIndex}" />
            </mwc:DataGrid.Columns>
        </mwc:DataGrid>
    </Grid>
</Window>

Note: If I just use plain numbers for DisplayIndex, everything works fine, so the problem is definitely with the bindings.


Update 5/1/2010

I was just doing a little maintenance on my project, and I noticed that when I ran it, the problem I discuss in this post had returned. I knew that it worked last time I ran it, so I eventually narrowed the problem down to the fact that I had installed a newer version of the WPF Toolkit (Feb '10). When I reverted to the June '09 version, everything worked fine again. So, I'm now doing something I should have done in the first place: I'm including the WPFToolkit.dll that works in my solution folder and checking it into version control. It's unfortunate, though, that the newer toolkit has a breaking change.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

冷情 2024-08-26 07:57:03

在 DisplayIndex 绑定中设置 FallbackValue=,其中 是列的索引。例如:

DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.IdDisplayIndex, FallbackValue=0}" />

Set FallbackValue=<idx> in your DisplayIndex binding, where <idx> is your column's index. For example:

DisplayIndex="{Binding Source={StaticResource spy}, Path=DataContext.IdDisplayIndex, FallbackValue=0}" />
别在捏我脸啦 2024-08-26 07:57:03

(我之前的答案偏离了轨道 - 所以我删除了它 - 我会在我的机器上重现错误后再次尝试回答)

您遇到的问题源于这样一个事实:当第一次评估绑定时, < code>DataContext 属性仍设置为 null。由于某种奇怪的原因(但我不知道),这导致绑定在 -1 处求值。但是,如果您确保在评估绑定之前设置DataContext,那么就可以了。不幸的是,这可能只能在代码隐藏中实现 - 因此在运行时,而不是在设计时。所以在设计的时候我还是不知道如何规避这个错误。

为此,请在调用 InitializeComponent 之前设置窗口的 DataContext 属性,如下所示:

public MainWindow()
{
    DataContext = new MainViewModel();
    InitializeComponent();
}

希望这会有所帮助。

(My previous answer was way off track - so I deleted it - I'll try to answer again after reproducing the error on my machine)

The problem you encounter stems from the fact that when the bindings are evaluated for the first time, the DataContext property is still set to null. This, for some strange reason, yet unknown to me, causes the binding to evaluate at -1. However if you make sure to set the DataContext before the bindings are evaluated you'll be ok. Unfortunately this might only be possible in code-behind - hence during run-time, and not in design time. So in design time I still don't know how to circumvent the error.

To do that, set the DataContext property of the window before the call to InitializeComponent thus:

public MainWindow()
{
    DataContext = new MainViewModel();
    InitializeComponent();
}

Hope this helps.

狼性发作 2024-08-26 07:57:03

我将其添加为答案,因为我还无法发表评论。 DisplayIndex 的值设置为 -1 的原因是:

添加前 DisplayIndex 属性的默认值为 -1
到 DataGrid.Columns 集合。当
列已添加到 DataGrid。

DataGrid 要求每列的 DisplayIndex 属性
必须是从 0 到列数 -1 的唯一整数。所以,
当一列的 DisplayIndex 发生变化时,通常会发生变化
导致其他列的 DisplayIndex 也发生变化。

对 DisplayIndex 值的限制由
ValidateValueCallback机制。如果您尝试设置一个值
无效,会抛出运行时异常。

当 DisplayIndex 属性的值改变时,
引发 DataGrid.ColumnDisplayIndexChanged 事件。

来源: http://msdn.microsoft.com/en-us/library/vstudio/system.windows.controls.datagridcolumn.displayindex(v=vs.100).aspx

所以我想可以检查当使用上述事件更改该值时。

Aviad 提到的解决方案对我不起作用。我的 Datagrid 位于 UserControl 中,与 OP 相同,并更改了 ViewModel 构造函数、DataContext 声明和 InitializeComponent() 的顺序代码> 方法没有帮助。

另一方面,Pakman 的 FallbackValue 方法效果非常好。

I'm adding this as an answer since I can't comment yet. The reason why the value of the DisplayIndex is set to -1 is :

The DisplayIndex property has a default value of -1 before it is added
to the DataGrid.Columns collection. This value is updated when the
column is added to the DataGrid.

The DataGrid requires that the DisplayIndex property of each column
must be a unique integer from 0 to the Count of Columns -1. Therefore,
when the DisplayIndex of one column changes, the change typically
causes the DisplayIndex of other columns to also change.

The restrictions on the DisplayIndex value are enforced by a
ValidateValueCallback mechanism. If you attempt to set a value that is
not valid, a run-time exception is thrown.

When the value of the DisplayIndex property is changed, the
DataGrid.ColumnDisplayIndexChanged event is raised.

Source: http://msdn.microsoft.com/en-us/library/vstudio/system.windows.controls.datagridcolumn.displayindex(v=vs.100).aspx

So I suppose that it is possible to check this value when it is changed using the aforementioned event.

The solution mentioned by Aviad didn't work for me. I have my Datagrid in a UserControl, same as the OP, and changing the order of the ViewModel constructor, the DataContext declaration, and the InitializeComponent() method didn't help.

Pakman's FallbackValue method worked very well, on the other hand.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文