In my shared project I have a need for resizing a BoxView at the click of a button. It should preferably switch between taking up no space and 25% of any screen size proportionally, for which I'd usually use AbsoluteLayout.
I've tried using AbsoluteLayout and LayoutTo but since LayoutTo operates in pixels I've been unable to resize proportionally.
I've then change my solution to utilize Grid and a custom animation as seen in the code below.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:AnimationTest"
x:Class="AnimationTest.MainPage">
<Grid ColumnSpacing="0" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="9*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" x:Name="LeftColumn"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<BoxView Color="DarkMagenta" Grid.Row="0" Grid.Column="0"/>
<Button Text="Animate" Grid.Row="1" Grid.Column="1" Clicked="Button_Clicked"/>
</Grid>
</ContentPage>
and the codebehind
namespace AnimationTest
{
public partial class MainPage : ContentPage
{
Animation _animation;
bool _boxCollapsed = false;
public MainPage()
{
InitializeComponent();
}
private void Button_Clicked(object sender, EventArgs e)
{
switch (_boxCollapsed)
{
case true:
_animation = new Animation(
(d) => LeftColumn.Width = new GridLength(1, GridUnitType.Star));
_animation.Commit(this, "the animation", 16, 1000, Easing.SinIn, null, null);
_boxCollapsed = false;
break;
case false:
_animation = new Animation(
(d) => LeftColumn.Width = new GridLength(0, GridUnitType.Star));
_animation.Commit(this, "the animation", 16, 1000, Easing.SinIn, null, null);
_boxCollapsed = true;
break;
}
}
}
}
This has presented two issues though.
While currently not a problem this solution obviously resizes the entire column and not just the BoxView which could potentially become a problem at later stages.
The second issue is that it seems to ignore the Easing parameter and just instantly goes between the two widthswithout actually animating.
I'm hoping someone can at the least inform me of what the issue is regarding the Easing or come up with a different and hopefully more elegant solution.
you'll just have to change a little bit the code on the animation line, i already give you hint on how to get the 25% of screen size. Hope it helps.
private void Button_Clicked(object sender, EventArgs e)
{
var twentyFivePercentOfScreen = this.Width * .25;
switch (_boxCollapsed)
{
case true:
_animation = new Animation(
(d) => LeftColumn.Width = d, 0, twentyFivePercentOfScreen);
_animation.Commit(this, "the animation", 16, 250, Easing.SinIn, null, null);
_boxCollapsed = false;
break;
case false:
_animation = new Animation(
(d) => LeftColumn.Width = d, twentyFivePercentOfScreen, 0);
_animation.Commit(this, "the animation", 16, 250, Easing.SinIn, null, null);
_boxCollapsed = true;
break;
}
}
Related
I have a custom frame that I have created in Xamarin.Forms that allows for a gradient background. I am trying to create a compound shape from two different Frames both with a gradient background, but I am wanting the gradient to be shared between the two. I have gotten the desired effect with using Skia.Sharp.Forms but I would like to know if there is a way to do this with just using Xamarin.Forms and custom renderers.
An example of what I am looking for:
An example of what I get when using custom 2 custom frames: (pay no attention to the slightly different shape)
EDIT
My idea is I want to encapsulate the two frames (or any controls for that matter) in a Custom grid that is given the gradient colors. Then in the custom renderer of the Grid it sets the backgrounds of the children controls to the gradient. This way the LinearGradient has the starting point (0,0) of the parent grid and isn't creating a new gradient for each child. Here's some code to explain what I mean, I just havent figured out the part where I set the children's backgrounds to the gradient yet, the SetLayerPaint( method doesnt seem to work..)
protected override void DispatchDraw(Canvas canvas)
{
_gradient = new Android.Graphics.LinearGradient(
0, 0, Width, Height,
new int[] { _startColor.ToAndroid(), _middleColor.ToAndroid(), _endColor.ToAndroid() },
null,
Android.Graphics.Shader.TileMode.Mirror);
for(var i = 0; i < ChildCount; i++ )
{
var paint = new Android.Graphics.Paint()
{
Dither = true
};
paint.SetShader(_gradient);
var child = GetChildAt(i);
child.SetLayerPaint(paint);
}
base.DispatchDraw(canvas);
}
Does anyone know if this is possible?
Here is my solution:
The custom renderer for the Grid
public class GradientGridRenderer_Android : ViewRenderer
{
private Xamarin.Forms.Color _startColor;
private Xamarin.Forms.Color _middleColor;
private Xamarin.Forms.Color _endColor;
LinearGradient _gradient;
public GradientGridRenderer_Android(Context context)
: base(context) { }
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.View> e)
{
base.OnElementChanged(e);
if(e.NewElement != null && e.NewElement is GradientGrid grid)
{
_startColor = grid.StartColor;
_middleColor = grid.MiddleColor;
_endColor = grid.EndColor;
}
}
protected override void DispatchDraw(Canvas canvas)
{
base.DispatchDraw(canvas);
_gradient = new LinearGradient(
0, 0, Width, Height,
new int[]
{
_startColor.ToAndroid(),
_middleColor.ToAndroid(),
_endColor.ToAndroid(),
},
null,
Shader.TileMode.Mirror);
for (var i = 0; i < ChildCount; i++)
{
var child = GetChildAt(i);
if(child is FrameRenderer_Android gFrame)
{
gFrame.Gradient = _gradient;
gFrame.Invalidate();
}
}
}
}
Here is the custom renderer for the Child, if needed you could abstract this out and make any number of custom renderer's that take a gradient, but for my purposes I just needed a Frame.
public class FrameRenderer_Android : Xamarin.Forms.Platform.Android.AppCompat.FrameRenderer
{
public LinearGradient Gradient;
public FrameRenderer_Android(Context context)
: base(context) { }
protected override void DispatchDraw(Canvas canvas)
{
if(Control != null && Gradient != null)
{
var paint = new Android.Graphics.Paint()
{
Dither = true,
};
paint.SetShader(Gradient);
canvas.DrawPaint(paint);
}
base.DispatchDraw(canvas);
}
}
And here is the xaml
<ContentPage.Content>
<cntrl:GradientGrid RowSpacing="0"
Margin="0,20,0,0"
StartColor="{StaticResource GracePink}"
MiddleColor="{StaticResource GracePurple}"
EndColor="{StaticResource GraceDarkPurple}"
IsClippedToBounds="True">
<Grid.RowDefinitions>
<RowDefinition Height="10*"/>
<RowDefinition Height="90*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<cntrl:CustomFrame Grid.Column="2"
Grid.Row="0"
IsClippedToBounds="True"
CornerRadius="20,20,0,0">
</cntrl:CustomFrame>
<cntrl:CustomFrame Grid.ColumnSpan="4"
Grid.Row="1"
IsClippedToBounds="True"
CornerRadius="40,40,0,0">
</cntrl:CustomFrame>
</cntrl:GradientGrid>
</ContentPage.Content>
We are working on show/ hide password toggle functionality in Xamarin traditional approach. What is the best place to implement it? Is it in Xamarin.iOS &. Droid or in Xamarin.Core?
If it is in Xamarin.Core, can you let us know the process. Is it by value convertors?
Thanks in advance.
Recently, Microsoft MVP Charlin, wrote an article showing how to do this using Event Triggers in the Xamarin Forms code:
She was able to do it simply using a new ShowPasswordTriggerAction of type TriggerAction that implemented INotifyPropertyChanged.
Therein, she created a HidePassword bool property that Invoke a PropertyChanged event which changes the Source of the Icon image:
protected override void Invoke(ImageButton sender)
{
sender.Source = HidePassword ? ShowIcon : HideIcon;
HidePassword = !HidePassword;
}
Then place the Entry and ImageButton inside a layout (like a Frame or horizontally oriented LinearLayout) as shown:
<Entry Placeholder="Password"
IsPassword="{Binding Source={x:Reference ShowPasswordActualTrigger}, Path=HidePassword}"/>
<ImageButton VerticalOptions="Center"
HeightRequest="20"
HorizontalOptions="End"
Source="ic_eye_hide">
<ImageButton.Triggers>
<EventTrigger Event="Clicked">
<local:ShowPasswordTriggerAction ShowIcon="ic_eye"
HideIcon="ic_eye_hide"
x:Name="ShowPasswordActualTrigger"/>
</EventTrigger>
</ImageButton.Triggers>
</ImageButton>
We always use custom controls to show/hide password while entering the password using effects.
Android:
Create the control manually in ‘OnDrawableTouchListener’ method where, we are adding the ShowPass and HidePass icons to the entry control, changing them on the basis of user touch action and attaching it on effect invocation which will be fired when the effect is added to the control.
public class OnDrawableTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
if (v is EditText && e.Action == MotionEventActions.Up)
{
EditText editText = (EditText)v;
if (e.RawX >= (editText.Right - editText.GetCompoundDrawables()[2].Bounds.Width()))
{
if (editText.TransformationMethod == null)
{
editText.TransformationMethod = PasswordTransformationMethod.Instance;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.ShowPass, 0);
}
else
{
editText.TransformationMethod = null;
editText.SetCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, Resource.Drawable.HidePass, 0);
}
return true;
}
}
return false;
}
}
Result:
IOS:
Create the control manually in 'ConfigureControl' method where we are adding the ShowPass and HidePassicons to the entry control, changing them on the basis of user touch action; and attaching it on effect invocation which will be fired when the effect will be added to the control.
private void ConfigureControl()
{
if (Control != null)
{
UITextField vUpdatedEntry = (UITextField)Control;
var buttonRect = UIButton.FromType(UIButtonType.Custom);
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
buttonRect.TouchUpInside += (object sender, EventArgs e1) =>
{
if (vUpdatedEntry.SecureTextEntry)
{
vUpdatedEntry.SecureTextEntry = false;
buttonRect.SetImage(new UIImage("HidePass"), UIControlState.Normal);
}
else
{
vUpdatedEntry.SecureTextEntry = true;
buttonRect.SetImage(new UIImage("ShowPass"), UIControlState.Normal);
}
};
vUpdatedEntry.ShouldChangeCharacters += (textField, range, replacementString) =>
{
string text = vUpdatedEntry.Text;
var result = text.Substring(0, (int)range.Location) + replacementString + text.Substring((int)range.Location + (int)range.Length);
vUpdatedEntry.Text = result;
return false;
};
buttonRect.Frame = new CoreGraphics.CGRect(10.0f, 0.0f, 15.0f, 15.0f);
buttonRect.ContentMode = UIViewContentMode.Right;
UIView paddingViewRight = new UIView(new System.Drawing.RectangleF(5.0f, -5.0f, 30.0f, 18.0f));
paddingViewRight.Add(buttonRect);
paddingViewRight.ContentMode = UIViewContentMode.BottomRight;
vUpdatedEntry.LeftView = paddingViewRight;
vUpdatedEntry.LeftViewMode = UITextFieldViewMode.Always;
Control.Layer.CornerRadius = 4;
Control.Layer.BorderColor = new CoreGraphics.CGColor(255, 255, 255);
Control.Layer.MasksToBounds = true;
vUpdatedEntry.TextAlignment = UITextAlignment.Left;
}
}
Result:
For more details, please refer to the article below.
https://www.c-sharpcorner.com/article/xamarin-forms-tip-implement-show-hide-password-using-effects/
You could download the source file from GitHub for reference.
https://github.com/techierathore/ShowHidePassEx.git
You can use the PhantomLib library to do this. It has a control which allows you to have a show/hide icon for the password with examples. Just install the nuget. https://github.com/OSTUSA/PhantomLib
Your UI codes like this having a entry and image button
source to named accroding to your ui
<Frame CornerRadius="30" Background="white" Padding="0" HeightRequest="43" Margin="0,17,0,0">
<StackLayout Orientation="Horizontal">
<Entry x:Name="eLoginPassword"
Margin="15,-10,0,-15"
HorizontalOptions="FillAndExpand"
IsPassword="True"
Placeholder="Password"/>
<ImageButton
x:Name="ibToggleLoginPass"
Clicked="IbToggleLoginPass"
Source="eyeclosed"
Margin="0,0,13,0"
BackgroundColor="White"
HorizontalOptions="End"
/>
</StackLayout>
</Frame>
in C# code
// IbToggleLoginPass your defined method in xaml
//"eye" is drawable name for open eye and "eyeclosed" is drawable name for closed eye
private void IbToggleLoginPass(object sender, EventArgs e)
{
bool isPass = eLoginPassword.IsPassword;
ibToggleLoginPa`enter code here`ss.Source = isPass ? "eye" : "eyeclosed";
eLoginPassword.IsPassword = !isPass;
}
Trigger and a command
The trigger changes the icon, and the command changes the entry.
View xaml
<Grid>
<Entry Placeholder="Password" Text="{Binding Password, Mode=TwoWay}" IsPassword="{Binding IsPassword}" />
<ImageButton BackgroundColor="Transparent" WidthRequest="24" VerticalOptions="Center" TranslationY="-5" TranslationX="-10" HorizontalOptions="End"
Command="{Binding ToggleIsPassword}"
Source="eye" >
<ImageButton.Triggers>
<DataTrigger TargetType="ImageButton" Binding="{Binding IsPassword}" Value="True" >
<Setter Property="Source" Value="eye-slash" />
</DataTrigger>
</ImageButton.Triggers>
</ImageButton>
</Grid>
And in my ViewModel
private bool _IsPassword = true;
public bool IsPassword
{
get
{
return _IsPassword;
}
set
{
_IsPassword = value;
RaisePropertyChanged(() => IsPassword);
}
}
public ICommand ToggleIsPassword => new Command(() => IsPassword = !IsPassword);
I tried to set the padding:
public class GP10Button : Button
{
// Grid Small Button
public GP10Button()
{
Padding = new Thickness(10, 0, 10, 0);
}
}
but it seems to be ignored. Then I tried to follow these instructions:
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/platform/android/button-padding-shadow
And it seems like it's not possible for me to set the default off.
Has anyone found a way to implement padding on the Android button that actually works. I found a lot of suggestions out there but none seem to work.
You can manipulate the space available to the button. You can use a Grid and have as many columns needed and then in the code behind file access the GridColumns by setting x:name and use the below code in the constructor of the Page. This may help.
if (Device.RuntimePlatform == Device.Android)
{
GColumn1.Width = new GridLength(1, GridUnitType.Star);
GColumn2.Width = new GridLength(1, GridUnitType.Star);
GColumn3.Width = new GridLength(1, GridUnitType.Star);
GColumn4.Width = new GridLength(1, GridUnitType.Star);
GColumn5.Width = new GridLength(1, GridUnitType.Star);
}
Whether the padding is not visible because your button is too wide,but it actually works,you could try to reduce the wide and hava a look.
class GP10Button:Button
{
public GP10Button()
{
switch (Device.RuntimePlatform)
{
case Device.iOS:
break;
case Device.Android:
Padding = new Thickness(10, 0, 10, 0);
break;
}
}
}
in xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App18"
x:Class="App18.Page20">
<ContentPage.Content>
<StackLayout>
<local:GP10Button Text="Welcome" HorizontalOptions="Start" WidthRequest="60" HeightRequest="50"
/>
<Button Text="Welcome" HorizontalOptions="Start" WidthRequest="60" HeightRequest="50"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
the effect :
I installed the Nuget package Rg.plugins.popup.
Tried to set a popup page that should appear on right.
Tried different ways but not able find a solution
<pages:PopupPage.Animation>
<animations:MoveAnimation
PositionIn="Right"
PositionOut="Right"
DurationIn="300"
DurationOut="300"
EasingIn="SinOut"
EasingOut="SinIn"
HasBackgroundAnimation="True"/>
</pages:PopupPage.Animation>
Any solution with RG plugin?
It's not about the animation, that will only control the appearance. You should make sure the content of the popup page is properly arranged. For instance, here is the XAML for a popup page that will display a square popup in the top right corner.
<?xml version="1.0" encoding="UTF-8"?>
<pages:PopupPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:pages="clr-namespace:Rg.Plugins.Popup.Pages;assembly=Rg.Plugins.Popup"
xmlns:animations="clr-namespace:Rg.Plugins.Popup.Animations;assembly=Rg.Plugins.Popup"
x:Class="MyApp.Views.RandomPopupPage">
<StackLayout
BackgroundColor="White"
HorizontalOptions="End"
VerticalOptions="Start"
Margin="20"
WidthRequest="100"
HeightRequest="100"
Spacing="0">
<Label
VerticalTextAlignment="Center"
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center"
Text="Some text here" />
</StackLayout>
</pages:PopupPage>
It has a StackLayout vertically aligned with start (TOP) and horizontally aligned with end (right). It also has a fixed width and height. You should arrange its elements as you do with a normal page keeping in mind it has a transparent background.
It looks like this: See image
Tried to set a popup page that should appear on right.
From Rg.plugins.popup document , can custom animations as follow:
Creat UserAnimation class
class UserAnimation : MoveAnimation
{
private double _defaultTranslationY;
public UserAnimation()
{
DurationIn = DurationOut = 300;
EasingIn = Easing.SinOut;
EasingOut = Easing.SinIn;
PositionIn = MoveAnimationOptions.Right;
PositionOut = MoveAnimationOptions.Right;
}
//1
public override void Preparing(View content, PopupPage page)
{
base.Preparing(content, page);
page.IsVisible = false;
if (content == null) return;
_defaultTranslationY = content.TranslationY;
}
//3
public override void Disposing(View content, PopupPage page)
{
base.Disposing(content, page);
page.IsVisible = true;
if (content == null) return;
content.TranslationY = _defaultTranslationY;
}
//2
public async override Task Appearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Appearing(content, page));
if (content != null)
{
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(content.Width, _defaultTranslationY, DurationIn, EasingIn));
};
page.IsVisible = true;
await Task.WhenAll(taskList);
}
//4
public async override Task Disappearing(View content, PopupPage page)
{
var taskList = new List<Task>();
taskList.Add(base.Disappearing(content, page));
if (content != null)
{
_defaultTranslationY = content.TranslationX - content.Width;
var topOffset = GetTopOffset(content, page);
var leftOffset = GetLeftOffset(content, page);
taskList.Add(content.TranslateTo(leftOffset, _defaultTranslationY, DurationOut, EasingOut));
};
await Task.WhenAll(taskList);
}
}
Use it in Xaml:
<pages:PopupPage.Animation>
<animations:UserAnimation/>
</pages:PopupPage.Animation>
Not sure if the effect below is what you want, but you can use this method to customize the animation.
I'm new to Xamarin but know some iOS, got someone else's code dumped in my lap to fix some bugs. I've googled quite a bit, but I can't solve this one.
Everything works fine deployed on Android, deployed on iPhone 4/iOS 7.1.2, and simulated as iPhone 6/iOS 9.3 in the iOS simulator.
Problem
The btnNews button is visible in the iOS simulator (iPhone 6/iOS 9.3) but not when the code is deployed to an iPhone 6/iOS 9.3.1.
MainPage.xaml in a portable project
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:c="clr-namespace:Cookies;assembly=Cookies"
x:Class="MyCompany.MainPage" Title="MyCompany">
<Grid ColumnSpacing="1" RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="50" x:Name ="buttonsRow"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<c:CookieWebView x:Name="webView" Grid.Row="0" Grid.ColumnSpan="2">
</c:CookieWebView>
<ActivityIndicator x:Name="activityIndicator"
VerticalOptions="FillAndExpand" HorizontalOptions="Center" Grid.Row="0" Grid.ColumnSpan="2" />
<Button x:Name="btnNews" Text="back to news list" BackgroundColor="#E15F59" BorderRadius="0" BorderColor="#E15F59"
TextColor="White" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" BorderWidth="0" IsVisible="False"
HorizontalOptions="FillAndExpand"/>
</Grid>
</ContentPage>
MainPage.xaml.cs MainPage()
namespace MyCompany
{
public partial class MainPage : ContentPage
{
private Button _btnNews;
private ActivityIndicator Loader { get; set; }
private RowDefinition _row;
public const string URL_NEWS = "http://www.mycompany.com/";
public const string QUERY = "master=app";
public static CookieWebView WebView;
public static string CurrentUrl;
bool externalUrlClicked = false;
public MainPage(string url = null)
{
InitializeComponent();
// cookies common : https://github.com/seansparkman/CookiesWebView + nuget package CookiesWebView nuget : https://www.nuget.org/packages/CookieWebView/1.0.0
// ios: http://stackoverflow.com/questions/29768019/cookies-in-web-view-xamarin-ios
NavigationPage.SetHasNavigationBar(this, false);
WebView = this.FindByName<CookieWebView>("webView");
_row = this.FindByName<RowDefinition>("buttonsRow");
Loader = this.FindByName<ActivityIndicator>("activityIndicator");
WebView.Navigated += _webView_Navigated;
WebView.Navigating += WebView_Navigating;
var source = new UrlWebViewSource();
source.Url = GetUrl(!string.IsNullOrEmpty(url) ? url : URL_NEWS);
WebView.Source = source;
ReadCookiesFromSettings();
_btnNews = this.FindByName<Button>("btnNews");
_btnNews.Clicked += _btnNews_Clicked;
// Keep free from iPhone status bar
this.Padding = new Thickness(0, Device.OnPlatform(20, 0, 0), 0, 0);
}
}
}
MainPage.xaml.cs _webView_Navigated()
This method runs whenever a link is clicked in the web view. If the url is a "news item page" then btnNews ("Back to news list") should be shown, else btnNews should be hidden. I have set breakpoints to validate that the correct if/else blocks run at appropriate times. The deployed app uses the same urls/web content as the simulated app.
public void _webView_Navigated(object sender, CookieNavigatedEventArgs args)
{
if (externalUrlClicked)
{
args.Url = CurrentUrl;
externalUrlClicked = false;
return;
}
else
CurrentUrl = args.Url;
if (args.Url != GetUrl(URL_NEWS))
{
_row.Height = 50;
_btnNews.IsVisible = true;
}
else
{
_row.Height = 0;
_btnNews.IsVisible = false;
}
try
{
var cookie = WebView.Cookies[Settings.CookiesSettingsKey];
Settings.CookiesSettings = cookie.Value;
}
catch (Exception)
{
}
Loader.IsVisible = Loader.IsRunning = false;
}
In a native iOS app I would have started investigating layout and things like setNeedsDisplay, but I haven't yet found out how to do that with Xamarin.
Thanks for helping out!