How to update progressbar from customwebview renderer in xamarin forms for android? - xamarin

I'm currently developing an app in Xamarin Forms with an Android background so I wanted to create an Android app first and an iOS app later.
I'm new to Xamarin Forms and I'm struggling on how to update a ProgressBar from a WebView using a custom renderer for the WebView.
In Android, you can do something like this, with the ProgressBar and WebView that are in the main_layout.xml
public class MainActivity extends Activity {
private ProgressBar progressBar;
private WebView webView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
progressBar = (ProgressBar) findViewById(R.id.progress);
webView = (AdvancedWebView) findViewById(R.id.webView);
// webview initialisation
webView.setWebChromeClient(new WebChromeClient(){
#Override
public void onProgressChanged(WebView view, int newProgress) {
// update progressbar progress
progressBar.setProgress(newProgress);
}
});
webView.setWebViewClient(new WebViewClient(this) {
#Override
public void onPageFinished(WebView view, String url) {
// hide progressbar when it's done
progressBar.setVisibility(View.GONE);
}
});
}
}
In Xamarin Forms I have this layout in MainPage.xaml in the shared project
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
NavigationPage.HasNavigationBar="False">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ProgressBar
x:Name="progress"
Grid.Row="3"
HorizontalOptions="FillAndExpand"
Progress="0"
VerticalOptions="Center" />
<WebView
x:Name="webview"
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="0"
HorizontalOptions="FillAndExpand"
IsVisible="False"
Source="https://google.com"
VerticalOptions="FillAndExpand" />
</Grid>
public partial class App : Application
{
public App ()
{
InitializeComponent();
MainPage = new MainPage();
}
}
And this custom WebView render for android in the android project
[assembly: ExportRenderer(typeof(WebView), typeof(CustomWebviewRenderer))]
namespace MyApp.Droid
{
public class CustomWebviewRenderer: WebViewRenderer
{
private readonly Context context;
public CustomWebviewRenderer(Context context) : base(context)
{
this.context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
var formsWebView = e.NewElement as Xamarin.Forms.WebView;
if (formsWebView != null)
{
var webView = Control as Android.Webkit.WebView;
webView.SetWebViewClient(new CustomWebViewClient());
webView.SetWebChromeClient(new CustomWebChromeClient());
webView.Settings.LoadWithOverviewMode = true;
webView.Settings.UseWideViewPort = true;
SetNativeControl(webView);
}
}
private class ScoritoWebChromeClient : WebChromeClient
{
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
// how to update progressbar progress?
base.OnProgressChanged(view, newProgress);
}
}
private class CustomWebViewClient : WebViewClient
{
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
// how to hide progressbar?
base.OnPageFinished(view, url);
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
return base.ShouldOverrideUrlLoading(view, request);
}
}
}
}
How can I update the ProgressBar that I have in my MainPage.xaml from the OnPageFinished in the CustomWebviewRenderer class to show the webpage has finished loading? Should I use the MainActivity.cs in the Android project?
Can someone point me in the right direction on how to solve this?

Update:
Create a custom WebView class in your PCL:
public class CustomWebView: WebView
{
public static readonly BindableProperty ActionProperty = BindableProperty.Create(propertyName: nameof(Action), returnType: typeof(Action),
declaringType: typeof(CustomWebView),
defaultValue: null,
defaultBindingMode: BindingMode.OneWay);
public void InvokeAction()
{
if (Action == null || data == null)
{
return;
}
Action.Invoke();
}
public Action Action
{
get { return (Action)GetValue(ActionProperty); }
set { SetValue(ActionProperty, value); }
}
}
Use this CustomWebView with its namespace like this:
<namespace:CustomWebView x:Name="webview"
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="0"
HorizontalOptions="FillAndExpand"
IsVisible="False"
Source="https://google.com"
VerticalOptions="FillAndExpand"/>
But if you still insist to use your renderer what you can do is update the progress bar using the element property and get a callback or change a property something like below:
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebviewRenderer))]
namespace MyApp.Droid
{
public class CustomWebviewRenderer: WebViewRenderer
{
private readonly Context context;
public CustomWebviewRenderer(Context context) : base(context)
{
this.context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
var formsWebView = e.NewElement as Xamarin.Forms.WebView;
if (formsWebView != null)
{
var webView = Control as Android.Webkit.WebView;
webView.SetWebViewClient(new CustomWebViewClient());
webView.SetWebChromeClient(new CustomWebChromeClient());
webView.Settings.LoadWithOverviewMode = true;
webView.Settings.UseWideViewPort = true;
SetNativeControl(webView);
}
}
private class ScoritoWebChromeClient : WebChromeClient
{
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
// how to update progressbar progress?
base.OnProgressChanged(view, newProgress);
}
}
private class CustomWebViewClient : WebViewClient
{
private _webView;
public CustomWebViewClient (Xamarin.Forms.WebView webView)
{
_webView=webView;
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
// how to hide progressbar?
_webView.InvokeAction();
base.OnPageFinished(view, url);
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
return base.ShouldOverrideUrlLoading(view, request);
}
}
}
}
Note that this is just an example and can be changed as per your requirements.
In case it doesn't work or you have queries revert!

So after 10 days I have finally found a solution to this by using bindableproperties...
So I've added a CustomWebView in the PCL
public class CustomWebView : WebView
{
public static readonly BindableProperty PageProgressProperty = BindableProperty.Create(
nameof(PageProgress),
typeof(int),
typeof(CustomWebView),
default(int));
public int PageProgress
{
get => (int)GetValue(PageProgressProperty);
set => SetValue(PageProgressProperty, value);
}
}
Also a CustomProgressBar in the PCL
public class CustomProgressBar : ProgressBar
{
public CustomProgressBar()
{
}
public static readonly BindableProperty CurrentProgressProperty =
BindableProperty.Create(
nameof(CurrentProgress),
typeof(int),
typeof(CustomProgressBar),
default(int),
propertyChanged: CustomProgressPropertyChanged);
private static void CustomProgressPropertyChanged(BindableObject sender, object oldValue, object newValue)
{
CustomProgressBar thisProgressBar = (CustomProgressBar)sender;
double percent = Convert.ToDouble(newValue) / 100;
thisProgressBar.ProgressTo(percent, 100, Easing.Linear);
}
public int CurrentProgress
{
get => (int)GetValue(CurrentProgressProperty);
set => SetValue(CurrentProgressProperty, value);
}
}
And now I can update the progress from my CustomWebviewRenderer as follows
public class CustomWebviewRenderer : WebViewRenderer
{
private readonly Context context;
public CustomWebviewRenderer(Context context) : base(context)
{
this.context = context;
}
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
var formsWebView = e.NewElement as WebView;
if (e.OldElement == null)
{
var x = Control;
var webView = Control as Android.Webkit.WebView;
webView.SetWebViewClient(new CustomWebViewClient(Element));
webView.SetWebChromeClient(new CustomWebChromeClient(Element));
webView.Settings.LoadWithOverviewMode = true;
SetNativeControl(webView);
}
}
private class CustomWebViewClient : WebViewClient
{
private readonly WebView formsWebView;
public CustomWebViewClient(WebView webView)
{
formsWebView = webView;
}
public override void OnReceivedError(Android.Webkit.WebView view, IWebResourceRequest request, WebResourceError error)
{
base.OnReceivedError(view, request, error);
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
base.OnPageFinished(view, url);
}
public override void OnPageStarted(Android.Webkit.WebView view, string url, Bitmap favicon)
{
base.OnPageStarted(view, url, favicon);
}
public override bool ShouldOverrideUrlLoading(Android.Webkit.WebView view, IWebResourceRequest request)
{
return base.ShouldOverrideUrlLoading(view, request);
}
}
private class CustomWebChromeClient : WebChromeClient
{
private WebView element;
public CustomWebChromeClient(WebView element)
{
this.element = element;
}
public override void OnProgressChanged(Android.Webkit.WebView view, int newProgress)
{
base.OnProgressChanged(view, newProgress);
element.SetValueFromRenderer(CustomWebView.PageProgressProperty, newProgress);
}
}
}
And finally in the .xaml views these properties: BindingContext and CurrentProgress. The BindingContext is set to the webview, and the CurrentProgress is a custom property bound to PageProgress which is a property that lives in the CustomWebView
<local:CustomProgressBar
x:Name="progress"
Grid.Row="3"
BindingContext="{x:Reference webview}"
CurrentProgress="{Binding Path=PageProgress}"
HorizontalOptions="FillAndExpand"
ProgressColor="#FFB800"
VerticalOptions="Center" />
<local:CustomWebView
x:Name="webview"
Grid.Row="0"
Grid.RowSpan="4"
Grid.Column="0"
HorizontalOptions="FillAndExpand"
IsVisible="False"
VerticalOptions="FillAndExpand" />
So yeah, took a while to figure this out...

Related

the view is invisible when Drop

I Create A Custom View named ScrollView
The ability of this view is just like its name
this view of children include only Label,
And support scroll and click,even drop
In actual use,scrolling and clicking works well,dropping was too
but the Label will invisible when drop
this label still occupy the layout,just invisible
the drop func will still execute when i drop once again in that invisible label
I execute apps on Android
ScrollPicker of XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="DebugTest.MyView.ApplyView.ScrollPicker">
<ContentView.Content>
<ScrollView x:Name="ScrollView" x:FieldModifier="Public">
<StackLayout x:Name="MainView" x:FieldModifier="Public">
</StackLayout>
</ScrollView>
</ContentView.Content>
</ContentView>
And C# Code
public partial class ScrollPicker : ContentView
{
public static readonly BindableProperty OrientationProperty = BindableProperty.Create("Orientation", typeof(StackOrientation), typeof(ScrollPicker), StackOrientation.Vertical, propertyChanged: Orientation_Changed);
public static readonly BindableProperty ItemSourceProperty = BindableProperty.Create("ItemSource", typeof(List<string>), typeof(ScrollPicker), propertyChanged: ItemSource_Changed);
private static void Orientation_Changed(BindableObject bindable, object oldValue, object newValue)
{
if(((StackOrientation)newValue) == StackOrientation.Horizontal)
{
((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Horizontal;
((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Horizontal;
}
else
{
((ScrollPicker)bindable).ScrollView.Orientation = ScrollOrientation.Vertical;
((ScrollPicker)bindable).MainView.Orientation = StackOrientation.Vertical;
}
}
private static void ItemSource_Changed(BindableObject bindable, object oldValue, object newValue)
{
var view = ((ScrollPicker)bindable);
view.ItemSourceList = (List<string>)newValue;
view.ResetLabelList();
}
public StackOrientation Orientation
{
get { return (StackOrientation)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public List<string> ItemSource
{
get { return (List<string>)GetValue(ItemSourceProperty); }
set { SetValue(ItemSourceProperty, value); }
}
public DropGestureRecognizer DropGR;
public event EventHandler<IndexArgs> ItemClickEvent;
public event EventHandler<IndexArgs> DropEvent;
public List<string> ItemSourceList;
public List<Label> LabelList = new List<Label>();
public ScrollPicker()
{
InitializeComponent();
ItemClickEvent += ScrollPicker_ItemClickEvent;
DropEvent += ScrollPicker_DropEvent;
}
private void ScrollPicker_DropEvent(object sender, EventArgs e)
{
}
private void CE_Click(Element obj)
{
if(LabelList != null && LabelList.Count != 0)
{
if(LabelList.Exists(label => label == obj))
{
int index = LabelList.IndexOf((Label)obj);
ItemClickEvent.Invoke(obj, new IndexArgs(index));
}
}
}
private void ScrollPicker_ItemClickEvent(object sender, EventArgs e)
{
}
private void ResetLabelList()
{
MainView.Children.Clear();
LabelList.Clear();
if (ItemSourceList != null && ItemSourceList.Count != 0)
{
for(int i=0;i< ItemSourceList.Count;i++)
{
//创建Label
Label temp = CreateSubView(ItemSourceList[i]);
//绑定CE
ClickEffect CE = new ClickEffect();
CE.Click += CE_Click;
Global.ClickEffect_BindClickEffect(temp, CE);
//添加Drop
DropGR = new DropGestureRecognizer();
DropGR.DragOver += DropGR_DragOver;
DropGR.DragLeave += DropGR_DragLeave;
DropGR.Drop += DropGR_Drop;
temp.GestureRecognizers.Add(DropGR);
//添加视图
LabelList.Add(temp);
MainView.Children.Add(temp);
if (i == 0) temp.Opacity = 0.1;
}
}
}
private void DropGR_DragOver(object sender, DragEventArgs e)
{
}
private void DropGR_DragLeave(object sender, DragEventArgs e)
{
}
private void DropGR_Drop(object sender, DropEventArgs e)
{
var view = (sender as GestureRecognizer).Parent as View;
int index = MainView.Children.IndexOf(view);
DropEvent.Invoke(view, new IndexArgs(index));
}
private Label CreateSubView(string text)
{
Label label = new Label()
{
Text = text,
FontSize = 20,
WidthRequest = 75,
VerticalTextAlignment = TextAlignment.Start,
HorizontalTextAlignment = TextAlignment.Start,
};
return label;
}
}
public abstract partial class ScrollPickerItemView : ContentView
{
}
public class IndexArgs: EventArgs
{
public int Index;
public IndexArgs(int index =0)
{
Index = index;
}
}
public enum ScrollPickerOrientation
{
Vertical,
Horizontal
}
How Use In Page:
......
<AbsoluteLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,0.1" BackgroundColor="#66CCFF">
<Label AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,0.2,1"
Text="Project:" FontSize="Large" VerticalTextAlignment="Center" HorizontalTextAlignment="Center"/>
<applyview:ScrollPicker x:Name="ProjectPicker" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="1,0,0.8,1" Orientation="Horizontal"
ItemClickEvent="ProjectPicker_ItemClickEvent" DropEvent="ProjectPicker_DropEvent"/>
</AbsoluteLayout>
I don't know how to solve this problem. Can someone help me?
I'm afraid I've found the problem
I added a PropertyChanged callback function to each label. I was surprised to find that when I triggered the drop function, the text of the label changed and became Empty
That's why he disappeared!!!
I'm very sorry, but it's a shame
Although the problem has been solved, I still don't know why the text of label becomes null when I trigger the drop function

Xamarin Forms MVVM iOS ListView doesn't show; SelectedItem has data

I followed this example https://medium.com/swlh/xamarin-forms-mvvm-how-to-work-with-sqlite-db-c-xaml-26fcae303edd
I put a breakpoint in my OnRouteSelected event handler, and e.SelectedItem has the selected object, even though the ListView doesn't display it.
Am I missing something glaring below?
Here is my code:
RoutesPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Content>
<ListView ItemsSource="{Binding Routes}" SelectedItem="{Binding SelectedRoute, Mode=TwoWay}" HasUnevenRows="False" SeparatorColor="Gray" ItemSelected="OnRouteSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label TextColor="Black" Text="{Binding ROName}"/>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
RoutesPage.xaml.cs
public partial class RoutesPage : ContentPage
{
public RoutesPage()
{
InitializeComponent();
var routeStore = new RouteStore(DependencyService.Get<ISQLiteDb>());
var pageService = new PageService();
ViewModel = new RoutesPageViewModel(routeStore, pageService);
}
protected override void OnAppearing()
{
base.OnAppearing();
ViewModel.LoadDataCommand.Execute(null);
}
void OnRouteSelected(object sender, SelectedItemChangedEventArgs e)
{
ViewModel.SelectRouteCommand.Execute(e.SelectedItem);
}
public RoutesPageViewModel ViewModel
{
get { return BindingContext as RoutesPageViewModel; }
set { BindingContext = value; }
}
}
RoutesPageViewModel.cs The LoadData() method gets the data and adds it to the Routes Collection successfully.
public class RoutesPageViewModel : BaseViewModel
{
private RouteViewModel _selectedRoute;
private IRouteStore _routeStore;
private IPageService _pageService;
private bool _isDataLoaded;
public ObservableCollection<RouteViewModel> Routes { get; private set; }
= new ObservableCollection<RouteViewModel>();
public RouteViewModel SelectedRoute
{
get { return _selectedRoute; }
set { SetValue(ref _selectedRoute, value); }
}
public ICommand LoadDataCommand { get; private set; }
public ICommand AddRouteCommand { get; private set; }
public ICommand SelectRouteCommand { get; private set; }
public ICommand DeleteRouteCommand { get; private set; }
public ICommand CallRouteCommand { get; private set; }
public RoutesPageViewModel(IRouteStore routeStore, IPageService pageService)
{
_routeStore = routeStore;
_pageService = pageService;
LoadDataCommand = new Command(async () => await LoadData());
AddRouteCommand = new Command(async () => await AddRoute());
SelectRouteCommand = new Command<RouteViewModel>(async c => await SelectRoute(c));
}
private async Task LoadData()
{
if (_isDataLoaded)
return;
_isDataLoaded = true;
var routes = await _routeStore.GetRoutesAsync();
foreach (var route in routes)
Routes.Add(new RouteViewModel(route));
}
private async Task AddRoute()
{
// await _pageService.PushAsync(new RoutesDetailPage(new RouteViewModel()));
}
private async Task SelectRoute(RouteViewModel route)
{
if (route == null)
return;
SelectedRoute = null;
// await _pageService.PushAsync(new RoutesDetailPage(route));
}
}
The property in the viewmodels are being set like this:
private string _roName;
public string ROName
{
get { return _roName; }
set
{
SetValue(ref _roName, value);
OnPropertyChanged(nameof(ROName));
}
}
The constructor:
public RouteViewModel(Route route)
{
//other properties
ROName = route.ROName;
}

How can we assign Application.Current to BindingContext

Iam very much new to xamarin and wondering about how BindingContext is used.
I was going through a tutorial wherein they used BindingContext = Application.Current
according to the docs Application.Current should return Application.
so how can this above statement work?
Firstly, create one property in APp.cs, implement interface INotifyPropertyChanged.
public partial class App : Application, INotifyPropertyChanged
{
private string _str;
public string str
{
get { return _str; }
set
{
_str = value;
RaisePropertyChanged("str");
}
}
public App()
{
InitializeComponent();
str = "this is test";
MainPage = new NavigationPage(new simplecontrol.Page26());
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
Then binding Application.Current for ContentPage BindingContext.
<ContentPage.Content>
<StackLayout>
<!--<local:View2 Items="{Binding modelas}" />-->
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding str}"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
this.BindingContext = Application.Current;

My UI did not response for orientation change

I am developing a native mobile app for all platforms. I have created my own theme content page. Then after deployment on android when I make phone landscape it did not respond. what's the reason here.
Here is my base content page.
public abstract class BaseContentPage : ContentPage
{
public readonly BaseViewModel BaseViewModel;
protected bool _isNavigated = false;
public BaseContentPage(BaseViewModel baseViewModel)
{
BaseViewModel = baseViewModel;
}
public abstract void Navigate(SelectedItemChangedEventArgs e);
protected abstract override void OnAppearing();
protected override void OnDisappearing()
{
_isNavigated = true;
}
public BaseContentPage()
{
}
}
here Xaml
<views:BaseContentPage
xmlns:views="clr-namespace:DipsDemoXaml.Views"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Resource="clr-namespace:DipsDemoXaml.Resources"
x:Class="DipsDemoXaml.Views.WardListPage" Title="{x:Static Resource:AppResources.WardListPageTitle}">
<StackLayout BackgroundColor="{StaticResource DefaultBackgroundColor}" Orientation="Vertical" x:Name="s1">
I even try this also in code behind constructor I call size changed and create a method called Wardpagesizechanged.
public WardListPage(WardListPageViewModel wardListViewModel) : base(wardListViewModel)
{
InitializeComponent();
this.SizeChanged += wardpagesizechanged;
}
Wardpagesizechanged method
private void wardpagesizechanged(object sender, EventArgs e)
{
if(this.Width> this.Height)
{
s1.Orientation = StackOrientation.Horizontal;
}
else
{
s1.Orientation = StackOrientation.Vertical;
}
}
what is the problem here, I am clueless

How can I share the value of a field between back-end C# and a renderer?

My C# looks like this:
public App()
{
InitializeComponent();
MainPage = new Japanese.MainPage();
}
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var phrasesPage = new NavigationPage(new PhrasesPage())
{
Title = "Play",
Icon = "ionicons-2-0-1-ios-play-outline-25.png"
};
public partial class PhrasesPage : ContentPage
{
public PhrasesFrame phrasesFrame;
public PhrasesPage()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
App.phrasesPage = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
App.dataChange = true;
phrasesFrame = new PhrasesFrame(this);
phrasesStackLayout.Children.Add(phrasesFrame);
}
public partial class PhrasesFrame : Frame
{
private async Task ShowCard()
{
if (pauseCard == false)
..
and I have an iOS renderer for a tab page
public class TabbedPageRenderer : TabbedRenderer
{
private MainPage _page;
private void OnTabBarReselected(object sender, UITabBarSelectionEventArgs e)
{
...
pauseCard = false;
...
My problem is there is no connection between the two and I would like to know how I can make it so that pauseCard could be set in one place and read in another.
Here is a simple custom Entry example using a bindable bool property that gets changed from the renderer every time the text changes in the entry.
Entry subclass w/ a bindable property called OnOff (bool)
public class CustomPropertyEntry : Entry
{
public static readonly BindableProperty OnOffProperty = BindableProperty.Create(
propertyName: "OnOff",
returnType: typeof(bool),
declaringType: typeof(CustomPropertyEntry),
defaultValue: false);
public bool OnOff
{
get { return (bool)GetValue(OnOffProperty); }
set { SetValue(OnOffProperty, value); }
}
}
iOS Renderer
Note: I keep a reference to the instance of the CustomPropertyEntry passed into OnElementChanged so later I can set its custom property when needed.
public class CustomPropertyEntryRenderer : ViewRenderer<CustomPropertyEntry, UITextField>
{
UITextField textField;
CustomPropertyEntry entry;
protected override void OnElementChanged(ElementChangedEventArgs<CustomPropertyEntry> e)
{
base.OnElementChanged(e);
if (Control == null)
{
textField = new UITextField();
SetNativeControl(textField);
}
if (e.OldElement != null)
{
textField.RemoveTarget(EditChangedHandler, UIControlEvent.EditingChanged);
entry = null;
}
if (e.NewElement != null)
{
textField.AddTarget(EditChangedHandler, UIControlEvent.EditingChanged);
entry = e.NewElement;
}
}
void EditChangedHandler(object sender, EventArgs e)
{
entry.OnOff = !entry.OnOff;
}
}
XAML Example:
<local:CustomPropertyEntry x:Name="customEntry" Text="" />
<Switch BindingContext="{x:Reference customEntry}" IsToggled="{Binding OnOff}" />

Resources