Dynamic ListView display based on Itemsource {Binding} to ObservableCollection<"this can change and dictates listview style"?> - xaml

Summary of the Question: What is the correct way to manipulate the same, single ObservableCollection in a xaml page's viewmodel (binded to the ListView of the page ), at runtime in order to show different sets of data, each variation of data providing its own ListView-Style via a StyleSelector?
Description:
I have a UWP xaml page with a single ListView, I want this listview to display all different possible data sets the user might want to see. e.g: A dataset could be between one to 15 columns of data, all with headers. The ListView's ItemSource will use binding to an ObservableCollection to populate it. The ObservableCollection can be populated manually or with one of many SQL sourced DataTable's.
<ListView x:Name="UserPageListView"
ItemsSource="{Binding MainListData, Mode=TwoWay}"
Grid.Column="1"
Width="auto"
Background="Gray"
ItemContainerStyleSelector="{StaticResource UserPage_StyleSelector}">
</ListView>
I have tried binding the ItemContainerStyleSelector to provide a xaml ListView Style (which is stored in a ResourceDictionary), based on the data type of the ObservableCollection, or at least that was the idea. I don't know whether the ObservableCollection's data type should be generic or a defined class per data set to view. The latter makes sense, since a StyleSelector would need it for logic to provide the relevant Style. I used StyleSelector instead of DataTemplateSelector since I want the Selector to include HeaderTemplate as well as ItemTemplate(headers of columns change with the different data sets):
public class UserPage_StyleSelector:StyleSelector
{
public Style WatchlistStyle { get; set; }
public Style UserDetailStyle { get; set; }
protected override Style SelectStyleCore(object item, DependencyObject container)
{
if (item is WatchlistData)
return WatchlistStyle;
if (item is UserDetailData)
return UserDetailStyle;
return base.SelectStyleCore(item, container);
}
}
Style example in ResourceDictionary:
<Style TargetType="ListView"
x:Key="UserDetail_ListView"
x:Name="UserDetail_ListView">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<Grid Padding="12"
Background="{ThemeResource SystemBaseLowColor}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock Text="UserDetails"
Style="{ThemeResource CaptionTextBlockStyle}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="{Binding Details}"
FontSize="12"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
StyleSelector defined in the xaml page:
<Page.Resources>
<viewModels:UserPage_StyleSelector x:Key="UserPage_StyleSelector"
WatchlistStyle="{StaticResource WatchList_ListView}"
UserDetailStyle="{StaticResource UserDetail_ListView}"/>
</Page.Resources>
The ResourceDictionary is defined in app.xaml.cs. Have I complicated this endeavour far too much by using the wrong approach?

ItemContainerStyle target type is ListViewItem, so you can't make ListView style in ItemContainerStyleSelector.
Derive from your requirement (headers of columns change with the different data sets), you need make GroupStyleSelector and ItemTemplateSelector for different header and columns.
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock
Margin="5"
FontSize="25"
Foreground="Gray"
Text="{Binding Name}"
/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
For more detail please refer this document.

Related

Xamarin Forms Rowspan in CollectionView

I have a problem.
I created this CollectionView with a few dummy trades, that looks like this right now:
Now this is almost like I want it, except for 1 thing: I want the last column to get a rowspan over both the rows, like this:
Now if this was a regular Grid, I could do it with Grid.RowSpawn, but it is in a CollectionView, because I can have a lot of trades. The downside of the CollectionView is that each row is a different Grid, so they are actually not connected! Here is my code right now:
<CollectionView ItemsSource="{Binding agentOrderList}" Margin="0" HeightRequest="450">
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid RowSpacing="0">
<StackLayout Orientation="Vertical">
<StackLayout HorizontalOptions="FillAndExpand" HeightRequest="1" BackgroundColor="White"/>
<Grid RowSpacing="0" Margin="5,0,5,0">
<Grid.RowDefinitions>
<RowDefinition Height="21" />
<RowDefinition Height="21" />
<RowDefinition Height="4" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65" />
<ColumnDefinition Width="95" />
<ColumnDefinition Width="40" />
<ColumnDefinition Width="75" />
<ColumnDefinition Width="82" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="0" Text="{Binding Date}" FontAttributes="Bold" TextColor="#00D8FF" FontSize="18" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="1" Text="{Binding Action}" TextColor="White" FontSize="18" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="2" Text="{Binding Coin}" TextColor="White" FontSize="18" VerticalOptions="CenterAndExpand"/>
<Label Grid.Row="0" Grid.RowSpan="2" Grid.Column="3" Text="{Binding Price}" TextColor="White" FontSize="18" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"/>
<Label Grid.Row="0" Grid.Column="4" Text="{Binding ProfitUSDT}" TextColor="{Binding ProfitColor}" FontSize="18" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"/>
<Label Grid.Row="1" Grid.Column="4" Text="{Binding ProfitPerc}" TextColor="{Binding ProfitColor}" FontSize="18" VerticalOptions="CenterAndExpand" HorizontalTextAlignment="End"/>
</Grid>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
Now what is the best way to achieve this? Do I need to make a different view?
Please let me know!
You can't have data cross over between cells in a ListView, so I think you will need to make each list item have a two row grid, so then you can have the last column span two rows.
However, I am confused. Your XAML code does not seem to match the screen shot of one cell. Your XAML code only seems to assign 5 columns, but I see 6 in your screen shots?
IN any case, you are correct, you will need to use one grid to display the Buy and Sell data so that that last column can span 2 rows.
UPDATE: Based on comments below, it seems a DataTemplateSelector may be required here. See: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/templates/data-templates/selector
Partial extract from above link:
A data template selector enables scenarios such as a ListView binding
to a collection of objects, where the appearance of each object in the
ListView can be chosen at runtime by the data template selector
returning a particular DataTemplate.
Creating a DataTemplateSelector A data template selector is
implemented by creating a class that inherits from
DataTemplateSelector. The OnSelectTemplate method is then overridden
to return a particular DataTemplate, as shown in the following code
example:
public class PersonDataTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidTemplate { get; set; }
public DataTemplate InvalidTemplate { get; set; }
protected override DataTemplate OnSelectTemplate (object item, BindableObject container)
{
return ((Person)item).DateOfBirth.Year >= 1980 ? ValidTemplate : InvalidTemplate;
}
}
The OnSelectTemplate method returns the
appropriate template based on the value of the DateOfBirth property.
The template to return is the value of the ValidTemplate property or
the InvalidTemplate property, which are set when consuming the
PersonDataTemplateSelector.
An instance of the data template selector class can then be assigned
to Xamarin.Forms control properties such as ListView.ItemTemplate. For
a list of valid properties, see Creating a DataTemplate.
Limitations
DataTemplateSelector instances have the following limitations:
The DataTemplateSelector subclass must always return the same template
for the same data if queried multiple times.
The DataTemplateSelector subclass must not return another DataTemplateSelector subclass.
The DataTemplateSelector subclass must not return new instances of a
DataTemplate on each call. Instead, the same instance must be
returned. Failure to do so will create a memory leak and will disable
virtualization.
On Android, there can be no more than 20 different
data templates per ListView.
Consuming a DataTemplateSelector in XAML
In XAML, the PersonDataTemplateSelector can be instantiated by
declaring it as a resource, as shown in the following code example:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:Selector;assembly=Selector" x:Class="Selector.HomePage">
<ContentPage.Resources>
<ResourceDictionary>
<DataTemplate x:Key="validPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<DataTemplate x:Key="invalidPersonTemplate">
<ViewCell>
...
</ViewCell>
</DataTemplate>
<local:PersonDataTemplateSelector x:Key="personDataTemplateSelector"
ValidTemplate="{StaticResource validPersonTemplate}"
InvalidTemplate="{StaticResource invalidPersonTemplate}" />
</ResourceDictionary>
</ContentPage.Resources>
...
</ContentPage>
This page level ResourceDictionary defines two DataTemplate instances and a
PersonDataTemplateSelector instance. The PersonDataTemplateSelector
instance sets its ValidTemplate and InvalidTemplate properties to the
appropriate DataTemplate instances by using the StaticResource markup
extension. Note that while the resources are defined in the page's
ResourceDictionary, they could also be defined at the control level or
application level.
The PersonDataTemplateSelector instance is consumed by assigning it to
the ListView.ItemTemplate property, as shown in the following code
example:
<ListView x:Name="listView" ItemTemplate="{StaticResource personDataTemplateSelector}" />
At runtime, the ListView calls the
PersonDataTemplateSelector.OnSelectTemplate method for each of the
items in the underlying collection, with the call passing the data
object as the item parameter. The DataTemplate that is returned by the
method is then applied to that object.

XAML: Binding scrollviewer with 2 sources

Trying to learn xaml designing. I got this picture from internet and use it as a model to practice. There is a problem: When I click one of day buttons, a scrollviewer with a list of day tasks appears. Each task has a list of hours in day, from start time to end time. I don't know whether they are from one source or 2 source (one describes day task and the other one is a list of hours of this task).
I tried with this
public async void ShowTasks()
{
ObservableCollection<DayTask> Data = await App.DataSource.GetData();
DateListBox.ItemsSource = Data;
DayTask daytask = Data.Where(x => x.Day.Date == App.Day.Date).FirstOrDefault();
TasksListView.ItemsSource = daytask.Tasks;
List<HoursModel> HoursList = new List<HoursModel>();
foreach (var simpletask in daytask.Tasks)
{
HoursList.Add(new HoursModel(simpletask.StartTimeHour, simpletask.EndTimeHour));
}
HoursListView.ItemSource = HoursList;
}
This is my xaml code:
<ScrollViewer Height="730" Width="300" >
<ListView
x:Name="TasksListView" Height="730" Width="300"
HorizontalAlignment="Right">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
x:Name="HoursListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock
x:Name="ShowedHours"
Text="{Binding}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Column="1">
<Rectangle
Fill="Pink"
Height="75"
Width="150"
Margin="60,20,0,0"/>
<TextBlock
Text="{Binding Description}"
FontSize="17"
TextWrapping="Wrap"
Margin="80,20,0,0"/>
</Grid>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
I got an error with HoursListView.ItemSource = HoursList;. The detail of this error is
The name 'HoursListView' does not exist in the current context. Does anyone have any suggestions how I can do this ?
You can't access control in DataTemplate directly by it's name. That makes sense because DataTemplate in this case is generated repeatedly for each item within TasksListView.
According to XAML markup posted, you want to have different HoursList for each DayTask. That can be achieved by adding an HoursList property in DayTask class. I'd suggest to use ObservableCollection instead of List because the former has built-in feature to notify bound UI to refresh whenever item added to or removed from collection :
public class DayTask
{
public ObservableCollection<HoursModel> HoursList { get; set; }
.....
}
Then bind HoursListView's ItemsSource to above property :
.....
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="7*"/>
</Grid.ColumnDefinitions>
<ListView
Grid.Column="0"
ItemsSource="{Binding HoursList}"
x:Name="HoursListView">
.....
</ListView>
</Grid>
</DataTemplate>
.....
Don't forget to populate all HoursList properties before setting DateListBox.ItemsSource.

Windows StoreApp XAML: Changing ItemTemplate in GridView based on Data type

I am working on developing Windows Store App using C#/XAML. I have experience primarily in iOS and to some extent Android app development, but not yet comfortable with C#/XAML world.
Here is my issue in GridView based page (Based on the nice template VS2012 generates).
I have a gridview and its collection is bound to a data retrieved from network and it works fine.
But I want to change the grid item depending on the data.
For example: I have files and folders that I would like to show using different grid view items.
My Question: How would I use a different DataTemplate for the ItemTemplate depending on the data? For example, for "Folders", I will have only one textblock which is vertically centered and for File, I will have the 2 textblocks and visually different.
Am I going the right path or should I be doing completely different?
The XAML Portion is
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="3"
Padding="116,137,40,46"
ItemsSource="{Binding Source={StaticResource groupedItemsViewSource}}"
ItemTemplate="{StaticResource FileEntriesTemplate}"
ItemClick="ItemView_ItemClick"
IsItemClickEnabled="True"
SelectionMode="None"
IsSwipeEnabled="false">
The Template is
<DataTemplate x:Key="FileEntriesTemplate">
<Grid HorizontalAlignment="Left" Width="400" Height="80" Background="Beige">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="{Binding Image}" Stretch="Uniform" Grid.Column="0" Margin="10,0,0,0" AutomationProperties.Name="{Binding Title}"/>
<StackPanel Orientation="Vertical" Grid.Column="1" Background="Transparent">
<TextBlock Text="{Binding Title}" Foreground="Black" Style="{StaticResource LargeTitleTextStyle}" Margin="20,20,10,0"/>
<TextBlock Text="{Binding Subtitle}" Foreground="gray" Style="{StaticResource CaptionTextStyle}" TextWrapping="NoWrap" Margin="20,10,0,30"/>
</StackPanel>
</Grid>
GridView exposes this through the ItemTemplateSelector property which is a class you can create that inherits from DataTemplateSelector. An example would be that I have a GridView that has Issues and Repositories bound to it and want to use different data templates for each.
My data template selector looks like:
public class IssueSummaryTemplateSelector : DataTemplateSelector
{
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
return item is IssueGroupViewModel ? IssueTemplate : RepositoryTemplate;
}
public DataTemplate RepositoryTemplate
{
get;
set;
}
public DataTemplate IssueTemplate
{
get;
set;
}
}
I then declare the selector as a Resource in xaml assigning the two templates I want to use for Repository and Issues.
<selectors:IssueSummaryTemplateSelector x:Key="IssueSummarySelector"
IssueTemplate="{StaticResource IssueGridZoomedOutTemplate}"
RepositoryTemplate="{StaticResource IssueGridRepositoryZoomedOutTemplate}"/>
You can then use it on your GridView.
<GridView ItemTemplateSelector="{StaticResource IssueSummarySelector}" />

Dynamically change a styled background color; XAML/VB in Visual Studio Express 2012 for Windows 8

In a DataTemplate in StandardStyles.xaml I have this StackPanel:
<DataTemplate x:Key="Standard160x160ItemTemplate">
<Grid HorizontalAlignment="Left" Width="160" Height="160">
...
<StackPanel
VerticalAlignment="Top"
Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
<TextBlock Text="{Binding UniqueID}"
Foreground="{StaticResource ListViewItemOverlayForegroundThemeBrush}"
Style="{StaticResource TitleTextStyle}" Height="30" Margin="15,0,15,0"/>
</StackPanel>
...
</Grid>
</DataTemplate>
"uniqueID" is a property of a "Product" class:
Public NotInheritable Class Product
Private Property _sUID As String = String.Empty
Public Property UniqueID As String
Get
Return Me._sUID
End Get
Set(value As String)
Me.SetProperty(Me._sUID, value)
End Set
End Property
...
End Class
I use above template "Standard160x160ItemTemplate" in a grid view item like this:
<GridView Height="210"
x:Name="ItemView"
SelectionMode="None"
ItemsSource="{Binding Source={StaticResource itemsViewSource}}">
<GridViewItem
x:Name="GridViewItem"
ContentTemplate="{StaticResource Standard160x160ItemTemplate}"
Tapped="GridViewItem_Tapped">
<GridViewItem.Style>
<Style TargetType="FrameworkElement">
<Setter Property="Margin" Value="0,0,0,0"/>
</Style>
</GridViewItem.Style>
</GridViewItem>
</GridView>
This works well and does what it should.
However, in some cases (depending on two other properties of the "Product" object, specifically if one of them has a lower UInt value than the other) I want to change the StackPanel's background to a solid "Red" instead of "{StaticResource ListViewItemOverlayBackgroundThemeBrush}".
I don't doubt it is possible, but I am new to XAML (not to VB though) and am still overwhelmed by the thousands of XAML tags and am really struggling to find a solution.
So the question is: How can I dynamically change the template's background, based on the "Product" properties "A" and "B"?
The best way would probably be to use custom IValueConverter. Here's the example from MSDN.

How to reuse Xaml code templates?

Say i have this XAML :
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
I'd like re use it across multiple places.
For example as a DataTemplate for a ItemsControl but also as The basis for something like a buttons content.
How would i go about doing this?
I'm thinking something like an ASP.NET Partial view.
I don't want to use a usercontrol as i don't require any code behind.
I managed to get it to work in a way using a style:
<Style x:Key="myStyle" TargetType="Control">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It insisted on me using a TargetType otherwise it complained about setting the template.
I can now use this on any control.
If i want to then use this essentially as a datatemplate, i can just set the style of the placeholder item within the datatemplate (probably a ContentControl) to use this.
Create a StylesResourceDictionary.xaml and create a staticresource in your App.Xaml. At runtime, the styles will get binded and then you can reference anything from the dictionary anywhere in your app, across usercontrols or datatemplates etc.