--- /dev/null
+using System;
+using System.Threading.Tasks;
+using Windows.ApplicationModel.Core;
+using Windows.UI.Core;
+using Windows.UI.ViewManagement;
+using Windows.UI.Xaml;
+using Xamarin.Forms;
+using Xamarin.Forms.ControlGallery.WindowsUniversal;
+using Xamarin.Forms.Controls;
+using Xamarin.Forms.Platform.UWP;
+
+[assembly: Dependency(typeof(SecondaryWindowService))]
+namespace Xamarin.Forms.ControlGallery.WindowsUniversal
+{
+ class SecondaryWindowService : ISecondaryWindowService
+ {
+ public async Task OpenSecondaryWindow(Type pageType)
+ {
+ CoreApplicationView newView = CoreApplication.CreateNewView();
+ int newViewId = 0;
+ await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ var frame = new Windows.UI.Xaml.Controls.Frame();
+ frame.Navigate(pageType);
+ Window.Current.Content = frame;
+ Window.Current.Activate();
+
+ newViewId = ApplicationView.GetForCurrentView().Id;
+ });
+ bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
+ }
+
+ public async Task OpenSecondaryWindow(ContentPage page)
+ {
+ CoreApplicationView newView = CoreApplication.CreateNewView();
+ int newViewId = 0;
+ await newView.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
+ {
+ var frame = new Windows.UI.Xaml.Controls.Frame();
+ frame.Navigate(page);
+ Window.Current.Content = frame;
+ Window.Current.Activate();
+
+ newViewId = ApplicationView.GetForCurrentView().Id;
+ });
+ bool viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(newViewId);
+ }
+ }
+}
<Compile Include="PlatformSpecificCoreGalleryFactory.cs" />
<Compile Include="RegistrarValidationService.cs" />
<Compile Include="SampleNativeControl.cs" />
+ <Compile Include="SecondaryWindowService.cs" />
<Compile Include="_2489CustomRenderer.cs" />
<Compile Include="_57114Renderer.cs" />
<Compile Include="_58406EffectRenderer.cs" />
using Xamarin.Forms.PlatformConfiguration.AndroidSpecific;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
using Xamarin.Forms.Controls.GalleryPages.VisualStateManagerGalleries;
+using Xamarin.Forms.Controls.Issues;
+
namespace Xamarin.Forms.Controls
{
[Preserve(AllMembers = true)]
}
};
+ var secondaryWindowService = DependencyService.Get<ISecondaryWindowService>();
+ if (secondaryWindowService != null)
+ {
+ var openSecondWindowButton = new Button() { Text = "Open Secondary Window" };
+ openSecondWindowButton.Clicked += (obj, args) => { secondaryWindowService.OpenSecondaryWindow(new Issue2482()); };
+ stackLayout.Children.Add(openSecondWindowButton);
+ }
+
this.SetAutomationPropertiesName("Gallery");
this.SetAutomationPropertiesHelpText("Lists all gallery pages");
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms.Controls
+{
+ public interface ISecondaryWindowService
+ {
+ Task OpenSecondaryWindow(Type pageType);
+ Task OpenSecondaryWindow(ContentPage page);
+ }
+}
public static Ticker Default
{
internal set { s_ticker = value; }
- get { return s_ticker ?? (s_ticker = Device.PlatformServices.CreateTicker()); }
+ get
+ {
+ if (s_ticker == null)
+ {
+ s_ticker = Device.PlatformServices.CreateTicker();
+ }
+
+ return s_ticker.GetTickerInstance();
+ }
+ }
+
+ protected virtual Ticker GetTickerInstance()
+ {
+ // This method is provided so platforms can override it and return something other than
+ // the normal Ticker singleton
+ return s_ticker;
}
public virtual int Insert(Func<long, bool> timeout)
EnableTimer();
}
}
-}
\ No newline at end of file
+}
return FlowDirection.MatchParent;
}
- static Windows.UI.Xaml.ResourceDictionary GetTabletResources()
+ internal static Windows.UI.Xaml.ResourceDictionary GetTabletResources()
{
return new Windows.UI.Xaml.ResourceDictionary {
Source = new Uri("ms-appx:///Xamarin.Forms.Platform.UAP/Resources.xbf")
_page = page;
+ var current = Windows.UI.Xaml.Application.Current;
+
+ if (!current.Resources.ContainsKey("RootContainerStyle"))
+ {
+ Windows.UI.Xaml.Application.Current.Resources.MergedDictionaries.Add(Forms.GetTabletResources());
+ }
+
_container = new Canvas
{
- Style = (Windows.UI.Xaml.Style)Windows.UI.Xaml.Application.Current.Resources["RootContainerStyle"]
+ Style = (Windows.UI.Xaml.Style)current.Resources["RootContainerStyle"]
};
_page.Content = _container;
{
internal abstract class WindowsBasePlatformServices : IPlatformServices
{
+ const string WrongThreadError = "RPC_E_WRONG_THREAD";
readonly CoreDispatcher _dispatcher;
protected WindowsBasePlatformServices(CoreDispatcher dispatcher)
{
- if (dispatcher == null)
- throw new ArgumentNullException(nameof(dispatcher));
-
- _dispatcher = dispatcher;
+ _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
}
- public void BeginInvokeOnMainThread(Action action)
+ public async void BeginInvokeOnMainThread(Action action)
{
- _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).WatchForError();
+ if (CoreApplication.Views.Count == 1)
+ {
+ // This is the normal scenario - one window only
+ _dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => action()).WatchForError();
+ return;
+ }
+
+ await TryAllDispatchers(action);
}
public Ticker CreateTicker()
return new WindowsIsolatedStorage(ApplicationData.Current.LocalFolder);
}
- public bool IsInvokeRequired => !_dispatcher.HasThreadAccess;
+ public bool IsInvokeRequired
+ {
+ get
+ {
+ if (CoreApplication.Views.Count == 1)
+ {
+ return !_dispatcher.HasThreadAccess;
+ }
+
+ if (Window.Current?.Dispatcher != null)
+ {
+ return !Window.Current.Dispatcher.HasThreadAccess;
+ }
+
+ return true;
+ }
+ }
public string RuntimePlatform => Device.UWP;
{
return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
}
+
+ async Task TryAllDispatchers(Action action)
+ {
+ // Our best bet is Window.Current; most of the time, that's the Dispatcher we need
+ var currentWindow = Window.Current;
+
+ if (currentWindow?.Dispatcher != null)
+ {
+ try
+ {
+ await TryDispatch(currentWindow.Dispatcher, action);
+ return;
+ }
+ catch (Exception ex) when (ex.Message.Contains(WrongThreadError))
+ {
+ // The current window is not the one we need
+ }
+ }
+
+ // Either Window.Current was the wrong Dispatcher, or Window.Current was null because we're on a
+ // non-UI thread (e.g., one from the thread pool). So now it's time to try all the available Dispatchers
+
+ var views = CoreApplication.Views;
+
+ for (int n = 0; n < views.Count; n++)
+ {
+ var dispatcher = views[n].Dispatcher;
+
+ if (dispatcher == null || dispatcher == currentWindow?.Dispatcher)
+ {
+ // Obviously null Dispatchers are no good, and we already tried the one from currentWindow
+ continue;
+ }
+
+ // We need to ignore Deactivated/Never Activated windows, but it's possible we can't access their
+ // properties from this thread. So we'll check those using the Dispatcher
+ bool activated = false;
+
+ await TryDispatch(dispatcher, () => {
+ var mode = views[n].CoreWindow.ActivationMode;
+ activated = (mode == CoreWindowActivationMode.ActivatedInForeground
+ || mode == CoreWindowActivationMode.ActivatedNotForeground);
+ });
+
+ if (!activated)
+ {
+ // This is a deactivated (or not yet activated) window; move on
+ continue;
+ }
+
+ try
+ {
+ await TryDispatch(dispatcher, action);
+ return;
+ }
+ catch (Exception ex) when (ex.Message.Contains(WrongThreadError))
+ {
+ // This was the incorrect dispatcher; move on to try another one
+ }
+ }
+ }
+
+ async Task<bool> TryDispatch(CoreDispatcher dispatcher, Action action)
+ {
+ if (dispatcher == null)
+ {
+ throw new ArgumentNullException(nameof(dispatcher));
+ }
+
+ var taskCompletionSource = new TaskCompletionSource<bool>();
+
+ await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
+ try
+ {
+ action();
+ taskCompletionSource.SetResult(true);
+ }
+ catch (Exception ex)
+ {
+ taskCompletionSource.SetException(ex);
+ }
+ });
+
+ return await taskCompletionSource.Task;
+ }
}
}
\ No newline at end of file
-using Windows.UI.Xaml.Media;
+using Windows.ApplicationModel.Core;
+using System;
+using Windows.UI.Xaml.Media;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Platform.UWP
{
internal class WindowsTicker : Ticker
{
+ [ThreadStatic]
+ static Ticker s_ticker;
+
protected override void DisableTimer()
{
CompositionTarget.Rendering -= RenderingFrameEventHandler;
{
SendSignals();
}
+
+ protected override Ticker GetTickerInstance()
+ {
+ if (CoreApplication.Views.Count > 1)
+ {
+ // We've got multiple windows open, we'll need to use the local ThreadStatic Ticker instead of the
+ // singleton in the base class
+ if (s_ticker == null)
+ {
+ s_ticker = new WindowsTicker();
+ }
+
+ return s_ticker;
+ }
+
+ return base.GetTickerInstance();
+ }
}
}
\ No newline at end of file