c# - 使用 x :Bind 设置 GridView 项目源的 UWP 问题

标签 c# gridview mvvm data-binding win-universal-app

我正在尝试使用数据虚拟化和编译绑定(bind)使用图片库中的照片填充我的 GridView 。

我采用了 Microsoft UWP ( Data Virtualization Sample ) 并使用它的 FileSource 作为我的基础,我修改它以使用我自己的 Picture 对象并尝试将它应用到我的 UWP 应用程序。我得到的只是一个空白页,设计师抛出了一个异常。

我想使用 x:Bind 绑定(bind)到模型中的数据源对象,因为我正在使用 MVVM 并且不想要代码隐藏。

我无法让它在我的应用程序中工作,所以我编写了一个甚至不是 MVVM 的小型测试应用程序,并尝试将 x:Bind 与我的数据源一起用作后面代码中的对象,但它无法绑定(bind)到收藏也是如此。

如果我直接在我的代码隐藏中设置 gridview 的源代码(这就是示例正在做的事情),我可以使用我的 Picture 对象来完成这项工作。

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

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

在我的代码中,我创建了 PicturesCollection 作为属性:
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;
        }

    }
}

并将其绑定(bind)到我的 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>

这给了我一个空白页,但如果我在代码隐藏中设置它,它就可以工作。谁能告诉我为什么会这样?我错过了什么?

最佳答案

这里的问题是当你的页面加载时,你的 PicturesCollection属性未设置,所以你的 PicturesGridItemsSourcenull你可以卖注意到是你的页面。

在您的 MainPage 的构造函数中,您正在使用 initdata获取所有数据的方法。然而这个方法是async void你没有等待它的完成。其实我们也不能用await在构造函数中。所以当你的页面加载完毕后,执行await FileDataSource.GetDataSoure(path);可能还没完,你PicturesCollection属性仍为 null在这里,但你的 PicturesGridItemsSource已绑定(bind)到您的PicturesCollection属性(property)。因此,ItemsSource为空,您什么也看不到。虽然您的 PicturesCollection属性稍后将设置为真实数据,但您没有为您的 PicturesCollection 实现属性更改通知属性(property)。对于 x:Bind默认 ModeOneTime ,所以你的 PicturesGridItemsSource将永远是 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(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;
        }
    }
}

在 XAML 中,设置 Modex:BindOneWay像:
<GridView x:Name="PicturesGrid"
          SelectionMode="Single"
          ShowsScrollingPlaceholders="False"
          ItemsSource="{x:Bind PicturesCollection, Mode=OneWay}">
    ...
</GridView>

在此之后,您的 x:Bind将工作。

更新:

如果您只需要对异步加载的数据进行一次性绑定(bind),您可以通过调用 来强制初始化一次性绑定(bind)。 this.Bindings.Update(); 在您加载如下数据后:
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();
    }
}

以这种方式初始化它们比使用单向绑定(bind)和监听更改要便宜得多,因为它只需要在代码中添加一个方法。但是,在使用 MVVM 时,这可能不是一个好的做法。更多信息,请查看如果您的数据异步加载 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.

关于c# - 使用 x :Bind 设置 GridView 项目源的 UWP 问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37167692/

相关文章:

c# - 有没有办法使用 C# 获取 autocad (.dwg) 中的所有多段线?

c# - 如何在特定时间每天在 C# 中调用一个方法?

c# - 如何使用 C# 在 gridview 中添加新行?

wpf - WPF可编辑组合框验证

mvvm - Prism mvvm 中的 ViewModelLocator

c# - 找不到服务客户端

asp.net - 如何使用自动生成的列隐藏ASP.NET GridView中的列?

flutter - 我不能在ScrollView中使用GridView,而只能在我展开时​​使用GridView。如何在Page中添加真正的ScrollView?

c# - RaiseCanExecuteChanged 在编译的 exe 中不起作用,但在调试时起作用

c# - 一种类型如何访问另一种类型属性的私有(private) setter ?