I am trying to custom a view with the object binded on it. This view is a preview of a taken photo. The photo can be an uploaded photo (already upload on the server) or an offline photo (if the smartphone hasn't a internet connection or if a problem occured during the upload). UploadedPhotoVM and OfflinePhotoVM inherits of PhotoVM. The object OfflinePhotoVM has more properties as IsInError and UploadedPourcentage.
I want to add additional information into the view if the object is an offlinePhoto, for exemplate a label with the upload pourcentage or a button to retry the upload.
However, I don't want to have binding errors if a property is missing and I want to avoid to duplicate code as the view is the same for the two objects except for the label/button.
I know there is "DataTemplate" & "DataTemplateSelector" which worked for items of a list but here this isn't a list, it is only a binded object and I didn't see a template control which can be used for my purpose.
As I don't find a solution to bind a template, I just try to do classic binding :
<Grid x:Name="previewPageContainer">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- Some information about offline photos -->
<Grid Grid.Row="0" BindingContext="{Binding Photo}" IsVisible="{Binding ., Converter={StaticResource IsOfflinePhotoConverter}}" >
<Border
BackgroundColor="Green" Stroke="Transparent">
<Label
TextColor="White"
Text="{Binding UploadedPourcentage}" />
<Border.Triggers>
<DataTrigger TargetType="Border" Binding="{Binding IsInError}" Value="True">
<Setter Property="IsVisible" Value="False"/>
</DataTrigger>
</Border.Triggers>
</Border>
<Button
BackgroundColor="Red"
TextColor="White"
Command="{Binding BindingContext.RetryCommand, Source={x:Reference previewPageContainer}}"
IsVisible="{Binding IsInError}" />
</Grid>
<!-- The picture -->
<Image Grid.Row="1">
[...]
</Image>
<!-- Details infos -->
<Border Grid.Row="2">
[...]
</Border>
</Grid>
-> This XAML works but I have binding errors with the property "IsInError" when a UploadedPhotoVM is binded.
In a similar spirit to DataTemplateSelector
, I would recommend creating your own ControlTemplateSelector
using a ContentView
, along the lines of:
<ContentView x:Name="customContentView">
<ContentView.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="template_False" >
<!-- ... ->
</ControlTemplate
<ControlTemplate x:Key="template_True" >
<!-- ... ->
</ControlTemplate
</ResourceDictionary>
</ControlView>
</ContentView>
Then, in the code-behind, you can implement the selector with the help of CommunityToolkit.Mvvm.Markup. For example:
using CommunityToolkit.Maui.Markup;
public partial class CustomContentView : ContentView
{
public static readonly BindableProperty IsInErrorProperty = BindableProperty.Create(nameof(IsInError), typeof(bool), typeof(CustomContentView), false);
public bool IsInError
{
get => (bool)GetValue(IsInErrorProperty);
set => SetValue(IsInErrorProperty, value);
}
// ...
public CustomContentView()
{
InitializeComponent();
// A basic ControlTemplate selector.
this
.Bind(
ContentView.ControlTemplateProperty,
(CustomContentView ctx) => ctx.IsInError,
source: this,
convert: (bool isInError) => isInError switch
{
false => Resources["template_False"],
true => Resources["template_True"]
}
);
}
}
[EDIT]
I included the bool isInError in the resource keys, so you could simplify the converter like this:
convert: (bool isInError) => Resources[$"template_{isInError}"]
You can generalize it by replacing the bool with an int or enum to support numerous ControlTemplates
.
References: