繁体   English   中英

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

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

所以我非常了解我的问题,我只是在为它开发解决方案时遇到问题。 假设我有一个带有数据网格的 MVVM WPF 应用程序。 ItemSource 绑定到集合 ObservableCollection。 这是通过转换器馈送的,因为数据模型相当复杂,但模型复杂性与问题无关。 然后我需要对我的可观察集合使用 Linq 查询,因为我希望它只显示该集合中的某些项目。

如果我直接从我的转换器返回 Linq 查询,我会收到错误“ 'EditItem' is not allowed for this view. ” 我意识到发生该错误是因为 Linq 查询返回一个 IEnumerable 集合,而 DataGrid TwoWay 数据不支持该集合捆绑。

但是,如果我将其作为新的 ObservableCollection 返回,则会收到错误“双向绑定需要 Path 或 XPath”。这是有道理的,因为新集合不是具有访问器和修改器的属性,而且,我们丢失了数据绑定以这种方式到原始来源。

所以这里的问题是我如何只从转换器返回我需要的项目,以这种方式将我的数据绑定到原始源并允许双向绑定?

我不确定以下是否有必要,但关于此应用程序的背景可能值得一提。 此应用程序在选项卡控件中动态生成了选项卡,每个选项卡项都包含这些 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>

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));
}

感谢您的任何帮助!!!

编辑

好的,所以我确实发现在使用 Linq 查询方法时,通过调用 ToList() 方法,我的数据绑定确实适用于现有项目(这是有道理的,因为它只是引用对象的旧实例)。 但是,当通过数据网格添加新项目时,应用程序会因错误“双向绑定需要 Path 或 XPath ”而中断 (这也是有道理的,因为它没有引用创建的新列表,而不是旧的可观察集合)。 如何使它仍然可以通过数据网格添加项目以在新列表上引用旧的可观察集合?

由于 View 决定了 VM 应该是什么样子,因此您的 MyObject 也应遵循此规则。

您想扩展 MyObject 类以包含项目是否可见的标志。

这是您的 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));
    }
}

以下是您在转换器中处理它的方法:

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];
}

当然,您希望在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