summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs')
-rw-r--r--Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs418
1 files changed, 418 insertions, 0 deletions
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs
new file mode 100644
index 00000000..54c8e0b3
--- /dev/null
+++ b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs
@@ -0,0 +1,418 @@
+´╗┐using System;
+using System.Collections.Generic;
+
+using Xamarin.Forms;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Threading.Tasks;
+using System.Linq;
+using System.ComponentModel;
+using System.Text;
+using Xamarin.Forms.CustomAttributes;
+
+namespace Xamarin.Forms.Controls
+{
+#if APP
+ [Preserve (AllMembers = true)]
+ [Issue (IssueTracker.Github, 2357, "Webview waits to load the content until webviews on previous pages are loaded", PlatformAffected.iOS | PlatformAffected.Android)]
+ public partial class Issue2357 : MasterDetailPage
+ {
+ public Issue2357 ()
+ {
+ MasterViewModel = new MasterViewModel ();
+ MasterViewModel.PageSelectionChanged += MasterViewModelOnPageSelectionChanged;
+ BindingContext = MasterViewModel;
+
+ Detail = new NavigationPage (new ContentPage {
+ Title = "Home",
+ Content = new Label {
+ Text = "Hello, Forms !",
+ VerticalOptions = LayoutOptions.CenterAndExpand,
+ HorizontalOptions = LayoutOptions.CenterAndExpand
+ }
+ });
+ InitializeComponent ();
+ }
+
+ public MasterViewModel MasterViewModel { get; set; }
+
+ async void MasterViewModelOnPageSelectionChanged (object sender, NavigationEventArgs eventArgs)
+ {
+ Debug.WriteLine ("MasterViewModelOnPageSelectionChanged");
+ IsPresented = false;
+ var page = eventArgs.Page;
+ await Detail.Navigation.PushAsync (page, true);
+ }
+
+ protected override async void OnAppearing ()
+ {
+ await TryInitializeMasterViewModel ();
+ base.OnAppearing ();
+ }
+
+ protected override void OnDisappearing ()
+ {
+ //MasterViewModel.PageSelectionChanged -= MasterViewModelOnPageSelectionChanged;
+ base.OnDisappearing ();
+ }
+
+ async Task TryInitializeMasterViewModel ()
+ {
+ while (true) {
+ string errorMessage;
+ try {
+ await MasterViewModel.InitializeAsync ();
+ break;
+ } catch (Exception ex) {
+ Insights.Report (ex, Insights.Severity.Error);
+ errorMessage = ex.Message;
+ }
+
+ if (!string.IsNullOrWhiteSpace (errorMessage)) {
+ var retry = await DisplayAlert ("Error", errorMessage, "Retry", "Close Application");
+ if (retry) {
+ continue;
+ }
+
+ }
+
+ break;
+ }
+ }
+
+ protected void ListViewOnItemTapped (object sender, ItemTappedEventArgs e)
+ {
+ Debug.WriteLine ("ListViewOnItemTapped");
+
+ if (((ListView)sender).SelectedItem == null)
+ return;
+
+ var menuItem = e.Item as MainMenuItem;
+
+ if (menuItem != null) {
+ switch (menuItem.MenuType) {
+ case MenuType.Login:
+ {
+ break;
+ }
+
+ case MenuType.WebView:
+ {
+ var webViewViewModel = new WebViewViewModel (menuItem);
+ MasterViewModel.CurrentDetailPage = new CustomWebView (webViewViewModel);
+ break;
+ }
+
+ default:
+ {
+ //MenuType Standard
+ break;
+ }
+ }
+
+ ((ListView)sender).SelectedItem = null;
+ }
+ }
+ }
+
+ internal class CustomWebView : ContentPage
+ {
+ WebView _titledWebView;
+
+ public CustomWebView ()
+ {
+ _titledWebView = new WebView ();
+ _titledWebView.SetBinding (WebView.SourceProperty, new Binding ("Url"));
+ _titledWebView.Navigating += WebView_OnNavigating;
+ this.SetBinding (TitleProperty, "Title");
+ Content = _titledWebView;
+ }
+
+ public CustomWebView (WebViewViewModel webViewViewModel) : this ()
+ {
+ Debug.WriteLine ("New WebView");
+
+ _titledWebView.BindingContext = webViewViewModel;
+ }
+
+ static void WebView_OnNavigating (object sender, WebNavigatingEventArgs e)
+ {
+ Debug.WriteLine ("OS: " + Device.OS + " Current Url: " + GetSourceUrl (((WebView)sender).Source) + "Destination Url: " + e.Url + " " + DateTime.Now);
+
+ if (e.Url.IsValidAbsoluteUrl ()) {
+ var destinationUri = new Uri (e.Url);
+ var sourceUri = GetSourceUrl (((WebView)sender).Source);
+ if (sourceUri.HasSameHost (destinationUri)) {
+ if (destinationUri == sourceUri) {
+ //Do nothing. This happens on webview load
+ Debug.WriteLine ("WebView_OnNavigating Same URI");
+ return;
+ }
+
+ //If it reaches here, A link could have been clicked.
+ e.Cancel = true;
+ Debug.WriteLine ("WebView_OnNavigating Same Host but different Uri");
+ } else {
+ //if external link is clicked
+ Debug.WriteLine ("WebView_OnNavigating, DIfferent Uri, so open in Native Browser");
+ e.Cancel = true;
+ Device.OpenUri (new Uri (e.Url));
+ }
+ }
+ }
+
+ static Uri GetSourceUrl (WebViewSource source)
+ {
+ Debug.Assert (source != null, "source cannot be null.");
+
+ var urlWebViewSource = source as UrlWebViewSource;
+ if (urlWebViewSource != null) {
+ if (urlWebViewSource.Url.IsValidAbsoluteUrl ()) {
+ return new Uri (urlWebViewSource.Url);
+ }
+ }
+
+ throw new InvalidOperationException ("WebViewSource is Invalid. Only UrlWebViewSource is accepted.");
+ }
+ }
+
+ public static class UriExtensions
+ {
+ public static bool HasSameHost (this Uri sourceUri, Uri destinationUri, UriFormat uriFormat = UriFormat.Unescaped)
+ {
+ Debug.Assert (sourceUri != null, "sourceUri cannot be null.");
+ Debug.Assert (destinationUri != null, "destinationUri cannot be null.");
+
+ return destinationUri.GetComponents (UriComponents.Host, uriFormat) ==
+ sourceUri.GetComponents (UriComponents.Host, uriFormat);
+ }
+ }
+
+ public static class StringExtensions
+ {
+ public static bool IsValidAbsoluteUrl (this string stringValue)
+ {
+ Uri result;
+ return !string.IsNullOrWhiteSpace (stringValue) && Uri.TryCreate (stringValue, UriKind.Absolute, out result) && (result.Scheme == "http" || result.Scheme == "https");
+ }
+ }
+ public delegate void PageSelectionChanged (object sender, NavigationEventArgs e);
+
+ public class MasterViewModel : ViewModelBase1
+ {
+ public static event PageSelectionChanged PageSelectionChanged;
+
+ ObservableCollection<MainMenuItem> _mainMenuItems;
+ Page _currentDetailPage;
+
+ public MasterViewModel ()
+ {
+ _mainMenuItems = new ObservableCollection<MainMenuItem> (Enumerable.Empty<MainMenuItem> ());
+ }
+
+ public async Task InitializeAsync ()
+ {
+ var items = new List<MainMenuItem> ();
+ items.Add (new MainMenuItem {
+ Title = "SHORT",
+ MenuType = MenuType.WebView,
+ Uri = new Uri ("http://api.morgans.bluearc-uat.com/mobile/SamplePage.aspx?page=Portfolio")
+ });
+ items.Add (new MainMenuItem {
+ Title = "LONG",
+ MenuType = MenuType.WebView,
+ Uri = new Uri ("http://api.morgans.bluearc-uat.com/mobile/SamplePage.aspx?page=long")
+ });
+
+ MainMenuItems = new ObservableCollection<MainMenuItem> (items);
+ }
+
+ public ObservableCollection<MainMenuItem> MainMenuItems {
+ get { return _mainMenuItems; }
+ set {
+ _mainMenuItems = value;
+ OnPropertyChanged ("MainMenuItems");
+ }
+ }
+
+ public Page CurrentDetailPage {
+ get { return _currentDetailPage; }
+ set {
+ _currentDetailPage = value;
+
+ var handler = PageSelectionChanged;
+ if (handler != null) {
+ handler (null, new NavigationEventArgs (value));
+ }
+ }
+ }
+ }
+
+ public class WebViewViewModel : ViewModelBase1
+ {
+ string _title;
+ string _url;
+
+ public WebViewViewModel (MainMenuItem menuItem)
+ {
+ Debug.WriteLine ("New WebViewViewModel");
+ _title = menuItem.Title;
+ _url = menuItem.Uri.AbsoluteUri;
+ }
+
+ public string Title {
+ get { return _title; }
+ set {
+ _title = value;
+ OnPropertyChanged ("Title");
+ }
+ }
+
+ public string Url {
+ get { return _url; }
+ set {
+ Debug.WriteLine ("WebViewViewModel Url Changed");
+ _url = value;
+ OnPropertyChanged ("Url");
+ }
+ }
+ }
+
+ public interface IMenuService
+ {
+ Task<IEnumerable<MainMenuItem>> GetMenuItemsAsync ();
+ }
+
+ public class MainMenuItem
+ {
+ public object Id { get; set; }
+
+ public MenuType MenuType { get; set; }
+
+ public string Title { get; set; }
+
+ public Uri Uri { get; set; }
+ }
+
+ public enum MenuType
+ {
+ Login,
+ WebView,
+ Standard
+ }
+
+ public class ViewModelBase1 : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ /// <summary>
+ /// Event for when IsBusy changes
+ /// </summary>
+ public event EventHandler IsBusyChanged;
+
+ /// <summary>
+ /// Event for when IsValid changes
+ /// </summary>
+ public event EventHandler IsValidChanged;
+
+ readonly List<string> _errors = new List<string> ();
+ bool _isBusy;
+
+ /// <summary>
+ /// Default constructor
+ /// </summary>
+ public ViewModelBase1 ()
+ {
+ //Make sure validation is performed on startup
+ Validate ();
+ }
+
+ /// <summary>
+ /// Returns true if the current state of the ViewModel is valid
+ /// </summary>
+ public bool IsValid {
+ get { return _errors.Count == 0; }
+ }
+
+ /// <summary>
+ /// A list of errors if IsValid is false
+ /// </summary>
+ protected List<string> Errors {
+ get { return _errors; }
+ }
+
+ /// <summary>
+ /// An aggregated error message
+ /// </summary>
+ public string Error {
+ get {
+ return _errors.Aggregate (new StringBuilder (), (b, s) => b.AppendLine (s)).ToString ().Trim ();
+ }
+ }
+
+ /// <summary>
+ /// Protected method for validating the ViewModel
+ /// - Fires PropertyChanged for IsValid and Errors
+ /// </summary>
+ protected void Validate ()
+ {
+ OnPropertyChanged ("IsValid");
+ OnPropertyChanged ("Errors");
+
+ var method = IsValidChanged;
+ if (method != null)
+ method (this, EventArgs.Empty);
+ }
+
+ /// <summary>
+ /// Other viewmodels should call this when overriding Validate, to validate each property
+ /// </summary>
+ /// <param name="validate">Func to determine if a value is valid</param>
+ /// <param name="error">The error message to use if not valid</param>
+ protected void ValidateProperty (Func<bool> validate, string error)
+ {
+ if (validate ()) {
+ if (!Errors.Contains (error))
+ Errors.Add (error);
+ } else {
+ Errors.Remove (error);
+ }
+ }
+
+ /// <summary>
+ /// Value indicating if a spinner should be shown
+ /// </summary>
+ public bool IsBusy {
+ get { return _isBusy; }
+ set {
+ if (_isBusy != value) {
+ _isBusy = value;
+
+ OnPropertyChanged ("IsBusy");
+ OnIsBusyChanged ();
+ }
+ }
+ }
+
+ /// <summary>
+ /// Other viewmodels can override this if something should be done when busy
+ /// </summary>
+ protected void OnIsBusyChanged ()
+ {
+ var ev = IsBusyChanged;
+ if (ev != null) {
+ ev (this, EventArgs.Empty);
+ }
+ }
+
+ protected void OnPropertyChanged (string name)
+ {
+ var ev = PropertyChanged;
+ if (ev != null) {
+ ev (this, new PropertyChangedEventArgs (name));
+ }
+ }
+ }
+#endif
+}
+