【问题标题】:UWP Toolkit DataGrid - How to bind CollectionViewSource to ItemsSource of DataGrid?UWP Toolkit DataGrid - 如何将 CollectionViewSource 绑定到 DataGrid 的 ItemsSource?
【发布时间】:2019-06-12 15:09:24
【问题描述】:

我正在尝试将数据项的分组集合绑定到 DataGrid。呈现数据的细节无关紧要,实际上所有内容现在都是用虚拟数据设置的。

我按照Microsoft's Sample App"How to: Group, sort and filter data in the DataGrid Control" 中的示例代码进行操作。

启动应用程序后,显示的 DataGrid 为空,绑定代码的调试输出显示:

Error: Converter failed to convert value of type 'Windows.UI.Xaml.Data.ICollectionView' to type 'IBindableIterable'; BindingExpression: Path='MyContents' DataItem='MyViewModel'; target element is 'Microsoft.Toolkit.Uwp.UI.Controls.DataGrid' (Name='null'); target property is 'ItemsSource' (type 'IBindableIterable').

这是我的 XAML 中有趣的部分:

<mstkcontrols:DataGrid ItemsSource="{Binding MyContents}">
    <!-- Irrelevant stuff left out... -->
</mstkcontrols:DataGrid>

在我的视图模型中,我有以下代码:

public ICollectionView MyContents { get; private set; }

public override void OnNavigatedTo(NavigationEventArgs e)
{
    // Irrelevant stuff left out...

    ObservableCollection<ObservableCollection<MyItemType>> groupedCollection = new ObservableCollection<ObservableCollection<MyItemType>>();

    // It doesn't matter how this grouped collection is filled...

    CollectionViewSource collectionViewSource = new CollectionViewSource();
    collectionViewSource.IsSourceGrouped = true;
    collectionViewSource.Source = groupedCollection;
    MyContents = collectionViewSource.View;
}

是否有从ICollectionViewIBindableIterable 的转换?如果有,是怎么做的?

我很清楚这些示例在代码中进行绑定,而不是在 XAML 中。这真的有影响吗?

如果这种方法是错误的,那么正确的方法是什么?

编辑:

对不起,我忘了提到我们使用的是"MVVM Light Toolkit" by GalaSoft。这就是为什么构建集合的代码在视图模型中,而不是在后面的代码中。它应该留在那里。

这对绑定类型有影响。要绑定到视图模型的属性,我们使用:

<mstkcontrols:DataGrid ItemsSource="{Binding MyContents}">

但是要绑定到后面代码的一个属性,必须是:

<mstkcontrols:DataGrid ItemsSource="{x:Bind MyContents}">

同时,非常感谢大家阅读和提出建议。我目前正在研究如何连接视图模型和代码。

【问题讨论】:

    标签: c# xaml uwp datagrid


    【解决方案1】:

    好的,我花了 2 位数的小时数才找到这个问题的根源。与x:Bind 相比,Binding 似乎有一种颠覆性的方式。

    {Binding} 默认情况下假定您绑定到标记页的 DataContext。”说文档"Data binding in depth"。而我页面的数据上下文就是视图模型。

    {x:Bind} 不使用 DataContext 作为默认源,而是使用页面或用户控件本身。”说文档"{x:Bind} markup extension"。嗯,编译时生成的代码对不同的数据类型没有问题。

    XAML 更改为(Mode 很重要,因为默认为OneTime):

    <mstkcontrols:DataGrid ItemsSource="{x:Bind MyContents, Mode=OneWay}" Loaded="DataGrid_Loaded">
        <!-- Irrelevant stuff left out... -->
    </mstkcontrols:DataGrid>
    

    后面的代码需要一个发送通知事件的属性。为此,它的类需要继承自INotifyPropertyChanged。您可以使用@NicoZhu 的答案中显示的Set()OnPropertyChanged() 方法,但是这个截图更清楚地显示了什么是重要的:

    private ICollectionView _myContents;
    public ICollectionView MyContents
    {
        get
        {
            return _myContents;
        }
        set
        {
            if (_myContents != value)
            {
                _myContents = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MyContents)));
            }
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    private void DataGrid_Loaded(object sender, RoutedEventArgs e)
    {
        if ((sender as DataGrid).DataContext is MyViewModel viewModel)
        {
            MyContents = viewModel.ContentsView();
        }
    }
    

    视图模型通过从后面的代码调用的方法提供内容视图(作为集合的集合)。这个方法和我之前用的代码几乎一模一样。

    internal ICollectionView ContentsView()
    {
        ObservableCollection<ObservableCollection<MyItemType>> groupedCollection = new ObservableCollection<ObservableCollection<MyItemType>>();
    
        // It doesn't matter how this grouped collection is filled...
    
        CollectionViewSource collectionViewSource = new CollectionViewSource();
        collectionViewSource.IsSourceGrouped = true;
        collectionViewSource.Source = groupedCollection;
        return collectionViewSource.View;
    }
    

    【讨论】:

      【解决方案2】:

      我按照这个tutorial 创建一个简单的示例来重现您的问题,并且绑定CollectionViewSource 效果很好。请参考以下代码。这是样本project

      Xaml

      <controls:DataGrid
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch"
          AlternatingRowBackground="Transparent"
          AlternatingRowForeground="Gray"
          AreRowDetailsFrozen="False"
          AreRowGroupHeadersFrozen="True"
          AutoGenerateColumns="False"
          CanUserReorderColumns="True"
          CanUserResizeColumns="True"
          CanUserSortColumns="False"
          ColumnHeaderHeight="32"
          FrozenColumnCount="0"
          GridLinesVisibility="None"
          HeadersVisibility="Column"
          HorizontalScrollBarVisibility="Visible"
          IsReadOnly="False"
          ItemsSource="{x:Bind GroupView, Mode=TwoWay}"
          Loaded="DataGrid_Loaded"
          MaxColumnWidth="400"
          RowDetailsVisibilityMode="Collapsed"
          RowGroupHeaderPropertyNameAlternative="Range"
          SelectionMode="Extended"
          VerticalScrollBarVisibility="Visible"
          >
          <controls:DataGrid.RowGroupHeaderStyles>
              <Style TargetType="controls:DataGridRowGroupHeader">
                  <Setter Property="Background" Value="LightGray" />
              </Style>
          </controls:DataGrid.RowGroupHeaderStyles>
      
          <controls:DataGrid.Columns>
              <controls:DataGridTextColumn
                  Binding="{Binding Name}"
                  Header="Rank"
                  Tag="Rank"
                  />
              <controls:DataGridComboBoxColumn
                  Binding="{Binding Complete}"
                  Header="Mountain"
                  Tag="Mountain"
                  />
          </controls:DataGrid.Columns>
      </controls:DataGrid>
      

      代码背后

      public sealed partial class MainPage : Page, INotifyPropertyChanged
      {
          public ObservableCollection<Item> MyClasses { get; set; } = new ObservableCollection<Item>();
      
          private ICollectionView _groupView;
          public ICollectionView GroupView
          {
              get
              {
                  return _groupView;
              }
              set
              {
                  Set(ref _groupView, value);
              }
          }
      
          public MainPage()
          {
              this.InitializeComponent();
      
              MyClasses.Add(new Item { Name = "Nico", Complete = false });
              MyClasses.Add(new Item { Name = "LIU", Complete = true });
              MyClasses.Add(new Item { Name = "He", Complete = true });
              MyClasses.Add(new Item { Name = "Wei", Complete = false });
              MyClasses.Add(new Item { Name = "Dong", Complete = true });
              MyClasses.Add(new Item { Name = "Ming", Complete = false });
      
          }
      
          private void DataGrid_Loaded(object sender, RoutedEventArgs e)
          {
              var groups = from c in MyClasses
                           group c by c.Complete;
      
              var cvs = new CollectionViewSource();
              cvs.Source = groups;
              cvs.IsSourceGrouped = true;
      
              var datagrid = sender as DataGrid;
              GroupView = cvs.View;
          }
      
          public event PropertyChangedEventHandler PropertyChanged;
      
          private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
          {
              if (Equals(storage, value))
              {
                  return;
              }
      
              storage = value;
              OnPropertyChanged(propertyName);
          }
      
          private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
      }
      

      【讨论】:

      • 非常感谢。由于示例项目,您将我推向了一个有趣的方向。好像是那种绑定的问题,不过还是要调查一下。
      • @thebusybee,如果回答对你有帮助,请考虑采纳,如果你已经解决了问题,你也可以标记自己。
      • 我已经尝试过执行此操作(后者),但需要等待延迟 - 仍然是 9 小时。 ;-)
      【解决方案3】:

      我不知道 WPF C# 对 UWP 的传递性如何,但这就是我在 WPF 中进行可观察集合数据绑定的方式

      在我窗口的 .cs 中:

      public partial class MainWindowView : Window, INotifyPropertyChanged
      { 
      
      public MainWindowView()
      {
          InitializeComponent();
          this.data.ItemsSource = etc;
      }
      public event PropertyChangedEventHandler PropertyChanged;
      
      public ObservableCollection<Stuff_NThings> etc = new     ObservableCollection<Stuff_NThings>();
      
      private void Button_Click(object sender, RoutedEventArgs e)
      {            
          Stuff_NThings t = new Stuff_NThings();
          t.stuff = 45;
          t.moreStuff = 44;
          t.things = 33;
          t.moreThings = 89;
          etc.Add(t);
      }
      

      我的班级:

      public class Stuff_NThings : INotifyPropertyChanged
      {
          private int _things;
          private int _moreThings;
          private int _stuff;
          private int _moreStuff;
      
          public int things
          {
              get
              {
                  return _things;
              }
              set
              {
                  _things = value;
                  NotifyPropertyChanged(nameof(things));
              }
          }
          public int moreThings
          {
              get
              {
                  return _moreThings;
              }
              set
              {
                  _moreThings = value;
                  NotifyPropertyChanged(nameof(moreThings));
              }
          }
          public int stuff
          {
              get
              {
                  return _stuff;
              }
              set
              {
                  _stuff = value;
                  NotifyPropertyChanged(nameof(stuff));
              }
          }
          public int moreStuff
          {
              get
              {
                  return _moreStuff;
              }
              set
              {
                  _moreStuff = value;
                  NotifyPropertyChanged(nameof(moreStuff));
              }
          }
      
          public event PropertyChangedEventHandler PropertyChanged;
          private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
          {
              if (PropertyChanged != null)
              {
                  PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
              }
          }
      }
      

      通过在 mainWindow 构造函数中设置 dataGrid 的项目源,它会根据类变量名称自动在 dataGrid 中创建表头。每当您将 Stuff'NThings 的实例(通过按钮、其他、其他等)添加到可观察集合时,都会引发触发器并更新 UI。希望其中一些实际适用!

      【讨论】:

      • 如果我使用List&lt;MyItemType&gt;ObservableCollection&lt;MyItemType&gt;没有问题。但我需要一个分组视图,为此需要一个集合集合。我在 SO for WPF 上找到了一些问题(和答案),但不幸的是,它们都没有帮助。我的目标系统是 UWP。
      • 嗯,那超出了我的范围,很高兴你找到了一些帮助!抱歉,我的示例对您的问题不太准确——编码愉快:)
      猜你喜欢
      • 1970-01-01
      • 2013-01-14
      • 2018-03-20
      • 2015-11-22
      • 2023-03-07
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 2015-04-02
      相关资源
      最近更新 更多