mayabelle
mayabelle

Reputation: 10014

Swapping out ListView Grid in XAML/Xamarin (Two grid versions, single code behind)

I am trying to implement a button that toggles between a list and tile view of some items. The code behind is identical for both and the only difference is the size of what is displayed and the properties. Currently I have the list view implemented and the XAML looks like this:

  <StackLayout>
    <ListView x:Name="FilesListView"
              ItemsSource="{Binding Files}"
              ItemSelected="FileItemSelected">
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <ViewCell.View>
              <Grid>
                <Grid.ColumnDefinitions>
                  <ColumnDefinition Width="Auto"/>
                  <ColumnDefinition Width="*"/>
                  <ColumnDefinition Width="Auto"/>
                  <ColumnDefinition Width="Auto"/>
                </Grid.ColumnDefinitions>
                <Image Grid.Column="0" Source="{Binding IsFolder, Converter={StaticResource fileTypeToImageConverter}}" WidthRequest="40" HeightRequest="40" HorizontalOptions="Start"/>
                <Label Grid.Column="1" Text="{Binding Path, Converter={StaticResource pathToFilenameConverter}}" HorizontalOptions="StartAndExpand"/>
                <Label Grid.Column="2" Text="{Binding ModifiedDate, Converter={StaticResource dateConverter}}" HorizontalOptions="EndAndExpand"/>
                <Label Grid.Column="3" Text="{Binding Size, Converter={StaticResource fileSizeConverter}}" HorizontalOptions="EndAndExpand"/>
              </Grid>
            </ViewCell.View>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackLayout>

This is a partial view; the FileBrowser control gets nested in another stack layout that contains the toggle button. The button is defined in XAML like this:

  <ContentPage.ToolbarItems>
    <ToolbarItem Text="Toggle View" Order="Primary" Icon="{Binding ViewIcon}" Command="{Binding ToggleViewCommand}"/>
  </ContentPage.ToolbarItems>

where ToggleViewCommand is:

    protected void ToggleView()
    {
        FileBrowserControl.ToggleView();
        ViewModel.ToggleViewIcon();
    }

I need help figuring out how to implement FileBrowserControl.ToggleView();. I need to either dynamically swap out the grid XAML with a different XAML snippet (representing the tile view), or update the grid in code. The first option seems easier in terms of defining what the tile view would look like, but I don't want to duplicate the code behind logic by creating FileBrowserXaml.xaml/FileBrowserXaml.xaml.cs which would have the same code behind logic as FileBrowser.xaml.cs. What is the Xamarin best practice for what I'm trying to do?

(As a side note, this needs to be cross platform.)


Update: I decided to try to build the data template in code. I have this so far:

    public void ToggleView()
    {
        DataTemplate itemTemplate;
        if (_view == FileBrowserView.List)
        {
            itemTemplate = new DataTemplate(() =>
            {
                var cell = new ViewCell();
                var grid = new Grid();

                // Define columns
                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) });
                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
                grid.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });

                // Define rows
                var image = new Image
                {
                    WidthRequest = 40,
                    HeightRequest = 40,
                    HorizontalOptions = LayoutOptions.Start
                };
                image.SetBinding(Image.SourceProperty,
                                 new Binding("IsFolder", BindingMode.Default, new FileTypeToImageValueConverter()));
                grid.Children.Add(image, 0, 0);

                var pathLabel = new Label {HorizontalOptions = LayoutOptions.StartAndExpand};
                pathLabel.SetBinding(Label.TextProperty,
                                     new Binding("Path", BindingMode.Default, new PathToFilenameValueConverter()));
                grid.Children.Add(pathLabel, 1, 0);

                var dateLabel = new Label { HorizontalOptions = LayoutOptions.StartAndExpand };
                dateLabel.SetBinding(Label.TextProperty,
                                     new Binding("ModifiedDate", BindingMode.Default, new DateTimeDisplayValueConverter()));
                grid.Children.Add(dateLabel, 2, 0);

                var sizeLabel = new Label { HorizontalOptions = LayoutOptions.EndAndExpand };
                dateLabel.SetBinding(Label.TextProperty,
                                     new Binding("Size", BindingMode.Default, new FileSizeDisplayValueConverter()));
                grid.Children.Add(sizeLabel, 3, 0);

                cell.View = grid;
                return cell;
            });
        }
        else
        {
            itemTemplate = new DataTemplate(() =>
            {
                var cell = new ViewCell();
                var grid = new Grid();

                // Define columns
                grid.ColumnDefinitions.Add(new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)});
                grid.ColumnDefinitions.Add(new ColumnDefinition {Width = new GridLength(1, GridUnitType.Star)});

                // Define rows
                var image = new Image
                {
                    WidthRequest = 80,
                    HeightRequest = 80,
                    HorizontalOptions = LayoutOptions.Start
                };
                image.SetBinding(Image.SourceProperty,
                                 new Binding("IsFolder", BindingMode.Default, new FileTypeToImageValueConverter()));
                grid.Children.Add(image, 0, 0);

                var pathLabel = new Label { HorizontalOptions = LayoutOptions.Start };
                pathLabel.SetBinding(Label.TextProperty,
                                     new Binding("Path", BindingMode.Default, new PathToFilenameValueConverter()));
                grid.Children.Add(pathLabel, 0, 1);

                cell.View = grid;
                return cell;
            });
        }
        FilesListView.ItemTemplate = itemTemplate;
    }

The issue I am having now is, in the tile view, every item is on its own row. I want them to wrap (the path should be underneath the image, but each path/image combo should be side-by-side with the next one, like in Windows Explorer or in a typical app launcher). What is the best way to build a data template to achieve this? Is a grid the correct template to use or is there a better one?

Upvotes: 2

Views: 2476

Answers (1)

trippedOverXaml
trippedOverXaml

Reputation: 171

Have you tried using the ResourceDictionary and StaticResource?

I am thinking you may be able to accomplish what you want by having the ListView ItemTemplate set to a StaticResource and then having the toggle button switch out which one is used for the view.

Below is kind of what I am thinking of:

<ContentPage>
    <ContentPage.Resources>
       <ResourceDictionary>

          <DataTemplate x:Key="TileViewTemplate">
             <ViewCell>
                <ViewCell.View>
                   <!-- the layout of your template here -->
                </ViewCell.View>
             </ViewCell>
          </DataTemplate>

          <DataTemplate x:Key="ListViewTemplate">
             <ViewCell>
                <ViewCell.View>
                   <!-- the layout of your template here -->
                </ViewCell.View>
             </ViewCell>
          </DataTemplate>

       </ResourceDictionary>
    </ContentPage.Resources>

 <ListView x:Name="FilesListView" ItemsSource="{Binding Files}" 
           ItemsSelected="FileItemSelected"
           ItemTemplate="{StaticResource TileViewTemplate}"/>

</ContentPage>

And then, the toggleview command could switch the ItemTemplate property. This way you are not having to worry about two ListViews - and it can keep the XAML a bit more manageable.

Just a thought, but the ResourceDictionary has really been helpful for me.

Upvotes: 0

Related Questions