我正在尝试将数据项的分组集合绑定到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;
}
是否存在从ICollectionView
到IBindableIterable
的转换?如果是这样,怎么做?
我很清楚这些示例在代码中进行绑定,而不是在XAML中。这真的有区别吗?
如果这种方法是错误的,那么正确的方法又如何呢?
编辑:
很抱歉,我忘了提到我们使用"MVVM Light Toolkit" by GalaSoft。这就是为什么构建集合的代码在视图模型中而不是背后的代码的原因。它应该留在那里。
这对绑定类型有影响。要绑定到视图模型的属性,我们使用:
<mstkcontrols:DataGrid ItemsSource="{Binding MyContents}">
但是要绑定到后面代码的属性,必须是:
<mstkcontrols:DataGrid ItemsSource="{x:Bind MyContents}">
同时,非常感谢您的阅读和提出建议。我目前正在研究如何在后面连接视图模型和代码。
答案 0 :(得分:1)
好的,我花了2位数的小时才找到此问题的根源。与Binding
相比,x:Bind
似乎被打乱了。
“ {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;
}
答案 1 :(得分:0)
我不知道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。希望其中一些确实适用!
答案 2 :(得分:0)
我按照此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));
}