c# Problèmes UWP Définition de la source des éléments GridView à l'aide de x: Bind




uwp table (2)

Le problème ici est que lorsque votre page est chargée, votre propriété PicturesCollection n'est pas définie, donc ItemsSource votre PicturesGrid est null et vous pouvez vendre noting is you page.

Dans le constructeur de votre MainPage , vous utilisez la méthode initdata pour obtenir toutes les données. Cependant, cette méthode est async void et vous n'avez pas attendu sa finition. En fait, nous ne pouvons pas non plus utiliser await dans le constructeur. Donc, lorsque votre page chargée, l'exécution de await FileDataSource.GetDataSoure(path); peut ne pas être terminé, votre propriété PicturesCollection est toujours null ici, mais ItemsSource de votre PicturesCollection a lié à votre propriété PicturesCollection . Ainsi, l' ItemsSource est null et vous ne pouvez rien voir. Bien que votre propriété PicturesCollection soit définie sur les données réelles ultérieurement, vous n'avez pas implémenté de notification de modification de propriété pour votre propriété PicturesCollection . Et pour x:Bind le Mode par défaut est OneTime , donc ItemsSource votre PicturesGrid sera toujours null .

Pour résoudre ce problème, vous pouvez implémenter la notification de modification de propriété pour la propriété PicturesCollection comme suit:

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

Et en XAML, définissez le Mode de x:Bind à OneWay comme:

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

Après cela, votre x:Bind fonctionnera.

Updata:

Si vous avez uniquement besoin de liaisons ponctuelles pour les données chargées de manière asynchrone, vous pouvez forcer l'initialisation des liaisons ponctuelles en appelant this.Bindings.Update(); après avoir chargé les données comme suit:

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

C'est beaucoup moins cher de les initialiser de cette façon que d'avoir des liaisons unidirectionnelles et d'écouter les changements car il suffit d'ajouter une méthode dans votre code. Cependant, cela peut ne pas être une bonne pratique lors de l'utilisation de MVVM. Pour plus d'informations, consultez Si vos données se chargent de manière asynchrone dans l' objet Binding déclaré à l'aide de {x: Bind}

Le code pour prendre en charge {x: Bind} est généré au moment de la compilation dans les classes partielles de vos pages. Ces fichiers peuvent être trouvés dans votre dossier obj , avec des noms comme (pour C #) <view name>.g.cs . Le code généré inclut un gestionnaire pour l'événement Loading de votre page, et ce gestionnaire appelle la méthode Initialize sur une classe générée qui représente les liaisons de votre page. Initialiser à son tour appelle Update pour commencer à déplacer des données entre la source de liaison et la cible. Le chargement est levé juste avant le premier passage de mesure de la page ou du contrôle utilisateur. Ainsi, si vos données sont chargées de manière asynchrone, elles peuvent ne pas être prêtes au moment où Initialize est appelée. Ainsi, après avoir chargé les données, vous pouvez forcer l'initialisation des liaisons ponctuelles en appelant this->Bindings->Update(); . Si vous n'avez besoin que de liaisons ponctuelles pour les données chargées de manière asynchrone, il est beaucoup moins coûteux de les initialiser de cette façon que d'avoir des liaisons unidirectionnelles et d'écouter les changements. Si vos données ne subissent pas de modifications précises et si elles sont susceptibles d'être mises à jour dans le cadre d'une action spécifique, vous pouvez créer vos liaisons une seule fois et forcer une mise à jour manuelle à tout moment avec un appel à Mettre à jour .

J'essaye de remplir mon gridview avec des photos de la bibliothèque d'images en utilisant la virtualisation de données et la liaison compilée.

J'ai pris le Microsoft UWP ( Data Virtualization Sample ) et en utilisant son FileSource comme base, je l'ai modifié pour utiliser mon propre objet Picture et j'ai essayé de l'appliquer à mon application UWP. Tout ce que je reçois est une page blanche, et le concepteur lance une exception.

Je veux utiliser x: Bind pour lier mon objet source de données dans mon modèle car j'utilise MVVM et je ne veux pas de code-behind.

Je n'ai pas réussi à faire fonctionner cette application dans mon application, j'ai donc écrit une petite application de test qui n'est même pas MVVM et j'ai essayé d'utiliser x: Bind avec ma source de données comme objet dans le code. collection aussi bien.

Je peux obtenir ce travail avec mon objet Picture si je place la source de gridview directement dans mon code-behind (ce que fait l'exemple).

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

La FileDataSource est définie comme suit:

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

Dans mon code, j'ai créé PicturesCollection en tant que propriété:

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

    }
}

et lié à mon 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>

Cela me donne une page blanche, mais si je la mets en code-behind, ça marche. Quelqu'un peut-il me dire pourquoi c'est ainsi? Qu'est-ce que je rate?


Pour utiliser x: Bind dans UWP, vous devez définir un objet du ViewModel en XAML comme ceci:

<Page.DataContext>
    <local:MyViewModel x:Name="MyViewModel"/>
</Page.DataContext>

Et se référer à cela comme ceci:

ItemsSource="{x:Bind MyViewModel.PicturesCollection}"

x: Bind peut uniquement lier à partir d'un objet ViewModel prédéfini en XAML.