We are prototyping an idea, so this code is far from production-ready. That said, I'll paste the relevant parts below.
We are using an IValueConverter to convert an integer value into a relative constraint to position a BoxView along the X-axis of an image. When the X value in the ViewModel gets updated, so does our binding (we can tell because our value converter is called) and a new constraint is created. However, the RelativeLayout does not update its layout when a constraint is changed.
Add to this, the relative layout is inside a DataTemplate and you get all kinds of fun.
Our question: how do you force a RelativeLayout (that is inside a DataTemplate) to redraw itself so the new constraint is used?
Here's part of our ViewModel:
private int _PosX;
public int PosX
{
get { return _PosX;}
set
{
if (_PosX == value)
return;
_PosX = value;
OnPropertyChanged(nameof(PosX));
}
}
The ContentPage:
<Slider Value="{Binding PosX, Mode=TwoWay}"
Minimum="0" Maximum="640" x:Name="xAdjustmentSlider" />
<cv:CarouselView x:Name="ImageCarousel" Grid.Row="2"
ItemsSource="{Binding ShotList}">
<cv:CarouselView.ItemTemplate>
<DataTemplate>
<Grid x:Name="TemplateGrid">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<RelativeLayout>
<Image Grid.Row="0"
x:Name="targetImage"
Source="{Binding TheImage}"
Aspect="AspectFit">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding Path=BindingContext.PreviewImageCommand, Source={x:Reference ImageViewPage}}"
CommandParameter="{Binding ImageNumber}" />
</Image.GestureRecognizers>
</Image>
<BoxView Color="Red"
RelativeLayout.XConstraint="{Binding Source={x:Reference xAdjustmentSlider}, Path=BindingContext.PosX, Converter={StaticResource xConv}, ConverterParameter={x:Reference targetImage}}"
WidthRequest="4" HeightRequest="4" />
</RelativeLayout>
</Grid>
</DataTemplate>
</cv:CarouselView.ItemTemplate>
</cv:CarouselView>
Here's the int -> relative layout constraint converter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is System.Int32) || parameter == null || !(parameter is Xamarin.Forms.View))
return null;
int val = (int)value;
View relativeView = (View)parameter;
var result = Constraint.RelativeToView(relativeView, (Parent, sibling) =>
{
return sibling.X + val;
});
return result;
}