diff options
author | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 13:02:25 -0700 |
---|---|---|
committer | Jason Smith <jason.smith@xamarin.com> | 2016-03-22 16:13:41 -0700 |
commit | 17fdde66d94155fc62a034fa6658995bef6fd6e5 (patch) | |
tree | b5e5073a2a7b15cdbe826faa5c763e270a505729 /Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue2357.xaml.cs | |
download | xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.gz xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.tar.bz2 xamarin-forms-17fdde66d94155fc62a034fa6658995bef6fd6e5.zip |
Initial import
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.cs | 418 |
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 +} + |