【问题标题】:Can I bind to a DataGrid's SelectedItem without updating a backing variable/property?我可以在不更新支持变量/属性的情况下绑定到 DataGrid 的 SelectedItem 吗?
【发布时间】:2017-05-30 14:54:17
【问题描述】:

所以,我有一个显示付款列表的 DataGrid。

<DataGrid x:Name="dataGrid" ItemsSource="{Binding Payments}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="PaymentDate" Binding="{Binding PaymentDate, StringFormat=\{0:d\}}" />
        <DataGridTextColumn Header="Amount" Binding="{Binding Amount, StringFormat=\{0:N\}}" />
        <DataGridTextColumn Header="Comment" Binding="{Binding Comment}" />
        <DataGridTextColumn Binding="{Binding EventCode}" Header="Event Code"/>
        <DataGridTextColumn Binding="{Binding DueDate, StringFormat=\{0:d\}}" Header="DueDate"/>
    </DataGrid.Columns>
</DataGrid>

此 DataGrid 绑定到 Payment 对象的 ObservableCollection。

public class Payment
{
    public Guid ID { get; set; }

    public DateTime PaymentDate { get; set; }

    public decimal Amount { get; set; }

    public string Comment { get; set; }

    public string EventCode { get; set; }

    public DateTime? DueDate { get; set; }

    List<Booking> Bookings
    {
        get { ...magic that retrieves booking info... }
    }
}

如您所见,每笔付款都有一个属性,它是一个 Booking 对象列表,显示每笔付款的分配方式。

Booking 对象非常简单。

public class Booking
{
    public string EventCode { get; set; }

    public decimal Amount { get; set; }

    public DateTime? BookingDate { get; set; }

    public string Designation { get; set; }

    public string Comment { get; set; }
}

我还有第二个 DataGrid,它应该显示选定付款的预订对象列表。

<DataGrid ItemsSource="{Binding SelectedItem.Bookings, ElementName=dataGrid}" AutoGenerateColumns="True" />

我的预期是,每当我在 DataGrid 1 中选择一个付款项目时,DataGrid 2 都会填充有关如何分配付款的详细信息。然而,我得到的是一个空的详细信息 DataGrid。

我知道我可以将 SelectedItem 属性绑定到我的 ViewModel 中的一个属性,并在每次更改该属性时通知我的视图,但似乎 DataGrid 2 应该知道 DataGrid 1 的 SelectedItem 属性已自动更改。是我要求太多,还是我做错了?

【问题讨论】:

  • 你描述的应该没问题,但魔鬼在细节中。试试这个,看看你在运行时在 VS 输出窗格中看到什么,当你改变主网格中的选择时:ItemsSource="{Binding SelectedItem.Bookings, ElementName=dataGrid, PresentationTraceSources.TraceLevel=High}"
  • 您是否仔细检查了所选项目的预订列表是否为空?给定代码看不到错误
  • 如果两个 DataGrid 位于相同的命名范围内,则应该可以工作。确保已填充所选付款的 Bookings 集合。
  • 你可以代替 ElmentName 尝试使用 x:Reference stackoverflow.com/questions/19244111/…
  • 噢,我真是个笨蛋。我没有公开我的 Bookings 属性。谢谢你的眼睛,伙计们。我以前从未使用过PresentationTraceSources.TraceLevel=High。感谢您提供新工具。

标签: c# wpf binding datagrid


【解决方案1】:

确保 Bookings 的财产是公共财产。

您可能还想在 SelectedItem 绑定中使用 UpdateSourceTrigger=PropertyChanged,如下所示:

ItemsSource="{Binding SelectedItem.Bookings, ElementName=dataGrid, UpdateSourceTrigger=PropertyChanged}"

如果这没有帮助,请尝试使用 Snoop 来确保数据实际上在 Bookings 中 - 如果是,但它没有显示,请使用 VM 内的属性并在您的数据类中实现 INotifyPropetyChanged,或者更新属性datagrid2 通过 datagrid1 的 SelectionChanged 事件处理程序

【讨论】:

  • 这就是问题所在。我的 Bookings 资源未设置为公开。
  • 由于将UpdateSourceTrigger=PropertyChanged 添加到绑定中的荒谬建议而撤回了投票。如果您建议这样做,您一定不知道这意味着什么;这显然是一个无操作。如果你不知道它的作用,不要自信地声称它对人的束缚有任何神奇的疗效。
【解决方案2】:

在 WPF 中管理从列表中选择父子的三种常用方法

  1. 在子网格的绑定中使用“/”语法
  2. 在viewmodel中专门添加一个SelectedItem属性,设置在 父网格并在子网格中读取它
  3. 以声明方式将父网格绑定到 CollectionViewSource 并设置 CurrentChanged 事件时子网格的数据源 CollectionViewSource 触发。

选项 2 是最常见的。就个人而言,我更喜欢使用 3,因为它使我能够在我的视图模型中移动优先/最后/下一个/上一个。

您似乎在寻找1,这是最简单但功能最弱的方法。您需要做的就是将子网格中的绑定更改为

<DataGrid ItemsSource="{Binding Payments/Bookings}" AutoGenerateColumns="True" />

请注意,我删除了 ElementName 引用,这是错误的;您正在绑定到您的 DataContext,而不是父网格上的属性。

【讨论】:

  • 为什么直接绑定到主DataGrid的SelectedItem错了?
  • 错误是使用的不正确的词。一个更好的表达方式是“坏习惯”。如果您重命名源控件或尝试在任何其他上下文中使用您的子控件,则绑定到其他控件的属性会中断。此外,您现在依赖于源代码管理的实现,它可能会在刷新时触发 SelectedItem 属性任意次数。最重要的是,绑定到属性元素更难进行单元测试。
猜你喜欢
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-04
  • 2017-03-29
相关资源
最近更新 更多