MAUI RadioButton with ControlTemplate and DataTemplate doesn't render content - radio-button

I am trying to create RadioButtons that bind to a list of content using a DataTemplate, and render based on a ControlTemplate. The data binding is working - I get a button for each element in the list. The VisualStateManager is also working - I get the requested state changes as I click on the buttons.
However, the ContentPresenter is not working - my buttons are empty. I'm also getting an InvalidCastException each time I click on a button. Here is my code.
<ContentPage.BindingContext>
<pages:ActivitiesViewModel/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ControlTemplate x:Key="MuscleGroupButtonsTemplate">
<Border
Stroke="{StaticResource SecondaryBrush}"
StrokeThickness="1"
Background="Transparent"
>
<Border.StrokeShape>
<RoundRectangle CornerRadius="8"/>
</Border.StrokeShape>
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CheckedStates">
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter
Property="Background"
Value="{StaticResource AccentBrush}"/>
<Setter
Property="Stroke"
Value="{StaticResource SecondaryBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.Setters>
<Setter
Property="Background"
Value="{StaticResource NeutralBrush}"/>
<Setter
Property="Stroke"
Value="{StaticResource SecondaryBrush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
<ContentPresenter HeightRequest="64"/>
</Border>
</ControlTemplate>
</ContentPage.Resources>
<VerticalStackLayout Spacing="12"
RadioButtonGroup.GroupName="MuscleGroups"
RadioButtonGroup.SelectedValue="{Binding SelectedMuscleGroup}"
BindableLayout.ItemsSource="{Binding MuscleGroupList}">
<VerticalStackLayout.Resources>
<Style TargetType="RadioButton">
<Setter Property="ControlTemplate" Value="{StaticResource MuscleGroupButtonsTemplate}"/>
</Style>
</VerticalStackLayout.Resources>
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="m:MuscleGroup">
<RadioButton Value="{Binding Id}" CheckedChanged="OnMuscleGroupChanged">
<RadioButton.Content>
<HorizontalStackLayout Margin="12,6,0,0" Spacing="8">
<Image WidthRequest="64"
Aspect="AspectFit"
Source="{Binding Icon}"/>
<Label Style="{StaticResource Headline}" VerticalOptions="Center">
<Label.FormattedText>
<FormattedString>
<Span Text="{Binding Name}"/>
</FormattedString>
</Label.FormattedText>
</Label>
</HorizontalStackLayout>
</RadioButton.Content>
</RadioButton>
</DataTemplate>
</BindableLayout.ItemTemplate>
</VerticalStackLayout>
I'm also puzzled because the Visual Tree shows the expected control hierarchy.
Oh, and BTW - my RadioButtonGroup.SelectedValue is not working either.
I would appreciate any assistance. Thanks.
Chuck

With Content part of ControlTemplate and use of TemplateBinding Content.<property>:
<ControlTemplate x:Key="RadioButtonControlTemplate">
<Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CheckedStates">
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Property="Stroke" Value="Red"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.Setters>
<Setter Property="Stroke" Value="Green"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
<VerticalStackLayout x:DataType="m:NameId">
<Label Text="{TemplateBinding Content.Name}"/>
<Label Text="{TemplateBinding Content.Id}"/>
</VerticalStackLayout>
</Border>
</ControlTemplate>
VerticalStackLayout:
<VerticalStackLayout
RadioButtonGroup.GroupName="NameIdGroup"
BindableLayout.ItemsSource="{Binding NameIdList}">
<VerticalStackLayout.Resources>
<Style TargetType="RadioButton">
<Setter Property="ControlTemplate"
Value="{StaticResource RadioButtonControlTemplate}"/>
</Style>
</VerticalStackLayout.Resources>
<BindableLayout.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding .}">
<RadioButton.GestureRecognizers>
<TapGestureRecognizer
Tapped="RadioButton_Tapped"
CommandParameter="{Binding .}" />
</RadioButton.GestureRecognizers>
</RadioButton>
</DataTemplate>
</BindableLayout.ItemTemplate>
</VerticalStackLayout>
NameId class and ViewModel:
public class NameId
{
public string Name { get; set; }
public string Id { get; set; }
}
public class ViewModel
{
public ObservableCollection<NameId> NameIdList { get; set; }
public ViewModel()
{
NameIdList = new ObservableCollection<NameId>
{
new NameId { Id = "Id A", Name = "Name A" },
new NameId { Id = "Id B", Name = "Name B" },
new NameId { Id = "Id C", Name = "Name C" }
};
}
}

RadioButton with ControlTemplate and DataTemplate doesn't render content.
You can add RadioButton to a CustomControl:
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Name="this"
x:Class="MauiApp1.CustomControls.CustomControls">
<ContentView.Resources>
<ControlTemplate x:Key="MuscleGroupButtonsTemplate">
<Border Stroke="{StaticResource SecondaryBrush}" StrokeThickness="2" Background="Transparent">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8"/>
</Border.StrokeShape>
<VisualStateManager.VisualStateGroups>
<VisualStateGroupList>
<VisualStateGroup x:Name="CheckedStates">
<VisualState x:Name="Checked">
<VisualState.Setters>
<Setter Property="Background" Value="LightSkyBlue"/>
<Setter Property="Stroke" Value="{StaticResource SecondaryBrush}"/>
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Unchecked">
<VisualState.Setters>
<Setter Property="Background" Value="YellowGreen"/>
<Setter Property="Stroke" Value="{StaticResource SecondaryBrush}"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroupList>
</VisualStateManager.VisualStateGroups>
<ContentPresenter HeightRequest="50" />
</Border>
</ControlTemplate>
<Style TargetType="RadioButton">
<Setter Property="ControlTemplate" Value="{StaticResource MuscleGroupButtonsTemplate}" />
</Style>
</ContentView.Resources>
<VerticalStackLayout BindingContext="{x:Reference this}">
<RadioButton Content="{Binding CardTitle}"
GroupName="MuscleGroups"
CheckedChanged="OnMuscleGroupChanged"/>
</VerticalStackLayout>
</ContentView>
CustomControls.xaml.cs:
public partial class CustomControls : ContentView
{
public static readonly BindableProperty CardTitleProperty
= BindableProperty.Create(nameof(CardTitle), typeof(string),
typeof(CustomControls), string.Empty);
public string CardTitle
{
get => (string)GetValue(CustomControls.CardTitleProperty);
set => SetValue(CustomControls.CardTitleProperty, value);
}
public CustomControls()
{
InitializeComponent();
}
private void OnMuscleGroupChanged(object sender, CheckedChangedEventArgs e)
{
RadioButton radioButton = (RadioButton)sender;
Console.WriteLine(radioButton.Content.ToString().Trim());
}
}
And then use it in Page.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:MauiApp1.CustomControls"
x:Class="MauiApp1.NewPage2"
Title="NewPage2">
<VerticalStackLayout Spacing="12">
<CollectionView x:Name="listview">
<CollectionView.ItemTemplate>
<DataTemplate>
<controls:CustomControls CardTitle="{Binding Name}"/>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
Page.xaml.cs:
public partial class NewPage2 : ContentPage
{
private List<Monkey> source = new List<Monkey>();
public NewPage2()
{
InitializeComponent();
CreateMonkeyCollection();
listview.ItemsSource = source;
}
void CreateMonkeyCollection()
{
source.Add(new Monkey
{
Name = "Golden Lion Tamarin",
Location = "Brazil",
Details = "The golden lion tamarin also known as the golden marmoset, is a small New World monkey of the family Callitrichidae.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/Golden_lion_tamarin_portrait3.jpg/220px-Golden_lion_tamarin_portrait3.jpg"
});
source.Add(new Monkey
{
Name = "Mandrill",
Location = "Southern Cameroon, Gabon, and Congo",
Details = "The mandrill is a primate of the Old World monkey family, closely related to the baboons and even more closely to the drill. It is found in southern Cameroon, Gabon, Equatorial Guinea, and Congo.",
ImageUrl = "https://upload.wikimedia.org/wikipedia/commons/thumb/7/75/Mandrill_at_san_francisco_zoo.jpg/220px-Mandrill_at_san_francisco_zoo.jpg"
});
source.Add(new Monkey
{
Name = "Proboscis Monkey",
Location = "Borneo",
Details= "The proboscis monkey or long-nosed monkey, known as the bekantan in Malay, is a reddish-brown arboreal Old World monkey that is endemic to the south-east Asian island of Borneo.",
ImageUrl= "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/Proboscis_Monkey_in_Borneo.jpg/250px-Proboscis_Monkey_in_Borneo.jpg"
});
source.Add(new Monkey
{
Name = "Golden Snub-nosed Monkey",
Location = "China",
Details= "The golden snub-nosed monkey is an Old World monkey in the Colobinae subfamily. It is endemic to a small area in temperate, mountainous forests of central and Southwest China. They inhabit these mountainous forests of Southwestern China at elevations of 1,500-3,400 m above sea level. The Chinese name is Sichuan golden hair monkey. It is also widely referred to as the Sichuan snub-nosed monkey. Of the three species of snub-nosed monkeys in China, the golden snub-nosed monkey is the most widely distributed throughout China.",
ImageUrl= "https://upload.wikimedia.org/wikipedia/commons/thumb/c/c8/Golden_Snub-nosed_Monkeys%2C_Qinling_Mountains_-_China.jpg/165px-Golden_Snub-nosed_Monkeys%2C_Qinling_Mountains_-_China.jpg"
});
}
}
The ContentPresenter works well. I don't know code structure of ActivitiesViewModel, so replace it with Monkey.

Related

UWP : Worries with itemtemplateselector with GridView

I have a problem on an UWP project I'm currently working on :
I have a viewmodel like this :
public class HomePageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<WidgetViewModel> Widgets { get; } = new ObservableCollection<WidgetViewModel>();
public HomePageViewModel()
{
}
public void OnNotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And the WidgetViewModel is defined like this :
public class WidgetViewModel : INotifyPropertyChanged
{
private string group;
public string Group
{
get=>group;
set
{
group=value;
OnNotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<ItemViewModel> Items { get; } = new ObservableCollection<ItemViewModel>();
public WidgetPageViewModel()
{
}
public void OnNotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And my ItemViewModel is defined like this :
public class ItemViewModel : INotifyPropertyChanged
{
private string name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get=>name;
set
{
name = value;
OnNotifyPropertyChanged();
}
}
public ItemViewModel()
{
}
public void OnNotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I have produced this xaml :
<Page x:Class="Project.TestPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="Project"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<CollectionViewSource x:Name="TestSource"
IsSourceGrouped="True"
Source="{Binding Widgets}" />
<local:ItemTemplateSelector x:Key="ItemTemplateSelector">
<local:ItemTemplateSelector.WidgetTemplate>
<DataTemplate>
<local:TestItemsControl ItemTemplateSelector="{StaticResource ItemTemplateSelector}"
ItemsSource="{Binding Items}">
<local:TestItemsControl.ItemContainerStyle>
<Style TargetType="local:TestItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</local:TestItemsControl.ItemContainerStyle>
</local:TestItemsControl>
</DataTemplate>
</local:ItemTemplateSelector.WidgetTemplate>
<local:ItemTemplateSelector.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</local:ItemTemplateSelector.ItemTemplate>
</local:ItemTemplateSelector>
<x:String x:Key="ChevronGlyph"></x:String>
<Style x:Key="TextButtonStyle" TargetType="ButtonBase">
<Setter Property="MinWidth" Value="0" />
<Setter Property="MinHeight" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ButtonBase">
<Grid Background="Transparent">
<ContentPresenter x:Name="Text" Content="{TemplateBinding Content}" />
<Rectangle x:Name="FocusVisualWhite"
IsHitTestVisible="False"
Opacity="0"
Stroke="{StaticResource FocusVisualWhiteStrokeThemeBrush}"
StrokeDashArray="1,1"
StrokeDashOffset="1.5"
StrokeEndLineCap="Square" />
<Rectangle x:Name="FocusVisualBlack"
IsHitTestVisible="False"
Opacity="0"
Stroke="{StaticResource FocusVisualBlackStrokeThemeBrush}"
StrokeDashArray="1,1"
StrokeDashOffset="0.5"
StrokeEndLineCap="Square" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPointerOverForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationPressedForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="FocusStates">
<VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="FocusVisualWhite"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
<DoubleAnimation Storyboard.TargetName="FocusVisualBlack"
Storyboard.TargetProperty="Opacity"
To="1"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unfocused" />
</VisualStateGroup>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked" />
<VisualState x:Name="Unchecked">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Text" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource ApplicationSecondaryForegroundThemeBrush}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Indeterminate" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="TextPrimaryButtonStyle"
BasedOn="{StaticResource TextButtonStyle}"
TargetType="ButtonBase">
<Setter Property="Foreground" Value="{StaticResource ApplicationHeaderForegroundThemeBrush}" />
</Style>
<Style x:Key="GroupHeaderTextStyle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="{StaticResource ContentControlThemeFontFamily}" />
<Setter Property="TextTrimming" Value="WordEllipsis" />
<Setter Property="TextWrapping" Value="NoWrap" />
<Setter Property="Typography.StylisticSet20" Value="True" />
<Setter Property="Typography.DiscretionaryLigatures" Value="True" />
<Setter Property="Typography.CaseSensitiveForms" Value="True" />
<Setter Property="FontSize" Value="26.667" />
<Setter Property="LineStackingStrategy" Value="BlockLineHeight" />
<Setter Property="FontWeight" Value="Light" />
<Setter Property="LineHeight" Value="30" />
<Setter Property="RenderTransform">
<Setter.Value>
<TranslateTransform X="-1" Y="6" />
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<GridView Grid.Row="1"
Padding="116,137,40,46"
IsItemClickEnabled="False"
ItemTemplateSelector="{StaticResource ItemTemplateSelector}"
ItemsSource="{Binding Source={StaticResource TestSource}}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollMode="Disabled">
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
</Style>
</GridView.ItemContainerStyle>
<GridView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Button AutomationProperties.Name="Group Title" Style="{StaticResource TextPrimaryButtonStyle}">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="3,-7,10,10"
Style="{StaticResource GroupHeaderTextStyle}"
Text="{Binding Key}" />
<TextBlock Margin="0,-7,0,10"
FontFamily="Segoe UI Symbol"
Style="{StaticResource GroupHeaderTextStyle}"
Text="{StaticResource ChevronGlyph}" />
</StackPanel>
</Button>
</DataTemplate>
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<VariableSizedWrapGrid Margin="0,0,80,0"
ItemHeight="200"
ItemWidth="200"
Orientation="Vertical" />
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</GridView.GroupStyle>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</GridView.ItemsPanel>
</GridView>
</Grid>
</Page>
The ItemTemplateSelector is defined like this :
public sealed class ItemTemplateSelector
: DataTemplateSelector
{
public DataTemplate WidgetTemplate{get;set;}
public DataTemplate ItemTemplate{get;set;}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
protected override DataTemplate SelectTemplateCore(object item)
{
if(item is ItemViewModel)
return ItemTemplate;
if(item is WidgetViewModel)
return WidgetTemplate;
return null;
}
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
if(item is ItemViewModel)
return ItemTemplate;
if(item is WidgetViewModel)
return WidgetTemplate;
return null;
}
}
}
And the TestItemsControl is :
public class TestItemsControl : ItemsControl
{
public TestItemsControl()
{
DefaultStyleKey = typeof(TestItemsControl);
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is TestItem;
}
protected override DependencyObject GetContainerForItemOverride()
{
return new TestItem();
}
}
And TestItem is basically :
public class TestItem : ContentControl
{
public TestItem()
{
DefaultStyleKey = typeof(TestItem);
}
}
And the style for them :
<Style TargetType="local:TestItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestItem">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
FontFamily="{TemplateBinding FontFamily}"
FontSize="{TemplateBinding FontSize}"
FontStyle="{TemplateBinding FontStyle}"
FontWeight="{TemplateBinding FontWeight}"
Foreground="{TemplateBinding Foreground}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:TestItemsControl">
<Setter Property="Padding" Value="0" />
<Setter Property="Margin" Value="0" />
<Setter Property="BorderBrush" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
<Setter Property="Background" Value="{ThemeResource ApplicationPageBackgroundThemeBrush}" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Stretch" />
<Setter Property="VerticalContentAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VariableSizedWrapGrid />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:TestItemsControl">
<Grid x:Name="PART_Root">
<Rectangle x:Name="PointerOverRect"
Margin="-2"
Fill="#DE000000"
Opacity="0" />
<Border x:Name="PART_Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid Margin="{TemplateBinding Padding}">
<ItemsPresenter />
</Grid>
</Border>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="PointerOver">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PointerOverRect"
Storyboard.TargetProperty="Opacity"
To="0.28"
Duration="0" />
</Storyboard>
</VisualState>
<VisualState x:Name="PointerPressed" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="PART_Border"
Storyboard.TargetProperty="Opacity"
To="0.3"
Duration="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And finally how I create the items :
public class TestPage : Page
{
public TestPage()
{
InitializeComponent();
var vm = new HomePageViewModel();
DataContext = vm;
var widget = new WidgetViewModel();
widget.Add(new ItemViewModel
{
Name = "Users"
});
widget.Add(new ItemViewModel
{
Name = "16"
});
vm.Widgets.Add(widget);
}
}
I have two problems with that :(
The first problem is : The ItemTemplate is set but it not display the name of the item but this text :
"Project.ItemViewModel" and I can't understand why
The second problem is the child doesn't fill its parent container.
To explain what I'm trying to achieve, the basic idea is to have a component which swap its content after a delay.
So here I have a GridView with items and each item could have many contents.
That's why I used a TestItemsControl. It's easier to manage swapping content with it...
Any Ideas?
EDIT : I made some steps. I have one remaining problem. It is :
The DataTemplate is set into the container but it is not applied.
When I break point on DataTemplateSelector, I see my TestItemContainer and the DataTemplateSelector is set in it, but the item always display a textblock with the text "XXXX.XXXX.ViewModel"
And with the datatemplate I need to show the property 'Name' of the ViewModel
Thanks for any help

How to apply DataTrigger with ComboBox ItemContainerStyle in UWP?

I am trying to change the value of ComboBoxItem based on the property value assigned to the ComboBox ItemSource.
I know that in WPF it can be achieved as below:
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Background" Value="#FFD2D2" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsValid}" Value="True">
<Setter Property="Background" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
In UWP I tried using behaviors, but still, it's not working.
<ComboBox.ItemContainerStyle>
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="Transparent" />
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsValid}" Value="True">
<core:ChangePropertyAction PropertyName="Background" Value="{ThemeResource MyBorderBrush}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Style>
</ComboBox.ItemContainerStyle>
I also tried to use VSM, but am not sure how to apply the conditional value.
<Style TargetType="ComboBoxItem">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Grid x:Name="LayoutRoot" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
// What should go here?
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
EDIT:
I would prefer a solution setting the background of ComboBoxItem itself, instead of creating separate Grid/Border and then using Converters for the background.
As far as I know, UWP xaml doesn't support such triggers in style. Generally, we use the Behavior datatrigger on the control's root child node. If you don't want to use the data binding as #touseefbsb's answer, but you want to change the ComboboxItem style base on your data model, I think you can try to operate the ComboBox's Itemtemplate and use the Behavior datatrigger in it.
<ComboBox Width="300" Height="60" Name="MyComBoBox" ItemsSource="{Binding models}">
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Name="MyGrid">
<TextBlock Text="{Binding Name}"></TextBlock>
<interactivity:Interaction.Behaviors>
<core:DataTriggerBehavior Binding="{Binding IsValid}" ComparisonCondition="Equal" Value="true">
<core:ChangePropertyAction PropertyName="Background" TargetObject="{Binding ElementName=MyGrid}"
Value="{StaticResource MyColor}" />
</core:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
This is how you bind a collection to a ComboBox in uwp, and set the background of each item with a property value of your collection items.
XAML
<ComboBox
x:Name="ComboBox1"
ItemsSource="{x:Bind Books}"
Header="Select A Book">
<ComboBox.ItemTemplate>
<DataTemplate x:DataType="models:Book">
<StackPanel
Background="{x:Bind MyBackground}"
Padding="8">
<TextBlock
Text="{x:Bind Title}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
C#
ObservableCollection<Book> Books = new ObservableCollection<Book>();
public MainPage()
{
this.InitializeComponent();
Books.Add(new Book("Book1",new SolidColorBrush(Colors.Red)));
Books.Add(new Book("Book2",new SolidColorBrush(Colors.Green)));
Books.Add(new Book("Book3",new SolidColorBrush(Colors.Blue)));
}
Models.Book
public class Book
{
public string Title {get; set;}
public SolidColorBrush MyBackground{get; set;}
public Book(string title,SolidColorBrush myBackground)
{
Title = title;
MyBackground = myBackground;
}
}
So Basically you just need to bind the background property of you Stackpanel in which you have wrapped the content of your ComboBoxItem you can wrap your content even in a Grid or a border as well whatever you prefer.

LongListSelector empty style

I want to show a TextBlock when the LongListSelector has no items. I can do it to every list in my app but that is not sane. So I am trying to edit the list style and do it to every list.
How can I bind to the LongListSelector ItemsSouce count inside its style?
Here is where I am now.
<Style TargetType="phone:LongListSelector">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="{StaticResource PhoneForegroundBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="phone:LongListSelector">
<Grid Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ScrollStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="00:00:00.5" />
</VisualStateGroup.Transitions>
<VisualState x:Name="Scrolling">
<Storyboard>
<DoubleAnimation Duration="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="VerticalScrollBar" />
</Storyboard>
</VisualState>
<VisualState x:Name="NotScrolling" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid Margin="{TemplateBinding Padding}">
<i:Interaction.Triggers>
<!-- HERE I CANT BIND TO COUNT -->
<ec:DataTrigger Binding="{TemplateBinding ItemsSource}" Comparison="Equal" Value="0">
<ec:ChangePropertyAction TargetObject="{Binding ElementName=EmptyListText}" PropertyName="Visibility" Value="Visible" />
</ec:DataTrigger>
</i:Interaction.Triggers>
<ViewportControl x:Name="ViewportControl" HorizontalContentAlignment="Stretch" VerticalAlignment="Top" />
<ScrollBar x:Name="VerticalScrollBar" Opacity="0" Background="{StaticResource ThemeBackground}" HorizontalAlignment="Right" Orientation="Vertical" />
<TextBlock x:Name="EmptyListText" Visibility="Collapsed" Text="{Binding Source={StaticResource Literals}, Path=Literals.noResults}" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
It's a DataBinding issue.
Try attaching it to the TemplateParent (which is the LongListSelector) like so:
<ec:DataTrigger
Binding="{Binding RelativeSource={RelativeSource Mode=TemplateParent}, Path=ItemsSource.Count}"
Comparison="Equal" Value="0">
</ec:DataTrigger>
That should set you straight if you want to use your triggers.
I like using Converters. It is a little simpler to understand so here's the full solution using a Converter.
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return Visibility.Collapsed;
else
{
if (value is System.Collections.IList)
{
System.Collections.IList list = (System.Collections.IList)value;
if (list.Count == 0)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
else
return Visibility.Collapsed;
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
So the LongListSelector Style is:
<TextBlock Visibility="{Binding ItemsSource, RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource MyConverter}}"></TextBlock>
Create a custom composite control based on the long list selector which does what is required and has the smarts to show a specific message per a dependency property.

How to Change the SelectedItem Foreground Text of ListBox Item

I have the following ListBox below. I am not sure how to change the Foreground of a selected item's textblock text when an item is selected, and then back to the original foreground color when an item is unselected (most likely occurring when another item in the ListBox is selected afterwards)?
<ListBox Name="ListBox" SelectionMode="Single" ItemsSource="{Binding}" Margin="{Binding}"
toolkit:TiltEffect.IsTiltEnabled="True" SelectionChanged="ListBox_SelectionChanged" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel ItemWidth="159" ItemHeight="Auto" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" >
<Image Source="{Binding Thumbnail}" Width="155" Height="155" />
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeNormal}" VerticalAlignment="Center" HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
You have to edit ItemContainerStyle (Edit additional templates > Edit generated Item Container (ItemContainerStyle)).
Within ItemContainerStyle is Selected visual state and you can change it.
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Padding" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border x:Name="LayoutRoot" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="MouseOver"/>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="LayoutRoot">
<DiscreteObjectKeyFrame KeyTime="0" Value="{StaticResource TransparentBrush}"/>
</ObjectAnimationUsingKeyFrames>
<DoubleAnimation Duration="0" To=".5" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ContentContainer"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
<VisualStateGroup x:Name="SelectionStates">
<VisualState x:Name="Unselected"/>
<VisualState x:Name="Selected">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentContainer">
<DiscreteObjectKeyFrame KeyTime="0" Value="YOUR_NEW_COLOR"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl x:Name="ContentContainer" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Foreground="{TemplateBinding Foreground}" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am considering that the ItemsSource of your ListBox is bound to an ObservableCollection of an examle class test.cs as below
ObservableCollection<test> coll = new ObservableCollection<test>();
and the DataContext is ListBox.DataContext = coll;
Bind the Foreground property of your TextBlock in the ListBoxItemTemplate
<TextBlock Text="{Binding Name}" Foreground="{Binding foreground}" TextWrapping="Wrap" FontSize="{StaticResource PhoneFontSizeNormal}" VerticalAlignment="Center" HorizontalAlignment="Center" />
Now define your SelectionChanged event
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
test tItem = (sender as ListBox).SelectedItem as test;
test.foreground = "#FFCB202D"; //this will change the color of the TextBlock
}
Make sure you extend your class test.cs with INotifyPropertyChanged and define the property foreground with the same or else dynamic changes will not be reflected.
private string tmpforeground;
public string foreground
{
get
{
return tmpforeground;
}
set
{
if (tmpforeground== value)
return;
tmpforeground= value;
NotifyPropertyChanged("foreground");
}
}
Also note that if you want the textblock to change color to green on one tap and then change its color again by tapping again, SelectionChanged event won't work, because it works only when a different item is selected. So if you want change of color on consecutive taps then use Tap event instead
<ListBox Name="ListBox" SelectionMode="Single" ItemsSource="{Binding}" Margin="{Binding}"
toolkit:TiltEffect.IsTiltEnabled="True" Tap="ListBox_Tap" >
private void ListBox_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
test tItem = (sender as ListBox).SelectedItem as test;
test.foreground = "#FFCB202D";
}

How to change background color of Windows Phone Listpicker in FullScreen Mode

This question has been asked before but the answers provided have not worked for myself and others. The style for the listpicker is provided by I haven't been able to get the background of the Listpicker to change color (in this case yellow) despite trying every property I can think of to get it to change. What is wrong with the code?
<Style TargetType="toolkit:ListPicker" x:Key="ListPickerStyle1">
<!--<Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}"/>-->
<!--<Setter Property="Background" Value="YellowGreen"/>-->
<Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}"/>
<Setter Property="FontSize" Value="{StaticResource PhoneFontSizeMediumLarge}"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Margin" Value="{StaticResource PhoneTouchTargetOverhang}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="toolkit:ListPicker">
<StackPanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="PickerStates">
<VisualState x:Name="Normal"/>
<VisualState x:Name="Expanded">
<Storyboard>
<!--<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="Background" Duration="0"> <DiscreteObjectKeyFrame Value="{StaticResource PhoneTextBoxEditBackgroundColor}" KeyTime="0"/> </ObjectAnimationUsingKeyFrames>-->
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="BorderBrush" Duration="0">
<DiscreteObjectKeyFrame Value="Yellow" KeyTime="0"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="Border" Storyboard.TargetProperty="BorderThickness" Duration="0">
<DiscreteObjectKeyFrame Value="200" KeyTime="0"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentControl Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}"
Foreground="{StaticResource PhoneSubtleBrush}" FontSize="{StaticResource PhoneFontSizeNormal}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0 0 0 8"/>
<Grid>
<!--<Border x:Name="Border" Background="Yellow" BorderBrush="{TemplateBinding Background}" BorderThickness="2">-->
<Border x:Name="Border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding Background}" BorderThickness="2">
<Canvas x:Name="ItemsPresenterHost" MinHeight="46">
<ItemsPresenter x:Name="ItemsPresenter">
<ItemsPresenter.RenderTransform>
<TranslateTransform x:Name="ItemsPresenterTranslateTransform"/>
</ItemsPresenter.RenderTransform>
</ItemsPresenter>
</Canvas>
</Border>
<Popup x:Name="FullModePopup">
<!--<Border Background="{StaticResource PhoneChromeBrush}">-->
<Border Background="Yellow" BorderThickness="200">
<!-- Popup.Child should always be a Border -->
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{TemplateBinding FullModeHeader}" Background="Yellow"
Foreground="{StaticResource PhoneForegroundBrush}"
FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="{StaticResource PhoneFontSizeMedium}"
HorizontalAlignment="Left" Margin="24 12 0 0"/>
<ListBox x:Name="FullModeSelector" Grid.Row="1" ItemTemplate="{TemplateBinding ActualFullModeItemTemplate}"
FontSize="{TemplateBinding FontSize}" Margin="{StaticResource PhoneMargin}" Background="Yellow">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Background="Yellow"/>
<!-- Ensures all containers will be available during the Loaded event -->
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</Grid>
</Border>
</Popup>
</Grid>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I did this by creating an interface to represent ListPickerPage, and changing any code references in the Toolkit to use the interface (IListPickerPage) instead.
Then, I just created a new PhoneApplicationPage, and copied & slightly modified the source (xaml & xaml.cs), (I made it implement IListPickerPage)
using System.Collections;
using System.Windows;
using System.Windows.Controls;
namespace Microsoft.Phone.Controls
{
public interface IListPickerPage
{
string HeaderText { get; set; }
IList Items { get; }
SelectionMode SelectionMode { get; set; }
object SelectedItem { get; set; }
IList SelectedItems { get; }
DataTemplate FullModeItemTemplate { get; set; }
bool IsOpen { get; set; }
}
}
and then, when you want to use it, you just specify your own page instead, by setting the PickerPageUri
<toolkit:ListPicker x:Name="lpStr"
Grid.RowSpan="2"
Width="1"
Height="1"
CacheMode="BitmapCache"
ExpansionMode="FullScreenOnly"
Foreground="Black"
FullModeItemTemplate="{StaticResource ListPickerStringLargeTemplate}"
IsHitTestVisible="False"
Opacity="0"
PickerPageUri="/Views/Globals/ToolkitPages/MyListPickerPage.xaml" />
</Grid>
The rest is simply customizing your own ListPickerPage xaml file to fit your requirements.