简体   繁体   English

如何在保持双向绑定 MVVM 的同时将 Linq 结果绑定到 WPF DataGrid

[英]How to bind a Linq result to a WPF DataGrid while keeping two way binding MVVM

So I understand my issue pretty well, I am just having issues developing a solution for it.所以我非常了解我的问题,我只是在为它开发解决方案时遇到问题。 Let's say I have an MVVM WPF application with a datagrid.假设我有一个带有数据网格的 MVVM WPF 应用程序。 The ItemSource is bound to a collection ObservableCollection. ItemSource 绑定到集合 ObservableCollection。 This is fed through a converter, as the data model is fairly complex, but the model complexity does not have anything to do with issue.这是通过转换器馈送的,因为数据模型相当复杂,但模型复杂性与问题无关。 I then need to use a Linq query on my observable collection, as I want it to only show certain items in that collection.然后我需要对我的可观察集合使用 Linq 查询,因为我希望它只显示该集合中的某些项目。

If I return the Linq query directly from my converter, I get the error " 'EditItem' is not allowed for this view. " I realize that that error occurs because the Linq query returns an IEnumerable collection, which is not supported with DataGrid TwoWay data binding.如果我直接从我的转换器返回 Linq 查询,我会收到错误“ 'EditItem' is not allowed for this view. ” 我意识到发生该错误是因为 Linq 查询返回一个 IEnumerable 集合,而 DataGrid TwoWay 数据不支持该集合捆绑。

However, if I return it as a new ObservableCollection, I get the error " Two-way binding requires Path or XPath. " This makes sense because the new collection is not a property with accessors and mutators, and plus, we lose the data binding to the original source this way.但是,如果我将其作为新的 ObservableCollection 返回,则会收到错误“双向绑定需要 Path 或 XPath”。这是有道理的,因为新集合不是具有访问器和修改器的属性,而且,我们丢失了数据绑定以这种方式到原始来源。

So the question here is how am I to return only the items that I need from the converter, in such a way that keeps my data binding to the original source and allows for two way binding?所以这里的问题是我如何只从转换器返回我需要的项目,以这种方式将我的数据绑定到原始源并允许双向绑定?

I'm not sure if the following is necessary, but it may be worth noting a bit about the background of this application.我不确定以下是否有必要,但关于此应用程序的背景可能值得一提。 This application Has dynamically Generated tabs in a tab control, and each tab item contains one of these DataGrids.此应用程序在选项卡控件中动态生成了选项卡,每个选项卡项都包含这些 DataGrid 之一。 These datagrids are populate with the same collection of objects, just with a different filter on each depending what tab it is contained in (this all ties back into the model).这些数据网格填充有相同的对象集合,只是每个对象都有不同的过滤器,具体取决于它包含在哪个选项卡中(这一切都与模型相关联)。 It is important that the converter is there as adding items to any of the datagrids will require a bit of extra coding.转换器在那里很重要,因为向任何数据网格添加项目都需要一些额外的编码。

Here is some code to demonstrate the issue.下面是一些代码来演示这个问题。

The XAML DataGrid: XAML 数据网格:

<DataGrid IsReadOnly="False">
    <DataGrid.ItemsSource>
        <MultiBinding Converter="{StaticResource MyObjectCollectionDataGridConverter}" UpdateSourceTrigger="PropertyChanged">
            <Binding Path="StateManager.MyObjectCollection" Mode="TwoWay"/>
            <Binding Mode="OneWay"/>
        </MultiBinding>
    </DataGrid.ItemsSource>
</DataGrid>

The C# Converter C# 转换器

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
    string header = ((MyObjectType)values[1]).Name;

    // Returning as a Linq query:
    // return ((ObservableCollection<MyObject>)values[0]).Where(c => c.Type.Name == header);

    // Returning as a new Observable Collection
    return new ObservableCollection<MyObject>(((ObservableCollection<MyObject>)values[0]).Where(c => c.Type.Name == header));
}

Thank you for any help!!!感谢您的任何帮助!!!

Edit编辑

Ok, so I did find out that when using the Linq query approach, by calling the ToList() method, my databinding does hold for existing items (which makes sense because it is just referencing the old instance of the objects).好的,所以我确实发现在使用 Linq 查询方法时,通过调用 ToList() 方法,我的数据绑定确实适用于现有项目(这是有道理的,因为它只是引用对象的旧实例)。 However, when a new item is added via the datagrid the application breaks with the error " Two-way binding requires Path or XPath. " (which also makes sense because it is not referencing the new list created, not the old observable collection).但是,当通过数据网格添加新项目时,应用程序会因错误“双向绑定需要 Path 或 XPath ”而中断 (这也是有道理的,因为它没有引用创建的新列表,而不是旧的可观察集合)。 How do I make it so that I can still add items via the datagrid to reference the old observable collection over the new list?如何使它仍然可以通过数据网格添加项目以在新列表上引用旧的可观察集合?

Since View is dictating what VM should look like your MyObject should also follow this rule.由于 View 决定了 VM 应该是什么样子,因此您的 MyObject 也应遵循此规则。

You want to extend your MyObject class to contain flag whether item is visible or not.您想扩展 MyObject 类以包含项目是否可见的标志。

This is how your RowViewModel might look:这是您的 RowViewModel 的外观:

public class MyObject : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged();
        }
    }

    private bool _isEnabled;
    public bool IsEnabled
    {
        get { return _isEnabled; }
        set
        {
            _isEnabled = value;
            OnPropertyChanged();
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged([CallerMemberName] string propName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

Here is how you would deal with it in converter:以下是您在转换器中处理它的方法:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
     string header = ((MyObject)values[1]).Name;
     foreach (var item in (ObservableCollection<MyObject>)values[0])
         item.IsEnabled = item.Name == header;

     return values[0];
}

And of course you want to expose enabled flag in DataTemplate xaml:当然,您希望在DataTemplate xaml 中公开启用的标志:

<DataGrid IsReadOnly="False" AutoGenerateColumns="False">
   <DataGrid.ItemsSource>
    <MultiBinding Converter="{StaticResource MyObjectCollectionDataGridConverter}" UpdateSourceTrigger="PropertyChanged">
        <Binding Path="StateManager.MyObjectCollection" Mode="TwoWay"/>
        <Binding Mode="OneWay"/>
    </MultiBinding>
   </DataGrid.ItemsSource>
   <DataGrid.Columns>
          <DataGridTemplateColumn>
              <DataGridTemplateColumn.CellTemplate>
                  <DataTemplate>
                      <TextBox Text="Hello" IsEnabled="{Binding IsEnabled}"/>
                  </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
          </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM