Xamarin.Forms: Changing RelativeLayout Constraints Later

Is it possible to change RelativeLayout constraints after they have been set once?

In the documentation, I see methods like GetBoundsConstraint(BindableObject) , but I think you can only use them if you have a XAML file. While I'm trying to do this in code. I get null if I try

 RelativeLayout.GetBoundsConstraint(this.label); 

The only way I found out is to remove the children from the layout and add it again with new restrictions.

Example:

 public class TestPage : ContentPage { private RelativeLayout relativeLayout; private BoxView view; private bool moreWidth = false; public TestPage() { this.view = new BoxView { BackgroundColor = Color.Yellow, }; Button button = new Button { Text = "change", TextColor = Color.Black, }; button.Clicked += Button_Clicked; this.relativeLayout = new RelativeLayout(); this.relativeLayout.Children.Add(this.view, Constraint.Constant(0), Constraint.Constant(0), Constraint.Constant(80), Constraint.RelativeToParent((parent) => { return parent.Height; })); this.relativeLayout.Children.Add(button, Constraint.RelativeToParent((parent) => { return parent.Width / 2; }), Constraint.RelativeToParent((parent) => { return parent.Height / 2; })); this.Content = this.relativeLayout; } private void Button_Clicked(object sender, EventArgs e) { double width = 0; if(this.moreWidth) { width = 120; } else { width = 80; } var c= BoundsConstraint.FromExpression((Expression<Func<Rectangle>>)(() => new Rectangle(0, 0, width, this.Content.Height)), new View[0]); RelativeLayout.SetBoundsConstraint(this.view, c); this.relativeLayout.ForceLayout(); this.moreWidth = !this.moreWidth; } } 
+9
c # layout xamarin xamarin.forms relativelayout
source share
3 answers

This is not possible with the current version of Xamarin Forms. The RelativeLayout container recommends restrictions when adding / removing items from its children’s collection (it caches the allowed restrictions - presumably for performance). Although various restrictions are implemented as Bindable properties, they still cannot be recalculated when changed.

I guess the intention is to someday respect constraint updates that would be useful, for example, for animations, but so far it doesn't work that way.

HOWEVER , I took a look at the decompiled RelativeLayout source, and you can hack it around, but this may not fit your needs, depending on how many functions are required and how complex your definitions of constraints are.

See this sample code (the key part sets the constraint using SetBoundsConstraint, which overrides the internally calculated boundaries of the added view, and then calls ForceLayout() ):

 public partial class App : Application { public App () { var label = new Label { Text = "Test", HorizontalTextAlignment = TextAlignment.Center, VerticalTextAlignment = TextAlignment.Center, BackgroundColor = Color.Silver }; var layout = new RelativeLayout (); layout.Children.Add (label, Constraint.Constant (50), Constraint.Constant (100), Constraint.Constant (260), Constraint.Constant (30)); MainPage = new ContentPage { Content = layout }; var fwd = true; layout.Animate ("bounce", (delta) => { var d = fwd ? delta : 1.0 - delta; var y = 100.0 + (50.0 * d); var c = BoundsConstraint.FromExpression ((Expression<Func<Rectangle>>)(() => new Rectangle (50, y, 260, 30)), new View [0]); RelativeLayout.SetBoundsConstraint(label, c); layout.ForceLayout (); }, 16, 800, Easing.SinInOut, (f, b) => { // reset direction fwd = !fwd; }, () => { // keep bouncing return true; }); } } 
+13
source share

If you understand correctly, I will try the following

Deploy SizeChanged event for relativeLayout

as: -

 var relativeLayout = new RelativeLayout (); relativeLayout.SizeChanged += someEventForTesting; 

and raise an event when you want to resize a relative layout or any specific child element.

 public void someEventForTesting(Object sender , EventArgs) { var layout = (RelativeLayout)sender ; //here you have access to layout and every child , you can add them again with new constraint . } 
+1
source share

Yes. It is possible. Location Code:

 <StackLayout RelativeLayout.XConstraint="{Binding XConstaint}" ...> 

VM Code:

 public Constraint XConstaint { get => _xConstaint; set { SetFieldValue(ref _xConstaint, value, nameof(XConstaint)); } } public override void OnAppearing() { base.OnAppearing(); XConstaint = Constraint.RelativeToParent((parent) => { return parent.Width - 128; }); } 
+1
source share

All Articles