Reputation: 10014
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
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