使用x:绑定设置GridView项目源的UWP问题

编程入门 行业动态 更新时间:2024-10-21 23:28:54
本文介绍了使用x:绑定设置GridView项目源的UWP问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述 我正在尝试使用数据虚拟化和编辑绑定从Pictures库中的照片填充我的gridview。

我采用了Microsoft UWP(数据虚拟化示例)并使用其FileSource作为我的基础,我修改了使用我自己的Picture对象,并尝试将其应用于我的UWP应用程序。我所得到的只是一个空白页面,设计师正在抛出一个异常。

我想使用x:绑定绑定到我的模型中的数据源对象我正在使用MVVM,而不是代码隐藏。

我无法让我在我的应用程序中工作,所以我写了一个小测试应用程序,甚至MVVM,并尝试使用x:绑定我的数据源作为代码背后的对象,它失败,以绑定到集合。

我可以得到这个工作与我的Picture对象,如果我将gridview的源直接设置在我的代码隐藏(这是示例正在做的)。

async void initdata() { StorageLibrary图片=等待StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures); string path = pictures.SaveFolder.Path; FileDataSource ds = await FileDataSource.GetDataSoure(path); if(ds.Count> 0) { PicturesGrid.ItemsSource = ds; } else { MainPage.Current.NotifyUser(错误:图片文件夹不包含任何文件,NotifyType.ErrorMessage); } }

FileDataSource定义如下:

///< summary> ///支持数据虚拟化的文件系统上的自定义数据源 ///< / summary> public class FileDataSource:INotifyCollectionChanged,System.Collections.IList,IItemsRangeInfo { ... }

在我的代码中,我创建了PicturesCollection作为一个属性:

公共密封部分类MainPage:页 { public FileDataSource _PicturesCollection; public FileDataSource PicturesCollection {get;私人集合} public MainPage() { this.InitializeComponent(); PicturesGrid.ContainerContentChanging + =图片Grid_ContainerContentChanging; PicturesCollection = null; initdata(); } private void PicturesGrid_ContainerContentChanging(ListViewBase sender,ContainerContentChangingEventArgs args) { if(!args.InRecycleQueue) { //在每个项目中设置一个文本块,指示其索引 // FrameworkElement ctr =(FrameworkElement)args.ItemContainer.ContentTemplateRoot; // if(ctr!= null) // { // TextBlock t =(TextBlock)ctr.FindName(idx); // t.Text = args.ItemIndex.ToString(); //} } } async void initdata() { StorageLibrary图片=等待StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures ); string path = pictures.SaveFolder.Path; _PicturesCollection = await FileDataSource.GetDataSoure(path); if(_PicturesCollection.Count> 0) { PicturesCollection = _PicturesCollection; //PicturesGrid.ItemsSource = ds; } } }

它到我的GridView:

< Grid Grid.Row =1> < GridView x:Name =PicturesGrid SelectionMode =Single ShowsScrollingPlaceholders =False ItemsSource ={x:Bind PicturesCollection}> < GridView.ItemTemplate> < DataTemplate x:DataType =local:Picture> < Grid Width =200Height =80> < Grid.RowDefinitions> < RowDefinition Height =*/> < RowDefinition Height =Auto/> < /Grid.RowDefinitions> < Border Grid.RowSpan =2Background =DimGrayOpacity =0.8/> < Image Width =130 Horizo​​ntalAlignment =Center Stretch =Uniform Source ={x:Bind ImageThumbNail,Converter = StaticResource StorageItemThumbnailoImageSourceConverter},Mode = OneWay}/> < TextBlock Grid.Row =1 MaxHeight =30 Text ={x:Bind Name}前景=白 Horizo​​ntalAlignment =Center TextTrimming =CharacterEllipsis/> < / Grid> < / DataTemplate> < /GridView.ItemTemplate> < / GridView> < / Grid>

这给我一个空白页面,但是如果我将其设置在代码隐藏中,它可以工作。任何人都可以告诉我为什么会这样吗?

解决方案

这里的问题是,当您的页面加载时,您的 PicturesCollection 属性未设置,因此您的 PicturesGrid 的 ItemsSource 是 null ,你可以卖出注意到你是页面。

在你的 MainPage ,您使用 initdata 方法获取所有数据。但是这个方法是 async void ,你没有等待完成。实际上,我们也不能在构造函数中使用 await 。所以当你的页面加载时,执行等待FileDataSource.GetDataSoure(path); 可能没有完成,你 PicturesCollection 属性仍然是 null 这里,但您的 PicturesGrid 的 ItemsSource 已绑定到您的 PicturesCollection 属性。因此, ItemsSource 为null,您什么也看不到。虽然您的 PicturesCollection 属性将被设置为真实数据,但您没有为 PicturesCollection 属性。而对于 x:绑定默认模式是 OneTime 所以你的 PicturesGrid 的 ItemsSource 将永远是 null 。

要解决此问题,您可以执行 PicturesCollection 属性的属性更改通知,如下所示:

public sealed partial class MainPage:Page,INotifyPropertyChanged { private FileDataSource _PicturesCollection; public event PropertyChangedEventHandler PropertyChanged; public FileDataSource PicturesCollection { get { return _PicturesCollection; } set { if(_PicturesCollection!= value) { _PicturesCollection = value; NotifyPropertyChanged(); } } } private void NotifyPropertyChanged([CallerMemberName] String propertyName = null) { PropertyChanged?.Invoke ,新的PropertyChangedEventArgs(propertyName)); } ... 私有async void initdata() { StorageLibrary图片=等待StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures ); string path = pictures.SaveFolder.Path; var source = await FileDataSource.GetDataSoure(path); if(source.Count> 0) { PicturesCollection = source; } } }

在XAML中,设置模式 x:绑定到 OneWay like: p>

< GridView x:Name =PicturesGrid SelectionMode =Single ShowsScrollingPlaceholders =False ItemsSource ={x:Bind PicturesCollection,Mode = OneWay}> ... < / GridView>

此后,您的 x:绑定将工作。

更新:

如果您只需要一次性绑定异步加载数据后,您可以通过调用 this.Bindings.Update(); 强制一次性绑定进行初始化数据如下:

async void initdata() { StorageLibrary图片=等待StorageLibrary.GetLibraryAsync KnownLibraryId.Pictures); string path = pictures.SaveFolder.Path; _PicturesCollection = await FileDataSource.GetDataSoure(path); if(_PicturesCollection.Count> 0) { PicturesCollection = _PicturesCollection; this.Bindings.Update(); } }

以这种方式初始化它们便宜得多具有单向绑定和监听更改,因为它只需要在代码中添加一个方法。但是,使用MVVM可能不是一个很好的做法。有关详细信息,请参阅如果您的数据异步加载 使用{x:Bind} 声明的绑定对象

支持 {x:Bind} 的代码是在编译时在您的页面的部分类中生成的。这些文件可以在您的 obj 文件夹中找到,其名称(对于C#) < view name> ; .g.cs 。生成的代码包括页面的 正在加载 的处理程序事件,并且该处理程序调用生成的类的 Initialize 方法代表您的页面的绑定。 初始化依次调用更新开始在绑定源和目标之间移动数据。 加载在页面或用户控件的第一个度量通过之前提出。因此,如果您的数据异步加载,则可能在调用初始化之前可能尚未准备就绪。因此,在加载数据之后,您可以通过调用 this-> Bindings-> Update(); 。如果您只需要一次性绑定来异步加载数据,那么以这种方式初始化它们比单向绑定和监听更改便宜得多。如果您的数据没有进行细粒度更改,并且如果可能会作为特定操作的一部分进行更新,那么您可以一次性进行绑定,并随时通过呼叫强制手动更新更新。

I am trying to populate my gridview with Photos from the Pictures library using data virtualization and compiled binding.

I've taken the Microsoft UWP (Data Virtualization Sample) and using its FileSource as my base, I modified it to use my own Picture object and tried to apply it to my UWP app. All I am getting is a blank page, and the designer is throwing an exception.

I want to use x:Bind to bind to my data source object in my model as I am using MVVM and don't want code-behind.

I couldn't get this to work in my app so I wrote a small test app that isn't even MVVM and tried to use x:Bind with my data source as an object in the code behind and it fails as to bind to the collection as well.

I can get this work with my Picture object if I set the gridview's source directly in my code-behind (which is what the sample is doing).

async void initdata() { StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures); string path = pictures.SaveFolder.Path; FileDataSource ds = await FileDataSource.GetDataSoure(path); if (ds.Count > 0) { PicturesGrid.ItemsSource = ds; } else { MainPage.Current.NotifyUser("Error: The pictures folder doesn't contain any files", NotifyType.ErrorMessage); } }

The FileDataSource is defined as follows:

/// <summary> /// A custom datasource over the file system that supports data virtualization /// </summary> public class FileDataSource : INotifyCollectionChanged, System.Collections.IList, IItemsRangeInfo { ... }

In my code, I have created the PicturesCollection as a property:

public sealed partial class MainPage : Page { public FileDataSource _PicturesCollection; public FileDataSource PicturesCollection { get; private set; } public MainPage() { this.InitializeComponent(); PicturesGrid.ContainerContentChanging += PicturesGrid_ContainerContentChanging; PicturesCollection = null; initdata(); } private void PicturesGrid_ContainerContentChanging(ListViewBase sender, ContainerContentChangingEventArgs args) { if (!args.InRecycleQueue) { // Sets a textblock in each item indicating its index //FrameworkElement ctr = (FrameworkElement)args.ItemContainer.ContentTemplateRoot; //if (ctr != null) //{ // TextBlock t = (TextBlock)ctr.FindName("idx"); // t.Text = args.ItemIndex.ToString(); //} } } async void initdata() { StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures); string path = pictures.SaveFolder.Path; _PicturesCollection = await FileDataSource.GetDataSoure(path); if (_PicturesCollection.Count > 0) { PicturesCollection = _PicturesCollection; //PicturesGrid.ItemsSource = ds; } } }

and bound it to my GridView:

<Grid Grid.Row="1"> <GridView x:Name="PicturesGrid" SelectionMode="Single" ShowsScrollingPlaceholders="False" ItemsSource="{x:Bind PicturesCollection}"> <GridView.ItemTemplate> <DataTemplate x:DataType="local:Picture" > <Grid Width="200" Height="80"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Border Grid.RowSpan="2" Background="DimGray" Opacity="0.8" /> <Image Width ="130" HorizontalAlignment="Center" Stretch="Uniform" Source="{x:Bind ImageThumbNail, Converter ={StaticResource StorageItemThumbnailoImageSourceConverter}, Mode=OneWay}" /> <TextBlock Grid.Row="1" MaxHeight="30" Text="{x:Bind Name}" Foreground="White" HorizontalAlignment="Center" TextTrimming="CharacterEllipsis"/> </Grid> </DataTemplate> </GridView.ItemTemplate> </GridView> </Grid>

This gives me a blank page, but if I set it in code-behind, it works. Can anyone tell me why this is so? What am I missing?

解决方案

The problem here is that when your page loaded, your PicturesCollection property is not set, so your PicturesGrid's ItemsSource is null and you can sell noting is you page.

In the constructor of your MainPage, you are using initdata method to get all data. However this method is async void and you didn't wait for its finishing. Actually, we also can't use await in constructor. So when your page loaded, the execution of await FileDataSource.GetDataSoure(path); may be not finished, you PicturesCollection property is still null here, but your PicturesGrid's ItemsSource has bound to your PicturesCollection property. Thus, the ItemsSource is null and you can see nothing. Although your PicturesCollection property will be set to the real data later, but you didn't implement property-change notification for your PicturesCollection property. And for x:Bind the default Mode is OneTime, so your PicturesGrid's ItemsSource will always be null.

To fix this issue, you can implement property-change notification for PicturesCollection property like following:

public sealed partial class MainPage : Page, INotifyPropertyChanged { private FileDataSource _PicturesCollection; public event PropertyChangedEventHandler PropertyChanged; public FileDataSource PicturesCollection { get { return _PicturesCollection; } set { if (_PicturesCollection != value) { _PicturesCollection = value; NotifyPropertyChanged(); } } } private void NotifyPropertyChanged([CallerMemberName] String propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } ... private async void initdata() { StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures); string path = pictures.SaveFolder.Path; var source = await FileDataSource.GetDataSoure(path); if (source.Count > 0) { PicturesCollection = source; } } }

And in XAML, set the Mode of x:Bind to OneWay like:

<GridView x:Name="PicturesGrid" SelectionMode="Single" ShowsScrollingPlaceholders="False" ItemsSource="{x:Bind PicturesCollection, Mode=OneWay}"> ... </GridView>

After this, your x:Bind will work.

Updata:

If you only need one-time bindings for asynchronously-loaded data, you can force one-time bindings to be initialized by calling this.Bindings.Update(); after you've loaded data like following:

async void initdata() { StorageLibrary pictures = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Pictures); string path = pictures.SaveFolder.Path; _PicturesCollection = await FileDataSource.GetDataSoure(path); if (_PicturesCollection.Count > 0) { PicturesCollection = _PicturesCollection; this.Bindings.Update(); } }

It’s much cheaper to initialize them this way than it is to have one-way bindings and to listen for changes as it only need to add one method in your code. However, this may be not a good practice when using MVVM. For more info, please see If your data loads asynchronously in Binding object declared using {x:Bind}

Code to support {x:Bind} is generated at compile-time in the partial classes for your pages. These files can be found in your obj folder, with names like (for C#) <view name>.g.cs. The generated code includes a handler for your page's Loading event, and that handler calls the Initialize method on a generated class that represent's your page's bindings. Initialize in turn calls Update to begin moving data between the binding source and the target. Loading is raised just before the first measure pass of the page or user control. So if your data is loaded asynchronously it may not be ready by the time Initialize is called. So, after you've loaded data, you can force one-time bindings to be initialized by calling this->Bindings->Update();. If you only need one-time bindings for asynchronously-loaded data then it’s much cheaper to initialize them this way than it is to have one-way bindings and to listen for changes. If your data does not undergo fine-grained changes, and if it's likely to be updated as part of a specific action, then you can make your bindings one-time, and force a manual update at any time with a call to Update.

更多推荐

使用x:绑定设置GridView项目源的UWP问题

本文发布于:2023-11-10 16:03:12,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1575825.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:绑定   项目   GridView   UWP

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!