Link
Link

Reputation: 76

Am I creating this grid properly?

I'm trying to create an app that (for now) looks through your camera roll and displays your images in a grid. I managed to get a single image to display initially, but when I tried to dynamically create grid rows, no images displayed. Can someone point out what I might be doing incorrectly and how to do this properly? I'm brand new to C# programming and Windows Phone Development so I apologize for the amateur question.

public async void PrintInformation()
    {
        try
        {
            TextBlock.Text = "";
            IReadOnlyCollection<StorageFile> PicLib = await KnownFolders.CameraRoll.GetFilesAsync();
            IEnumerator<StorageFile> PicLibEnum = PicLib.GetEnumerator();
            Debug.WriteLine(PicLib.Count);
            int Count = 0;
            foreach (StorageFile Pic in PicLib)
            {
                if (Count % 3 == 0)
                {
                    Debug.WriteLine("Creating new row..." + Count % 3);
                    MainGrid.RowDefinitions.Add(new RowDefinition());
                }
                Image imageblock = new Image();
                imageblock.SetValue(Grid.ColumnProperty, Count % 3);
                imageblock.SetValue(Grid.RowProperty, Count / 3);
                Debug.WriteLine(imageblock);
                TextBlock.Text += Pic.Path + "\n";
                IRandomAccessStream PicStream = await Pic.OpenReadAsync();
                BitmapImage Bmp = new BitmapImage();
                await Bmp.SetSourceAsync(PicStream);
                //Uri Uri = new Uri(Pic.Path, UriKind.Absolute);
                //Bmp.UriSource = Uri;
                imageblock.Source = Bmp;
                //break;
                Count++;
            }
        }
        catch (Exception e)
        {
            TextBlock.Text = e.ToString();
        }
    }

Upvotes: 0

Views: 80

Answers (2)

Sergio0694
Sergio0694

Reputation: 4567

First thing, don't use:

imageblock.SetValue(Grid.ColumnProperty, Count % 3);

And use instead is equivalent to

Grid.SetColumn(imageblock, Count % 3);

and the second one looks more straightforward to use in my opinion :)

Second thing, dinamically adding new rows and columns is not a good idea: you'd have to update all the column and row indexes of all your images every time you add a new row / column delete an item from your collection. Try using a ListView, bind the source to an observable collection and use an Horizontal WrapGrid inside the ListView. Then simply create your own DataTemplate with an image, Bind its source to each element of your ObservableCollection and you're up :)

You'll be able to add new images without worrying about the layout and the Grid indexes.

EDIT: This is the general idea, in XAML

<ListView ItemsSource="{Binding Source}"
          ItemTemplate="{StaticResource ImageTemplate}"                  
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <WrapGrid Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
</ListView>

Or as Rob Caplan said, you can use a GridView, the end result is almost the same.

Then in your Page.Resources part, something like:

<DataTemplate x:Key="ImageTemplate">
    <Grid Margin="5,5,5,0" Height="45" Width="45">
        <Image Source="{Binding}"/>
    </Grid>
</DataTemplate>

Then you have to create a class that implements INotyfyPropertyChanged, create an instance in your page constructor and assign it to your DataContext.

Inside that class, you'll have something like this:

private ObservableCollection<ImageSource> _Source = new ObservableCollection<ImageSource>();

public ObservableCollection<ImageSource> Source
{
    get
    {
        return this._Source;
    }
    set
    {
        if (this._Source != value)
        {
            this._Source = value;
            this.OnPropertyChanged();
        }
    }
}

Now the ListView will get the source collection from that Property in your ViewModel, and since the Source Property is an ObservableColleciton, every time you add an item the ListView will be notified and it will update its layout :)

Upvotes: 1

Rob Caplan - MSFT
Rob Caplan - MSFT

Reputation: 21899

The reason the Images don't show up is that you don't add them to the Grid. After setting up the images add them to the MainGrid's Children and your code should work

MainGrid.Children.Add(imageblock);

Other that that your code is fine, although more complicated than necessary. As Sergio suggests you can simplify this greatly via data binding. Data binding will let you keep the data and the presentation separate: you can keep a collection of your images in the code and describe how they appear in the Xaml.

If you create an ObservableCollection containing the BitmapImages from your camera roll you can then add to it as needed and the binding will automatically display the new images in the Xaml (the ObservableCollection implements the INotifyCollectionChanged magic to report when items are added or removed). You can also bind to multiple controls with different appearances. If you want just the images you can add them directly to the collection. More commonly you'll bind to a class with other data as well, for example you might include the image and its name.

In Xaml you'll use an ItemsControl to display the collection. Since you want a grid the GridView is a good choice. If you want a List with each item in its own row then a ListView would be better. You can set an ItemTemplate to control how the images appear:

<GridView ItemsSource="{Binding}">
    <GridView.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <Image Source="{Binding Image}" />
                <TextBlock Text="{Binding Name}" />
            </StackPanel>
        </DataTemplate>
    </GridView.ItemTemplate>            
</GridView>

See Data binding overview (XAML) on MSDN for more details

Upvotes: 1

Related Questions