Synchronize the XSF with Xamarin.Form (#159)
author유리나/Common Platform Lab(SR)/Staff Engineer/삼성전자 <rina6350.you@samsung.com>
Mon, 23 Mar 2020 04:54:59 +0000 (13:54 +0900)
committer안주원/Common Platform Lab(SR)/Principal Engineer/삼성전자 <juwon.ahn@samsung.com>
Mon, 23 Mar 2020 04:54:59 +0000 (13:54 +0900)
* [Core] Removed null values from the DisplayActionSheet  (#8445) fixes #4744

* Fixed IndicatorView issue resetting the template when data updates (#8709)

* Fixed IndicatorView template resets when data updates

* Fixed failing UITests

* Undo unnecessary changes in the ControlGallery Android csproj

* Removed unnecessary changes in the ControlGallery Android csproj

* Fix wrong ControlGallery Android csproj

* [Tizen] Implement DisplayPromptAsync for Tizen (#8820) fixes #6713

* Implement DisplayPromptAsync in Tizen

* Update Xamarin.Forms.Platform.Tizen/Platform.cs

Fix BackButtonPressed handler

Co-Authored-By: campersau <buchholz.bastian@googlemail.com>
* Queue DisplayPromptAsync for display after pages loads; fixes #8526 (#8788) fixes #8526

* Custom/Embedded fonts (#6013)

* Android fonts now work with shorter notation

"Lobster-Regular.ttf#Lobster-Regular" now works as just "Lobster-Regular"

* iOS Sample page now loads

* UWP now uses simple font names

* Fixed UWP Font loading!

UWP now supports `PTM55FT#PTMono`

* Added UWP Font Caching!

* iOS now supports Custom fonts like `PTM55FT#PTMono`

* Android now supports all the font formats!

* Add new styles to show how they can work

* Android fonts now work with shorter notation

"Lobster-Regular.ttf#Lobster-Regular" now works as just "Lobster-Regular"

* iOS Sample page now loads

* UWP now uses simple font names

* Fixed UWP Font loading!

UWP now supports `PTM55FT#PTMono`

* Added UWP Font Caching!

* iOS now supports Custom fonts like `PTM55FT#PTMono`

* Android now supports all the font formats!

* Add new styles to show how they can work

* Fixed iOS Font Loading

* iOS now can load embedded resources!

* Android can now load from embedded resources

* Moved FontFile parsing to Xamarin.Forms.Core, Added Unit Tests!

* IEmeddedFont now returns the path on success.

* removed unused code

* Embedded fonts now load in UWP

* Fixed crash in fonts

* changed the String.Contains to a string, instead of char to make VS windows happy

* netStandard1.0 won't let me have nice things

* grammer fixes :D

* smidgen of cleanup

* fix merge issues

* Update Registrar.cs

* [Tizen] Creates the GestureDetector on demand (#8874)

* [Xaml] Throw XamlParseException in MarkupExpressionParser.GetNextPiece (#8447)

* [XamlC] Support variance (#8535)

* [Xaml[C]] Accept prefixed property names of markup extensions (#5186)

* [XamlC] Dispose assembly resolver constructed by XamlCTask (#8397)

An assembly resolver holds opened files, and leaks if it is not disposed.
It is problematic if you run Xamarin.Forms.Xaml.UnitTests on Linux with
the default configuration because it has a small limit for the number of
opened files. It may cause stability issues also on other platforms,
depending on the situation.

* Typo Fix and change to nameof (#8942) fixes #8935

* Improve resiliency of Shell when removing/adding items (#9023)

* Add IsVisible property to ShellItem/ShellSection/ShellContent

* - fix formatting

* - simplify interface

* - fix local deploy

* -fixes

* - work

* - check visibility after parent is set

* - fix formatting

* - embedding fix

* - menu item not getting added to Visible Items
- Process Items change on Visible Items

* - fix incorrect cast

* - fix casting

* - fix route testing to not invlude not visible

* - unsubscribe

* - set shell variable

* - remove IsVisible from BaseShellItem

* - fix failing unit test

* [Xaml] Create value from positional argument text of markup extension (#8980)

The behavior introduced with this change conforms to the description of
[MS-XAML-2009] page 80 and 81.
fixes #6905

* [C,X] Resolve indexers on base class (#8968)

- fixes #8936

this was regressed by #7836

* Resolve issues with Shell ToolbarItems not reacting to CanExecute Changes (#8889) fixes #8741 fixes #8327

* Fixing various toolbar item display issues

* - fix csproj file

* - added UI test

* - fix possible breaks with Navigation Page

* Fix for 8741: Assert that ToolbarItem is grayed-out (PR 8889) (#8892)

* Assert that ToolbarItem is grayed-out

* Check alpha values on Android

* - fix toolbar item ordering

* - toolbar tracker improvements

* - remove whitespace

* - just use an array from the get go

* - primary

Co-authored-by: Brian Runck <brunck@users.noreply.github.com>
* [Tizen] Fix back button issue in Shell navigation (#8982)

* [Tizen] Fix back button issue in Shell navigation

* [Tizen] Add navigation/back button click effect

* Enhance LightweightPlatform (#8873)

* [Tizen] Gif Animation Support (#9068) fixes #1704

* [core] perf improvements for Assembly attributes reflection (#8938)

When profiling startup, I started looking into the number of calls
retrieving custom attributes from assemblies:

    Method call summary
    Total(ms) Self(ms)      Calls Method name
          24        0         301 System.Reflection.RuntimeAssembly:GetCustomAttributes (System.Type,bool)

I saw a pattern such as:

    object[] attributes = assembly.GetCustomAttributes(attrType, true);
    var handlerAttributes = new HandlerAttribute[attributes.Length];
    Array.Copy(attributes, handlerAttributes, attributes.Length);
    RegisterRenderers(handlerAttributes);

We can avoid the allocation and copying of the array completely here.
We can simply cast `attributes` to `HandlerAttribute[]`.

The other thing I saw was:

    string resolutionName = assembly.FullName;
    var resolutionNameAttribute = (ResolutionGroupNameAttribute)assembly.GetCustomAttribute(typeof(ResolutionGroupNameAttribute));
    if (resolutionNameAttribute != null)
        resolutionName = resolutionNameAttribute.ShortName;

This was happening even if the assembly had no `[assembly:Effect]`.

I reordered the code to not look for `[assembly: ResolutionGroupName]`
unless there was an `[assembly: Export]`.

This reduced the calls to `GetCustomAttributes` to:

    Method call summary
    Total(ms) Self(ms)      Calls Method name
          21        0         251 System.Reflection.RuntimeAssembly:GetCustomAttributes (System.Type,bool)

I also did a small amount of refactoring:

* `ReflectionExtensions.GetCustomAttributesSafe` had a variable
  declaration that could be removed.
* `ReflectionExtensions.GetCustomAttributesSafe` was a nice wrapper to
  avoid `#if` and also has a `try-catch` for the previewer. I used
  this in places where there was duplicated code.

~~ Results ~~

    Before:
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    12-17 13:08:57.119  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +588ms
    12-17 13:09:00.852  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +583ms
    12-17 13:09:04.602  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +573ms
    12-17 13:09:08.388  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms
    12-17 13:09:12.137  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +576ms
    12-17 13:09:15.887  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +583ms
    12-17 13:09:19.621  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +578ms
    12-17 13:09:23.388  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +588ms
    12-17 13:09:27.123  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +587ms
    12-17 13:09:30.892  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +571ms
    Average(ms): 580.8
    Std Err(ms): 1.94250697124446
    Std Dev(ms): 6.1427463998877

    After:
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    Launching: com.xamarin.forms.helloforms
    12-17 13:10:29.762  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +579ms
    12-17 13:10:33.514  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +572ms
    12-17 13:10:37.263  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +564ms
    12-17 13:10:40.996  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +572ms
    12-17 13:10:44.748  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +569ms
    12-17 13:10:48.467  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +573ms
    12-17 13:10:52.231  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +577ms
    12-17 13:10:55.981  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +558ms
    12-17 13:10:59.765  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +574ms
    12-17 13:11:03.499  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +569ms
    Average(ms): 570.7
    Std Err(ms): 1.94393644157644
    Std Dev(ms): 6.1472667819844

I think this saves ~10ms on startup on Android, but this should help
all platforms.

These numbers were taken running on a Pixel 3 XL, a Blank
Xamarin.Forms app template using Xamarin.Forms/master.

Using: https://github.com/xamarin/Xamarin.Forms/pull/8867

* [core] improve Color & Clamp performance (#8884)

* [core] improve Color & Clamp performance

When profiling startup of a HelloForms app on a Pixel 3 XL, I noticed:

    Total(ms) Self(ms)      Calls Method name
            3        1        572 Xamarin.Forms.Internals.NumericExtensions:Clamp (double,double,double)

This method is called multiple times for every `Color` created and
~142 are created on startup. This is why it shows up 572 times for an
app with a single `Label`.

I found there is a `Math.Clamp` implementation in corefx:

https://github.com/dotnet/runtime/blob/6662a0f2fd05298af1f9b1b020fa526595f336f7/src/libraries/System.Private.CoreLib/src/System/Math.cs#L224-L225

The only difference is this version can throw an exception, so we
could return the incoming value instead. That would be bad to change!

If I rework `NumericExtensions` to use corefx's implementation it is a
bit faster, I did a BenchmarkDotNet comparison running with Mono on
macOS:

     Method |      Mean |    Error |   StdDev |
    ------- |----------:|---------:|---------:|
     Clamp2 |  53.89 ns | 0.668 ns | 0.522 ns |
     Clamp1 |  61.84 ns | 1.270 ns | 2.289 ns |
     Color2 | 112.50 ns | 2.643 ns | 3.705 ns |
     Color1 | 129.03 ns | 2.603 ns | 4.760 ns |

Maybe every `Color` is ~13% faster?

Code for the benchmark is here:

https://github.com/jonathanpeppers/Benchmarks/blob/clamp/Benchmarks/Clamp.cs

Additionally, I reworked the list of default `Color` fields so that
they call a new private constructor that does less math and avoids
`Clamp` completely. We should still keep the original change, as it
would help any cases where the `Color.To*` methods would be used in
apps.

I seem to be able to see a small difference in a Release build running
on a Pixel 3 XL:

    Before:
    12-18 13:04:27.154  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +606ms
    12-18 13:04:30.851  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +589ms
    12-18 13:04:34.601  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +587ms
    12-18 13:04:38.352  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +575ms
    12-18 13:04:42.084  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +583ms
    12-18 13:04:45.802  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +573ms
    12-18 13:04:49.566  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +592ms
    12-18 13:04:53.284  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +583ms
    12-18 13:04:57.015  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +594ms
    12-18 13:05:00.715  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms
    Average(ms): 586.3
    Std Err(ms): 3.05886689260364
    Std Dev(ms): 9.67298643990917

    After:
    12-18 13:08:16.677  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +593ms
    12-18 13:08:20.377  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +577ms
    12-18 13:08:24.107  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +583ms
    12-18 13:08:27.827  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +576ms
    12-18 13:08:31.574  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms
    12-18 13:08:35.324  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +584ms
    12-18 13:08:39.056  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +587ms
    12-18 13:08:42.773  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms
    12-18 13:08:46.523  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +584ms
    12-18 13:08:50.256  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +587ms
    Average(ms): 584.3
    Std Err(ms): 1.56382721409865
    Std Dev(ms): 4.94525586350753

This change seems low risk and would help all platforms.

* One last tweak byte -> int

Doing some reading: https://stackoverflow.com/a/43158214/132442

It seems `int` performs even better than `byte`. I did another test
run with just this change:

    Before:
    12-18 13:37:23.347  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +576ms
    12-18 13:37:27.079  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +575ms
    12-18 13:37:30.828  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms
    12-18 13:37:34.578  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +588ms
    12-18 13:37:38.296  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +572ms
    12-18 13:37:42.046  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +579ms
    12-18 13:37:45.781  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +576ms
    12-18 13:37:49.526  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms
    12-18 13:37:53.276  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms
    12-18 13:37:57.009  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +582ms
    Average(ms): 580.1
    Std Err(ms): 1.70912583243924
    Std Dev(ms): 5.40473043833928

    After:
    12-18 13:35:38.745  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +572ms
    12-18 13:35:42.459  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +572ms
    12-18 13:35:46.209  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms
    12-18 13:35:49.974  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms
    12-18 13:35:53.724  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +574ms
    12-18 13:35:57.474  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +580ms
    12-18 13:36:01.207  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +573ms
    12-18 13:36:04.957  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +568ms
    12-18 13:36:08.707  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +566ms
    12-18 13:36:12.407  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +565ms
    Average(ms): 573.2
    Std Err(ms): 1.87853370714738
    Std Dev(ms): 5.94044517598546

* Fixed the conflict of Registrar.cs

* Detect when pages are popped from clicking on tab (#9086)

* Fixed the conflict shell

* send remove events (#9124)

* [Tizen] Shell: FlyoutBackgroundImage, FlyoutBackgroundImageAspect (#8905)

* [Tizen] Add BackgroundImage properties in NavigationView

* [Tizen] Update INavigationView interface

* [Tizen] Remove unused attribute and namespace

* [Tizen] Update MaterialNavigationView
fixes #4410

* Added IconColor property for managing navigation icon color (#5185)

Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
* [android/ios] improve perf when not using Application.Properties (#8887)

* [android/ios] improve perf when not using Application.Properties

When profiling startup, I was seeing things like:

    Method call summary
    Total(ms) Self(ms)      Calls Method name
          52        0          3 System.IO.IsolatedStorage.IsolatedStorageFile:GetUserStoreForApplication ()
          52        0          3 (wrapper remoting-invoke-with-check) System.IO.IsolatedStorage.IsolatedStorageFile:PostInit ()
          51        0          3 System.IO.IsolatedStorage.IsolatedStorageFile:PostInit ()
          51        0          2 (wrapper remoting-invoke-with-check) System.IO.IsolatedStorage.IsolatedStorageFile:FileExists (string)
          51        0          2 System.IO.IsolatedStorage.IsolatedStorageFile:FileExists (string)
          49        0          4 System.IO.IsolatedStorage.IsolatedStorageFile:IsPathInStorage (string)

This app has no `Properties` at all, but it seems to still be doing
the file I/O for it.

I found that a file was being written that contain a serialized empty
dictionary:

    > adb shell run-as com.xamarin.forms.helloforms cat /data/user/0/com.xamarin.forms.helloforms/files/.config/.isolated-storage/PropertyStore.forms
    @▲ArrayOfKeyValueOfstringanyTyp9http://schemas.microsoft.com/2003/10/Serialization/Arrays       ☺i)http://www.w3.org/2001/XMLSchema-instance☺

Any subsequent startup would parse this file.

Two changes will help here:

1) For writes, use a single instance of:

    using (var store = IsolatedStorageFile.GetUserStoreForApplication())

It appears this call is expensive.

2) On writes, we don't need to write a file at all if the `Properties`
   dictionary is empty and no file is on disk. This way apps that
   don't use `Properties` at all won't load a file with an empty
   dictionary.

~~ Results ~~

Using a Release build of the Blank Forms app template on a Pixel 3 XL:

    Before:
    12-13 14:01:42.826  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +606ms
    12-13 14:01:46.541  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +588ms
    12-13 14:01:50.325  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +597ms
    12-13 14:01:54.088  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +600ms
    12-13 14:01:57.855  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +579ms
    12-13 14:02:01.619  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +594ms
    12-13 14:02:05.371  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +605ms
    12-13 14:02:09.106  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +597ms
    12-13 14:02:12.869  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +600ms
    12-13 14:02:16.635  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +601ms
    Average(ms): 596.7
    Std Err(ms): 2.56493448042808
    Std Dev(ms): 8.11103500725332
    After:
    12-13 13:59:46.260  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +600ms
    12-13 13:59:49.993  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +579ms
    12-13 13:59:53.739  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +587ms
    12-13 13:59:57.506  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +592ms
    12-13 14:00:01.255  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +586ms
    12-13 14:00:05.007  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +580ms
    12-13 14:00:08.841  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +581ms
    12-13 14:00:12.587  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +591ms
    12-13 14:00:16.320  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +585ms
    12-13 14:00:20.052  1490  1517 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +577ms
    Average(ms): 585.8
    Std Err(ms): 2.23507394856536
    Std Dev(ms): 7.06792441637257

I think this change saves ~10ms on startup for apps that don't use
`Application.Properties` at all.

I made this change for Android & iOS, as they had similar
implementations.

* Moved the properties check earlier

This way it won't even open the Stream on writes when there are no
properties.

* [tizen] include the same optimization

As suggested by @rookiejava:

https://github.com/xamarin/Xamarin.Forms/pull/8887#pullrequestreview-332378887

* [tizen] make implementation match Android exactly

Context: https://github.com/xamarin/Xamarin.Forms/pull/8887#discussion_r359089505

As suggested by @rookiejava, we can make the implementation match
other platforms exactly.

I took the changes as-is, except matched the formatting in Android's
`Deserializer.cs` so the two files are identical except for the
namespace. I also had to use `Internals.Log`.

* Add null check to GetIconColor (#9172)

* [Tizen] Supports Custom/Embedded and System fonts (#9138)

* Update GitInfo.txt

* fix Profile FrameEnd call (#9117)

* Fix SeachBarRenderer CreateNativeControl issue (#8946)

* [Tizen] Supports Custom/Embedded and System fonts

Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
Co-authored-by: Shane Neuville <shane94@hotmail.com>
Co-authored-by: Rui Marinho <me@ruimarinho.net>
Co-authored-by: TingtingAn <antingting2009@qq.com>
* [Core] performance improvements around bindings (#9114)

I made most of these changes through the benchmarks here:

https://github.com/jonathanpeppers/Benchmarks/blob/6725a72844c5f18320a31223b6f37abd7b28ece4/Benchmarks/BindingBenchmarks.cs

1. `Clone()` was doing `new Binding()` but then using the property
   setters instead of the ctor parameters. `Clone()` seems to be
   called for `DataTemplate`, `ListView`, etc.

2. `BindingBase.ThrowIfApplied()` is called very frequently, so I
   added `MethodImplOptions.AggressiveInlining`.

3. `BindingBaseExtensions.GetRealizedMode` is called ~3 times per
   binding applied, added `MethodImplOptions.AggressiveInlining`.

4. In `BindingExpression` a `bool isLast` value was always calculated,
   even though it was only sometimes used inside an `if`. It can just
   do this check inside the `if`.

5. `object value = property.DefaultValue;` was defined prior to
   calling `TryGetValue`, which will always overwrite it. I just
   removed the call, since a Roslyn analyzer was showing me this.

6. `BindingExpression` was doing a `string.Split('.')` which always
   allocates a `char[]`. I defined the array as a `static` field
   instead.

7. `BindingExpression.GetPart` used `IEnumerable` and `yield return`.
   In the most common case, this function returned a single item and
   in the rare case two items. I removed this function and a `foreach`
   and moved logic inline within the `ParsePath` method. This avoids
   allocations around `foreach`, when it isn't needed.

These are all small changes, but I was only able to see some
difference after everything came together. If we need to split some of
these up, we can do that.

~~ Results ~~

Benchmarks running on mono/macOS:

    BenchmarkDotNet=v0.11.3, OS=macOS Mojave 10.14.6 (18G95) [Darwin 18.7.0]
    Intel Core i7-6567U CPU 3.30GHz (Skylake), 1 CPU, 4 logical and 2 physical cores
      [Host]     : Mono 6.6.0.155 (2019-08/296a9afdb24 Thu), 64bit
      DefaultJob : Mono 6.6.0.155 (2019-08/296a9afdb24 Thu), 64bit
             Method |        Mean |      Error |     StdDev |      Median |
    --------------- |------------:|-----------:|-----------:|------------:|
        CtorSingle2 |    355.9 ns |   2.600 ns |   2.432 ns |    356.1 ns |
        CtorSingle1 |    447.0 ns |  28.825 ns |  29.601 ns |    432.4 ns |
      CtorMultiple2 |  1,316.6 ns |   7.092 ns |   6.287 ns |  1,316.6 ns |
      CtorMultiple1 |  1,611.0 ns |  77.914 ns | 109.225 ns |  1,560.9 ns |
             Clone2 |  2,670.6 ns |  38.075 ns |  33.753 ns |  2,655.7 ns |
             Clone1 |  3,154.3 ns |  15.010 ns |  14.040 ns |  3,151.7 ns |
       ApplySingle2 |  8,065.5 ns |  87.537 ns |  77.599 ns |  8,071.3 ns |
       ApplySingle1 |  8,555.2 ns | 183.373 ns | 508.126 ns |  8,268.9 ns |
     ApplyMultiple2 | 28,933.1 ns | 252.589 ns | 236.272 ns | 28,937.1 ns |
     ApplyMultiple1 | 29,388.9 ns | 269.263 ns | 251.868 ns | 29,296.0 ns |

This appears to save 0.5-1ms per binding applied. It is also somewhat
concerning that this shows a complex binding takes ~30ms on mono? I
will look into that further.

Benchmarks running on Windows/.NET framework, allow us to see the
memory usage as well (memory usage not implemented in BDN on mono):

    BenchmarkDotNet=v0.11.3, OS=Windows 10.0.18362
    Intel Core i9-9900K CPU 3.60GHz, 1 CPU, 16 logical and 8 physical cores
      [Host]     : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4075.0
      DefaultJob : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 32bit LegacyJIT-v4.8.4075.0
             Method |       Mean |      Error |     StdDev | Allocated Memory/Op |
    --------------- |-----------:|-----------:|-----------:|--------------------:|
        CtorSingle2 |   122.6 ns |  0.5047 ns |  0.4721 ns |               369 B |
        CtorSingle1 |   156.3 ns |  0.5229 ns |  0.4636 ns |               421 B |
      CtorMultiple2 |   337.7 ns |  2.5286 ns |  2.3652 ns |               857 B |
      CtorMultiple1 |   435.4 ns |  1.0472 ns |  0.9796 ns |              1017 B |
             Clone2 |   667.0 ns |  2.5739 ns |  2.0095 ns |              1715 B |
             Clone1 |   890.6 ns |  9.1459 ns |  8.5551 ns |              2035 B |
       ApplySingle2 | 1,512.4 ns |  9.0686 ns |  7.0802 ns |               521 B |
       ApplySingle1 | 1,655.3 ns | 31.1960 ns | 35.9253 ns |               573 B |
     ApplyMultiple2 | 4,501.1 ns | 21.8014 ns | 20.3930 ns |              1394 B |
     ApplyMultiple1 | 4,510.7 ns | 25.4709 ns | 22.5793 ns |              1554 B |

This looks like it saves ~160 bytes of allocations per binding applied.

I also tested the Blank Forms app template after adding 100 `Label`
with a single binding:

    Before:
    01-13 16:23:09.406  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +746ms
    01-13 16:23:13.103  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +716ms
    01-13 16:23:16.888  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +732ms
    01-13 16:23:20.656  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +728ms
    01-13 16:23:24.390  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +726ms
    01-13 16:23:28.192  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +723ms
    01-13 16:23:31.910  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +725ms
    01-13 16:23:35.660  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +726ms
    01-13 16:23:39.410  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +728ms
    01-13 16:23:43.126  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +724ms
    Average(ms): 727.4
    Std Err(ms): 2.44585817704589
    Std Dev(ms): 7.73448267321236

    After:
    01-13 16:26:21.557  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +732ms
    01-13 16:26:25.294  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +716ms
    01-13 16:26:29.042  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +736ms
    01-13 16:26:32.760  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +721ms
    01-13 16:26:36.490  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +722ms
    01-13 16:26:40.223  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +722ms
    01-13 16:26:43.957  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +724ms
    01-13 16:26:47.726  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +718ms
    01-13 16:26:51.476  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +722ms
    01-13 16:26:55.224  1473  1503 I ActivityTaskManager: Displayed com.xamarin.forms.helloforms/crc6450e568c951913723.MainActivity: +724ms
    Average(ms): 723.7
    Std Err(ms): 1.90933379888262
    Std Dev(ms): 6.0378436180109

In this scenario the changes seems to save ~3.7ms to overall startup.
The code for this project is here:

https://github.com/jonathanpeppers/Xamarin.Forms/commit/a319b6709894379b5d08703f80238e5ef5a92ba0

I timed a Release build running on a Pixel 3 XL.

* Fixed the rounding issue when the stepper value is incremented. (#7383)

* Fixed the rounding issue when the stepper value is incremented and the increment value is small like say 0.5 per step. This is due to Double being used as the value so when it's unboxed from the binding object precision is lost.

* Updated so that it's not using Math.Round as this will limit the number of decimal places in code, this version will use an internal counter to track the number of clicks the user makes and calculate a clean value using the counter times the increment.  Tests updated as well to test for stepping away from and back to zero...

* fixed issue with tests failing which was caused by the use of (int) causing a rounding down error...

* Update Xamarin.Forms.Core/Stepper.cs

Co-Authored-By: Gerald Versluis <github@geraldversluis.nl>
* Updated to use nameof() instead of strings in teh creation of the new BindableProperties...

* Changed the Bindable StepperPositionProperty from being public to private so that there is not an API change with this PR as suggested by Sam Houts.Once this PR is accepted it may be an idea to revert this change so that the Positon Property can be controled and used as it's an int saves the need to work with doubles for calculating the position...

Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
Co-authored-by: Gerald Versluis <github@geraldversluis.nl>
fixes #5168

* Fixed the build issue

* Avoid NRE removing WeakReferences (and remove null ones) (#9195) fixes #9183

* [CSS] reapply Stylesheet on StyleClass changes (#9157)

- fixes #2678

* [HR] complement the sourceinfo with assemblyname (#9294)

77 files changed:
Xamarin.Forms/Xamarin.Forms.Build.Tasks/ExpandMarkupsVisitor.cs
Xamarin.Forms/Xamarin.Forms.Build.Tasks/TypeReferenceExtensions.cs [changed mode: 0644->0755]
Xamarin.Forms/Xamarin.Forms.Build.Tasks/XamlCTask.cs [changed mode: 0644->0755]
Xamarin.Forms/Xamarin.Forms.Core/ActionSheetArguments.cs
Xamarin.Forms/Xamarin.Forms.Core/Binding.cs
Xamarin.Forms/Xamarin.Forms.Core/BindingBase.cs
Xamarin.Forms/Xamarin.Forms.Core/BindingBaseExtensions.cs
Xamarin.Forms/Xamarin.Forms.Core/BindingExpression.cs
Xamarin.Forms/Xamarin.Forms.Core/Color.cs
Xamarin.Forms/Xamarin.Forms.Core/DependencyService.cs
Xamarin.Forms/Xamarin.Forms.Core/Element_StyleSheets.cs
Xamarin.Forms/Xamarin.Forms.Core/EmbeddedFont.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Core/ExportFontAttribute.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Core/FontFile.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Core/FontRegistrar.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Core/IEmbeddedFontLoader.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Core/IndicatorView.cs
Xamarin.Forms/Xamarin.Forms.Core/Interactivity/AttachedCollection.cs
Xamarin.Forms/Xamarin.Forms.Core/Internals/ToolbarTracker.cs
Xamarin.Forms/Xamarin.Forms.Core/MergedStyle.cs
Xamarin.Forms/Xamarin.Forms.Core/NavigationPage.cs
Xamarin.Forms/Xamarin.Forms.Core/NumericExtensions.cs
Xamarin.Forms/Xamarin.Forms.Core/Page.cs
Xamarin.Forms/Xamarin.Forms.Core/ReflectionExtensions.cs
Xamarin.Forms/Xamarin.Forms.Core/Registrar.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/IShellContentController.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/IShellController.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/IShellItemController.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/IShellSectionController.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/NavigableElement.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/PresentationMode.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Core/Shell/Shell.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellContent.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellContentCollection.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellExtensions.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellItem.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellItemCollection.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellSection.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellSectionCollection.cs
Xamarin.Forms/Xamarin.Forms.Core/Shell/ShellUriHandler.cs
Xamarin.Forms/Xamarin.Forms.Core/StackLayout.cs
Xamarin.Forms/Xamarin.Forms.Core/Stepper.cs
Xamarin.Forms/Xamarin.Forms.Core/TabIndexExtensions.cs
Xamarin.Forms/Xamarin.Forms.Core/Xaml/Diagnostics/VisualDiagnostics.cs
Xamarin.Forms/Xamarin.Forms.Material.Tizen/Shell/MaterialNavigationView.cs
Xamarin.Forms/Xamarin.Forms.Material.Tizen/Xamarin.Forms.Material.Tizen.csproj
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Deserializer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/EmbeddedFontLoader.cs [new file with mode: 0755]
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Extensions/FontExtensions.cs [new file with mode: 0755]
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Extensions/ImageExtensions.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Forms.cs [changed mode: 0644->0755]
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/LightweightPlatform.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Native/Image.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Platform.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/PopupManager.cs [new file with mode: 0644]
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Properties/AssemblyInfo.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/ButtonRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/DatePickerRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/EditorRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/EntryRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/ImageRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/LabelRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/SearchBarRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Renderers/TimePickerRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Shell/INavigationView.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Shell/NavigationView.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Shell/ShellNavBar.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Shell/ShellRenderer.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Shell/ShellSectionNavigation.cs
Xamarin.Forms/Xamarin.Forms.Platform.Tizen/StaticRegistrar.cs
Xamarin.Forms/Xamarin.Forms.Xaml/ApplyPropertiesVisitor.cs
Xamarin.Forms/Xamarin.Forms.Xaml/ExpandMarkupsVisitor.cs
Xamarin.Forms/Xamarin.Forms.Xaml/MarkupExpressionParser.cs
Xamarin.Forms/Xamarin.Forms.Xaml/MarkupExtensionParser.cs
Xamarin.Forms/Xamarin.Forms.Xaml/XamlLoader.cs
Xamarin.Forms/Xamarin.Forms.Xaml/XamlParser.cs
Xamarin.Forms/Xamarin.Forms.Xaml/XamlServiceProvider.cs

index d8d7f95..b132775 100644 (file)
@@ -127,27 +127,69 @@ namespace Xamarin.Forms.Build.Tasks
                                if (split.Length > 2)
                                        throw new ArgumentException();
 
-                               string prefix, name;
-                               if (split.Length == 2) {
-                                       prefix = split[0];
-                                       name = split[1];
-                               }
-                               else {
-                                       prefix = "";
-                                       name = split[0];
-                               }
+                               var (prefix, name) = ParseName(match);
 
                                var namespaceuri = nsResolver.LookupNamespace(prefix) ?? "";
                                if (!string.IsNullOrEmpty(prefix) && string.IsNullOrEmpty(namespaceuri))
                                        throw new XamlParseException($"Undeclared xmlns prefix '{prefix}'", xmlLineInfo);
+
+                               IList<XmlType> typeArguments = null;
+                               var childnodes = new List<(XmlName, INode)>();
+                               var contentname = new XmlName(null, null);
+
+                               if (remaining.StartsWith("}", StringComparison.Ordinal))
+                               {
+                                       remaining = remaining.Substring(1);
+                               }
+                               else
+                               {
+                                       Property parsed;
+                                       do
+                                       {
+                                               parsed = ParseProperty(serviceProvider, ref remaining);
+
+                                               XmlName childname;
+
+                                               if (parsed.name == null)
+                                               {
+                                                       childname = contentname;
+                                               }
+                                               else
+                                               {
+                                                       var (propertyPrefix, propertyName) = ParseName(parsed.name);
+
+                                                       childname = XamlParser.ParsePropertyName(new XmlName(
+                                                               propertyPrefix == "" ? "" : nsResolver.LookupNamespace(propertyPrefix),
+                                                               propertyName));
+
+                                                       if (childname.NamespaceURI == null && childname.LocalName == null)
+                                                               continue;
+                                               }
+
+                                               if (childname == XmlName.xTypeArguments)
+                                               {
+                                                       typeArguments = TypeArgumentsParser.ParseExpression(parsed.strValue, nsResolver, xmlLineInfo);
+                                                       childnodes.Add((childname, new ValueNode(typeArguments, nsResolver)));
+                                               }
+                                               else
+                                               {
+                                                       var childnode = parsed.value as INode ?? new ValueNode(parsed.strValue, nsResolver);
+                                                       childnodes.Add((childname, childnode));
+                                               }
+                                       }
+                                       while (!parsed.last);
+                               }
+
                                //The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
                                XmlType type;
-                               try {
-                                       type = new XmlType(namespaceuri, name + "Extension", null);
+                               try
+                               {
+                                       type = new XmlType(namespaceuri, name + "Extension", typeArguments);
                                        type.GetTypeReference(contextProvider.Context.Module, null);
                                }
-                               catch (XamlParseException) {
-                                       type = new XmlType(namespaceuri, name, null);
+                               catch (XamlParseException)
+                               {
+                                       type = new XmlType(namespaceuri, name, typeArguments);
                                }
 
                                if (type == null)
@@ -157,30 +199,18 @@ namespace Xamarin.Forms.Build.Tasks
                                        ? new ElementNode(type, "", nsResolver)
                                        : new ElementNode(type, "", nsResolver, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
 
-                               if (remaining.StartsWith("}", StringComparison.Ordinal)) {
-                                       remaining = remaining.Substring(1);
-                                       return _node;
+                               foreach (var (childname, childnode) in childnodes) {
+                                       if (childname == contentname) {
+                                               //ContentProperty
+                                               _node.CollectionItems.Add(childnode);
+                                       }
+                                       else {
+                                               _node.Properties[childname] = childnode;
+                                       }
                                }
 
-                               string piece;
-                               while ((piece = GetNextPiece(ref remaining, out var next)) != null)
-                                       HandleProperty(piece, serviceProvider, ref remaining, next != '=');
-
                                return _node;
                        }
-
-                       protected override void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
-                       {
-                               if (value == null && strValue == null)
-                                       throw new XamlParseException($"No value found for property '{prop}' in markup expression", serviceProvider);
-                               var nsResolver = serviceProvider.GetService(typeof(IXmlNamespaceResolver)) as IXmlNamespaceResolver;
-                               if (prop != null) {
-                                       var name = new XmlName(_node.NamespaceURI, prop);
-                                       _node.Properties[name] = value as INode ?? new ValueNode(strValue, nsResolver);
-                               }
-                               else //ContentProperty
-                                       _node.CollectionItems.Add(value as INode ?? new ValueNode(strValue, nsResolver));
-                       }
                }
        }
 }
\ No newline at end of file
old mode 100644 (file)
new mode 100755 (executable)
index 93e6d2f..74df619
@@ -182,29 +182,52 @@ namespace Xamarin.Forms.Build.Tasks
 
                public static bool InheritsFromOrImplements(this TypeReference typeRef, TypeReference baseClass)
                {
-                       if (TypeRefComparer.Default.Equals(typeRef, baseClass))
-                               return true;
-
-                       if (typeRef.IsValueType)
-                               return false;
+                       if (typeRef is GenericInstanceType genericInstance) {
+                               if (baseClass is GenericInstanceType genericInstanceBaseClass &&
+                                               TypeRefComparer.Default.Equals(genericInstance.ElementType, genericInstanceBaseClass.ElementType)) {
+                                       foreach (var parameter in genericInstanceBaseClass.ElementType.ResolveCached().GenericParameters) {
+                                               var argument = genericInstance.GenericArguments[parameter.Position];
+                                               var baseClassArgument = genericInstanceBaseClass.GenericArguments[parameter.Position];
+
+                                               if (parameter.IsCovariant) {
+                                                       if (!argument.InheritsFromOrImplements(baseClassArgument))
+                                                               return false;
+                                               } else if (parameter.IsContravariant) {
+                                                       if (!baseClassArgument.InheritsFromOrImplements(argument))
+                                                               return false;
+                                               } else if (!TypeRefComparer.Default.Equals(argument, baseClassArgument)) {
+                                                       return false;
+                                               }
+                                       }
 
-                       if (typeRef.IsArray) {
-                               var array = (ArrayType)typeRef;
-                               var arrayType = typeRef.ResolveCached();
-                               if (arrayInterfaces.Contains(baseClass.FullName))
                                        return true;
-                               if (array.IsVector &&  //generic interfaces are not implemented on multidimensional arrays
-                                   arrayGenericInterfaces.Contains(baseClass.ResolveCached().FullName) &&
-                                       baseClass.IsGenericInstance &&
-                                       TypeRefComparer.Default.Equals((baseClass as GenericInstanceType).GenericArguments[0], arrayType))
+                               }
+                       }
+                       else {
+                               if (TypeRefComparer.Default.Equals(typeRef, baseClass))
                                        return true;
-                               return baseClass.FullName == "System.Object";
+
+                               if (typeRef.IsArray) {
+                                       var array = (ArrayType)typeRef;
+                                       var arrayType = typeRef.ResolveCached();
+                                       if (arrayInterfaces.Contains(baseClass.FullName))
+                                               return true;
+                                       if (array.IsVector &&  //generic interfaces are not implemented on multidimensional arrays
+                                               arrayGenericInterfaces.Contains(baseClass.ResolveCached().FullName) &&
+                                               baseClass.IsGenericInstance &&
+                                               TypeRefComparer.Default.Equals((baseClass as GenericInstanceType).GenericArguments[0], arrayType))
+                                               return true;
+                                       return baseClass.FullName == "System.Object";
+                               }
                        }
 
+                       if (typeRef.IsValueType)
+                               return false;
+
                        if (typeRef.FullName == "System.Object")
                                return false;
                        var typeDef = typeRef.ResolveCached();
-                       if (typeDef.Interfaces.Any(ir => TypeRefComparer.Default.Equals(ir.InterfaceType.ResolveGenericParameters(typeRef), baseClass)))
+                       if (typeDef.Interfaces.Any(ir => ir.InterfaceType.ResolveGenericParameters(typeRef).InheritsFromOrImplements(baseClass)))
                                return true;
                        if (typeDef.BaseType == null)
                                return false;
@@ -367,14 +390,22 @@ namespace Xamarin.Forms.Build.Tasks
                public static TypeReference ResolveGenericParameters(this TypeReference self, TypeReference declaringTypeReference)
                {
                        var genericdeclType = declaringTypeReference as GenericInstanceType;
-                       if (genericdeclType == null)
+                       var genericParameterSelf = self as GenericParameter;
+                       var genericself = self as GenericInstanceType;
+
+                       if (genericdeclType == null && genericParameterSelf == null && genericself == null)
                                return self;
 
-                       var genericParameterSelf = self as GenericParameter;
+                       if (genericdeclType == null && genericParameterSelf!=null)
+                       {
+                               var typeDef = declaringTypeReference.Resolve();
+                               if (typeDef.BaseType == null || typeDef.BaseType.FullName == "System.Object")
+                                       return self;
+                               return self.ResolveGenericParameters(typeDef.BaseType.ResolveGenericParameters(declaringTypeReference));
+                       }
                        if (genericParameterSelf != null)
                                return genericdeclType.GenericArguments[genericParameterSelf.Position];
 
-                       var genericself = self as GenericInstanceType;
                        if (genericself != null)
                                return genericself.ResolveGenericParameters(declaringTypeReference);
 
old mode 100644 (file)
new mode 100755 (executable)
index 3e7307f..fd0d584
@@ -52,212 +52,214 @@ namespace Xamarin.Forms.Build.Tasks
                                return true;
                        }
 
-                       var resolver = DefaultAssemblyResolver ?? new XamlCAssemblyResolver();
-                       if (resolver is XamlCAssemblyResolver xamlCResolver) {
-                               if (!string.IsNullOrEmpty(DependencyPaths)) {
-                                       foreach (var dep in DependencyPaths.Split(';').Distinct()) {
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {dep}");
-                                               xamlCResolver.AddSearchDirectory(dep);
+                       using (var fallbackResolver = DefaultAssemblyResolver == null ? new XamlCAssemblyResolver() : null) {
+                               var resolver = DefaultAssemblyResolver ?? fallbackResolver;
+                               if (resolver is XamlCAssemblyResolver xamlCResolver) {
+                                       if (!string.IsNullOrEmpty(DependencyPaths)) {
+                                               foreach (var dep in DependencyPaths.Split(';').Distinct()) {
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {dep}");
+                                                       xamlCResolver.AddSearchDirectory(dep);
+                                               }
                                        }
-                               }
 
-                               if (!string.IsNullOrEmpty(ReferencePath)) {
-                                       var paths = ReferencePath.Replace("//", "/").Split(';').Distinct();
-                                       foreach (var p in paths) {
-                                               var searchpath = Path.GetDirectoryName(p);
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {searchpath}");
-                                               xamlCResolver.AddSearchDirectory(searchpath);
+                                       if (!string.IsNullOrEmpty(ReferencePath)) {
+                                               var paths = ReferencePath.Replace("//", "/").Split(';').Distinct();
+                                               foreach (var p in paths) {
+                                                       var searchpath = Path.GetDirectoryName(p);
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Adding searchpath {searchpath}");
+                                                       xamlCResolver.AddSearchDirectory(searchpath);
+                                               }
                                        }
                                }
-                       }
-                       else
-                       LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Ignoring dependency and reference paths due to an unsupported resolver");
-
-                       var debug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none");
-
-                       var readerParameters = new ReaderParameters {
-                               AssemblyResolver = resolver,
-                               ReadWrite = !ValidateOnly,
-                               ReadSymbols = debug && !ValidateOnly, // We don't need symbols for ValidateOnly, since we won't be writing
-                       };
-
-                       using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(Path.GetFullPath(Assembly),readerParameters)) {
-                               CustomAttribute xamlcAttr;
-                               if (assemblyDefinition.HasCustomAttributes &&
-                                       (xamlcAttr =
-                                               assemblyDefinition.CustomAttributes.FirstOrDefault(
-                                                       ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) {
-                                       var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
-                                       if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
-                                               skipassembly = true;
-                                       if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
-                                               skipassembly = false;
-                               }
+                               else
+                               LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Ignoring dependency and reference paths due to an unsupported resolver");
+
+                               var debug = DebugSymbols || (!string.IsNullOrEmpty(DebugType) && DebugType.ToLowerInvariant() != "none");
 
-                               foreach (var module in assemblyDefinition.Modules) {
-                                       var skipmodule = skipassembly;
-                                       if (module.HasCustomAttributes &&
+                               var readerParameters = new ReaderParameters {
+                                       AssemblyResolver = resolver,
+                                       ReadWrite = !ValidateOnly,
+                                       ReadSymbols = debug && !ValidateOnly, // We don't need symbols for ValidateOnly, since we won't be writing
+                               };
+
+                               using (var assemblyDefinition = AssemblyDefinition.ReadAssembly(Path.GetFullPath(Assembly),readerParameters)) {
+                                       CustomAttribute xamlcAttr;
+                                       if (assemblyDefinition.HasCustomAttributes &&
                                                (xamlcAttr =
-                                                       module.CustomAttributes.FirstOrDefault(
+                                                       assemblyDefinition.CustomAttributes.FirstOrDefault(
                                                                ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) {
                                                var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
                                                if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
-                                                       skipmodule = true;
+                                                       skipassembly = true;
                                                if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
-                                                       skipmodule = false;
+                                                       skipassembly = false;
                                        }
 
-                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Module: {module.Name}");
-                                       var resourcesToPrune = new List<EmbeddedResource>();
-                                       foreach (var resource in module.Resources.OfType<EmbeddedResource>()) {
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Resource: {resource.Name}");
-                                               string classname;
-                                               if (!resource.IsXaml(module, out classname)) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}skipped.");
-                                                       continue;
-                                               }
-                                               TypeDefinition typeDef = module.GetType(classname);
-                                               if (typeDef == null) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no type found... skipped.");
-                                                       continue;
-                                               }
-                                               var skiptype = skipmodule;
-                                               if (typeDef.HasCustomAttributes &&
+                                       foreach (var module in assemblyDefinition.Modules) {
+                                               var skipmodule = skipassembly;
+                                               if (module.HasCustomAttributes &&
                                                        (xamlcAttr =
-                                                               typeDef.CustomAttributes.FirstOrDefault(
+                                                               module.CustomAttributes.FirstOrDefault(
                                                                        ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) {
                                                        var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
                                                        if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
-                                                               skiptype = true;
+                                                               skipmodule = true;
                                                        if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
-                                                               skiptype = false;
+                                                               skipmodule = false;
                                                }
 
-                                               if (Type != null)
-                                                       skiptype = !(Type == classname);
+                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}Module: {module.Name}");
+                                               var resourcesToPrune = new List<EmbeddedResource>();
+                                               foreach (var resource in module.Resources.OfType<EmbeddedResource>()) {
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Resource: {resource.Name}");
+                                                       string classname;
+                                                       if (!resource.IsXaml(module, out classname)) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}skipped.");
+                                                               continue;
+                                                       }
+                                                       TypeDefinition typeDef = module.GetType(classname);
+                                                       if (typeDef == null) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no type found... skipped.");
+                                                               continue;
+                                                       }
+                                                       var skiptype = skipmodule;
+                                                       if (typeDef.HasCustomAttributes &&
+                                                               (xamlcAttr =
+                                                                       typeDef.CustomAttributes.FirstOrDefault(
+                                                                               ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlCompilationAttribute")) != null) {
+                                                               var options = (XamlCompilationOptions)xamlcAttr.ConstructorArguments[0].Value;
+                                                               if ((options & XamlCompilationOptions.Skip) == XamlCompilationOptions.Skip)
+                                                                       skiptype = true;
+                                                               if ((options & XamlCompilationOptions.Compile) == XamlCompilationOptions.Compile)
+                                                                       skiptype = false;
+                                                       }
 
-                                               if (skiptype && !ForceCompile) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}has XamlCompilationAttribute set to Skip and not Compile... skipped.");
-                                                       continue;
-                                               }
+                                                       if (Type != null)
+                                                               skiptype = !(Type == classname);
 
-                                               var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
-                                               if (initComp == null) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no InitializeComponent found... skipped.");
-                                                       continue;
-                                               }
+                                                       if (skiptype && !ForceCompile) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}has XamlCompilationAttribute set to Skip and not Compile... skipped.");
+                                                               continue;
+                                                       }
 
-                                               CustomAttribute xamlFilePathAttr;
-                                               var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlFilePathAttribute")) != null ?
-                                                                                                 (string)xamlFilePathAttr.ConstructorArguments[0].Value :
-                                                                                                 resource.Name;
-
-                                               MethodDefinition initCompRuntime = null;
-                                               if (!ValidateOnly)
-                                               {
-                                                       initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
-                                                       if (initCompRuntime != null)
-                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}__InitComponentRuntime already exists... not creating");
-                                                       else
-                                                       {
-                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Creating empty {typeDef.Name}.__InitComponentRuntime");
-                                                               initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType);
-                                                               initCompRuntime.Body.InitLocals = true;
-                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
-                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Copying body of InitializeComponent to __InitComponentRuntime");
-                                                               initCompRuntime.Body = new MethodBody(initCompRuntime);
-                                                               var iCRIl = initCompRuntime.Body.GetILProcessor();
-                                                               foreach (var instr in initComp.Body.Instructions)
-                                                                       iCRIl.Append(instr);
-                                                               initComp.Body.Instructions.Clear();
-                                                               initComp.Body.GetILProcessor().Emit(OpCodes.Ret);
-                                                               initComp.Body.InitLocals = true;
-                                                               typeDef.Methods.Add(initCompRuntime);
-                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
+                                                       var initComp = typeDef.Methods.FirstOrDefault(md => md.Name == "InitializeComponent");
+                                                       if (initComp == null) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}no InitializeComponent found... skipped.");
+                                                               continue;
                                                        }
-                                               }
 
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
-                                               var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
-                                               if (rootnode == null) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
-                                                       continue;
-                                               }
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
-
-                                               hasCompiledXamlResources = true;
-
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
-                                               Exception e;
-                                               if (!TryCoreCompile(initComp, initCompRuntime, rootnode, xamlFilePath, out e)) {
-                                                       success = false;
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
-                                                       (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
-                                                       if (e is XamlParseException xpe)
-                                                               LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
-                                                       else if (e is XmlException xe)
-                                                               LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
-                                                       else
-                                                               LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
-                                                       LoggingHelper.LogMessage(Low, e.StackTrace);
-                                                       continue;
-                                               }
-                                               if (Type != null)
-                                                   InitCompForType = initComp;
+                                                       CustomAttribute xamlFilePathAttr;
+                                                       var xamlFilePath = typeDef.HasCustomAttributes && (xamlFilePathAttr = typeDef.CustomAttributes.FirstOrDefault(ca => ca.AttributeType.FullName == "Xamarin.Forms.Xaml.XamlFilePathAttribute")) != null ?
+                                                                                                         (string)xamlFilePathAttr.ConstructorArguments[0].Value :
+                                                                                                         resource.Name;
 
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
+                                                       MethodDefinition initCompRuntime = null;
+                                                       if (!ValidateOnly)
+                                                       {
+                                                               initCompRuntime = typeDef.Methods.FirstOrDefault(md => md.Name == "__InitComponentRuntime");
+                                                               if (initCompRuntime != null)
+                                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}__InitComponentRuntime already exists... not creating");
+                                                               else
+                                                               {
+                                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Creating empty {typeDef.Name}.__InitComponentRuntime");
+                                                                       initCompRuntime = new MethodDefinition("__InitComponentRuntime", initComp.Attributes, initComp.ReturnType);
+                                                                       initCompRuntime.Body.InitLocals = true;
+                                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
+                                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Copying body of InitializeComponent to __InitComponentRuntime");
+                                                                       initCompRuntime.Body = new MethodBody(initCompRuntime);
+                                                                       var iCRIl = initCompRuntime.Body.GetILProcessor();
+                                                                       foreach (var instr in initComp.Body.Instructions)
+                                                                               iCRIl.Append(instr);
+                                                                       initComp.Body.Instructions.Clear();
+                                                                       initComp.Body.GetILProcessor().Emit(OpCodes.Ret);
+                                                                       initComp.Body.InitLocals = true;
+                                                                       typeDef.Methods.Add(initCompRuntime);
+                                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
+                                                               }
+                                                       }
+
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Parsing Xaml");
+                                                       var rootnode = ParseXaml(resource.GetResourceStream(), typeDef);
+                                                       if (rootnode == null) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
+                                                               continue;
+                                                       }
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
 
-                                               if (ValidateOnly)
-                                                       continue;
+                                                       hasCompiledXamlResources = true;
+
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Replacing {0}.InitializeComponent ()");
+                                                       Exception e;
+                                                       if (!TryCoreCompile(initComp, initCompRuntime, rootnode, xamlFilePath, out e)) {
+                                                               success = false;
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}failed.");
+                                                               (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
+                                                               if (e is XamlParseException xpe)
+                                                                       LoggingHelper.LogError(null, null, null, xamlFilePath, xpe.XmlInfo.LineNumber, xpe.XmlInfo.LinePosition, 0, 0, xpe.Message, xpe.HelpLink, xpe.Source);
+                                                               else if (e is XmlException xe)
+                                                                       LoggingHelper.LogError(null, null, null, xamlFilePath, xe.LineNumber, xe.LinePosition, 0, 0, xe.Message, xe.HelpLink, xe.Source);
+                                                               else
+                                                                       LoggingHelper.LogError(null, null, null, xamlFilePath, 0, 0, 0, 0, e.Message, e.HelpLink, e.Source);
+                                                               LoggingHelper.LogMessage(Low, e.StackTrace);
+                                                               continue;
+                                                       }
+                                                       if (Type != null)
+                                                           InitCompForType = initComp;
 
-                                               if (OptimizeIL) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Optimizing IL");
-                                                       initComp.Body.Optimize();
                                                        LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
-                                               }
+
+                                                       if (ValidateOnly)
+                                                               continue;
+
+                                                       if (OptimizeIL) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Optimizing IL");
+                                                               initComp.Body.Optimize();
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
+                                                       }
 
 #pragma warning disable 0618
-                                               if (OutputGeneratedILAsCode)
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Decompiling option has been removed. Use a 3rd party decompiler to admire the beauty of the IL generated");
+                                                       if (OutputGeneratedILAsCode)
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Decompiling option has been removed. Use a 3rd party decompiler to admire the beauty of the IL generated");
 #pragma warning restore 0618
-                                               resourcesToPrune.Add(resource);
+                                                       resourcesToPrune.Add(resource);
+                                               }
+                                               if (hasCompiledXamlResources) {
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Changing the module MVID");
+                                                       module.Mvid = Guid.NewGuid();
+                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}done.");
+                                               }
+                                               if (!KeepXamlResources) {
+                                                       if (resourcesToPrune.Any())
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Removing compiled xaml resources");
+                                                       foreach (var resource in resourcesToPrune) {
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Removing {resource.Name}");
+                                                               module.Resources.Remove(resource);
+                                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
+                                                       }
+                                               }
                                        }
-                                       if (hasCompiledXamlResources) {
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Changing the module MVID");
-                                               module.Mvid = Guid.NewGuid();
-                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}done.");
+                                       if (ValidateOnly) {
+                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}ValidateOnly=True. Skipping writing assembly.");
+                                               return success;
                                        }
-                                       if (!KeepXamlResources) {
-                                               if (resourcesToPrune.Any())
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 4)}Removing compiled xaml resources");
-                                               foreach (var resource in resourcesToPrune) {
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 6)}Removing {resource.Name}");
-                                                       module.Resources.Remove(resource);
-                                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 8)}done.");
-                                               }
+                                       if (!hasCompiledXamlResources) {
+                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}No compiled resources. Skipping writing assembly.");
+                                               return success;
+                                       }
+
+                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}Writing the assembly");
+                                       try {
+                                               assemblyDefinition.Write(new WriterParameters {
+                                                       WriteSymbols = debug,
+                                               });
+                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}done.");
+                                       } catch (Exception e) {
+                                               LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}failed.");
+                                               LoggingHelper.LogErrorFromException(e);
+                                               (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
+                                               LoggingHelper.LogMessage(Low, e.StackTrace);
+                                               success = false;
                                        }
-                               }
-                               if (ValidateOnly) {
-                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}ValidateOnly=True. Skipping writing assembly.");
-                                       return success;
-                               }
-                               if (!hasCompiledXamlResources) {
-                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}No compiled resources. Skipping writing assembly.");
-                                       return success;
-                               }
-                               
-                               LoggingHelper.LogMessage(Low, $"{new string(' ', 0)}Writing the assembly");
-                               try {
-                                       assemblyDefinition.Write(new WriterParameters {
-                                               WriteSymbols = debug,
-                                       });
-                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}done.");
-                               } catch (Exception e) {
-                                       LoggingHelper.LogMessage(Low, $"{new string(' ', 2)}failed.");
-                                       LoggingHelper.LogErrorFromException(e);
-                                       (thrownExceptions = thrownExceptions ?? new List<Exception>()).Add(e);
-                                       LoggingHelper.LogMessage(Low, e.StackTrace);
-                                       success = false;
                                }
                        }
                        return success;
index c3f7e8a..c459d18 100644 (file)
@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.ComponentModel;
+using System.Linq;
 using System.Threading.Tasks;
 
 namespace Xamarin.Forms.Internals
@@ -12,7 +13,7 @@ namespace Xamarin.Forms.Internals
                        Title = title;
                        Cancel = cancel;
                        Destruction = destruction;
-                       Buttons = buttons;
+                       Buttons = buttons?.Where(c => c != null);
                        Result = new TaskCompletionSource<string>();
                }
 
index 3ba88ec..33ab46d 100644 (file)
@@ -276,11 +276,7 @@ namespace Xamarin.Forms
 
                internal override BindingBase Clone()
                {
-                       return new Binding(Path, Mode) {
-                               Converter = Converter,
-                               ConverterParameter = ConverterParameter,
-                               StringFormat = StringFormat,
-                               Source = Source,
+                       return new Binding(Path, Mode, Converter, ConverterParameter, StringFormat, Source) {
                                UpdateSourceEventName = UpdateSourceEventName,
                                TargetNullValue = TargetNullValue,
                                FallbackValue = FallbackValue,
index 1b675c4..6f8cead 100644 (file)
@@ -87,6 +87,7 @@ namespace Xamarin.Forms
                        SynchronizedCollections.Add(collection, new CollectionSynchronizationContext(context, callback));
                }
 
+               [MethodImpl(MethodImplOptions.AggressiveInlining)]
                protected void ThrowIfApplied()
                {
                        if (IsApplied)
index 5da40f2..6998aa4 100644 (file)
@@ -1,7 +1,10 @@
-namespace Xamarin.Forms
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms
 {
        static class BindingBaseExtensions
        {
+               [MethodImpl(MethodImplOptions.AggressiveInlining)]
                public static BindingMode GetRealizedMode(this BindingBase self, BindableProperty property)
                {
                        return self.Mode != BindingMode.Default ? self.Mode : property.DefaultBindingMode;
index 91c722a..166a2bc 100644 (file)
@@ -13,6 +13,7 @@ namespace Xamarin.Forms
        internal class BindingExpression
        {
                internal const string PropertyNotFoundErrorMessage = "'{0}' property not found on '{1}', target property: '{2}.{3}'";
+               static readonly char[] ExpressionSplit = new[] { '.' };
 
                readonly List<BindingExpressionPart> _parts = new List<BindingExpressionPart>();
 
@@ -110,7 +111,6 @@ namespace Xamarin.Forms
                        for (var i = 0; i < _parts.Count; i++)
                        {
                                part = _parts[i];
-                               bool isLast = i + 1 == _parts.Count;
 
                                if (!part.IsSelf && current != null)
                                {
@@ -119,7 +119,7 @@ namespace Xamarin.Forms
                                        if (part.LastGetter == null || !part.LastGetter.DeclaringType.GetTypeInfo().IsAssignableFrom(currentType))
                                                SetupPart(currentType, part);
 
-                                       if (!isLast)
+                                       if (i < _parts.Count - 1)
                                                part.TryGetValue(current, out current);
                                }
 
@@ -140,8 +140,7 @@ namespace Xamarin.Forms
 
                        if (needsGetter)
                        {
-                               object value = property.DefaultValue;
-                               if (part.TryGetValue(current, out value) || part.IsSelf) {
+                               if (part.TryGetValue(current, out object value) || part.IsSelf) {
                                        value = Binding.GetSourceValue(value, property.ReturnType);
                                }
                                else
@@ -185,38 +184,6 @@ namespace Xamarin.Forms
                        }
                }
 
-               IEnumerable<BindingExpressionPart> GetPart(string part)
-               {
-                       part = part.Trim();
-                       if (part == string.Empty)
-                               throw new FormatException("Path contains an empty part");
-
-                       BindingExpressionPart indexer = null;
-
-                       int lbIndex = part.IndexOf('[');
-                       if (lbIndex != -1)
-                       {
-                               int rbIndex = part.LastIndexOf(']');
-                               if (rbIndex == -1)
-                                       throw new FormatException("Indexer did not contain closing bracket");
-
-                               int argLength = rbIndex - lbIndex - 1;
-                               if (argLength == 0)
-                                       throw new FormatException("Indexer did not contain arguments");
-
-                               string argString = part.Substring(lbIndex + 1, argLength);
-                               indexer = new BindingExpressionPart(this, argString, true);
-
-                               part = part.Substring(0, lbIndex);
-                               part = part.Trim();
-                       }
-
-                       if (part.Length > 0)
-                               yield return new BindingExpressionPart(this, part);
-                       if (indexer != null)
-                               yield return indexer;
-               }
-
                void ParsePath()
                {
                        string p = Path.Trim();
@@ -232,14 +199,44 @@ namespace Xamarin.Forms
                                p = p.Substring(1);
                        }
 
-                       string[] pathParts = p.Split('.');
+                       string[] pathParts = p.Split(ExpressionSplit);
                        for (var i = 0; i < pathParts.Length; i++)
                        {
-                               foreach (BindingExpressionPart part in GetPart(pathParts[i]))
+                               string part = pathParts[i].Trim();
+                               if (part == string.Empty)
+                                       throw new FormatException("Path contains an empty part");
+
+                               BindingExpressionPart indexer = null;
+
+                               int lbIndex = part.IndexOf('[');
+                               if (lbIndex != -1)
                                {
-                                       last.NextPart = part;
-                                       _parts.Add(part);
-                                       last = part;
+                                       int rbIndex = part.LastIndexOf(']');
+                                       if (rbIndex == -1)
+                                               throw new FormatException("Indexer did not contain closing bracket");
+
+                                       int argLength = rbIndex - lbIndex - 1;
+                                       if (argLength == 0)
+                                               throw new FormatException("Indexer did not contain arguments");
+
+                                       string argString = part.Substring(lbIndex + 1, argLength);
+                                       indexer = new BindingExpressionPart(this, argString, true);
+
+                                       part = part.Substring(0, lbIndex);
+                                       part = part.Trim();
+                               }
+                               if (part.Length > 0)
+                               {
+                                       var next = new BindingExpressionPart(this, part);
+                                       last.NextPart = next;
+                                       _parts.Add(next);
+                                       last = next;
+                               }
+                               if (indexer != null)
+                               {
+                                       last.NextPart = indexer;
+                                       _parts.Add(indexer);
+                                       last = indexer;
                                }
                        }
                }
@@ -279,15 +276,16 @@ namespace Xamarin.Forms
                                        return pi;
                        }
 
+                       //defined on a base class ?
+                       if (sourceType.BaseType is Type baseT && GetIndexer(baseT.GetTypeInfo(), indexerName, content) is PropertyInfo p)
+                               return p;
+
                        //defined on an interface ?
                        foreach (var face in sourceType.ImplementedInterfaces) {
                                if (GetIndexer(face.GetTypeInfo(), indexerName, content) is PropertyInfo pi)
                                        return pi;
                        }
 
-                       //defined on a base class ?
-                       if (sourceType.BaseType is Type baseT && GetIndexer(baseT.GetTypeInfo(), indexerName, content) is PropertyInfo p)
-                               return p;
                        return null;
                }
 
index c52ae6d..db0b04d 100644 (file)
@@ -114,6 +114,26 @@ namespace Xamarin.Forms
                        }
                }
 
+               Color(int r, int g, int b)
+               {
+                       _mode = Mode.Rgb;
+                       _r = r / 255f;
+                       _g = g / 255f;
+                       _b = b / 255f;
+                       _a = 1;
+                       ConvertToHsl(_r, _g, _b, _mode, out _hue, out _saturation, out _luminosity);
+               }
+
+               Color(int r, int g, int b, int a)
+               {
+                       _mode = Mode.Rgb;
+                       _r = r / 255f;
+                       _g = g / 255f;
+                       _b = b / 255f;
+                       _a = a / 255f;
+                       ConvertToHsl(_r, _g, _b, _mode, out _hue, out _saturation, out _luminosity);
+               }
+
                public Color(double r, double g, double b) : this(r, g, b, 1)
                {
                }
@@ -418,150 +438,150 @@ namespace Xamarin.Forms
                #region Color Definitions
 
                // matches colors in WPF's System.Windows.Media.Colors
-               public static readonly Color AliceBlue = FromRgb(240, 248, 255);
-               public static readonly Color AntiqueWhite = FromRgb(250, 235, 215);
-               public static readonly Color Aqua = FromRgb(0, 255, 255);
-               public static readonly Color Aquamarine = FromRgb(127, 255, 212);
-               public static readonly Color Azure = FromRgb(240, 255, 255);
-               public static readonly Color Beige = FromRgb(245, 245, 220);
-               public static readonly Color Bisque = FromRgb(255, 228, 196);
-               public static readonly Color Black = FromRgb(0, 0, 0);
-               public static readonly Color BlanchedAlmond = FromRgb(255, 235, 205);
-               public static readonly Color Blue = FromRgb(0, 0, 255);
-               public static readonly Color BlueViolet = FromRgb(138, 43, 226);
-               public static readonly Color Brown = FromRgb(165, 42, 42);
-               public static readonly Color BurlyWood = FromRgb(222, 184, 135);
-               public static readonly Color CadetBlue = FromRgb(95, 158, 160);
-               public static readonly Color Chartreuse = FromRgb(127, 255, 0);
-               public static readonly Color Chocolate = FromRgb(210, 105, 30);
-               public static readonly Color Coral = FromRgb(255, 127, 80);
-               public static readonly Color CornflowerBlue = FromRgb(100, 149, 237);
-               public static readonly Color Cornsilk = FromRgb(255, 248, 220);
-               public static readonly Color Crimson = FromRgb(220, 20, 60);
-               public static readonly Color Cyan = FromRgb(0, 255, 255);
-               public static readonly Color DarkBlue = FromRgb(0, 0, 139);
-               public static readonly Color DarkCyan = FromRgb(0, 139, 139);
-               public static readonly Color DarkGoldenrod = FromRgb(184, 134, 11);
-               public static readonly Color DarkGray = FromRgb(169, 169, 169);
-               public static readonly Color DarkGreen = FromRgb(0, 100, 0);
-               public static readonly Color DarkKhaki = FromRgb(189, 183, 107);
-               public static readonly Color DarkMagenta = FromRgb(139, 0, 139);
-               public static readonly Color DarkOliveGreen = FromRgb(85, 107, 47);
-               public static readonly Color DarkOrange = FromRgb(255, 140, 0);
-               public static readonly Color DarkOrchid = FromRgb(153, 50, 204);
-               public static readonly Color DarkRed = FromRgb(139, 0, 0);
-               public static readonly Color DarkSalmon = FromRgb(233, 150, 122);
-               public static readonly Color DarkSeaGreen = FromRgb(143, 188, 143);
-               public static readonly Color DarkSlateBlue = FromRgb(72, 61, 139);
-               public static readonly Color DarkSlateGray = FromRgb(47, 79, 79);
-               public static readonly Color DarkTurquoise = FromRgb(0, 206, 209);
-               public static readonly Color DarkViolet = FromRgb(148, 0, 211);
-               public static readonly Color DeepPink = FromRgb(255, 20, 147);
-               public static readonly Color DeepSkyBlue = FromRgb(0, 191, 255);
-               public static readonly Color DimGray = FromRgb(105, 105, 105);
-               public static readonly Color DodgerBlue = FromRgb(30, 144, 255);
-               public static readonly Color Firebrick = FromRgb(178, 34, 34);
-               public static readonly Color FloralWhite = FromRgb(255, 250, 240);
-               public static readonly Color ForestGreen = FromRgb(34, 139, 34);
-               public static readonly Color Fuchsia = FromRgb(255, 0, 255);
+               public static readonly Color AliceBlue = new Color(240, 248, 255);
+               public static readonly Color AntiqueWhite = new Color(250, 235, 215);
+               public static readonly Color Aqua = new Color(0, 255, 255);
+               public static readonly Color Aquamarine = new Color(127, 255, 212);
+               public static readonly Color Azure = new Color(240, 255, 255);
+               public static readonly Color Beige = new Color(245, 245, 220);
+               public static readonly Color Bisque = new Color(255, 228, 196);
+               public static readonly Color Black = new Color(0, 0, 0);
+               public static readonly Color BlanchedAlmond = new Color(255, 235, 205);
+               public static readonly Color Blue = new Color(0, 0, 255);
+               public static readonly Color BlueViolet = new Color(138, 43, 226);
+               public static readonly Color Brown = new Color(165, 42, 42);
+               public static readonly Color BurlyWood = new Color(222, 184, 135);
+               public static readonly Color CadetBlue = new Color(95, 158, 160);
+               public static readonly Color Chartreuse = new Color(127, 255, 0);
+               public static readonly Color Chocolate = new Color(210, 105, 30);
+               public static readonly Color Coral = new Color(255, 127, 80);
+               public static readonly Color CornflowerBlue = new Color(100, 149, 237);
+               public static readonly Color Cornsilk = new Color(255, 248, 220);
+               public static readonly Color Crimson = new Color(220, 20, 60);
+               public static readonly Color Cyan = new Color(0, 255, 255);
+               public static readonly Color DarkBlue = new Color(0, 0, 139);
+               public static readonly Color DarkCyan = new Color(0, 139, 139);
+               public static readonly Color DarkGoldenrod = new Color(184, 134, 11);
+               public static readonly Color DarkGray = new Color(169, 169, 169);
+               public static readonly Color DarkGreen = new Color(0, 100, 0);
+               public static readonly Color DarkKhaki = new Color(189, 183, 107);
+               public static readonly Color DarkMagenta = new Color(139, 0, 139);
+               public static readonly Color DarkOliveGreen = new Color(85, 107, 47);
+               public static readonly Color DarkOrange = new Color(255, 140, 0);
+               public static readonly Color DarkOrchid = new Color(153, 50, 204);
+               public static readonly Color DarkRed = new Color(139, 0, 0);
+               public static readonly Color DarkSalmon = new Color(233, 150, 122);
+               public static readonly Color DarkSeaGreen = new Color(143, 188, 143);
+               public static readonly Color DarkSlateBlue = new Color(72, 61, 139);
+               public static readonly Color DarkSlateGray = new Color(47, 79, 79);
+               public static readonly Color DarkTurquoise = new Color(0, 206, 209);
+               public static readonly Color DarkViolet = new Color(148, 0, 211);
+               public static readonly Color DeepPink = new Color(255, 20, 147);
+               public static readonly Color DeepSkyBlue = new Color(0, 191, 255);
+               public static readonly Color DimGray = new Color(105, 105, 105);
+               public static readonly Color DodgerBlue = new Color(30, 144, 255);
+               public static readonly Color Firebrick = new Color(178, 34, 34);
+               public static readonly Color FloralWhite = new Color(255, 250, 240);
+               public static readonly Color ForestGreen = new Color(34, 139, 34);
+               public static readonly Color Fuchsia = new Color(255, 0, 255);
                [Obsolete("Fuschia is obsolete as of version 1.3.0. Please use Fuchsia instead.")]
                [EditorBrowsable(EditorBrowsableState.Never)]
-               public static readonly Color Fuschia = FromRgb(255, 0, 255);
-               public static readonly Color Gainsboro = FromRgb(220, 220, 220);
-               public static readonly Color GhostWhite = FromRgb(248, 248, 255);
-               public static readonly Color Gold = FromRgb(255, 215, 0);
-               public static readonly Color Goldenrod = FromRgb(218, 165, 32);
-               public static readonly Color Gray = FromRgb(128, 128, 128);
-               public static readonly Color Green = FromRgb(0, 128, 0);
-               public static readonly Color GreenYellow = FromRgb(173, 255, 47);
-               public static readonly Color Honeydew = FromRgb(240, 255, 240);
-               public static readonly Color HotPink = FromRgb(255, 105, 180);
-               public static readonly Color IndianRed = FromRgb(205, 92, 92);
-               public static readonly Color Indigo = FromRgb(75, 0, 130);
-               public static readonly Color Ivory = FromRgb(255, 255, 240);
-               public static readonly Color Khaki = FromRgb(240, 230, 140);
-               public static readonly Color Lavender = FromRgb(230, 230, 250);
-               public static readonly Color LavenderBlush = FromRgb(255, 240, 245);
-               public static readonly Color LawnGreen = FromRgb(124, 252, 0);
-               public static readonly Color LemonChiffon = FromRgb(255, 250, 205);
-               public static readonly Color LightBlue = FromRgb(173, 216, 230);
-               public static readonly Color LightCoral = FromRgb(240, 128, 128);
-               public static readonly Color LightCyan = FromRgb(224, 255, 255);
-               public static readonly Color LightGoldenrodYellow = FromRgb(250, 250, 210);
-               public static readonly Color LightGray = FromRgb(211, 211, 211);
-               public static readonly Color LightGreen = FromRgb(144, 238, 144);
-               public static readonly Color LightPink = FromRgb(255, 182, 193);
-               public static readonly Color LightSalmon = FromRgb(255, 160, 122);
-               public static readonly Color LightSeaGreen = FromRgb(32, 178, 170);
-               public static readonly Color LightSkyBlue = FromRgb(135, 206, 250);
-               public static readonly Color LightSlateGray = FromRgb(119, 136, 153);
-               public static readonly Color LightSteelBlue = FromRgb(176, 196, 222);
-               public static readonly Color LightYellow = FromRgb(255, 255, 224);
-               public static readonly Color Lime = FromRgb(0, 255, 0);
-               public static readonly Color LimeGreen = FromRgb(50, 205, 50);
-               public static readonly Color Linen = FromRgb(250, 240, 230);
-               public static readonly Color Magenta = FromRgb(255, 0, 255);
-               public static readonly Color Maroon = FromRgb(128, 0, 0);
-               public static readonly Color MediumAquamarine = FromRgb(102, 205, 170);
-               public static readonly Color MediumBlue = FromRgb(0, 0, 205);
-               public static readonly Color MediumOrchid = FromRgb(186, 85, 211);
-               public static readonly Color MediumPurple = FromRgb(147, 112, 219);
-               public static readonly Color MediumSeaGreen = FromRgb(60, 179, 113);
-               public static readonly Color MediumSlateBlue = FromRgb(123, 104, 238);
-               public static readonly Color MediumSpringGreen = FromRgb(0, 250, 154);
-               public static readonly Color MediumTurquoise = FromRgb(72, 209, 204);
-               public static readonly Color MediumVioletRed = FromRgb(199, 21, 133);
-               public static readonly Color MidnightBlue = FromRgb(25, 25, 112);
-               public static readonly Color MintCream = FromRgb(245, 255, 250);
-               public static readonly Color MistyRose = FromRgb(255, 228, 225);
-               public static readonly Color Moccasin = FromRgb(255, 228, 181);
-               public static readonly Color NavajoWhite = FromRgb(255, 222, 173);
-               public static readonly Color Navy = FromRgb(0, 0, 128);
-               public static readonly Color OldLace = FromRgb(253, 245, 230);
-               public static readonly Color Olive = FromRgb(128, 128, 0);
-               public static readonly Color OliveDrab = FromRgb(107, 142, 35);
-               public static readonly Color Orange = FromRgb(255, 165, 0);
-               public static readonly Color OrangeRed = FromRgb(255, 69, 0);
-               public static readonly Color Orchid = FromRgb(218, 112, 214);
-               public static readonly Color PaleGoldenrod = FromRgb(238, 232, 170);
-               public static readonly Color PaleGreen = FromRgb(152, 251, 152);
-               public static readonly Color PaleTurquoise = FromRgb(175, 238, 238);
-               public static readonly Color PaleVioletRed = FromRgb(219, 112, 147);
-               public static readonly Color PapayaWhip = FromRgb(255, 239, 213);
-               public static readonly Color PeachPuff = FromRgb(255, 218, 185);
-               public static readonly Color Peru = FromRgb(205, 133, 63);
-               public static readonly Color Pink = FromRgb(255, 192, 203);
-               public static readonly Color Plum = FromRgb(221, 160, 221);
-               public static readonly Color PowderBlue = FromRgb(176, 224, 230);
-               public static readonly Color Purple = FromRgb(128, 0, 128);
-               public static readonly Color Red = FromRgb(255, 0, 0);
-               public static readonly Color RosyBrown = FromRgb(188, 143, 143);
-               public static readonly Color RoyalBlue = FromRgb(65, 105, 225);
-               public static readonly Color SaddleBrown = FromRgb(139, 69, 19);
-               public static readonly Color Salmon = FromRgb(250, 128, 114);
-               public static readonly Color SandyBrown = FromRgb(244, 164, 96);
-               public static readonly Color SeaGreen = FromRgb(46, 139, 87);
-               public static readonly Color SeaShell = FromRgb(255, 245, 238);
-               public static readonly Color Sienna = FromRgb(160, 82, 45);
-               public static readonly Color Silver = FromRgb(192, 192, 192);
-               public static readonly Color SkyBlue = FromRgb(135, 206, 235);
-               public static readonly Color SlateBlue = FromRgb(106, 90, 205);
-               public static readonly Color SlateGray = FromRgb(112, 128, 144);
-               public static readonly Color Snow = FromRgb(255, 250, 250);
-               public static readonly Color SpringGreen = FromRgb(0, 255, 127);
-               public static readonly Color SteelBlue = FromRgb(70, 130, 180);
-               public static readonly Color Tan = FromRgb(210, 180, 140);
-               public static readonly Color Teal = FromRgb(0, 128, 128);
-               public static readonly Color Thistle = FromRgb(216, 191, 216);
-               public static readonly Color Tomato = FromRgb(255, 99, 71);
-               public static readonly Color Transparent = FromRgba(255, 255, 255, 0);
-               public static readonly Color Turquoise = FromRgb(64, 224, 208);
-               public static readonly Color Violet = FromRgb(238, 130, 238);
-               public static readonly Color Wheat = FromRgb(245, 222, 179);
-               public static readonly Color White = FromRgb(255, 255, 255);
-               public static readonly Color WhiteSmoke = FromRgb(245, 245, 245);
-               public static readonly Color Yellow = FromRgb(255, 255, 0);
-               public static readonly Color YellowGreen = FromRgb(154, 205, 50);
+               public static readonly Color Fuschia = new Color(255, 0, 255);
+               public static readonly Color Gainsboro = new Color(220, 220, 220);
+               public static readonly Color GhostWhite = new Color(248, 248, 255);
+               public static readonly Color Gold = new Color(255, 215, 0);
+               public static readonly Color Goldenrod = new Color(218, 165, 32);
+               public static readonly Color Gray = new Color(128, 128, 128);
+               public static readonly Color Green = new Color(0, 128, 0);
+               public static readonly Color GreenYellow = new Color(173, 255, 47);
+               public static readonly Color Honeydew = new Color(240, 255, 240);
+               public static readonly Color HotPink = new Color(255, 105, 180);
+               public static readonly Color IndianRed = new Color(205, 92, 92);
+               public static readonly Color Indigo = new Color(75, 0, 130);
+               public static readonly Color Ivory = new Color(255, 255, 240);
+               public static readonly Color Khaki = new Color(240, 230, 140);
+               public static readonly Color Lavender = new Color(230, 230, 250);
+               public static readonly Color LavenderBlush = new Color(255, 240, 245);
+               public static readonly Color LawnGreen = new Color(124, 252, 0);
+               public static readonly Color LemonChiffon = new Color(255, 250, 205);
+               public static readonly Color LightBlue = new Color(173, 216, 230);
+               public static readonly Color LightCoral = new Color(240, 128, 128);
+               public static readonly Color LightCyan = new Color(224, 255, 255);
+               public static readonly Color LightGoldenrodYellow = new Color(250, 250, 210);
+               public static readonly Color LightGray = new Color(211, 211, 211);
+               public static readonly Color LightGreen = new Color(144, 238, 144);
+               public static readonly Color LightPink = new Color(255, 182, 193);
+               public static readonly Color LightSalmon = new Color(255, 160, 122);
+               public static readonly Color LightSeaGreen = new Color(32, 178, 170);
+               public static readonly Color LightSkyBlue = new Color(135, 206, 250);
+               public static readonly Color LightSlateGray = new Color(119, 136, 153);
+               public static readonly Color LightSteelBlue = new Color(176, 196, 222);
+               public static readonly Color LightYellow = new Color(255, 255, 224);
+               public static readonly Color Lime = new Color(0, 255, 0);
+               public static readonly Color LimeGreen = new Color(50, 205, 50);
+               public static readonly Color Linen = new Color(250, 240, 230);
+               public static readonly Color Magenta = new Color(255, 0, 255);
+               public static readonly Color Maroon = new Color(128, 0, 0);
+               public static readonly Color MediumAquamarine = new Color(102, 205, 170);
+               public static readonly Color MediumBlue = new Color(0, 0, 205);
+               public static readonly Color MediumOrchid = new Color(186, 85, 211);
+               public static readonly Color MediumPurple = new Color(147, 112, 219);
+               public static readonly Color MediumSeaGreen = new Color(60, 179, 113);
+               public static readonly Color MediumSlateBlue = new Color(123, 104, 238);
+               public static readonly Color MediumSpringGreen = new Color(0, 250, 154);
+               public static readonly Color MediumTurquoise = new Color(72, 209, 204);
+               public static readonly Color MediumVioletRed = new Color(199, 21, 133);
+               public static readonly Color MidnightBlue = new Color(25, 25, 112);
+               public static readonly Color MintCream = new Color(245, 255, 250);
+               public static readonly Color MistyRose = new Color(255, 228, 225);
+               public static readonly Color Moccasin = new Color(255, 228, 181);
+               public static readonly Color NavajoWhite = new Color(255, 222, 173);
+               public static readonly Color Navy = new Color(0, 0, 128);
+               public static readonly Color OldLace = new Color(253, 245, 230);
+               public static readonly Color Olive = new Color(128, 128, 0);
+               public static readonly Color OliveDrab = new Color(107, 142, 35);
+               public static readonly Color Orange = new Color(255, 165, 0);
+               public static readonly Color OrangeRed = new Color(255, 69, 0);
+               public static readonly Color Orchid = new Color(218, 112, 214);
+               public static readonly Color PaleGoldenrod = new Color(238, 232, 170);
+               public static readonly Color PaleGreen = new Color(152, 251, 152);
+               public static readonly Color PaleTurquoise = new Color(175, 238, 238);
+               public static readonly Color PaleVioletRed = new Color(219, 112, 147);
+               public static readonly Color PapayaWhip = new Color(255, 239, 213);
+               public static readonly Color PeachPuff = new Color(255, 218, 185);
+               public static readonly Color Peru = new Color(205, 133, 63);
+               public static readonly Color Pink = new Color(255, 192, 203);
+               public static readonly Color Plum = new Color(221, 160, 221);
+               public static readonly Color PowderBlue = new Color(176, 224, 230);
+               public static readonly Color Purple = new Color(128, 0, 128);
+               public static readonly Color Red = new Color(255, 0, 0);
+               public static readonly Color RosyBrown = new Color(188, 143, 143);
+               public static readonly Color RoyalBlue = new Color(65, 105, 225);
+               public static readonly Color SaddleBrown = new Color(139, 69, 19);
+               public static readonly Color Salmon = new Color(250, 128, 114);
+               public static readonly Color SandyBrown = new Color(244, 164, 96);
+               public static readonly Color SeaGreen = new Color(46, 139, 87);
+               public static readonly Color SeaShell = new Color(255, 245, 238);
+               public static readonly Color Sienna = new Color(160, 82, 45);
+               public static readonly Color Silver = new Color(192, 192, 192);
+               public static readonly Color SkyBlue = new Color(135, 206, 235);
+               public static readonly Color SlateBlue = new Color(106, 90, 205);
+               public static readonly Color SlateGray = new Color(112, 128, 144);
+               public static readonly Color Snow = new Color(255, 250, 250);
+               public static readonly Color SpringGreen = new Color(0, 255, 127);
+               public static readonly Color SteelBlue = new Color(70, 130, 180);
+               public static readonly Color Tan = new Color(210, 180, 140);
+               public static readonly Color Teal = new Color(0, 128, 128);
+               public static readonly Color Thistle = new Color(216, 191, 216);
+               public static readonly Color Tomato = new Color(255, 99, 71);
+               public static readonly Color Transparent = new Color(255, 255, 255, 0);
+               public static readonly Color Turquoise = new Color(64, 224, 208);
+               public static readonly Color Violet = new Color(238, 130, 238);
+               public static readonly Color Wheat = new Color(245, 222, 179);
+               public static readonly Color White = new Color(255, 255, 255);
+               public static readonly Color WhiteSmoke = new Color(245, 245, 245);
+               public static readonly Color Yellow = new Color(255, 255, 0);
+               public static readonly Color YellowGreen = new Color(154, 205, 50);
 
                #endregion
        }
index f246ca9..658162b 100644 (file)
@@ -109,33 +109,15 @@ namespace Xamarin.Forms
                                if (s_initialized)
                                        return;
 
-                               Type targetAttrType = typeof(DependencyAttribute);
-
                                // Don't use LINQ for performance reasons
                                // Naive implementation can easily take over a second to run
                                foreach (Assembly assembly in assemblies)
                                {
-                                       object[] attributes;
-                                       try
-                                       {
-#if NETSTANDARD2_0
-                                               attributes = assembly.GetCustomAttributes(targetAttrType, true);
-#else
-                                               attributes = assembly.GetCustomAttributes(targetAttrType).ToArray();
-#endif
-                                       }
-                                       catch (System.IO.FileNotFoundException)
-                                       {
-                                               // Sometimes the previewer doesn't actually have everything required for these loads to work
-                                               Log.Warning(nameof(Registrar), "Could not load assembly: {0} for Attibute {1} | Some renderers may not be loaded", assembly.FullName, targetAttrType.FullName);
-                                               continue;
-                                       }
-
-                                       var length = attributes.Length;
-                                       if (length == 0)
+                                       object[] attributes = assembly.GetCustomAttributesSafe(typeof(DependencyAttribute));
+                                       if (attributes == null)
                                                continue;
 
-                                       for (int i = 0; i < length; i++)
+                                       for (int i = 0; i < attributes.Length; i++)
                                        {
                                                DependencyAttribute attribute = (DependencyAttribute)attributes[i];
                                                if (!DependencyTypes.Contains(attribute.Implementor))
index 705509f..690fb47 100644 (file)
@@ -38,7 +38,7 @@ namespace Xamarin.Forms
                IStyleSelectable IStyleSelectable.Parent => Parent;
 
                //on parent set, or on parent stylesheet changed, reapply all
-               void ApplyStyleSheetsOnParentSet()
+               internal void ApplyStyleSheetsOnParentSet()
                {
                        var parent = Parent;
                        if (parent == null)
diff --git a/Xamarin.Forms/Xamarin.Forms.Core/EmbeddedFont.cs b/Xamarin.Forms/Xamarin.Forms.Core/EmbeddedFont.cs
new file mode 100644 (file)
index 0000000..5765bc7
--- /dev/null
@@ -0,0 +1,11 @@
+using System;
+using System.IO;
+
+namespace Xamarin.Forms
+{
+       public class EmbeddedFont
+       {
+               public string FontName { get; set; }
+               public Stream ResourceStream { get; set; }
+       }
+}
diff --git a/Xamarin.Forms/Xamarin.Forms.Core/ExportFontAttribute.cs b/Xamarin.Forms/Xamarin.Forms.Core/ExportFontAttribute.cs
new file mode 100644 (file)
index 0000000..14c0d8a
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+namespace Xamarin.Forms
+{
+       [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+       public class ExportFontAttribute : Attribute
+       {
+               public string Alias { get; set; }
+               public ExportFontAttribute(string fontFileName)
+               {
+                       FontFileName = fontFileName;
+               }
+
+               public string EmbeddedFontResourceId { get; set; }
+               public string FontFileName { get; }
+       }
+}
diff --git a/Xamarin.Forms/Xamarin.Forms.Core/FontFile.cs b/Xamarin.Forms/Xamarin.Forms.Core/FontFile.cs
new file mode 100644 (file)
index 0000000..3211fa7
--- /dev/null
@@ -0,0 +1,108 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace Xamarin.Forms.Core
+{
+       public class FontFile
+       {
+               public string FileName { get; set; }
+               public string Extension { get; set; }
+               public string FileNameWithExtension(string extension) => $"{FileName}{extension}";
+               public string FileNameWithExtension() => FileNameWithExtension(Extension);
+               public string PostScriptName { get; set; }
+
+               public string GetPostScriptNameWithSpaces() =>
+                       string.Join(" ", GetFontName(PostScriptName));
+
+               public static readonly string[] Extensions = {
+                               ".ttf",
+                               ".otf",
+                       };
+
+               public static FontFile FromString(string input)
+               {
+                       var hashIndex = input.IndexOf("#", System.StringComparison.Ordinal);
+                       //UWP names require Spaces. Sometimes people may use those, "CuteFont-Regular#Cute Font" should be "CuteFont-Regular#CuteFont"
+                       var postScriptName = hashIndex > 0 ? input.Substring(hashIndex + 1).Replace(" ", "") : input;
+                       //Get the fontFamily name;
+                       var fontFamilyName = hashIndex > 0 ? input.Substring(0, hashIndex) : input;
+
+                       var foundExtension = Extensions.
+                               FirstOrDefault(x => fontFamilyName.EndsWith(x, StringComparison.OrdinalIgnoreCase));
+
+                       if (!string.IsNullOrWhiteSpace(foundExtension))
+                               fontFamilyName = fontFamilyName.Substring(0, fontFamilyName.Length - foundExtension.Length);
+
+                       return new FontFile
+                       {
+                               FileName = fontFamilyName,
+                               Extension = foundExtension,
+                               PostScriptName = postScriptName,
+                       };
+               }
+
+
+               static IEnumerable<string> GetFontName(string fontFamily)
+               {
+                       if (fontFamily.Contains(" "))
+                       {
+                               yield return fontFamily;
+                               //We are done, they have spaces, they have it handled.
+                               yield break;
+                       }
+                       string currentString = "";
+                       char lastCharacter = ' ';
+                       var index = fontFamily.LastIndexOf("-", StringComparison.Ordinal);
+                       bool multipleCaps = false;
+                       var cleansedString = index > 0 ? fontFamily.Substring(0, index) : fontFamily;
+                       foreach (var c in cleansedString)
+                       {
+                               //Always break on these characters
+                               if (c == '_' || c == '-')
+                               {
+                                       yield return currentString;
+                                       //Reset everything,
+                                       currentString = "";
+                                       lastCharacter = ' ';
+                                       multipleCaps = false;
+                               }
+                               else
+                               {
+
+                                       if (char.IsUpper(c))
+                                       {
+                                               //If the last character is lowercase, we are in a new CamelCase font
+                                               if (char.IsLower(lastCharacter))
+                                               {
+                                                       yield return currentString;
+                                                       currentString = "";
+                                                       lastCharacter = ' ';
+                                               }
+                                               else if (char.IsUpper(lastCharacter))
+                                               {
+                                                       multipleCaps = true;
+                                               }
+                                       }
+
+                                       //Detect multiple UpperCase letters so we can separate things like PTSansNarrow into "PT Sans Narrow"
+                                       else if (multipleCaps && currentString.Length > 1)
+                                       {
+                                               var last = currentString[currentString.Length - 1];
+                                               yield return currentString.Substring(0, currentString.Length - 1);
+                                               //Reset everything so it doesnt do a space
+                                               multipleCaps = false;
+                                               lastCharacter = ' ';
+                                               currentString = last.ToString();
+                                       }
+
+                                       currentString += c;
+                                       lastCharacter = c;
+                               }
+                       }
+                       //Send what is left!
+                       if (!string.IsNullOrWhiteSpace(currentString))
+                               yield return currentString.Trim();
+               }
+       }
+}
diff --git a/Xamarin.Forms/Xamarin.Forms.Core/FontRegistrar.cs b/Xamarin.Forms/Xamarin.Forms.Core/FontRegistrar.cs
new file mode 100644 (file)
index 0000000..ca444dd
--- /dev/null
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+
+namespace Xamarin.Forms.Internals
+{
+       public static class FontRegistrar
+       {
+               internal static readonly Dictionary<string, (ExportFontAttribute attribute, Assembly assembly)> EmbeddedFonts = new Dictionary<string, (ExportFontAttribute attribute, Assembly assembly)>();
+
+               public static void Register(ExportFontAttribute fontAttribute, Assembly assembly)
+               {
+                       EmbeddedFonts[fontAttribute.FontFileName] = (fontAttribute, assembly);
+               }
+
+               //TODO: Investigate making this Async
+               public static (bool hasFont, string fontPath) HasFont(string font)
+               {
+                       try
+                       {
+                               if (!EmbeddedFonts.TryGetValue(font, out var foundFont))
+                               {
+                                       return (false, null);
+                               }
+
+                               var fontStream = GetEmbeddedResourceStream(foundFont.assembly, font);
+
+                               var type = Registrar.Registered.GetHandlerType(typeof(EmbeddedFont));
+                               var fontHandler = (IEmbeddedFontLoader)Activator.CreateInstance(type);
+                               return fontHandler.LoadFont(new EmbeddedFont { FontName = font, ResourceStream = fontStream });
+
+                       }
+                       catch (Exception ex)
+                       {
+                               Debug.WriteLine(ex);
+                       }
+                       return (false, null);
+               }
+
+               static Stream GetEmbeddedResourceStream(Assembly assembly, string resourceFileName)
+               {
+                       var resourceNames = assembly.GetManifestResourceNames();
+
+                       var resourcePaths = resourceNames
+                               .Where(x => x.EndsWith(resourceFileName, StringComparison.CurrentCultureIgnoreCase))
+                               .ToArray();
+
+                       if (!resourcePaths.Any())
+                       {
+                               throw new Exception(string.Format("Resource ending with {0} not found.", resourceFileName));
+                       }
+                       if (resourcePaths.Length > 1)
+                       {
+                               resourcePaths = resourcePaths.Where(x => IsFile(x, resourceFileName)).ToArray();
+                       }
+
+                       return assembly.GetManifestResourceStream(resourcePaths.FirstOrDefault());
+               }
+
+               static bool IsFile(string path, string file)
+               {
+                       if (!path.EndsWith(file, StringComparison.Ordinal))
+                               return false;
+                       return path.Replace(file, "").EndsWith(".", StringComparison.Ordinal);
+               }
+       }
+}
diff --git a/Xamarin.Forms/Xamarin.Forms.Core/IEmbeddedFontLoader.cs b/Xamarin.Forms/Xamarin.Forms.Core/IEmbeddedFontLoader.cs
new file mode 100644 (file)
index 0000000..94f46aa
--- /dev/null
@@ -0,0 +1,8 @@
+using System;
+namespace Xamarin.Forms
+{
+       public interface IEmbeddedFontLoader
+       {
+               (bool success, string filePath) LoadFont(EmbeddedFont font);
+       }
+}
index 2bd441e..92d4add 100644 (file)
@@ -1,7 +1,5 @@
 using System.Collections;
-using System.Collections.Generic;
 using System.Collections.Specialized;
-using System.Runtime.CompilerServices;
 using Xamarin.Forms.Platform;
 
 namespace Xamarin.Forms
@@ -21,8 +19,8 @@ namespace Xamarin.Forms
 
                public static readonly BindableProperty MaximumVisibleProperty = BindableProperty.Create(nameof(MaximumVisible), typeof(int), typeof(IndicatorView), int.MaxValue);
 
-               public static readonly BindableProperty IndicatorTemplateProperty = BindableProperty.Create(nameof(IndicatorTemplate), typeof(DataTemplate), typeof(IndicatorView), default(DataTemplate), propertyChanged: (bindable, oldValue, newValue)
-                       => UpdateIndicatorLayout(((IndicatorView)bindable), newValue));
+               public static readonly BindableProperty IndicatorTemplateProperty = BindableProperty.Create(nameof(IndicatorTemplate), typeof(DataTemplate), typeof(IndicatorView), default(DataTemplate), propertyChanging: (bindable, oldValue, newValue)
+                       => UpdateIndicatorLayout((IndicatorView)bindable, newValue));
 
                public static readonly BindableProperty HideSingleProperty = BindableProperty.Create(nameof(HideSingle), typeof(bool), typeof(IndicatorView), true);
 
@@ -192,6 +190,5 @@ namespace Xamarin.Forms
                        }
                        Count = count;
                }
-
        }
 }
\ No newline at end of file
index 1acb7a1..f4bf238 100644 (file)
@@ -135,7 +135,7 @@ namespace Xamarin.Forms
                                return;
                        }
 
-                       _associatedObjects.RemoveAll(t => !t.IsAlive);
+                       _associatedObjects.RemoveAll(t => t == null || !t.IsAlive);
                        _cleanupThreshold = _associatedObjects.Count + CleanupTrigger;
                }
        }
index 4b95d37..3ad43c8 100644 (file)
@@ -12,6 +12,11 @@ namespace Xamarin.Forms.Internals
        {
                int _masterDetails;
                Page _target;
+               ToolBarItemComparer _toolBarItemComparer;
+               public ToolbarTracker()
+               {
+                       _toolBarItemComparer = new ToolBarItemComparer();
+               }
 
                public IEnumerable<Page> AdditionalTargets { get; set; }
 
@@ -44,12 +49,20 @@ namespace Xamarin.Forms.Internals
                        get
                        {
                                if (Target == null)
-                                       return Enumerable.Empty<ToolbarItem>();
-                               IEnumerable<ToolbarItem> items = GetCurrentToolbarItems(Target);
+                                       return new ToolbarItem[0];
+
+                               // I realize this is sorting on every single get but we don't have 
+                               // a mechanism in place currently to invalidate a stored version of this
+
+                               List<ToolbarItem> returnValue = GetCurrentToolbarItems(Target);
+
                                if (AdditionalTargets != null)
-                                       items = items.Concat(AdditionalTargets.SelectMany(t => t.ToolbarItems));
+                                       foreach(var item in AdditionalTargets)
+                                               foreach(var toolbarItem in item.ToolbarItems)
+                                                       returnValue.Add(toolbarItem);
 
-                               return items.OrderBy(ti => ti.Priority);
+                               returnValue.Sort(_toolBarItemComparer);
+                               return returnValue;
                        }
                }
 
@@ -58,7 +71,7 @@ namespace Xamarin.Forms.Internals
                void EmitCollectionChanged()
                        => CollectionChanged?.Invoke(this, EventArgs.Empty);
 
-               IEnumerable<ToolbarItem> GetCurrentToolbarItems(Page page)
+               List<ToolbarItem> GetCurrentToolbarItems(Page page)
                {
                        var result = new List<ToolbarItem>();
                        result.AddRange(page.ToolbarItems);
@@ -178,5 +191,10 @@ namespace Xamarin.Forms.Internals
                        page.DescendantRemoved -= OnChildRemoved;
                        page.PropertyChanged -= OnPropertyChanged;
                }
+
+               class ToolBarItemComparer : IComparer<ToolbarItem>
+               {
+                       public int Compare(ToolbarItem x, ToolbarItem y) => x.Priority.CompareTo(y.Priority);
+               }
        }
 }
\ No newline at end of file
index 25ee49c..c87ebf9 100644 (file)
@@ -53,7 +53,7 @@ namespace Xamarin.Forms
                                if (_styleClass == value)
                                        return;
 
-                               if (_styleClass != null && _classStyles != null)
+                               if (_styleClass != null && _classStyleProperties != null)
                                        foreach (var classStyleProperty in _classStyleProperties)
                                                Target.RemoveDynamicResource(classStyleProperty);
 
@@ -67,6 +67,10 @@ namespace Xamarin.Forms
                                                _classStyleProperties.Add (classStyleProperty);
                                                Target.OnSetDynamicResource (classStyleProperty, Forms.Style.StyleClassPrefix + styleClass);
                                        }
+
+                                       //reapply the css stylesheets
+                                       if (Target is Element targetelement)
+                                               targetelement.ApplyStyleSheetsOnParentSet();
                                }
                        }
                }
index 28a68cc..321ae47 100644 (file)
@@ -32,6 +32,8 @@ namespace Xamarin.Forms
                [EditorBrowsable(EditorBrowsableState.Never)]
                public static readonly BindableProperty TitleIconProperty = TitleIconImageSourceProperty;
 
+               public static readonly BindableProperty IconColorProperty = BindableProperty.CreateAttached("IconColor", typeof(Color), typeof(NavigationPage), Color.Default);
+
                public static readonly BindableProperty TitleViewProperty = BindableProperty.CreateAttached("TitleView", typeof(View), typeof(NavigationPage), null, propertyChanging: TitleViewPropertyChanging);
 
                static readonly BindablePropertyKey CurrentPagePropertyKey = BindableProperty.CreateReadOnly("CurrentPage", typeof(Page), typeof(NavigationPage), null);
@@ -159,6 +161,16 @@ namespace Xamarin.Forms
                        return (View)bindable.GetValue(TitleViewProperty);
                }
 
+               public static Color GetIconColor(BindableObject bindable)
+               {
+                       if (bindable == null)
+                       {
+                               return Color.Default;           
+                       }
+
+                       return (Color)bindable.GetValue(IconColorProperty);
+               }
+
                public Task<Page> PopAsync()
                {
                        return PopAsync(true);
@@ -278,6 +290,11 @@ namespace Xamarin.Forms
                        bindable.SetValue(TitleViewProperty, value);
                }
 
+               public static void SetIconColor(BindableObject bindable, Color value)
+               {
+                       bindable.SetValue(IconColorProperty, value);
+               }
+
                protected override bool OnBackButtonPressed()
                {
                        if (CurrentPage.SendBackButtonPressed())
index f5fb5de..2651ae9 100644 (file)
@@ -1,20 +1,48 @@
 using System;
 using System.ComponentModel;
+using System.Runtime.CompilerServices;
 
 namespace Xamarin.Forms.Internals
 {
        [EditorBrowsable(EditorBrowsableState.Never)]
        public static class NumericExtensions
        {
-
+               [MethodImpl(MethodImplOptions.AggressiveInlining)]
                public static double Clamp(this double self, double min, double max)
                {
-                       return Math.Min(max, Math.Max(self, min));
+                       if (max < min)
+                       {
+                               return max;
+                       }
+                       else if (self < min)
+                       {
+                               return min;
+                       }
+                       else if (self > max)
+                       {
+                               return max;
+                       }
+
+                       return self;
                }
 
+               [MethodImpl(MethodImplOptions.AggressiveInlining)]
                public static int Clamp(this int self, int min, int max)
                {
-                       return Math.Min(max, Math.Max(self, min));
+                       if (max < min)
+                       {
+                               return max;
+                       }
+                       else if (self < min)
+                       {
+                               return min;
+                       }
+                       else if (self > max)
+                       {
+                               return max;
+                       }
+
+                       return self;
                }
        }
 }
\ No newline at end of file
index 2a4bff6..fd5f2b7 100644 (file)
@@ -225,7 +225,12 @@ namespace Xamarin.Forms
                public Task<string> DisplayPromptAsync(string title, string message, string accept = "OK", string cancel = "Cancel", string placeholder = null, int maxLength = -1, Keyboard keyboard = default(Keyboard), string initialValue = "")
                {
                        var args = new PromptArguments(title, message, accept, cancel, placeholder, maxLength, keyboard, initialValue);
-                       MessagingCenter.Send(this, PromptSignalName, args);
+
+                       if (IsPlatformEnabled)
+                               MessagingCenter.Send(this, PromptSignalName, args);
+                       else
+                               _pendingActions.Add(() => MessagingCenter.Send(this, PromptSignalName, args));
+
                        return args.Result.Task;
                }
 
index fd47061..9ea553c 100644 (file)
@@ -47,13 +47,12 @@ namespace Xamarin.Forms.Internals
 
                internal static object[] GetCustomAttributesSafe(this Assembly assembly,  Type attrType)
                {
-                       object[] attributes = null;
                        try
                        {
 #if NETSTANDARD2_0
-                               attributes = assembly.GetCustomAttributes(attrType, true);
+                               return assembly.GetCustomAttributes(attrType, true);
 #else
-                               attributes = assembly.GetCustomAttributes(attrType).ToArray();
+                               return assembly.GetCustomAttributes(attrType).ToArray();
 #endif
                        }
                        catch (System.IO.FileNotFoundException)
@@ -62,7 +61,7 @@ namespace Xamarin.Forms.Internals
                                Log.Warning(nameof(Registrar), "Could not load assembly: {0} for Attribute {1} | Some renderers may not be loaded", assembly.FullName, attrType.FullName);
                        }
 
-                       return attributes;
+                       return null;
                }
 
                public static Type[] GetExportedTypes(this Assembly assembly)
index 0fa4a32..dd8eafc 100644 (file)
@@ -263,6 +263,7 @@ namespace Xamarin.Forms.Internals
                //typeof(ExportRendererAttribute);
                //typeof(ExportCellAttribute);
                //typeof(ExportImageSourceHandlerAttribute);
+               //TODO this is no longer used?
                public static void RegisterRenderers(HandlerAttribute[] attributes)
                {
                        var length = attributes.Length;
@@ -336,37 +337,39 @@ namespace Xamarin.Forms.Internals
 
                                foreach (Type attrType in attrTypes)
                                {
-                                       object[] attributes;
-                                       try
-                                       {
-#if NETSTANDARD2_0
-                                               attributes = assembly.GetCustomAttributes(attrType, true);
-#else
-                                               attributes = assembly.GetCustomAttributes(attrType).ToArray();
-#endif
-                                       }
-                                       catch (System.IO.FileNotFoundException)
-                                       {
-                                               // Sometimes the previewer doesn't actually have everything required for these loads to work
-                                               Log.Warning(nameof(Registrar), "Could not load assembly: {0} for Attibute {1} | Some renderers may not be loaded", assembly.FullName, attrType.FullName);
+                                       object[] attributes = assembly.GetCustomAttributesSafe(attrType);
+                                       if (attributes == null || attributes.Length == 0)
                                                continue;
+                                       
+                                       var length = attributes.Length;
+                                       for (var i = 0; i < length; i++)
+                                       {
+                                               var a = attributes[i];
+                                               var attribute = a as HandlerAttribute;
+                                               if(attribute == null && (a is ExportFontAttribute fa))
+                                               {
+                                                       FontRegistrar.Register(fa, assembly);
+                                               }
+                                               else
+                                               {
+                                                       if (attribute.ShouldRegister())
+                                                               Registered.Register(attribute.HandlerType, attribute.TargetType, attribute.SupportedVisuals, attribute.Priority);
+                                               }
                                        }
+                               }
 
-                                       var handlerAttributes = new HandlerAttribute[attributes.Length];
-                                       Array.Copy(attributes, handlerAttributes, attributes.Length);
-                                       RegisterRenderers(handlerAttributes);
+                               object[] effectAttributes = assembly.GetCustomAttributesSafe(typeof (ExportEffectAttribute));
+                               if (effectAttributes == null || effectAttributes.Length == 0)
+                               {
+                                       Profile.FrameEnd(assemblyName);
+                                       continue;
                                }
 
                                string resolutionName = assembly.FullName;
                                var resolutionNameAttribute = (ResolutionGroupNameAttribute)assembly.GetCustomAttribute(typeof(ResolutionGroupNameAttribute));
                                if (resolutionNameAttribute != null)
                                        resolutionName = resolutionNameAttribute.ShortName;
-
-#if NETSTANDARD2_0
-                               object[] effectAttributes = assembly.GetCustomAttributes(typeof(ExportEffectAttribute), true);
-#else
-                               object[] effectAttributes = assembly.GetCustomAttributes(typeof(ExportEffectAttribute)).ToArray();
-#endif
+                               //NOTE: a simple cast to ExportEffectAttribute[] failed on UWP, hence the Array.Copy
                                var typedEffectAttributes = new ExportEffectAttribute[effectAttributes.Length];
                                Array.Copy(effectAttributes, typedEffectAttributes, effectAttributes.Length);
                                RegisterEffects(resolutionName, typedEffectAttributes);
index 6427f82..d7a6d8f 100644 (file)
@@ -1,4 +1,6 @@
-namespace Xamarin.Forms
+using System;
+
+namespace Xamarin.Forms
 {
        public interface IShellContentController : IElementController
        {
@@ -7,5 +9,6 @@
                void RecyclePage(Page page);
 
                Page Page { get; }
+               event EventHandler IsPageVisibleChanged;
        }
 }
\ No newline at end of file
index fcf8e0a..9ac3f42 100644 (file)
@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
 using System.Threading.Tasks;
 
 namespace Xamarin.Forms
@@ -43,5 +45,9 @@ namespace Xamarin.Forms
                bool RemoveFlyoutBehaviorObserver(IFlyoutBehaviorObserver observer);
 
                void UpdateCurrentState(ShellNavigationSource source);
+
+               ReadOnlyCollection<ShellItem> GetItems();
+
+               event NotifyCollectionChangedEventHandler ItemsCollectionChanged;
        }
 }
\ No newline at end of file
index f394943..d72a31a 100644 (file)
@@ -1,11 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
 
 namespace Xamarin.Forms
 {
        public interface IShellItemController : IElementController
        {
                bool ProposeSection(ShellSection shellSection, bool setValue = true);
+
+               ReadOnlyCollection<ShellSection> GetItems();
+               event NotifyCollectionChangedEventHandler ItemsCollectionChanged;
        }
 }
\ No newline at end of file
index 7841cce..1f05682 100644 (file)
@@ -1,6 +1,9 @@
 using System;
 using System.Collections.Generic;
+using System.ComponentModel;
 using System.Threading.Tasks;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
 using Xamarin.Forms.Internals;
 
 namespace Xamarin.Forms
@@ -21,8 +24,23 @@ namespace Xamarin.Forms
 
                void SendInsetChanged(Thickness inset, double tabThickness);
 
+               void SendPopping(Task poppingCompleted);
+               void SendPoppingToRoot(Task finishedPopping);
+
+               [Obsolete]
+               [EditorBrowsable(EditorBrowsableState.Never)]
                void SendPopped();
+
+               [Obsolete]
+               [EditorBrowsable(EditorBrowsableState.Never)]
                void SendPopping(Page page);
+
+               [Obsolete]
+               [EditorBrowsable(EditorBrowsableState.Never)]
                void SendPopped(Page page);
+
+               ReadOnlyCollection<ShellContent> GetItems();
+
+               event NotifyCollectionChangedEventHandler ItemsCollectionChanged;
        }
-}
\ No newline at end of file
+}
index cc5fcfc..0676fa0 100644 (file)
@@ -1,7 +1,6 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using Xamarin.Forms.Internals;
-using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
 
 namespace Xamarin.Forms
 {
@@ -21,11 +20,7 @@ namespace Xamarin.Forms
                internal NavigableElement()
                {
                        Navigation = new NavigationProxy();
-
-                       if (OptionalFeatureValues.UseStyle)
-                       {
-                               _mergedStyle = new MergedStyle(GetType(), this);
-                       }
+                       _mergedStyle = new MergedStyle(GetType(), this);
                }
 
                public INavigation Navigation {
@@ -35,10 +30,7 @@ namespace Xamarin.Forms
 
                public Style Style {
                        get { return (Style)GetValue(StyleProperty); }
-                       set {
-                               if (OptionalFeatureValues.UseStyle)
-                                       SetValue(StyleProperty, value);
-                       }
+                       set { SetValue(StyleProperty, value); }
                }
 
                [TypeConverter(typeof(ListStringTypeConverter))]
@@ -50,10 +42,7 @@ namespace Xamarin.Forms
                [TypeConverter(typeof(ListStringTypeConverter))]
                public IList<string> @class {
                        get { return _mergedStyle.StyleClass; }
-                       set {
-                               if (OptionalFeatureValues.UseStyle)
-                                       _mergedStyle.StyleClass = value;
-                       }
+                       set { _mergedStyle.StyleClass = value; }
                }
 
                [EditorBrowsable(EditorBrowsableState.Never)]
diff --git a/Xamarin.Forms/Xamarin.Forms.Core/Shell/PresentationMode.cs b/Xamarin.Forms/Xamarin.Forms.Core/Shell/PresentationMode.cs
new file mode 100644 (file)
index 0000000..6ad3e7e
--- /dev/null
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xamarin.Forms
+{
+       [Flags]
+       public enum PresentationMode
+       {
+               NotAnimated = 1,
+               Animated = 1 << 1,
+               Modal = 1 << 2,
+               ModalAnimated = PresentationMode.Animated | PresentationMode.Modal,
+               ModalNotAnimated = PresentationMode.NotAnimated | PresentationMode.Modal
+       }
+}
index 8327d6a..02a5ac4 100644 (file)
@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Collections.Specialized;
 using System.ComponentModel;
 using System.Linq;
@@ -25,6 +26,8 @@ namespace Xamarin.Forms
                                SetInheritedBindingContext(newHandlerBehavior, bindable.BindingContext);
                }
 
+               public static readonly BindableProperty PresentationModeProperty = BindableProperty.CreateAttached("PresentationMode", typeof(PresentationMode), typeof(Shell), PresentationMode.Animated);
+
                public static readonly BindableProperty FlyoutBehaviorProperty =
                        BindableProperty.CreateAttached("FlyoutBehavior", typeof(FlyoutBehavior), typeof(Shell), FlyoutBehavior.Flyout,
                                propertyChanged: OnFlyoutBehaviorChanged);
@@ -69,6 +72,9 @@ namespace Xamarin.Forms
                public static BackButtonBehavior GetBackButtonBehavior(BindableObject obj) => (BackButtonBehavior)obj.GetValue(BackButtonBehaviorProperty);
                public static void SetBackButtonBehavior(BindableObject obj, BackButtonBehavior behavior) => obj.SetValue(BackButtonBehaviorProperty, behavior);
 
+               public static PresentationMode GetPresentationMode(BindableObject obj) => (PresentationMode)obj.GetValue(PresentationModeProperty);
+               public static void SetPresentationMode(BindableObject obj, PresentationMode presentationMode) => obj.SetValue(PresentationModeProperty, presentationMode);
+
                public static FlyoutBehavior GetFlyoutBehavior(BindableObject obj) => (FlyoutBehavior)obj.GetValue(FlyoutBehaviorProperty);
                public static void SetFlyoutBehavior(BindableObject obj, FlyoutBehavior value) => obj.SetValue(FlyoutBehaviorProperty, value);
 
@@ -204,6 +210,8 @@ namespace Xamarin.Forms
 
                View IShellController.FlyoutHeader => FlyoutHeaderView;
 
+               IShellController ShellController => this;
+
                void IShellController.AddAppearanceObserver(IAppearanceObserver observer, Element pivot)
                {
                        _appearanceObservers.Add((observer, pivot));
@@ -216,7 +224,7 @@ namespace Xamarin.Forms
 
                        // We need to wait until the visible page has been created before we try to calculate
                        // the flyout behavior
-                       if(GetVisiblePage() != null)
+                       if (GetVisiblePage() != null)
                                observer.OnFlyoutBehaviorChanged(GetEffectiveFlyoutBehavior());
                }
 
@@ -268,7 +276,7 @@ namespace Xamarin.Forms
                }
 
                ShellNavigationState IShellController.GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, bool includeStack)
-                       => GetNavigationState(shellItem, shellSection, shellContent, includeStack ? shellSection.Stack.ToList() : null);
+                       => GetNavigationState(shellItem, shellSection, shellContent, includeStack ? shellSection.Stack.ToList() : null, includeStack ? shellSection.Navigation.ModalStack.ToList() : null);
 
                async void IShellController.OnFlyoutItemSelected(Element element)
                {
@@ -309,17 +317,34 @@ namespace Xamarin.Forms
                        shellSection = shellSection ?? shellItem.CurrentItem;
                        shellContent = shellContent ?? shellSection?.CurrentItem;
 
-                       var state = GetNavigationState(shellItem, shellSection, shellContent, null);
+                       var state = GetNavigationState(shellItem, shellSection, shellContent, null, null);
 
                        if (FlyoutIsPresented && FlyoutBehavior == FlyoutBehavior.Flyout)
                                SetValueFromRenderer(FlyoutIsPresentedProperty, false);
 
-                       await GoToAsync(state).ConfigureAwait(false);
+                       if (shellSection == null)
+                               shellItem.PropertyChanged += OnShellItemPropertyChanged;
+                       else if (shellContent == null)
+                               shellSection.PropertyChanged += OnShellItemPropertyChanged;
+                       else
+                               await GoToAsync(state).ConfigureAwait(false);
+               }
+
+               void OnShellItemPropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == CurrentItemProperty.PropertyName)
+                       {
+                               (sender as BindableObject).PropertyChanged -= OnShellItemPropertyChanged;
+                               if (sender is ShellItem item)
+                                       ((IShellController)this).OnFlyoutItemSelected(item);
+                               else if (sender is ShellSection section)
+                                       ((IShellController)this).OnFlyoutItemSelected(section.Parent);
+                       }
                }
 
                bool IShellController.ProposeNavigation(ShellNavigationSource source, ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> stack, bool canCancel)
                {
-                       var proposedState = GetNavigationState(shellItem, shellSection, shellContent, stack);
+                       var proposedState = GetNavigationState(shellItem, shellSection, shellContent, stack, shellSection.Navigation.ModalStack);
                        return ProposeNavigation(source, proposedState, canCancel);
                }
 
@@ -346,12 +371,20 @@ namespace Xamarin.Forms
                        var shellSection = shellItem?.CurrentItem;
                        var shellContent = shellSection?.CurrentItem;
                        var stack = shellSection?.Stack;
-                       var result = GetNavigationState(shellItem, shellSection, shellContent, stack);
+                       var modalStack = shellSection?.Navigation?.ModalStack;
+                       var result = GetNavigationState(shellItem, shellSection, shellContent, stack, modalStack);
 
                        SetValueFromRenderer(CurrentStatePropertyKey, result);
 
                        OnNavigated(new ShellNavigatedEventArgs(oldState, CurrentState, source));
                }
+               ReadOnlyCollection<ShellItem> IShellController.GetItems() => ((ShellItemCollection)Items).VisibleItems;
+
+               event NotifyCollectionChangedEventHandler IShellController.ItemsCollectionChanged
+               {
+                       add { ((ShellItemCollection)Items).VisibleItemsChanged += value; }
+                       remove { ((ShellItemCollection)Items).VisibleItemsChanged -= value; }
+               }
 
                public static Shell Current => Application.Current?.MainPage as Shell;
 
@@ -385,12 +418,17 @@ namespace Xamarin.Forms
                        return routes;
                }
 
-               public Task GoToAsync(ShellNavigationState state, bool animate = true)
+               public Task GoToAsync(ShellNavigationState state)
+               {
+                       return GoToAsync(state, null, false);
+               }
+
+               public Task GoToAsync(ShellNavigationState state, bool animate)
                {
                        return GoToAsync(state, animate, false);
                }
 
-               internal async Task GoToAsync(ShellNavigationState state, bool animate, bool enableRelativeShellRoutes)
+               internal async Task GoToAsync(ShellNavigationState state, bool? animate, bool enableRelativeShellRoutes)
                {
                        // FIXME: This should not be none, we need to compute the delta and set flags correctly
                        var accept = ProposeNavigation(ShellNavigationSource.Unknown, state, true);
@@ -408,11 +446,23 @@ namespace Xamarin.Forms
 
                        var shellItem = navigationRequest.Request.Item;
                        var shellSection = navigationRequest.Request.Section;
+                       var currentShellSection = CurrentItem?.CurrentItem;
+                       var nextActiveSection = shellSection ?? shellItem?.CurrentItem;
+                       
                        ShellContent shellContent = navigationRequest.Request.Content;
+                       bool modalStackPreBuilt = false;
 
+                       // If we're replacing the whole stack and there are global routes then build the navigation stack before setting the shell section visible
+                       if (navigationRequest.Request.GlobalRoutes.Count > 0 && nextActiveSection != null && navigationRequest.StackRequest == NavigationRequest.WhatToDoWithTheStack.ReplaceIt)
+                       {
+                               modalStackPreBuilt = true;
+                               await nextActiveSection.GoToAsync(navigationRequest, queryData, false);
+                       }
+                       
                        if (shellItem != null)
                        {
                                ApplyQueryAttributes(shellItem, queryData, navigationRequest.Request.Section == null);
+                               bool navigatedToNewShellElement = false;
 
                                if (shellSection != null && shellContent != null)
                                {
@@ -420,6 +470,7 @@ namespace Xamarin.Forms
                                        if (shellSection.CurrentItem != shellContent)
                                        {
                                                shellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, shellContent);
+                                               navigatedToNewShellElement = true;
                                        }
                                }
 
@@ -429,20 +480,32 @@ namespace Xamarin.Forms
                                        if (shellItem.CurrentItem != shellSection)
                                        {
                                                shellItem.SetValueFromRenderer(ShellItem.CurrentItemProperty, shellSection);
+                                               navigatedToNewShellElement = true;
                                        }
                                }
 
                                if (CurrentItem != shellItem)
                                {
                                        SetValueFromRenderer(CurrentItemProperty, shellItem);
+                                       navigatedToNewShellElement = true;
                                }
 
-                               if (navigationRequest.Request.GlobalRoutes.Count > 0)
+                               if (!modalStackPreBuilt && currentShellSection?.Navigation.ModalStack.Count > 0)
+                               {
+                                       // - navigating to new shell element so just pop everything
+                                       // - or route contains no global route requests
+                                       if (navigatedToNewShellElement || navigationRequest.Request.GlobalRoutes.Count == 0)
+                                       {
+                                               await currentShellSection.PopModalStackToPage(null, animate);
+                                       }
+                               }
+
+                               if (navigationRequest.Request.GlobalRoutes.Count > 0 && navigationRequest.StackRequest != NavigationRequest.WhatToDoWithTheStack.ReplaceIt)
                                {
                                        // TODO get rid of this hack and fix so if there's a stack the current page doesn't display
                                        Device.BeginInvokeOnMainThread(async () =>
                                        {
-                                               await CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, false);
+                                               await CurrentItem.CurrentItem.GoToAsync(navigationRequest, queryData, animate);
                                        });
                                }
                        }
@@ -472,9 +535,9 @@ namespace Xamarin.Forms
                        //if the lastItem is implicitly wrapped, get the actual ShellContent
                        if (isLastItem)
                        {
-                               if (element is ShellItem shellitem && shellitem.Items.FirstOrDefault() is ShellSection section)
+                               if (element is IShellItemController shellitem && shellitem.GetItems().FirstOrDefault() is ShellSection section)
                                        element = section;
-                               if (element is ShellSection shellsection && shellsection.Items.FirstOrDefault() is ShellContent content)
+                               if (element is IShellSectionController shellsection && shellsection.GetItems().FirstOrDefault() is ShellContent content)
                                        element = content;
                                if (element is ShellContent shellcontent && shellcontent.Content is Element e)
                                        element = e;
@@ -501,7 +564,7 @@ namespace Xamarin.Forms
                                element.SetValue(ShellContent.QueryAttributesProperty, query);
                }
 
-               ShellNavigationState GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> sectionStack)
+               ShellNavigationState GetNavigationState(ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> sectionStack, IReadOnlyList<Page> modalStack)
                {
                        StringBuilder stateBuilder = new StringBuilder($"//");
                        Dictionary<string, string> queryData = new Dictionary<string, string>();
@@ -537,6 +600,28 @@ namespace Xamarin.Forms
                                                                stateBuilder.Append("/");
                                                }
                                        }
+
+                                       if (modalStack != null && modalStack.Count > 0)
+                                       {
+                                               if (!stackAtRoot && sectionStack.Count > 0)
+                                                       stateBuilder.Append("/");
+
+                                               for (int i = 0; i < modalStack.Count; i++)
+                                               {
+                                                       var topPage = modalStack[i];
+
+                                                       if(i > 0)
+                                                               stateBuilder.Append("/");
+
+                                                       stateBuilder.Append(Routing.GetRoute(topPage));
+
+                                                       for (int j = 1; j < topPage.Navigation.NavigationStack.Count; j++)
+                                                       {
+                                                               stateBuilder.Append("/");
+                                                               stateBuilder.Append(Routing.GetRoute(topPage.Navigation.NavigationStack[j]));
+                                                       }
+                                               }
+                                       }
                                }
                        }
 
@@ -588,8 +673,42 @@ namespace Xamarin.Forms
                public Shell()
                {
                        Navigation = new NavigationImpl(this);
-                       ((INotifyCollectionChanged)Items).CollectionChanged += (s, e) => SendStructureChanged();
                        Route = Routing.GenerateImplicitRoute("shell");
+                       Initialize();
+               }
+
+               void Initialize()
+               {
+                       if (CurrentItem != null)
+                               SetCurrentItem();
+
+                       ShellController.ItemsCollectionChanged += (s, e) =>
+                       {
+                               SetCurrentItem();
+                               SendStructureChanged();
+                       };
+
+                       void SetCurrentItem()
+                       {
+                               var shellItems = ShellController.GetItems();
+
+                               if (CurrentItem != null && shellItems.Contains(CurrentItem))
+                                       return;
+
+                               ShellItem shellItem = null;
+
+                               foreach (var item in shellItems)
+                               {
+                                       if (item is ShellItem && ValidDefaultShellItem(item))
+                                       {
+                                               shellItem = item;
+                                               break;
+                                       }
+                               }
+
+                               if (shellItem != null)
+                                       ShellController.OnFlyoutItemSelected(shellItem);
+                       }
                }
 
                public ScrollMode FlyoutVerticalScrollMode
@@ -736,13 +855,13 @@ namespace Xamarin.Forms
                                }
                        }
 
-                       foreach (var shellItem in Items)
+                       foreach (var shellItem in ShellController.GetItems())
                        {
                                if (shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
                                {
                                        IncrementGroup();
 
-                                       foreach (var shellSection in shellItem.Items)
+                                       foreach (var shellSection in (shellItem as IShellItemController).GetItems())
                                        {
                                                if (shellSection.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
                                                {
@@ -760,11 +879,11 @@ namespace Xamarin.Forms
                                                }
                                                else
                                                {
-                                                       if(!(shellSection.Parent is TabBar))
+                                                       if (!(shellSection.Parent is TabBar))
                                                                currentGroup.Add(shellSection);
 
                                                        // If we have only a single child we will also show the items menu items
-                                                       if (shellSection.Items.Count == 1 && shellSection == shellItem.CurrentItem)
+                                                       if ((shellSection as IShellSectionController).GetItems().Count == 1 && shellSection == shellItem.CurrentItem)
                                                        {
                                                                currentGroup.AddRange(shellSection.CurrentItem.MenuItems);
                                                        }
@@ -801,34 +920,6 @@ namespace Xamarin.Forms
                        return false;
                }
 
-               protected override void OnChildAdded(Element child)
-               {
-                       base.OnChildAdded(child);
-
-                       if (child is ShellItem shellItem && CurrentItem == null && ValidDefaultShellItem(child))
-                       {
-                               ((IShellController)this).OnFlyoutItemSelected(shellItem);
-                       }
-               }
-
-               protected override void OnChildRemoved(Element child)
-               {
-                       base.OnChildRemoved(child);
-
-                       if (child == CurrentItem)
-                       {
-                               for (var i = 0; i < Items.Count; i++)
-                               {
-                                       var item = Items[i];
-                                       if (ValidDefaultShellItem(item))
-                                       {
-                                               ((IShellController)this).OnFlyoutItemSelected(item);
-                                               break;
-                                       }
-                               }
-                       }
-               }
-
                bool ValidDefaultShellItem(Element child) => !(child is MenuShellItem);
 
                internal override IEnumerable<Element> ChildrenNotDrawnByThisElement
@@ -877,8 +968,8 @@ namespace Xamarin.Forms
                        var shell = (Shell)bindable;
                        UpdateChecked(shell);
 
-                       ((IShellController)shell).AppearanceChanged(shell, false);
-                       ((IShellController)shell).UpdateCurrentState(ShellNavigationSource.ShellItemChanged);
+                       shell.ShellController.AppearanceChanged(shell, false);
+                       shell.ShellController.UpdateCurrentState(ShellNavigationSource.ShellItemChanged);
                }
 
                static void OnCurrentItemChanging(BindableObject bindable, object oldValue, object newValue)
@@ -893,7 +984,7 @@ namespace Xamarin.Forms
                                var shellSection = shellItem.CurrentItem;
                                var shellContent = shellSection.CurrentItem;
                                var stack = shellSection.Stack;
-                               ((IShellController)shell).ProposeNavigation(ShellNavigationSource.ShellItemChanged, shellItem, shellSection, shellContent, stack, false);
+                               shell.ShellController.ProposeNavigation(ShellNavigationSource.ShellItemChanged, shellItem, shellSection, shellContent, stack, false);
                        }
                }
 
@@ -909,7 +1000,7 @@ namespace Xamarin.Forms
                        if (root is Shell shell)
                        {
                                ShellItem currentItem = shell.CurrentItem;
-                               var items = shell.Items;
+                               var items = shell.ShellController.GetItems();
                                var count = items.Count;
                                for (int i = 0; i < count; i++)
                                {
@@ -920,7 +1011,7 @@ namespace Xamarin.Forms
                        else if (root is ShellItem shellItem)
                        {
                                var currentItem = shellItem.CurrentItem;
-                               var items = shellItem.Items;
+                               var items = (shellItem as IShellItemController).GetItems();
                                var count = items.Count;
                                for (int i = 0; i < count; i++)
                                {
@@ -931,7 +1022,7 @@ namespace Xamarin.Forms
                        else if (root is ShellSection shellSection)
                        {
                                var currentItem = shellSection.CurrentItem;
-                               var items = shellSection.Items;
+                               var items = (shellSection as IShellSectionController).GetItems();
                                var count = items.Count;
                                for (int i = 0; i < count; i++)
                                {
@@ -1167,8 +1258,13 @@ namespace Xamarin.Forms
                                if (ModalStack.Count > 0)
                                        ModalStack[ModalStack.Count - 1].SendDisappearing();
 
-                               if (ModalStack.Count == 1)
-                                       _shell.CurrentItem.SendAppearing();
+                               if (!_shell.CurrentItem.CurrentItem.IsPoppingModalStack)
+                               {
+                                       if (ModalStack.Count == 1)
+                                               _shell.CurrentItem.SendAppearing();
+                                       else if (ModalStack.Count > 1)
+                                               ModalStack[ModalStack.Count - 2].SendAppearing();
+                               }
 
                                return base.OnPopModal(animated);
                        }
@@ -1177,7 +1273,9 @@ namespace Xamarin.Forms
                                if (ModalStack.Count == 0)
                                        _shell.CurrentItem.SendDisappearing();
 
-                               modal.SendAppearing();
+                               if(!_shell.CurrentItem.CurrentItem.IsPushingModalStack)
+                                       modal.SendAppearing();
+
                                return base.OnPushModal(modal, animated);
                        }
                }
index 60454fd..f27363f 100644 (file)
@@ -9,6 +9,7 @@ using System.Linq;
 #endif
 
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using Xamarin.Forms.Internals;
 
 namespace Xamarin.Forms
@@ -45,6 +46,9 @@ namespace Xamarin.Forms
 
                Page IShellContentController.Page => ContentCache;
 
+               EventHandler _isPageVisibleChanged;
+               event EventHandler IShellContentController.IsPageVisibleChanged { add => _isPageVisibleChanged += value; remove => _isPageVisibleChanged -= value; }
+
                Page IShellContentController.GetOrCreateContent()
                {
                        var template = ContentTemplate;
@@ -125,13 +129,35 @@ namespace Xamarin.Forms
                protected override void OnChildAdded(Element child)
                {
                        base.OnChildAdded(child);
-                       if (child is Page page && IsVisibleContent)
+                       if (child is Page page)
                        {
-                               SendAppearing();
-                               SendPageAppearing(page);
+                               if (IsVisibleContent && page.IsVisible)
+                               {
+                                       SendAppearing();
+                                       SendPageAppearing(page);
+                               }
+
+                               page.PropertyChanged += OnPagePropertyChanged;
+                               _isPageVisibleChanged?.Invoke(this, EventArgs.Empty);
                        }
                }
 
+               protected override void OnChildRemoved(Element child)
+               {
+                       base.OnChildRemoved(child);
+                       if (child is Page page)
+                       {
+                               page.PropertyChanged -= OnPagePropertyChanged;
+                       }
+               }
+               
+
+               void OnPagePropertyChanged(object sender, PropertyChangedEventArgs e)
+               {
+                       if (e.PropertyName == Page.IsVisibleProperty.PropertyName)
+                               _isPageVisibleChanged?.Invoke(this, EventArgs.Empty);
+               }
+
                Page ContentCache
                {
                        get => _contentCache;
@@ -147,7 +173,7 @@ namespace Xamarin.Forms
                                        ((ShellSection)Parent).UpdateDisplayedPage();
                        }
                }
-
+               
                public static implicit operator ShellContent(TemplatedPage page)
                {
                        var shellContent = new ShellContent();
index 75f335e..36f4a04 100644 (file)
@@ -1,20 +1,95 @@
-using System.Collections;
+using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Collections.Specialized;
+using System.Linq;
 
 namespace Xamarin.Forms
 {
        internal sealed class ShellContentCollection : IList<ShellContent>, INotifyCollectionChanged
        {
+               public event NotifyCollectionChangedEventHandler VisibleItemsChanged;
                ObservableCollection<ShellContent> _inner = new ObservableCollection<ShellContent>();
+               ObservableCollection<ShellContent> _visibleContents = new ObservableCollection<ShellContent>();
 
-               event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
+               public ReadOnlyCollection<ShellContent> VisibleItems { get; }
+
+               public ShellContentCollection()
                {
-                       add { ((INotifyCollectionChanged)_inner).CollectionChanged += value; }
-                       remove { ((INotifyCollectionChanged)_inner).CollectionChanged -= value; }
+                       _inner.CollectionChanged += InnerCollectionChanged;
+                       VisibleItems = new ReadOnlyCollection<ShellContent>(_visibleContents);
+                       _visibleContents.CollectionChanged += (_, args) =>
+                       {
+                               VisibleItemsChanged?.Invoke(VisibleItems, args);
+                       };
+               }
+
+               void InnerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       if (e.NewItems != null)
+                       {
+                               foreach (ShellContent element in e.NewItems)
+                               {
+                                       if (element is IShellContentController controller)
+                                               controller.IsPageVisibleChanged += OnIsPageVisibleChanged;
+                                       CheckVisibility(element);
+                               }
+                       }
+
+                       if (e.OldItems != null)
+                       {
+                               Removing(e.OldItems);
+                       }
+                       
+                       CollectionChanged?.Invoke(this, e);
                }
 
+               void Removing(IEnumerable items)
+               {
+                       foreach (ShellContent element in items)
+                       {
+                               if (_visibleContents.Contains(element))
+                                       _visibleContents.Remove(element);
+                               
+                               if (element is IShellContentController controller)
+                                       controller.IsPageVisibleChanged -= OnIsPageVisibleChanged;
+                       }
+               }
+
+               void OnIsPageVisibleChanged(object sender, EventArgs e)
+               {
+                       CheckVisibility((ShellContent)sender);
+               }
+
+               void CheckVisibility(ShellContent shellContent)
+               {
+                       if (shellContent is IShellContentController controller)
+                       {
+                               // Assume incoming page will be visible
+                               if (controller.Page == null)
+                               {
+                                       if (!_visibleContents.Contains(shellContent))
+                                               _visibleContents.Add(shellContent);
+                               }
+                               else if(controller.Page.IsVisible)
+                               {
+                                       if (!_visibleContents.Contains(shellContent))
+                                               _visibleContents.Add(shellContent);
+                               }
+                               else
+                               {
+                                       _visibleContents.Remove(shellContent);
+                               }
+                       }
+                       else if (_visibleContents.Contains(shellContent))
+                       {
+                               _visibleContents.Remove(shellContent);
+                       }
+               }
+
+               public event NotifyCollectionChangedEventHandler CollectionChanged;
+
                public int Count => _inner.Count;
 
                public bool IsReadOnly => ((IList<ShellContent>)_inner).IsReadOnly;
@@ -27,7 +102,14 @@ namespace Xamarin.Forms
 
                public void Add(ShellContent item) => _inner.Add(item);
 
-               public void Clear() => _inner.Clear();
+               public void Clear()
+               {
+                       var list = _inner.ToList();
+                       Removing(_inner);
+                       _inner.Clear();
+                       
+                       CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, list));
+               }
 
                public bool Contains(ShellContent item) => _inner.Contains(item);
 
index 37aaf93..8fb822e 100644 (file)
@@ -20,11 +20,12 @@ namespace Xamarin.Forms.Core
                        SearchForPart(shell, (p) => p.Route == route);
 
 
-               public static BaseShellItem SearchForPart(this Shell shell, Func<BaseShellItem, bool> searchBy)
+               public static BaseShellItem SearchForPart(this IShellController shell, Func<BaseShellItem, bool> searchBy)
                {
-                       for (var i = 0; i < shell.Items.Count; i++)
+                       var items = shell.GetItems();
+                       for (var i = 0; i < items.Count; i++)
                        {
-                               var result = SearchForPart(shell.Items[i], searchBy);
+                               var result = SearchForPart(items[i], searchBy);
                                if (result != null)
                                        return result;
                        }
@@ -40,16 +41,16 @@ namespace Xamarin.Forms.Core
                        BaseShellItem baseShellItem = null;
                        switch (part)
                        {
-                               case ShellItem item:
-                                       foreach (var section in item.Items)
+                               case IShellItemController item:
+                                       foreach (var section in item.GetItems())
                                        {
                                                baseShellItem = SearchForPart(section, searchBy);
                                                if (baseShellItem != null)
                                                        return baseShellItem;
                                        }
                                        break;
-                               case ShellSection section:
-                                       foreach (var content in section.Items)
+                               case IShellSectionController section:
+                                       foreach (var content in section.GetItems())
                                        {
                                                baseShellItem = SearchForPart(content, searchBy);
                                                if (baseShellItem != null)
index 2d46e7c..6570393 100644 (file)
@@ -41,12 +41,14 @@ namespace Xamarin.Forms
 
                #region IShellItemController
 
+               IShellItemController ShellItemController => this;
+
                internal Task GoToPart(NavigationRequest request, Dictionary<string, string> queryData)
                {
                        var shellSection = request.Request.Section;
 
                        if (shellSection == null)
-                               shellSection = Items[0];
+                               shellSection = ShellItemController.GetItems()[0];
 
                        Shell.ApplyQueryAttributes(shellSection, queryData, request.Request.Content == null);
 
@@ -77,6 +79,14 @@ namespace Xamarin.Forms
                        return accept;
                }
 
+               ReadOnlyCollection<ShellSection> IShellItemController.GetItems() => ((ShellSectionCollection)Items).VisibleItems;
+
+               event NotifyCollectionChangedEventHandler IShellItemController.ItemsCollectionChanged
+               {
+                       add { ((ShellSectionCollection)Items).VisibleItemsChanged += value; }
+                       remove { ((ShellSectionCollection)Items).VisibleItemsChanged -= value; }
+               }
+
                #endregion IShellItemController
 
                #region IPropertyPropagationController
@@ -99,7 +109,9 @@ namespace Xamarin.Forms
 
                public ShellItem()
                {
-                       ((INotifyCollectionChanged)Items).CollectionChanged += ItemsCollectionChanged;
+                       ShellItemController.ItemsCollectionChanged += (_, __) => SendStructureChanged();
+                       (Items as INotifyCollectionChanged).CollectionChanged += ItemsCollectionChanged;
+
                        _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<ShellItem>>(() => new PlatformConfigurationRegistry<ShellItem>(this));
                }
 
@@ -196,10 +208,10 @@ namespace Xamarin.Forms
                        base.OnChildRemoved(child);
                        if (CurrentItem == child)
                        {
-                               if (Items.Count == 0)
+                               if (ShellItemController.GetItems().Count == 0)
                                        ClearValue(CurrentItemProperty);
                                else
-                                       SetValueFromRenderer(CurrentItemProperty, Items[0]);
+                                       SetValueFromRenderer(CurrentItemProperty, ShellItemController.GetItems()[0]);
                        }
                }
 
@@ -237,8 +249,6 @@ namespace Xamarin.Forms
                                foreach (Element element in e.OldItems)
                                        OnChildRemoved(element);
                        }
-
-                       SendStructureChanged();
                }
 
                internal override void SendAppearing()
index 6611768..dfd1cfc 100644 (file)
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Collections.Specialized;
+using System.Linq;
 
 namespace Xamarin.Forms
 {
        internal sealed class ShellItemCollection : IList<ShellItem>, INotifyCollectionChanged
        {
-               event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
+               public event NotifyCollectionChangedEventHandler VisibleItemsChanged;
+
+               IList<ShellItem> _inner;
+               ObservableCollection<ShellItem> _visibleContents = new ObservableCollection<ShellItem>();
+               public ReadOnlyCollection<ShellItem> VisibleItems { get; }
+
+               public ShellItemCollection()
                {
-                       add { ((INotifyCollectionChanged)Inner).CollectionChanged += value; }
-                       remove { ((INotifyCollectionChanged)Inner).CollectionChanged -= value; }
+                       VisibleItems = new ReadOnlyCollection<ShellItem>(_visibleContents);
+                       _visibleContents.CollectionChanged += (_, args) =>
+                       {
+                               VisibleItemsChanged?.Invoke(VisibleItems, args);
+                       };
                }
 
+               public event NotifyCollectionChangedEventHandler CollectionChanged;
+
                public int Count => Inner.Count;
                public bool IsReadOnly => ((IList<ShellItem>)Inner).IsReadOnly;
-               internal IList<ShellItem> Inner { get; set; }
+               internal IList<ShellItem> Inner
+               {
+                       get
+                       {
+                               return _inner;
+                       }
+                       set
+                       {                       
+                               _inner = value;
+                               ((INotifyCollectionChanged)_inner).CollectionChanged += InnerCollectionChanged;
+                       }
+               }
+
+               void InnerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       if (e.NewItems != null)
+                       {
+                               foreach (ShellItem element in e.NewItems)
+                               {
+                                       if (element is IShellItemController controller)
+                                               controller.ItemsCollectionChanged += OnShellItemControllerItemsCollectionChanged;
+
+                                       CheckVisibility(element);
+                               }
+                       }
+
+                       if (e.OldItems != null)
+                       {
+                               Removing(e.OldItems);
+                       }
+                       
+                       CollectionChanged?.Invoke(this, e);
+               }
+               
+               
+               void Removing(IEnumerable items)
+               {
+                       foreach (ShellItem element in items)
+                       {
+                               if (_visibleContents.Contains(element))
+                                       _visibleContents.Remove(element);
+
+                               if (element is IShellSectionController controller)
+                                       controller.ItemsCollectionChanged -= OnShellItemControllerItemsCollectionChanged;
+                       }
+               }
+               
+               void OnShellItemControllerItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       foreach (ShellSection section in (e.NewItems ?? e.OldItems ?? (IList)_inner))
+                       {
+                               if (section.Parent == null)
+                                       section.ParentSet += OnParentSet;
+                               else
+                                       CheckVisibility(section.Parent as ShellItem);
+                       }
+
+                       void OnParentSet(object s, System.EventArgs __)
+                       {
+                               var shellSection = (ShellSection)s;
+                               shellSection.ParentSet -= OnParentSet;
+                               CheckVisibility(shellSection.Parent as ShellItem);
+                       }
+               }
+
+               void CheckVisibility(ShellItem shellItem)
+               {
+                       if (IsShellItemVisible(shellItem))
+                       {
+                               if (_visibleContents.Contains(shellItem))
+                                       return;
+
+                               int visibleIndex = 0;
+                               for (var i = 0; i < _inner.Count; i++)
+                               {
+                                       var item = _inner[i];
+
+                                       if (!IsShellItemVisible(item))
+                                               continue;
+
+                                       if (item == shellItem)
+                                       {
+                                               _visibleContents.Insert(visibleIndex, shellItem);
+                                               break;
+                                       }
+
+                                       visibleIndex++;
+                               }
+                       }
+                       else if (_visibleContents.Contains(shellItem))
+                       {
+                               _visibleContents.Remove(shellItem);
+                       }
+
+                       bool IsShellItemVisible(ShellItem item)
+                       {
+                               return (item is IShellItemController itemController && itemController.GetItems().Count > 0) ||
+                                       item is IMenuItemController;
+                       }
+               }
+
+               
 
                public ShellItem this[int index]
                {
@@ -36,7 +150,7 @@ namespace Xamarin.Forms
                                )
                        {
                                int i = Count - 1;
-                               if (i >= 0 &&  this[i] is TabBar && Routing.IsImplicit(this[i]))
+                               if (i >= 0 && this[i] is TabBar && Routing.IsImplicit(this[i]))
                                {
                                        this[i].Items.Add(item.Items[0]);
                                        return;
@@ -46,7 +160,13 @@ namespace Xamarin.Forms
                        Inner.Add(item);
                }
 
-               public void Clear() => Inner.Clear();
+               public void Clear()
+               {
+                       var list = Inner.ToList();
+                       Removing(Inner);
+                       Inner.Clear();
+                       CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, list));
+               }
 
                public bool Contains(ShellItem item) => Inner.Contains(item);
 
index 761396f..607990e 100644 (file)
@@ -29,6 +29,7 @@ namespace Xamarin.Forms
 
                #region IShellSectionController
 
+               IShellSectionController ShellSectionController => this;
                readonly List<(object Observer, Action<Page> Callback)> _displayedPageObservers =
                        new List<(object Observer, Action<Page> Callback)>();
                readonly List<IShellContentInsetObserver> _observers = new List<IShellContentInsetObserver>();
@@ -47,6 +48,14 @@ namespace Xamarin.Forms
                {
                        get
                        {
+                               if (Navigation.ModalStack.Count > 0)
+                               {
+                                       if (Navigation.ModalStack[Navigation.ModalStack.Count - 1] is NavigationPage np)
+                                               return np.Navigation.NavigationStack[np.Navigation.NavigationStack.Count - 1];
+
+                                       return Navigation.ModalStack[0];
+                               }
+                               
                                if (_navStack.Count > 1)
                                        return _navStack[_navStack.Count - 1];
                                return ((IShellContentController)CurrentItem)?.Page;
@@ -72,7 +81,7 @@ namespace Xamarin.Forms
                        ShellContent shellContent = request.Request.Content;
 
                        if (shellContent == null)
-                               shellContent = Items[0];
+                               shellContent = ShellSectionController.GetItems()[0];
 
                        if (request.Request.GlobalRoutes.Count > 0)
                        {
@@ -118,6 +127,45 @@ namespace Xamarin.Forms
                        _lastTabThickness = tabThickness;
                }
 
+               async void IShellSectionController.SendPopping(Task poppingCompleted)
+               {
+                       if (_navStack.Count <= 1)
+                               throw new Exception("Nav Stack consistency error");
+
+                       var page = _navStack[_navStack.Count - 1];
+
+                       _navStack.Remove(page);
+                       UpdateDisplayedPage();
+
+                       await poppingCompleted;
+
+                       RemovePage(page);
+                       SendUpdateCurrentState(ShellNavigationSource.Pop);
+               }
+
+               async void IShellSectionController.SendPoppingToRoot(Task finishedPopping)
+               {
+                       if (_navStack.Count <= 1)
+                               throw new Exception("Nav Stack consistency error");
+
+                       var oldStack = _navStack;
+                       _navStack = new List<Page> { null };
+
+                       for (int i = 1; i < oldStack.Count; i++)
+                               oldStack[i].SendDisappearing();
+
+                       UpdateDisplayedPage();
+                       await finishedPopping;
+
+                       for (int i = 1; i < oldStack.Count; i++)
+                               RemovePage(oldStack[i]);
+
+                       SendUpdateCurrentState(ShellNavigationSource.PopToRoot);
+               }
+
+               [Obsolete]
+               [EditorBrowsable(EditorBrowsableState.Never)]
+
                void IShellSectionController.SendPopped()
                {
                        if (_navStack.Count <= 1)
@@ -131,6 +179,10 @@ namespace Xamarin.Forms
                        SendUpdateCurrentState(ShellNavigationSource.Pop);
                }
 
+               ReadOnlyCollection<ShellContent> IShellSectionController.GetItems() => ((ShellContentCollection)Items).VisibleItems;
+
+               [Obsolete]
+               [EditorBrowsable(EditorBrowsableState.Never)]
                void IShellSectionController.SendPopping(Page page)
                {
                        if (_navStack.Count <= 1)
@@ -140,15 +192,24 @@ namespace Xamarin.Forms
                        SendAppearanceChanged();
                }
 
+               [Obsolete]
+               [EditorBrowsable(EditorBrowsableState.Never)]
                void IShellSectionController.SendPopped(Page page)
                {
-                       if(_navStack.Contains(page))
+                       if (_navStack.Contains(page))
                                _navStack.Remove(page);
 
                        RemovePage(page);
                        SendUpdateCurrentState(ShellNavigationSource.Pop);
                }
 
+
+               event NotifyCollectionChangedEventHandler IShellSectionController.ItemsCollectionChanged
+               {
+                       add { ((ShellContentCollection)Items).VisibleItemsChanged += value; }
+                       remove { ((ShellContentCollection)Items).VisibleItemsChanged -= value; }
+               }
+
                #endregion IShellSectionController
 
                #region IPropertyPropagationController
@@ -170,13 +231,15 @@ namespace Xamarin.Forms
                ReadOnlyCollection<Element> _logicalChildrenReadOnly;
 
                List<Page> _navStack = new List<Page> { null };
+               internal bool IsPushingModalStack { get; private set; }
+               internal bool IsPoppingModalStack { get; private set; }
 
                public ShellSection()
                {
-                       ((INotifyCollectionChanged)Items).CollectionChanged += ItemsCollectionChanged;
+                       (Items as INotifyCollectionChanged).CollectionChanged += ItemsCollectionChanged;
                        Navigation = new NavigationImpl(this);
                }
-
+                               
                public ShellContent CurrentItem
                {
                        get { return (ShellContent)GetValue(CurrentItemProperty); }
@@ -245,44 +308,163 @@ namespace Xamarin.Forms
                        return (ShellSection)(ShellContent)page;
                }
 
-               internal async Task GoToAsync(NavigationRequest request, IDictionary<string, string> queryData, bool animate)
+               internal async Task GoToAsync(NavigationRequest request, IDictionary<string, string> queryData, bool? animate)
                {
-                       List<string> routes = request.Request.GlobalRoutes;
-                       if (routes == null || routes.Count == 0)
+                       List<string> globalRoutes = request.Request.GlobalRoutes;
+                       List<Page> navStack = null;
+                       string route = String.Empty;
+
+                       if (globalRoutes == null || globalRoutes.Count == 0)
                        {
-                               await Navigation.PopToRootAsync(animate);
+                               await Navigation.PopToRootAsync(animate ?? false);
                                return;
                        }
 
-                       for (int i = 0; i < routes.Count; i++)
+                       int whereToStartNavigation = 0;
+                       
+                       // Pop the stack down to where it no longer matches 
+                       if (request.StackRequest == NavigationRequest.WhatToDoWithTheStack.ReplaceIt)
                        {
-                               bool isLast = i == routes.Count - 1;
-                               var route = routes[i];
-                               var navPage = _navStack.Count > i + 1 ? _navStack[i + 1] : null;
-
-                               if (navPage != null)
+                               for (int i = 0; i < globalRoutes.Count; i++)
                                {
-                                       if (Routing.GetRoute(navPage) == route)
+                                       whereToStartNavigation = i;
+                                       bool isLast = i == globalRoutes.Count - 1;
+                                       route = globalRoutes[i];
+                                       
+                                       navStack = BuildFlattenedNavigationStack(new List<Page>(_navStack), Navigation?.ModalStack);
+                                       
+                                       // if the navStack count is one that means there is nothing pushed
+                                       if (navStack.Count == 1)
+                                               break;
+                                       
+                                       Page navPage = navStack.Count > i + 1 ? navStack[i + 1] : null;
+
+                                       if (navPage != null)
                                        {
-                                               Shell.ApplyQueryAttributes(navPage, queryData, isLast);
-                                               continue;
-                                       }
+                                               // if the routes don't match then pop this route off the stack
+                                               int popCount = i + 1;
 
-                                       if (request.StackRequest == NavigationRequest.WhatToDoWithTheStack.ReplaceIt)
-                                       {
-                                               while (_navStack.Count > i + 1)
+                                               if (Routing.GetRoute(navPage) == route)
                                                {
-                                                       await OnPopAsync(false);
+                                                       // if the routes do match and this is the last in the loop
+                                                       // pop everything after this route
+                                                       popCount = i + 2;
+                                                       whereToStartNavigation++;
+                                                       Shell.ApplyQueryAttributes(navPage, queryData, isLast);
+
+                                                       // If we're not on the last loop of the stack then continue
+                                                       // otherwise pop the rest of the stack
+                                                       if(!isLast)
+                                                               continue;
                                                }
+
+                                               IsPoppingModalStack = true;
+                                               while (navStack.Count > popCount)
+                                               {
+                                                       if (Navigation.ModalStack.Contains(navStack[navStack.Count - 1]))
+                                                       {
+                                                               await Navigation.PopModalAsync(false);
+                                                       }
+                                                       else if (Navigation.ModalStack.Count > 0)
+                                                       {
+                                                               await Navigation.ModalStack[Navigation.ModalStack.Count - 1].Navigation.PopAsync(false);
+                                                       }
+                                                       else
+                                                       {
+                                                               await OnPopAsync(false);
+                                                       }
+                                                       
+                                                       navStack = BuildFlattenedNavigationStack(new List<Page>(_navStack), Navigation?.ModalStack);
+                                               }
+                                               IsPoppingModalStack = false;
+
+                                               break;
                                        }
+
+                                       var content = Routing.GetOrCreateContent(route) as Page;
+                                       if (content == null)
+                                               break;
+
+                                       Shell.ApplyQueryAttributes(content, queryData, isLast);
                                }
+                       }
+                       
+                       List<Page> modalPageStacks = new List<Page>();
+                       List<Page> nonModalPageStacks = new List<Page>();
 
+                       if (Navigation?.ModalStack?.Count > 0)
+                               modalPageStacks.AddRange(Navigation.ModalStack);
+
+                       // populate global routes and build modal stacks
+                       for (int i = whereToStartNavigation; i < globalRoutes.Count; i++)
+                       {
+                               bool isLast = i == globalRoutes.Count - 1;
+                               route = globalRoutes[i];
                                var content = Routing.GetOrCreateContent(route) as Page;
                                if (content == null)
                                        break;
 
+                               var isModal = (Shell.GetPresentationMode(content) & PresentationMode.Modal) == PresentationMode.Modal;
+
                                Shell.ApplyQueryAttributes(content, queryData, isLast);
-                               await OnPushAsync(content, i == routes.Count - 1 && animate);
+                               if (isModal)
+                               {
+                                       modalPageStacks.Add(content);
+                               }
+                               else if (modalPageStacks.Count > 0)
+                               {
+                                       if (modalPageStacks[modalPageStacks.Count - 1] is NavigationPage navigationPage)
+                                               await navigationPage.Navigation.PushAsync(content);
+                                       else
+                                               throw new InvalidOperationException($"Shell cannot push a page to the following type: {modalPageStacks[modalPageStacks.Count - 1]}. The visible modal page needs to be a NavigationPage");
+                               }
+                               else
+                               {
+                                       nonModalPageStacks.Add(content);
+                               }
+                       }
+
+                       for (int i = Navigation.ModalStack.Count; i < modalPageStacks.Count; i++)
+                       {
+                               bool isLast = i == modalPageStacks.Count - 1;
+                               bool isAnimated = animate ?? (Shell.GetPresentationMode(modalPageStacks[i]) & PresentationMode.NotAnimated) != PresentationMode.NotAnimated;
+                               IsPushingModalStack = !isLast;
+                               await ((NavigationImpl)Navigation).PushModalAsync(modalPageStacks[i], isAnimated);
+                       }
+
+                       for (int i = nonModalPageStacks.Count - 1; i >= 0; i--)
+                       {
+                               bool isLast = i == nonModalPageStacks.Count - 1;
+                                       
+                               if(isLast)
+                               {
+                                       bool isAnimated = animate ?? (Shell.GetPresentationMode(nonModalPageStacks[i]) & PresentationMode.NotAnimated) != PresentationMode.NotAnimated;
+                                       await OnPushAsync(nonModalPageStacks[i], isAnimated);
+                               }
+                               else
+                                       Navigation.InsertPageBefore(nonModalPageStacks[i], nonModalPageStacks[nonModalPageStacks.Count - 1]);
+                       }
+
+                       if (Parent?.Parent is IShellController shell)
+                       {
+                               shell.UpdateCurrentState(ShellNavigationSource.ShellSectionChanged);
+                       }
+
+                       List<Page> BuildFlattenedNavigationStack(List<Page> startingList, IReadOnlyList<Page> modalStack)
+                       {
+                               if (modalStack == null)
+                                       return startingList;
+
+                               for (int i = 0; i < modalStack.Count; i++)
+                               {
+                                       startingList.Add(modalStack[i]);
+                                       for (int j = 1; j < modalStack[i].Navigation.NavigationStack.Count; j++)
+                                       {
+                                               startingList.Add(modalStack[i].Navigation.NavigationStack[j]);
+                                       }
+                               }
+
+                               return startingList;
                        }
                }
 
@@ -307,12 +489,12 @@ namespace Xamarin.Forms
                        else
                        {
                                IShellContentController currentItem = CurrentItem;
-                               if (currentItem.Page != null)
-                                       DisplayedPage = currentItem.Page;
+                               DisplayedPage = currentItem?.Page;
                        }
 
                        if (previousPage != DisplayedPage)
                        {
+                               previousPage?.SendDisappearing();
                                PresentedPageAppearing();
                                SendAppearanceChanged();
                        }
@@ -321,10 +503,11 @@ namespace Xamarin.Forms
                protected override void OnChildAdded(Element child)
                {
                        base.OnChildAdded(child);
-                       if (CurrentItem == null && Items.Contains(child))
+                       if (CurrentItem == null && ((IShellSectionController)this).GetItems().Contains(child))
                                SetValueFromRenderer(CurrentItemProperty, child);
 
-                       UpdateDisplayedPage();
+                       if(CurrentItem != null)
+                               UpdateDisplayedPage();
                }
 
                protected override void OnChildRemoved(Element child)
@@ -332,7 +515,8 @@ namespace Xamarin.Forms
                        base.OnChildRemoved(child);
                        if (CurrentItem == child)
                        {
-                               if (Items.Count == 0)
+                               var items = ShellSectionController.GetItems();
+                               if (items.Count == 0)
                                        ClearValue(CurrentItemProperty);
                                else
                                {
@@ -340,7 +524,7 @@ namespace Xamarin.Forms
                                        Device.BeginInvokeOnMainThread(() =>
                                        {
                                                if (CurrentItem == null)
-                                                       SetValueFromRenderer(CurrentItemProperty, Items[0]);
+                                                       SetValueFromRenderer(CurrentItemProperty, items[0]);
                                        });
                                }
                        }
@@ -497,6 +681,41 @@ namespace Xamarin.Forms
                        return args.Task;
                }
 
+               internal async Task PopModalStackToPage(Page page, bool? animated)
+               {
+                       try
+                       {
+                               IsPoppingModalStack = true;
+                               int modalStackCount = Navigation.ModalStack.Count;
+                               for (int i = 0; i < modalStackCount; i++)
+                               {
+                                       var pageToPop = Navigation.ModalStack[Navigation.ModalStack.Count - 1];
+                                       if (pageToPop == page)
+                                               break;
+
+                                       // indicate that we are done popping down the stack to the modal page requested
+                                       // This is mainly used by life cycle events so they don't fire onappearing
+                                       if(page == null && Navigation.ModalStack.Count == 1)
+                                       {
+                                               IsPoppingModalStack = false;
+                                       }
+                                       else if(Navigation.ModalStack.Count > 1 && Navigation.ModalStack[Navigation.ModalStack.Count - 2] == page)
+                                       {
+                                               IsPoppingModalStack = false;
+                                       }
+
+                                       bool isAnimated = animated ?? (Shell.GetPresentationMode(pageToPop) & PresentationMode.NotAnimated) != PresentationMode.NotAnimated;
+                                       await Navigation.PopModalAsync(isAnimated);
+                               }
+
+                               ((IShellController)Shell).UpdateCurrentState(ShellNavigationSource.ShellSectionChanged);
+                       }
+                       finally
+                       {
+                               IsPoppingModalStack = false;
+                       }
+               }
+
                protected virtual void OnRemovePage(Page page)
                {
                        if (!_navStack.Contains(page))
@@ -673,9 +892,6 @@ namespace Xamarin.Forms
 
                        protected override Task<Page> OnPopModal(bool animated)
                        {
-                               if (ModalStack.Count > 0)
-                                       ModalStack[ModalStack.Count - 1].SendDisappearing();
-
                                if(ModalStack.Count == 1)
                                {
                                        _owner.PresentedPageAppearing();
@@ -683,6 +899,7 @@ namespace Xamarin.Forms
 
                                return base.OnPopModal(animated);
                        }
+
                        protected override Task OnPushModal(Page modal, bool animated)
                        {
                                if (ModalStack.Count == 0)
@@ -690,7 +907,6 @@ namespace Xamarin.Forms
                                        _owner.PresentedPageDisappearing();
                                }
 
-                               modal.SendAppearing();
                                return base.OnPushModal(modal, animated);
                        }
                }
index b8de8a5..f2d7c87 100644 (file)
 using System.Collections;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
 using System.Collections.Specialized;
+using System.Linq;
 
 namespace Xamarin.Forms
 {
-       internal sealed class ShellSectionCollection :  IList<ShellSection>, INotifyCollectionChanged
+       internal sealed class ShellSectionCollection : IList<ShellSection>, INotifyCollectionChanged
        {
-               event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
+               public event NotifyCollectionChangedEventHandler VisibleItemsChanged;
+               IList<ShellSection> _inner;
+               ObservableCollection<ShellSection> _visibleContents = new ObservableCollection<ShellSection>();
+
+               public ShellSectionCollection()
                {
-                       add { ((INotifyCollectionChanged)Inner).CollectionChanged += value; }
-                       remove { ((INotifyCollectionChanged)Inner).CollectionChanged -= value; }
+                       VisibleItems = new ReadOnlyCollection<ShellSection>(_visibleContents);
+                       _visibleContents.CollectionChanged += (_, args) =>
+                       {
+                               VisibleItemsChanged?.Invoke(VisibleItems, args);
+                       };
                }
 
+               public ReadOnlyCollection<ShellSection> VisibleItems { get; }
+
+               public event NotifyCollectionChangedEventHandler CollectionChanged;
+
                public int Count => Inner.Count;
                public bool IsReadOnly => Inner.IsReadOnly;
-               internal IList<ShellSection> Inner { get; set; }
+               internal IList<ShellSection> Inner
+               {
+                       get => _inner;
+                       set
+                       {
+                               _inner = value;
+                               ((INotifyCollectionChanged)_inner).CollectionChanged += InnerCollectionChanged;
+                       }
+               }
+
+               void InnerCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       if (e.NewItems != null)
+                       {
+                               foreach (ShellSection element in e.NewItems)
+                               {
+                                       if (element is IShellSectionController controller)
+                                               controller.ItemsCollectionChanged += OnShellSectionControllerItemsCollectionChanged;
+
+                                       CheckVisibility(element);
+                               }
+                       }
+
+                       if (e.OldItems != null)
+                       {
+                               Removing(e.OldItems);
+                       }
+                       
+                       CollectionChanged?.Invoke(this, e);
+               }
+
+               void Removing(IEnumerable items)
+               {
+                       foreach (ShellSection element in items)
+                       {
+                               if (_visibleContents.Contains(element))
+                                       _visibleContents.Remove(element);
+
+                               if (element is IShellSectionController controller)
+                                       controller.ItemsCollectionChanged -= OnShellSectionControllerItemsCollectionChanged;
+                       }
+               }
+
+               void OnShellSectionControllerItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
+               {
+                       foreach (ShellContent content in (e.NewItems ?? e.OldItems ?? (IList)_inner))
+                       {
+                               if(content.Parent == null)
+                                       content.ParentSet += OnParentSet;
+                               else    
+                                       CheckVisibility(content.Parent as ShellSection);
+                       }
+
+                       void OnParentSet(object s, System.EventArgs __)
+                       {
+                               var shellContent = (ShellContent)s;
+                               shellContent.ParentSet -= OnParentSet;
+                               CheckVisibility(shellContent.Parent as ShellSection);
+                       }
+               }
+
+               void CheckVisibility(ShellSection section)
+               {
+                       if (section is IShellSectionController controller && controller.GetItems().Count > 0)
+                       {
+                               if (_visibleContents.Contains(section))
+                                       return;
+
+                               int visibleIndex = 0;
+                               for (var i = 0; i < _inner.Count; i++)
+                               {
+                                       var item = _inner[i];
+
+                                       if (item == section)
+                                       {
+                                               _visibleContents.Insert(visibleIndex, section);
+                                               break;
+                                       }
+
+                                       visibleIndex++;
+                               }
+                       }
+                       else if (_visibleContents.Contains(section))
+                       {
+                               _visibleContents.Remove(section);
+                       }
+               }
 
                public ShellSection this[int index]
                {
@@ -24,7 +123,13 @@ namespace Xamarin.Forms
 
                public void Add(ShellSection item) => Inner.Add(item);
 
-               public void Clear() => Inner.Clear();
+               public void Clear()
+               {
+                       var list = Inner.ToList();
+                       Removing(Inner);
+                       Inner.Clear();
+                       CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, list));
+               }
 
                public bool Contains(ShellSection item) => Inner.Contains(item);
 
index 30aac4c..f1cefd8 100644 (file)
@@ -499,14 +499,14 @@ namespace Xamarin.Forms
                        IEnumerable results = null;
                        switch (node)
                        {
-                               case Shell shell:
-                                       results = shell.Items;
+                               case IShellController shell:
+                                       results = shell.GetItems();
                                        break;
-                               case ShellItem item:
-                                       results = item.Items;
+                               case IShellItemController item:
+                                       results = item.GetItems();
                                        break;
-                               case ShellSection section:
-                                       results = section.Items;
+                               case IShellSectionController section:
+                                       results = section.GetItems();
                                        break;
                                case ShellContent content:
                                        results = new object[0];
index c4f9322..493d93d 100644 (file)
@@ -6,10 +6,10 @@ namespace Xamarin.Forms
 {
        public class StackLayout : Layout<View>, IElementConfiguration<StackLayout>
        {
-               public static readonly BindableProperty OrientationProperty = BindableProperty.Create("Orientation", typeof(StackOrientation), typeof(StackLayout), StackOrientation.Vertical,
+               public static readonly BindableProperty OrientationProperty = BindableProperty.Create(nameof(Orientation), typeof(StackOrientation), typeof(StackLayout), StackOrientation.Vertical,
                        propertyChanged: (bindable, oldvalue, newvalue) => ((StackLayout)bindable).InvalidateLayout());
 
-               public static readonly BindableProperty SpacingProperty = BindableProperty.Create("Spacing", typeof(double), typeof(StackLayout), 6d,
+               public static readonly BindableProperty SpacingProperty = BindableProperty.Create(nameof(Spacing), typeof(double), typeof(StackLayout), 6d,
                        propertyChanged: (bindable, oldvalue, newvalue) => ((StackLayout)bindable).InvalidateLayout());
 
                LayoutInformation _layoutInformation = new LayoutInformation();
@@ -40,7 +40,7 @@ namespace Xamarin.Forms
 
                protected override void LayoutChildren(double x, double y, double width, double height)
                {
-                       if (!HasVisibileChildren())
+                       if (!HasVisibleChildren())
                        {
                                return;
                        }
@@ -70,7 +70,7 @@ namespace Xamarin.Forms
                [EditorBrowsable(EditorBrowsableState.Never)]
                protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
                {
-                       if (!HasVisibileChildren())
+                       if (!HasVisibleChildren())
                        {
                                return new SizeRequest();
                        }
@@ -386,7 +386,7 @@ namespace Xamarin.Forms
                        }
                }
 
-               bool HasVisibileChildren()
+               bool HasVisibleChildren()
                {
                        for (var index = 0; index < InternalChildren.Count; index++)
                        {
index 6aa4ca4..b4e7d21 100644 (file)
@@ -32,7 +32,9 @@ namespace Xamarin.Forms
                public static readonly BindableProperty ValueProperty = BindableProperty.Create("Value", typeof(double), typeof(Stepper), 0.0, BindingMode.TwoWay, coerceValue: (bindable, value) =>
                {
                        var stepper = (Stepper)bindable;
-                       return ((double)value).Clamp(stepper.Minimum, stepper.Maximum);
+                       stepper.StepperPosition = Convert.ToInt32(((double)value - stepper.Minimum) / stepper.Increment);
+                       var stepVal = stepper.Minimum + (stepper.StepperPosition * stepper.Increment);
+                       return stepVal.Clamp(stepper.Minimum, stepper.Maximum);
                }, propertyChanged: (bindable, oldValue, newValue) =>
                {
                        var stepper = (Stepper)bindable;
@@ -41,7 +43,9 @@ namespace Xamarin.Forms
                                eh(stepper, new ValueChangedEventArgs((double)oldValue, (double)newValue));
                });
 
-               public static readonly BindableProperty IncrementProperty = BindableProperty.Create("Increment", typeof(double), typeof(Stepper), 1.0);
+               public static readonly BindableProperty IncrementProperty = BindableProperty.Create(nameof(Increment), typeof(double), typeof(Stepper), 1.0);
+
+               private static readonly BindableProperty StepperPositionProperty = BindableProperty.Create(nameof(StepperPosition), typeof(int), typeof(Stepper), 0);
 
                readonly Lazy<PlatformConfigurationRegistry<Stepper>> _platformConfigurationRegistry;
 
@@ -65,8 +69,9 @@ namespace Xamarin.Forms
                                Maximum = max;
                        }
 
-                       Value = val.Clamp(min, max);
+                       StepperPosition = (int)((val - min) / increment);
                        Increment = increment;
+                       Value = val.Clamp(min, max);
                }
 
                public double Increment
@@ -93,11 +98,17 @@ namespace Xamarin.Forms
                        set { SetValue(ValueProperty, value); }
                }
 
+               public int StepperPosition
+               {
+                       get { return (int)GetValue(StepperPositionProperty); }
+                       set { SetValue(StepperPositionProperty, value); }
+               }
+
                public event EventHandler<ValueChangedEventArgs> ValueChanged;
-               
+
                public IPlatformElementConfiguration<T, Stepper> On<T>() where T : IConfigPlatform
                {
                        return _platformConfigurationRegistry.Value.On<T>();
                }
        }
-}
\ No newline at end of file
+}
index 1bd0c0c..5a39ed1 100644 (file)
@@ -22,8 +22,8 @@ namespace Xamarin.Forms
 
                        var descendantsOnPage = parentPage?.VisibleDescendants();
 
-                       if (parentPage is Shell shell)
-                               descendantsOnPage = shell.Items;
+                       if (parentPage is IShellController shell)
+                               descendantsOnPage = shell.GetItems();
 
                        if (descendantsOnPage == null)
                                return new Dictionary<int, List<ITabStopElement>>();
index 17bbe9b..b133994 100644 (file)
@@ -22,7 +22,7 @@ namespace Xamarin.Forms.Xaml.Diagnostics
                internal static void SendVisualTreeChanged(object parent, object child)
                {
                        if (DebuggerHelper.DebuggerIsAttached)
-                               VisualTreeChanged?.Invoke(parent, new VisualTreeChangeEventArgs(parent, child, -1, VisualTreeChangeType.Add));
+                               VisualTreeChanged?.Invoke(parent, new VisualTreeChangeEventArgs(parent, child, -1, child != null ? VisualTreeChangeType.Add : VisualTreeChangeType.Remove));
                }
 
                public static event EventHandler<VisualTreeChangeEventArgs> VisualTreeChanged;
index db2d42f..6adab07 100644 (file)
@@ -3,11 +3,16 @@ using System.Collections.Generic;
 using ElmSharp;
 using Tizen.NET.MaterialComponents;
 using Xamarin.Forms.Platform.Tizen;
+using EImage = ElmSharp.Image;
 
 namespace Xamarin.Forms.Material.Tizen
 {
        public class MaterialNavigationView : MNavigationView, INavigationView
        {
+               ImageSource _bgImageSource;
+               EImage _bg;
+               Aspect _bgImageAspect;
+
                IDictionary<MItem, Element> _flyoutMenu = new Dictionary<MItem, Element>();
 
                public MaterialNavigationView(EvasObject parent) : base(parent)
@@ -15,6 +20,48 @@ namespace Xamarin.Forms.Material.Tizen
                        MenuItemSelected += OnSelectedItemChanged;
                }
 
+               public Aspect BackgroundImageAspect
+               {
+                       get
+                       {
+                               return _bgImageAspect;
+                       }
+                       set
+                       {
+                               _bgImageAspect = value;
+                               if (_bg != null)
+                               {
+                                       _bg.ApplyAspect(_bgImageAspect);
+                               }
+                       }
+               }
+
+               public ImageSource BackgroundImageSource
+               {
+                       get
+                       {
+                               return _bgImageSource;
+                       }
+                       set
+                       {
+                               _bgImageSource = value;
+                               if(_bgImageSource != null)
+                               {
+                                       if(_bg == null)
+                                       {
+                                               _bg = new EImage(this);
+                                       }
+                                       _bg.ApplyAspect(_bgImageAspect);
+                                       BackgroundImage = _bg;
+                                       _ = _bg.LoadFromImageSourceAsync(_bgImageSource);
+                               }
+                               else
+                               {
+                                       BackgroundImage = null;
+                               }
+                       }
+               }
+
                public event EventHandler<SelectedItemChangedEventArgs> SelectedItemChanged;
 
                public void BuildMenu(List<List<Element>> flyoutGroups)
index 28c6154..588e4db 100644 (file)
@@ -32,7 +32,7 @@
 
   <ItemGroup>
     <PackageReference Include="Tizen.NET" Version="4.0.0" />
-    <PackageReference Include="Tizen.NET.MaterialComponents" Version="0.9.7-preview" />
+    <PackageReference Include="Tizen.NET.MaterialComponents" Version="0.9.9-pre1" />
   </ItemGroup>
 
   <ItemGroup>
index 3366e06..035d7ea 100644 (file)
@@ -1,15 +1,14 @@
 using System;
 using System.Collections.Generic;
-using System.Threading.Tasks;
+using System.Diagnostics;
+using System.IO.IsolatedStorage;
 using System.Runtime.Serialization;
+using System.Threading.Tasks;
 using System.Xml;
-using System.Diagnostics;
-using System.IO;
-using Xamarin.Forms.Internals;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
-       internal class Deserializer : IDeserializer
+       internal class Deserializer : Internals.IDeserializer
        {
                const string PropertyStoreFile = "PropertyStore.forms";
 
@@ -19,33 +18,29 @@ namespace Xamarin.Forms.Platform.Tizen
                        // Make sure to use Internal
                        return Task.Run(() =>
                        {
-                               var store = new TizenIsolatedStorageFile();
-                               Stream stream = null;
-                               try
+                               using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                                {
-                                       stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.OpenOrCreate);
-                                       if (stream.Length == 0)
-                                       {
+                                       if (!store.FileExists(PropertyStoreFile))
                                                return null;
-                                       }
+
+                                       using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile, System.IO.FileMode.Open, System.IO.FileAccess.Read))
                                        using (XmlDictionaryReader reader = XmlDictionaryReader.CreateBinaryReader(stream, XmlDictionaryReaderQuotas.Max))
                                        {
-                                               stream = null;
-                                               var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
-                                               return (IDictionary<string, object>)dcs.ReadObject(reader);
-                                       }
-                               }
-                               catch (Exception e)
-                               {
-                                       Debug.WriteLine("Could not deserialize properties: " + e.Message);
-                                       Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while reading Application properties: {e}");
-                               }
-                               finally
-                               {
-                                       if (stream != null)
-                                       {
-                                               stream.Dispose();
+                                               if (stream.Length == 0)
+                                                       return null;
+
+                                               try
+                                               {
+                                                       var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
+                                                       return (IDictionary<string, object>)dcs.ReadObject(reader);
+                                               }
+                                               catch (Exception e)
+                                               {
+                                                       Debug.WriteLine("Could not deserialize properties: " + e.Message);
+                                                       Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while reading Application properties: {e}");
+                                               }
                                        }
+
                                }
 
                                return null;
@@ -59,49 +54,43 @@ namespace Xamarin.Forms.Platform.Tizen
                        // Make sure to use Internal
                        return Task.Run(() =>
                        {
-                               var success = false;
-                               var store = new TizenIsolatedStorageFile();
-                               Stream stream = null;
-                               try
+                               using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication())
                                {
-                                       stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate);
-                                       using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
+                                       // No need to write 0 properties if no file exists
+                                       if (properties.Count == 0 && !store.FileExists(PropertyStoreFile))
                                        {
-                                               stream = null;
-                                               var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
-                                               dcs.WriteObject(writer, properties);
-                                               writer.Flush();
-                                               success = true;
+                                               return;
                                        }
-                               }
-                               catch (Exception e)
-                               {
-                                       Debug.WriteLine("Could not serialize properties: " + e.Message);
-                                       Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
-                               }
-                               finally
-                               {
-                                       if (stream != null)
+                                       using (IsolatedStorageFileStream stream = store.OpenFile(PropertyStoreFile + ".tmp", System.IO.FileMode.OpenOrCreate))
+                                       using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateBinaryWriter(stream))
                                        {
-                                               stream.Dispose();
+                                               try
+                                               {
+                                                       var dcs = new DataContractSerializer(typeof(Dictionary<string, object>));
+                                                       dcs.WriteObject(writer, properties);
+                                                       writer.Flush();
+                                               }
+                                               catch (Exception e)
+                                               {
+                                                       Debug.WriteLine("Could not serialize properties: " + e.Message);
+                                                       Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
+                                                       return;
+                                               }
                                        }
-                               }
-
-                               if (!success)
-                                       return;
 
-                               try
-                               {
-                                       if (store.FileExists(PropertyStoreFile))
-                                               store.DeleteFile(PropertyStoreFile);
-                                       store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
-                               }
-                               catch (Exception e)
-                               {
-                                       Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
-                                       Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
+                                       try
+                                       {
+                                               if (store.FileExists(PropertyStoreFile))
+                                                       store.DeleteFile(PropertyStoreFile);
+                                               store.MoveFile(PropertyStoreFile + ".tmp", PropertyStoreFile);
+                                       }
+                                       catch (Exception e)
+                                       {
+                                               Debug.WriteLine("Could not move new serialized property file over old: " + e.Message);
+                                               Internals.Log.Warning("Xamarin.Forms PropertyStore", $"Exception while writing Application properties: {e}");
+                                       }
                                }
                        });
                }
        }
-}
+}
\ No newline at end of file
diff --git a/Xamarin.Forms/Xamarin.Forms.Platform.Tizen/EmbeddedFontLoader.cs b/Xamarin.Forms/Xamarin.Forms.Platform.Tizen/EmbeddedFontLoader.cs
new file mode 100755 (executable)
index 0000000..db5e295
--- /dev/null
@@ -0,0 +1,41 @@
+using System;
+using System.IO;
+using ElmSharp;
+using TApplication = Tizen.Applications.Application;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class EmbeddedFontLoader : IEmbeddedFontLoader, IRegisterable
+       {
+               const string _fontCacheFolderName = "fonts";
+
+               public DirectoryInfo FontCacheDirectory { get; private set; }
+
+               public EmbeddedFontLoader()
+               {
+                       FontCacheDirectory = Directory.CreateDirectory(Path.Combine(TApplication.Current.DirectoryInfo.Data, _fontCacheFolderName));
+                       Utility.AppendGlobalFontPath(FontCacheDirectory.FullName);
+               }
+
+               public (bool success, string filePath) LoadFont(EmbeddedFont font)
+               {
+                       var filePath = Path.Combine(FontCacheDirectory.FullName, font.FontName);
+                       if (File.Exists(filePath))
+                               return (true, filePath);
+                       try
+                       {
+                               using (var fileStream = File.Create(filePath))
+                               {
+                                       font.ResourceStream.CopyTo(fileStream);
+                               }
+                               return (true, filePath);
+                       }
+                       catch (Exception ex)
+                       {
+                               Log.Error(ex.Message);
+                               File.Delete(filePath);
+                       }
+                       return (false, null);
+               }
+       }
+}
\ No newline at end of file
diff --git a/Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Extensions/FontExtensions.cs b/Xamarin.Forms/Xamarin.Forms.Platform.Tizen/Extensions/FontExtensions.cs
new file mode 100755 (executable)
index 0000000..6ba8c15
--- /dev/null
@@ -0,0 +1,50 @@
+using Xamarin.Forms.Core;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public static class FontExtensions
+       {
+               public static string ToNativeFontFamily(this string self)
+               {
+                       if (string.IsNullOrEmpty(self))
+                               return null;
+
+                       var cleansedFont = CleanseFontName(self);
+                       int index = cleansedFont.LastIndexOf('-');
+                       if (index != -1)
+                       {
+                               string font = cleansedFont.Substring(0, index);
+                               string style = cleansedFont.Substring(index+1);
+                               return $"{font}:style={style}";
+                       }
+                       else
+                       {
+                               return cleansedFont;
+                       }
+               }
+
+               static string CleanseFontName(string fontName)
+               {
+                       var fontFile = FontFile.FromString(fontName);
+
+                       if (!string.IsNullOrWhiteSpace(fontFile.Extension))
+                       {
+                               var (hasFont, _) = FontRegistrar.HasFont(fontFile.FileNameWithExtension());
+                               if (hasFont)
+                                       return fontFile.PostScriptName;
+                       }
+                       else
+                       {
+                               foreach (var ext in FontFile.Extensions)
+                               {
+                                       var formated = fontFile.FileNameWithExtension(ext);
+                                       var (hasFont, filePath) = FontRegistrar.HasFont(formated);
+                                       if (hasFont)
+                                               return fontFile.PostScriptName;
+                               }
+                       }
+                       return fontFile.PostScriptName;
+               }
+       }
+}
index 1eab7ab..f85f7c0 100644 (file)
@@ -1,3 +1,4 @@
+using System.Threading.Tasks;
 using EImage = ElmSharp.Image;
 
 namespace Xamarin.Forms.Platform.Tizen
@@ -28,6 +29,32 @@ namespace Xamarin.Forms.Platform.Tizen
                        }
                }
 
+               public static async Task<bool> LoadFromImageSourceAsync(this EImage image, ImageSource source)
+               {
+                       IImageSourceHandler handler;
+                       bool isLoadComplate = false;
+                       if (source != null && (handler = Forms.GetHandlerForObject<IImageSourceHandler>(source)) != null)
+                       {
+                               isLoadComplate = await handler.LoadImageAsync(image, source);
+                       }
+                       if (!isLoadComplate)
+                       {
+                               //If it fails, call the Load function to remove the previous image.
+                               image.Load(string.Empty);
+                       }
+
+                       return isLoadComplate;
+               }
+
+               public static bool LoadFromFile(this EImage image, string file)
+               {
+                       if (!string.IsNullOrEmpty(file))
+                       {
+                               return image.Load(ResourcePath.GetPath(file));
+                       }
+                       return false;
+               }
+
                public static bool IsNullOrEmpty(this ImageSource imageSource) =>
                        imageSource == null || imageSource.IsEmpty;
        }
old mode 100644 (file)
new mode 100755 (executable)
index fb6a3c4..ea71ab5
@@ -425,6 +425,7 @@ namespace Xamarin.Forms
                                }
                                Elementary.Initialize();
                                Elementary.ThemeOverlay();
+                               Utility.AppendGlobalFontPath(@"/usr/share/fonts");
 
                                Device.PlatformServices = new TizenPlatformServices();
                                if (Device.info != null)
@@ -485,7 +486,8 @@ namespace Xamarin.Forms
                                                                                typeof(ExportRendererAttribute),
                                                                                typeof(ExportImageSourceHandlerAttribute),
                                                                                typeof(ExportCellAttribute),
-                                                                               typeof(ExportHandlerAttribute)
+                                                                               typeof(ExportHandlerAttribute),
+                                                                               typeof(ExportFontAttribute)
                                                                        });
                                                                }
                                                }
@@ -496,7 +498,8 @@ namespace Xamarin.Forms
                                                                typeof(ExportRendererAttribute),
                                                                typeof(ExportImageSourceHandlerAttribute),
                                                                typeof(ExportCellAttribute),
-                                                               typeof(ExportHandlerAttribute)
+                                                               typeof(ExportHandlerAttribute),
+                                                               typeof(ExportFontAttribute)
                                                        });
                                                }
                                        }
@@ -530,7 +533,8 @@ namespace Xamarin.Forms
                                                typeof(ExportRendererAttribute),
                                                typeof(ExportImageSourceHandlerAttribute),
                                                typeof(ExportCellAttribute),
-                                               typeof(ExportHandlerAttribute)
+                                               typeof(ExportHandlerAttribute),
+                                               typeof(ExportFontAttribute)
                                        });
                                }
                        }
index 132d22b..f6dff38 100644 (file)
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
+using System.Threading.Tasks;
 using ElmSharp;
 using Xamarin.Forms.Internals;
+using EColor = ElmSharp.Color;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
-       public class LightweightPlatform : BindableObject, ITizenPlatform
+       public class LightweightPlatform : ITizenPlatform, INavigation, IDisposable
        {
-               Page _page;
-               EvasObject _rootView;
-               bool _disposed;
+               NavigationModel _navModel = new NavigationModel();
+               Native.Canvas _viewStack;
+               readonly PopupManager _popupManager;
+               bool _hasAlpha;
+               readonly EColor _defaultPlatformColor;
 
-               internal LightweightPlatform(EvasObject parent)
+               public LightweightPlatform(EvasObject parent)
                {
                        Forms.NativeParent = parent;
+                       _defaultPlatformColor = Device.Idiom == TargetIdiom.Phone ? EColor.White : EColor.Transparent;
+                       _viewStack = new Native.Canvas(parent)
+                       {
+                               BackgroundColor = _defaultPlatformColor,
+                       };
+                       _viewStack.SetAlignment(-1, -1);
+                       _viewStack.SetWeight(1.0, 1.0);
+                       _viewStack.LayoutUpdated += OnLayout;
+                       _viewStack.Show();
+
+                       if (Forms.UseMessagingCenter)
+                       {
+                               _popupManager = new PopupManager(this);
+                       }
                }
 
+#pragma warning disable 0067
                public event EventHandler<RootNativeViewChangedEventArgs> RootNativeViewChanged;
+#pragma warning restore 0067
 
-               public bool HasAlpha { get => false; set { } }
-
-               public void Dispose()
+               public bool HasAlpha
                {
-                       Dispose(true);
-                       GC.SuppressFinalize(this);
+                       get => _hasAlpha;
+                       set
+                       {
+                               _hasAlpha = value;
+                               _viewStack.BackgroundColor = _hasAlpha ? EColor.Transparent : _defaultPlatformColor;
+                       }
                }
 
-               public EvasObject GetRootNativeView() => _rootView;
+               IPageController CurrentPageController => _navModel.CurrentPage as IPageController;
+
+               IReadOnlyList<Page> INavigation.ModalStack => _navModel.Modals.ToList();
+
+               IReadOnlyList<Page> INavigation.NavigationStack => new List<Page>();
+
+               public void SetPage(Page page)
+               {
+                       ResetChildren();
+                       _navModel = new NavigationModel();
+                       if (page == null)
+                               return;
+
+                       _navModel.Push(page, null);
+
+#pragma warning disable CS0618 // Type or member is obsolete
+                       page.Platform = this;
+#pragma warning restore CS0618 // Type or member is obsolete
+                       ((Application)page.RealParent).NavigationProxy.Inner = this;
+
+                       var renderer = Platform.CreateRenderer(page);
+                       renderer.NativeView.Geometry = _viewStack.Geometry;
+                       _viewStack.Children.Add(renderer.NativeView);
+
+                       CurrentPageController?.SendAppearing();
+               }
 
                public bool SendBackButtonPressed()
                {
-                       if (_page == null) return false;
-                       return _page.SendBackButtonPressed();
+                       return _navModel?.CurrentPage?.SendBackButtonPressed() ?? false;
                }
 
-               public void SetPage(Page page)
+               public EvasObject GetRootNativeView()
                {
-                       if (_page == page) return;
-                       if (_page != null)
+                       return _viewStack;
+               }
+
+               public bool PageIsChildOfPlatform(Page page)
+               {
+                       var parent = page.AncestorToRoot();
+                       return _navModel.Modals.FirstOrDefault() == page || _navModel.Roots.Contains(parent);
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
                        {
-                               var oldRenderer = Platform.GetRenderer(_page);
-                               oldRenderer?.Dispose();
+                               _popupManager?.Dispose();
+                               _viewStack?.Unrealize();
                        }
+               }
 
-                       _page = page;
+               Task<Page> INavigation.PopModalAsync()
+               {
+                       return (this as INavigation).PopModalAsync(true);
+               }
 
-                       if (_page == null) return;
+               Task<Page> INavigation.PopModalAsync(bool animated)
+               {
+                       Page page = _navModel.PopModal();
+                       (page as IPageController)?.SendDisappearing();
 
-#pragma warning disable CS0618 // Type or member is obsolete
-                       // The Platform property is no longer necessary, but we have to set it because some third-party
-                       // library might still be retrieving it and using it
-                       _page.Platform = this;
-#pragma warning restore CS0618 // Type or member is obsolete
+                       var renderer = Platform.GetRenderer(page);
+                       _viewStack.Children.Remove(renderer.NativeView);
+                       renderer.Dispose();
 
-                       var renderer = Platform.CreateRenderer(_page);
-                       _rootView = renderer.NativeView;
-                       RootNativeViewChanged?.Invoke(this, new RootNativeViewChangedEventArgs(_rootView));
-                       _rootView.Show();
+                       _viewStack.Children.LastOrDefault()?.Show();
 
-                       Device.StartTimer(TimeSpan.Zero, () =>
-                       {
-                               _page?.SendAppearing();
-                               return false;
-                       });
+                       CurrentPageController?.SendAppearing();
+                       return Task.FromResult(page);
                }
 
-               protected override void OnBindingContextChanged()
+               Task INavigation.PushModalAsync(Page modal)
                {
-                       BindableObject.SetInheritedBindingContext(_page, base.BindingContext);
-                       base.OnBindingContextChanged();
+                       return (this as INavigation).PushModalAsync(modal, true);
                }
 
-               protected virtual void Dispose(bool disposing)
+               Task INavigation.PushModalAsync(Page page, bool animated)
                {
-                       if (_disposed) return;
-                       if (disposing)
+                       var previousPage = CurrentPageController;
+                       previousPage?.SendDisappearing();
+
+                       _navModel.PushModal(page);
+
+                       var lastTop = _viewStack.Children.LastOrDefault();
+
+                       var renderer = Platform.GetOrCreateRenderer(page);
+                       renderer.NativeView.Geometry = _viewStack.Geometry;
+
+                       _viewStack.Children.Add(renderer.NativeView);
+                       if (lastTop != null)
                        {
-                               SetPage(null);
+                               lastTop.Hide();
+                               renderer.NativeView.StackAbove(lastTop);
                        }
-                       _disposed = true;
+
+                       // Verify that the modal is still on the stack
+                       if (_navModel.CurrentPage == page)
+                               CurrentPageController.SendAppearing();
+                       return Task.CompletedTask;
+               }
+
+               void INavigation.InsertPageBefore(Page page, Page before)
+               {
+                       throw new InvalidOperationException("InsertPageBefore is not supported globally on Tizen, please use a NavigationPage.");
+               }
+
+               Task<Page> INavigation.PopAsync()
+               {
+                       return ((INavigation)this).PopAsync(true);
+               }
+
+               Task<Page> INavigation.PopAsync(bool animated)
+               {
+                       throw new InvalidOperationException("PopAsync is not supported globally on Tizen, please use a NavigationPage.");
+               }
+
+               Task INavigation.PopToRootAsync()
+               {
+                       return ((INavigation)this).PopToRootAsync(true);
+               }
+
+               Task INavigation.PopToRootAsync(bool animated)
+               {
+                       throw new InvalidOperationException("PopToRootAsync is not supported globally on Tizen, please use a NavigationPage.");
+               }
+
+               Task INavigation.PushAsync(Page root)
+               {
+                       return ((INavigation)this).PushAsync(root, true);
+               }
+
+               Task INavigation.PushAsync(Page root, bool animated)
+               {
+                       throw new InvalidOperationException("PushAsync is not supported globally on Tizen, please use a NavigationPage.");
+               }
+
+               void INavigation.RemovePage(Page page)
+               {
+                       throw new InvalidOperationException("RemovePage is not supported globally on Tizen, please use a NavigationPage.");
                }
 
                SizeRequest IPlatform.GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
                {
                        return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
                }
+
+               void OnLayout(object sender, Native.LayoutEventArgs e)
+               {
+                       foreach (var child in _viewStack.Children)
+                       {
+                               child.Geometry = _viewStack.Geometry;
+                       }
+               }
+
+               void ResetChildren()
+               {
+                       var children = _viewStack.Children.ToList();
+                       _viewStack.Children.Clear();
+                       foreach (var child in children)
+                       {
+                               child.Unrealize();
+                       }
+               }
        }
 }
index 85c3398..3f06bbc 100644 (file)
@@ -15,38 +15,7 @@ namespace Xamarin.Forms.Platform.Tizen.Native
                /// </summary>
                /// <param name="parent">The parent EvasObject.</param>
                public Image(EvasObject parent) : base(parent)
-               {                       
-               }
-
-               /// <summary>
-               /// Loads image data from the given <see cref="Xamarin.Forms.ImageSource"/> asynchronously.
-               /// </summary>
-               /// <returns>A task which will be completed when image data is loaded.</returns>
-               /// <param name="source">Image source specifying from where the image data has to be loaded.</param>
-               public async Task<bool> LoadFromImageSourceAsync(ImageSource source)
-               {
-                       IImageSourceHandler handler;
-                       bool isLoadComplate = false;
-                       if (source != null && (handler = Forms.GetHandlerForObject<IImageSourceHandler>(source)) != null)
-                       {
-                               isLoadComplate = await handler.LoadImageAsync(this, source);
-                       }
-                       if (!isLoadComplate)
-                       {
-                               //If it fails, call the Load function to remove the previous image.
-                               Load(string.Empty);
-                       }
-
-                       return isLoadComplate;
-               }
-
-               public bool LoadFromFile(string file)
                {
-                       if (!string.IsNullOrEmpty(file))
-                       {
-                               return Load(ResourcePath.GetPath(file));
-                       }
-                       return false;
                }
 
                /// <summary>
index 76db94e..fe4620f 100644 (file)
@@ -1,15 +1,11 @@
 using System;
 using System.Collections.Generic;
-using System.Runtime.CompilerServices;
+using System.ComponentModel;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Threading.Tasks;
-using System.ComponentModel;
-using Xamarin.Forms.Internals;
-using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
 using ElmSharp;
-using EProgressBar = ElmSharp.ProgressBar;
-using EButton = ElmSharp.Button;
-using EColor = ElmSharp.Color;
+using Xamarin.Forms.Internals;
 
 [assembly: InternalsVisibleTo("Xamarin.Forms.Material")]
 
@@ -85,6 +81,7 @@ namespace Xamarin.Forms.Platform.Tizen
                EvasObject GetRootNativeView();
                bool HasAlpha { get; set; }
                event EventHandler<RootNativeViewChangedEventArgs> RootNativeViewChanged;
+               bool PageIsChildOfPlatform(Page page);
        }
 
        public class RootNativeViewChangedEventArgs : EventArgs
@@ -97,9 +94,8 @@ namespace Xamarin.Forms.Platform.Tizen
        {
                NavigationModel _navModel = new NavigationModel();
                bool _disposed;
-               Native.Dialog _pageBusyDialog;
-               int _pageBusyCount;
                readonly Naviframe _internalNaviframe;
+               readonly PopupManager _popupManager;
 
                readonly HashSet<EvasObject> _alerts = new HashSet<EvasObject>();
 
@@ -110,13 +106,6 @@ namespace Xamarin.Forms.Platform.Tizen
                internal DefaultPlatform(EvasObject parent)
                {
                        Forms.NativeParent = parent;
-                       _pageBusyCount = 0;
-                       if (Forms.UseMessagingCenter)
-                       {
-                               MessagingCenter.Subscribe<Page, bool>(this, Page.BusySetSignalName, BusySetSignalNameHandler);
-                               MessagingCenter.Subscribe<Page, AlertArguments>(this, Page.AlertSignalName, AlertSignalNameHandler);
-                               MessagingCenter.Subscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName, ActionSheetSignalNameHandler);
-                       }
 
                        _internalNaviframe = new Naviframe(Forms.NativeParent)
                        {
@@ -127,6 +116,11 @@ namespace Xamarin.Forms.Platform.Tizen
                        _internalNaviframe.SetWeight(1.0, 1.0);
                        _internalNaviframe.Show();
                        _internalNaviframe.AnimationFinished += NaviAnimationFinished;
+
+                       if (Forms.UseMessagingCenter)
+                       {
+                               _popupManager = new PopupManager(this);
+                       }
                }
 
                ~DefaultPlatform()
@@ -223,14 +217,18 @@ namespace Xamarin.Forms.Platform.Tizen
                        return _internalNaviframe as EvasObject;
                }
 
+               public bool PageIsChildOfPlatform(Page page)
+               {
+                       var parent = page.AncestorToRoot();
+                       return Page == parent || _navModel.Roots.Contains(parent);
+               }
+
                protected virtual void Dispose(bool disposing)
                {
                        if (_disposed) return;
                        if (disposing)
                        {
-                               MessagingCenter.Unsubscribe<Page, AlertArguments>(this, "Xamarin.SendAlert");
-                               MessagingCenter.Unsubscribe<Page, bool>(this, "Xamarin.BusySet");
-                               MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, "Xamarin.ShowActionSheet");
+                               _popupManager?.Dispose();
                                SetPage(null);
                                _internalNaviframe.Unrealize();
                        }
@@ -400,171 +398,6 @@ namespace Xamarin.Forms.Platform.Tizen
                        tcs?.SetResult(true);
                }
 
-               void BusySetSignalNameHandler(Page sender, bool enabled)
-               {
-                       // Verify that the page making the request is child of this platform
-                       if (!PageIsChildOfPlatform(sender))
-                               return;
-
-                       if (null == _pageBusyDialog)
-                       {
-                               _pageBusyDialog = new Native.Dialog(Forms.NativeParent)
-                               {
-                                       Orientation = PopupOrientation.Center,
-                                       BackgroundColor = EColor.Transparent
-                               };
-
-                               if (Device.Idiom == TargetIdiom.Phone)
-                               {
-                                       _pageBusyDialog.SetPartColor("bg_title", EColor.Transparent);
-                                       _pageBusyDialog.SetPartColor("bg_content", EColor.Transparent);
-                               }
-                               else if (Device.Idiom == TargetIdiom.Watch)
-                               {
-                                       _pageBusyDialog.Style = "circle";
-                               }
-
-                               var activity = new EProgressBar(_pageBusyDialog)
-                               {
-                                       Style = "process_large",
-                                       IsPulseMode = true,
-                               };
-                               activity.PlayPulse();
-                               activity.Show();
-
-                               _pageBusyDialog.Content = activity;
-
-                       }
-                       _pageBusyCount = Math.Max(0, enabled ? _pageBusyCount + 1 : _pageBusyCount - 1);
-                       if (_pageBusyCount > 0)
-                       {
-                               _pageBusyDialog.Show();
-                       }
-                       else
-                       {
-                               _pageBusyDialog.Dismiss();
-                               _pageBusyDialog = null;
-                       }
-               }
-
-               void AlertSignalNameHandler(Page sender, AlertArguments arguments)
-               {
-                       // Verify that the page making the request is child of this platform
-                       if (!PageIsChildOfPlatform(sender))
-                               return;
-
-                       Native.Dialog alert = Native.Dialog.CreateDialog(Forms.NativeParent, (arguments.Accept != null));
-
-                       alert.Title = arguments.Title;
-                       var message = arguments.Message.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace(Environment.NewLine, "<br>");
-                       alert.Message = message;
-
-                       EButton cancel = new EButton(alert) { Text = arguments.Cancel };
-                       alert.NegativeButton = cancel;
-                       cancel.Clicked += (s, evt) =>
-                       {
-                               arguments.SetResult(false);
-                               alert.Dismiss();
-                       };
-
-                       if (arguments.Accept != null)
-                       {
-                               EButton ok = new EButton(alert) { Text = arguments.Accept };
-                               alert.NeutralButton = ok;
-                               ok.Clicked += (s, evt) =>
-                               {
-                                       arguments.SetResult(true);
-                                       alert.Dismiss();
-                               };
-                       }
-
-                       alert.BackButtonPressed += (s, evt) =>
-                       {
-                               arguments.SetResult(false);
-                               alert.Dismiss();
-                       };
-
-                       alert.Show();
-                       _alerts.Add(alert);
-                       alert.Dismissed += (s, e) => _alerts.Remove(alert);
-               }
-
-               void ActionSheetSignalNameHandler(Page sender, ActionSheetArguments arguments)
-               {
-                       // Verify that the page making the request is child of this platform
-                       if (!PageIsChildOfPlatform(sender))
-                               return;
-
-                       Native.Dialog alert = Native.Dialog.CreateDialog(Forms.NativeParent);
-
-                       alert.Title = arguments.Title;
-                       Box box = new Box(alert);
-
-                       if (null != arguments.Destruction)
-                       {
-                               Native.Button destruction = new Native.Button(alert)
-                               {
-                                       Text = arguments.Destruction,
-                                       Style = ButtonStyle.Text,
-                                       TextColor = EColor.Red,
-                                       AlignmentX = -1
-                               };
-                               destruction.Clicked += (s, evt) =>
-                               {
-                                       arguments.SetResult(arguments.Destruction);
-                                       alert.Dismiss();
-                               };
-                               destruction.Show();
-                               box.PackEnd(destruction);
-                       }
-
-                       foreach (string buttonName in arguments.Buttons)
-                       {
-                               Native.Button button = new Native.Button(alert)
-                               {
-                                       Text = buttonName,
-                                       Style = ButtonStyle.Text,
-                                       AlignmentX = -1
-                               };
-                               button.Clicked += (s, evt) =>
-                               {
-                                       arguments.SetResult(buttonName);
-                                       alert.Dismiss();
-                               };
-                               button.Show();
-                               box.PackEnd(button);
-                       }
-
-                       box.Show();
-                       alert.Content = box;
-
-                       if (null != arguments.Cancel)
-                       {
-                               EButton cancel = new EButton(Forms.NativeParent) { Text = arguments.Cancel };
-                               alert.NegativeButton = cancel;
-                               cancel.Clicked += (s, evt) =>
-                               {
-                                       alert.Dismiss();
-                               };
-                       }
-
-                       alert.BackButtonPressed += (s, evt) =>
-                       {
-                               alert.Dismiss();
-                       };
-
-                       alert.Show();
-
-                       _alerts.Add(alert);
-                       alert.Dismissed += (s, e) => _alerts.Remove(alert);
-               }
-
-               bool PageIsChildOfPlatform(Page page)
-               {
-                       var parent = page.AncestorToRoot();
-                       return Page == parent || _navModel.Roots.Contains(parent);
-               }
-
                SizeRequest IPlatform.GetNativeSize(VisualElement view, double widthConstraint, double heightConstraint)
                {
                        return Platform.GetNativeSize(view, widthConstraint, heightConstraint);
diff --git a/Xamarin.Forms/Xamarin.Forms.Platform.Tizen/PopupManager.cs b/Xamarin.Forms/Xamarin.Forms.Platform.Tizen/PopupManager.cs
new file mode 100644 (file)
index 0000000..5f489ed
--- /dev/null
@@ -0,0 +1,295 @@
+using System;
+using System.Collections.Generic;
+using ElmSharp;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+using EButton = ElmSharp.Button;
+using EColor = ElmSharp.Color;
+using EProgressBar = ElmSharp.ProgressBar;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+       public class PopupManager : IDisposable
+       {
+               ITizenPlatform _platform;
+               Native.Dialog _pageBusyDialog;
+               int _pageBusyCount;
+               readonly HashSet<EvasObject> _alerts = new HashSet<EvasObject>();
+
+               public PopupManager(ITizenPlatform platform)
+               {
+                       _platform = platform;
+                       MessagingCenter.Subscribe<Page, bool>(this, Page.BusySetSignalName, OnBusySetRequest);
+                       MessagingCenter.Subscribe<Page, AlertArguments>(this, Page.AlertSignalName, OnAlertRequest);
+                       MessagingCenter.Subscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName, OnActionSheetRequest);
+                       MessagingCenter.Subscribe<Page, PromptArguments>(this, Page.PromptSignalName, OnPromptRequested);
+               }
+
+               public void Dispose()
+               {
+                       Dispose(true);
+               }
+
+               protected virtual void Dispose(bool disposing)
+               {
+                       if (disposing)
+                       {
+                               MessagingCenter.Unsubscribe<Page, AlertArguments>(this, Page.AlertSignalName);
+                               MessagingCenter.Unsubscribe<Page, bool>(this, Page.BusySetSignalName);
+                               MessagingCenter.Unsubscribe<Page, ActionSheetArguments>(this, Page.ActionSheetSignalName);
+                               MessagingCenter.Unsubscribe<Page, PromptArguments>(this, Page.PromptSignalName);
+                       }
+               }
+
+               void OnBusySetRequest(Page sender, bool enabled)
+               {
+                       // Verify that the page making the request is child of this platform
+                       if (!_platform.PageIsChildOfPlatform(sender))
+                               return;
+
+                       if (null == _pageBusyDialog)
+                       {
+                               _pageBusyDialog = new Native.Dialog(Forms.NativeParent)
+                               {
+                                       Orientation = PopupOrientation.Center,
+                                       BackgroundColor = EColor.Transparent
+                               };
+
+                               if (Device.Idiom == TargetIdiom.Phone)
+                               {
+                                       _pageBusyDialog.SetPartColor("bg_title", EColor.Transparent);
+                                       _pageBusyDialog.SetPartColor("bg_content", EColor.Transparent);
+                               }
+                               else if (Device.Idiom == TargetIdiom.Watch)
+                               {
+                                       _pageBusyDialog.Style = "circle";
+                               }
+
+                               var activity = new EProgressBar(_pageBusyDialog)
+                               {
+                                       Style = "process_large",
+                                       IsPulseMode = true,
+                               };
+                               activity.PlayPulse();
+                               activity.Show();
+
+                               _pageBusyDialog.Content = activity;
+
+                       }
+                       _pageBusyCount = Math.Max(0, enabled ? _pageBusyCount + 1 : _pageBusyCount - 1);
+                       if (_pageBusyCount > 0)
+                       {
+                               _pageBusyDialog.Show();
+                       }
+                       else
+                       {
+                               _pageBusyDialog.Dismiss();
+                               _pageBusyDialog = null;
+                       }
+               }
+
+               void OnAlertRequest(Page sender, AlertArguments arguments)
+               {
+                       // Verify that the page making the request is child of this platform
+                       if (!_platform.PageIsChildOfPlatform(sender))
+                               return;
+
+                       var alert = Native.Dialog.CreateDialog(Forms.NativeParent, (arguments.Accept != null));
+
+                       alert.Title = arguments.Title;
+                       var message = arguments.Message.Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;").Replace(Environment.NewLine, "<br>");
+                       alert.Message = message;
+
+                       var cancel = new EButton(alert) { Text = arguments.Cancel };
+                       alert.NegativeButton = cancel;
+                       cancel.Clicked += (s, evt) =>
+                       {
+                               arguments.SetResult(false);
+                               alert.Dismiss();
+                       };
+
+                       if (arguments.Accept != null)
+                       {
+                               var ok = new EButton(alert) { Text = arguments.Accept };
+                               alert.NeutralButton = ok;
+                               ok.Clicked += (s, evt) =>
+                               {
+                                       arguments.SetResult(true);
+                                       alert.Dismiss();
+                               };
+                       }
+
+                       alert.BackButtonPressed += (s, evt) =>
+                       {
+                               arguments.SetResult(false);
+                               alert.Dismiss();
+                       };
+
+                       alert.Show();
+                       _alerts.Add(alert);
+                       alert.Dismissed += (s, e) => _alerts.Remove(alert);
+               }
+
+               void OnActionSheetRequest(Page sender, ActionSheetArguments arguments)
+               {
+                       // Verify that the page making the request is child of this platform
+                       if (!_platform.PageIsChildOfPlatform(sender))
+                               return;
+
+                       var alert = Native.Dialog.CreateDialog(Forms.NativeParent);
+
+                       alert.Title = arguments.Title;
+                       var box = new Box(alert);
+
+                       if (null != arguments.Destruction)
+                       {
+                               var destruction = new Native.Button(alert)
+                               {
+                                       Text = arguments.Destruction,
+                                       Style = ButtonStyle.Text,
+                                       TextColor = EColor.Red,
+                                       AlignmentX = -1
+                               };
+                               destruction.Clicked += (s, evt) =>
+                               {
+                                       arguments.SetResult(arguments.Destruction);
+                                       alert.Dismiss();
+                               };
+                               destruction.Show();
+                               box.PackEnd(destruction);
+                       }
+
+                       foreach (string buttonName in arguments.Buttons)
+                       {
+                               var button = new Native.Button(alert)
+                               {
+                                       Text = buttonName,
+                                       Style = ButtonStyle.Text,
+                                       AlignmentX = -1
+                               };
+                               button.Clicked += (s, evt) =>
+                               {
+                                       arguments.SetResult(buttonName);
+                                       alert.Dismiss();
+                               };
+                               button.Show();
+                               box.PackEnd(button);
+                       }
+
+                       box.Show();
+                       alert.Content = box;
+
+                       if (null != arguments.Cancel)
+                       {
+                               var cancel = new EButton(Forms.NativeParent) { Text = arguments.Cancel };
+                               alert.NegativeButton = cancel;
+                               cancel.Clicked += (s, evt) =>
+                               {
+                                       alert.Dismiss();
+                               };
+                       }
+
+                       alert.BackButtonPressed += (s, evt) =>
+                       {
+                               alert.Dismiss();
+                       };
+
+                       alert.Show();
+
+                       _alerts.Add(alert);
+                       alert.Dismissed += (s, e) => _alerts.Remove(alert);
+               }
+
+               void OnPromptRequested(Page sender, PromptArguments args)
+               {
+                       // Verify that the page making the request is child of this platform
+                       if (!_platform.PageIsChildOfPlatform(sender))
+                               return;
+
+                       var prompt = Native.Dialog.CreateDialog(Forms.NativeParent, (args.Accept != null));
+                       prompt.Title = args.Title;
+
+                       var entry = new Entry
+                       {
+                               MinimumWidthRequest = 200,
+                               HorizontalOptions = LayoutOptions.FillAndExpand,
+                               BackgroundColor = Color.FromRgb(250, 250, 250),
+                               TextColor = Color.Black,
+                               Keyboard = args.Keyboard,
+                       };
+
+                       if (!string.IsNullOrEmpty(args.Placeholder))
+                       {
+                               entry.Placeholder = args.Placeholder;
+                       }
+                       if (args.MaxLength > 0)
+                       {
+                               entry.MaxLength = args.MaxLength;
+                       }
+
+                       var layout = new StackLayout
+                       {
+                               Spacing = 10,
+                               Children =
+                               {
+                                       new Label
+                                       {
+                                               LineBreakMode = LineBreakMode.CharacterWrap,
+                                               TextColor = Device.Idiom == TargetIdiom.Watch ? Color.White : Color.Accent,
+                                               Text = args.Message,
+                                               HorizontalOptions = LayoutOptions.FillAndExpand,
+                                               HorizontalTextAlignment = TextAlignment.Center,
+                                               FontSize = Device.GetNamedSize(NamedSize.Subtitle, typeof(Label)),
+                                       },
+                                       entry,
+                               }
+                       };
+
+                       layout.Parent = sender;
+                       var layoutrenderer = Platform.GetOrCreateRenderer(layout);
+
+                       var request = layout.Measure(Device.Idiom == TargetIdiom.Watch ? sender.Width * 0.7 : sender.Width, sender.Height);
+                       (layoutrenderer as LayoutRenderer).RegisterOnLayoutUpdated();
+                       layoutrenderer.NativeView.MinimumHeight = Forms.ConvertToScaledPixel(request.Request.Height);
+                       layoutrenderer.NativeView.MinimumWidth = Forms.ConvertToScaledPixel(request.Request.Width);
+
+                       prompt.Content = layoutrenderer.NativeView;
+
+                       var cancel = new EButton(prompt) { Text = args.Cancel };
+                       prompt.NegativeButton = cancel;
+                       cancel.Clicked += (s, evt) =>
+                       {
+                               args.SetResult(null);
+                               prompt.Dismiss();
+                       };
+
+                       if (args.Accept != null)
+                       {
+                               var ok = new EButton(prompt) { Text = args.Accept };
+                               prompt.NeutralButton = ok;
+                               ok.Clicked += (s, evt) =>
+                               {
+                                       args.SetResult(entry.Text);
+                                       prompt.Dismiss();
+                               };
+                       }
+
+                       entry.Completed += (s, e) =>
+                       {
+                               args.SetResult(entry.Text);
+                               prompt.Dismiss();
+                       };
+
+                       prompt.BackButtonPressed += (s, evt) =>
+                       {
+                               prompt.Dismiss();
+                       };
+
+                       prompt.Show();
+
+                       _alerts.Add(prompt);
+                       prompt.Dismissed += (s, e) => _alerts.Remove(prompt);
+               }
+
+       }
+}
\ No newline at end of file
index 668c6ee..66d93e0 100644 (file)
@@ -52,6 +52,8 @@ using Xamarin.Forms.Platform.Tizen;
 [assembly: ExportCell(typeof(EntryCell), typeof(EntryCellRenderer))]
 [assembly: ExportCell(typeof(ViewCell), typeof(ViewCellRenderer))]
 
+[assembly: ExportRenderer(typeof(EmbeddedFont), typeof(EmbeddedFontLoader))]
+
 [assembly: ExportHandler(typeof(TapGestureRecognizer), typeof(TapGestureHandler))]
 [assembly: ExportHandler(typeof(PinchGestureRecognizer), typeof(PinchGestureHandler))]
 [assembly: ExportHandler(typeof(PanGestureRecognizer), typeof(PanGestureHandler))]
index aee2845..55f8534 100644 (file)
@@ -118,7 +118,7 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        if (Control is IButton ib)
                        {
-                               ib.FontFamily = Element.FontFamily;
+                               ib.FontFamily = Element.FontFamily.ToNativeFontFamily();
                        }                       
                }
 
index b374c6d..9575dac 100644 (file)
@@ -118,7 +118,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
                void UpdateFontFamily()
                {
-                       Control.FontFamily = Element.FontFamily;
+                       Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
                }
 
                void UpdateFontAttributes()
index dd57006..8534174 100644 (file)
@@ -111,7 +111,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
                void UpdateFontFamily()
                {
-                       Control.FontFamily = Element.FontFamily;
+                       Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
                }
 
                void UpdateFontAttributes()
index 85c7d11..d936cd9 100644 (file)
@@ -145,7 +145,7 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        if (Control is IEntry ie)
                        {
-                               ie.FontFamily = Element.FontFamily;
+                               ie.FontFamily = Element.FontFamily.ToNativeFontFamily();
                        }
                }
 
index 1d40f67..b534002 100644 (file)
@@ -1,6 +1,7 @@
 using System.ComponentModel;
 using System.Threading;
 using System.Threading.Tasks;
+using EImage = ElmSharp.Image;
 using Xamarin.Forms.Platform.Tizen.Native;
 using Specific = Xamarin.Forms.PlatformConfiguration.TizenSpecific.Image;
 
@@ -13,6 +14,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        RegisterPropertyHandler(Image.SourceProperty, UpdateSource);
                        RegisterPropertyHandler(Image.AspectProperty, UpdateAspect);
                        RegisterPropertyHandler(Image.IsOpaqueProperty, UpdateIsOpaque);
+                       RegisterPropertyHandler(Image.IsAnimationPlayingProperty, UpdateIsAnimationPlaying);
                        RegisterPropertyHandler(Specific.BlendColorProperty, UpdateBlendColor);
                        RegisterPropertyHandler(Specific.FileProperty, UpdateFile);
                }
@@ -71,6 +73,7 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        UpdateIsOpaque(initialize);
                        UpdateBlendColor(initialize);
+                       UpdateIsAnimationPlaying(initialize);
                }
 
                void UpdateAspect(bool initialize)
@@ -89,6 +92,15 @@ namespace Xamarin.Forms.Platform.Tizen
                        Control.IsOpaque = Element.IsOpaque;
                }
 
+               void UpdateIsAnimationPlaying(bool initialize)
+               {
+                       if (initialize && !Element.IsAnimationPlaying)
+                               return;
+
+                       Control.IsAnimated = Element.IsAnimationPlaying;
+                       Control.IsAnimationPlaying = Element.IsAnimationPlaying;
+               }
+
                void UpdateBlendColor(bool initialize)
                {
                        if (initialize && Specific.GetBlendColor(Element).IsDefault)
@@ -100,12 +112,12 @@ namespace Xamarin.Forms.Platform.Tizen
 
        public interface IImageSourceHandler : IRegisterable
        {
-               Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken));
+               Task<bool> LoadImageAsync(EImage image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken));
        }
 
        public sealed class FileImageSourceHandler : IImageSourceHandler
        {
-               public Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+               public Task<bool> LoadImageAsync(EImage image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
                {
                        var filesource = imageSource as FileImageSource;
                        if (filesource != null)
@@ -120,7 +132,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
        public sealed class StreamImageSourceHandler : IImageSourceHandler
        {
-               public async Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+               public async Task<bool> LoadImageAsync(EImage image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
                {
                        var streamsource = imageSource as StreamImageSource;
                        if (streamsource != null && streamsource.Stream != null)
@@ -137,7 +149,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
        public sealed class UriImageSourceHandler : IImageSourceHandler
        {
-               public Task<bool> LoadImageAsync(Native.Image image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
+               public Task<bool> LoadImageAsync(EImage image, ImageSource imageSource, CancellationToken cancelationToken = default(CancellationToken))
                {
                        var urisource = imageSource as UriImageSource;
                        if (urisource != null && urisource.Uri != null)
index 7b35473..f916e2e 100644 (file)
@@ -106,7 +106,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
                        Control.FontSize = Element.FontSize;
                        Control.FontAttributes = Element.FontAttributes;
-                       Control.FontFamily = Element.FontFamily;
+                       Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
 
                        Control.BatchCommit();
                }
index 1fc450b..3176880 100644 (file)
@@ -93,7 +93,7 @@ namespace Xamarin.Forms.Platform.Tizen
                /// </summary>
                void FontFamilyPropertyHandler()
                {
-                       Control.FontFamily = Element.FontFamily;
+                       Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
                }
 
                /// <summary>
index 35c91d2..9a5d864 100644 (file)
@@ -127,7 +127,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
                void UpdateFontFamily()
                {
-                       Control.FontFamily = Element.FontFamily;
+                       Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
                }
 
                void UpdateFontAttributes()
index f88d603..c088d25 100644 (file)
@@ -1,9 +1,7 @@
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using ElmSharp;
 using EColor = ElmSharp.Color;
-using Xamarin.Forms;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
@@ -13,6 +11,10 @@ namespace Xamarin.Forms.Platform.Tizen
 
                EColor BackgroundColor { get; set; }
 
+               ImageSource BackgroundImageSource { get; set; }
+
+               Aspect BackgroundImageAspect { get; set; }
+
                void BuildMenu(List<List<Element>> flyout);
 
                event EventHandler<SelectedItemChangedEventArgs> SelectedItemChanged;
index 7719045..8b27c09 100644 (file)
@@ -1,26 +1,30 @@
 using System;
-using System.Collections;
 using System.Collections.Generic;
 using ElmSharp;
 using EColor = ElmSharp.Color;
+using EImage = ElmSharp.Image;
+using NBox = Xamarin.Forms.Platform.Tizen.Native.Box;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
-       public class NavigationView : Native.Box, INavigationView
+       public class NavigationView : Background, INavigationView
        {
+               NBox _box;
+               EImage _bg;
+               Aspect _bgImageAspect;
+               ImageSource _bgImageSource;
                EvasObject _header;
                GenList _menu;
                GenItemClass _defaultClass;
                EColor _backgroundColor;
                EColor _defaultBackgroundColor = EColor.White;
-
                List<Group> _groups;
                IDictionary<Item, Element> _flyoutMenu = new Dictionary<Item, Element>();
 
                public NavigationView(EvasObject parent) : base(parent)
                {
                        Initialize(parent);
-                       LayoutUpdated += (s, e) =>
+                       _box.LayoutUpdated += (s, e) =>
                        {
                                UpdateChildGeometry();
                        };
@@ -39,10 +43,58 @@ namespace Xamarin.Forms.Platform.Tizen
                        set
                        {
                                _backgroundColor = value;
-
                                EColor effectiveColor = _backgroundColor.IsDefault ? _defaultBackgroundColor : _backgroundColor;
-                               _menu.BackgroundColor = effectiveColor;
                                base.BackgroundColor = effectiveColor;
+
+                               if(_bg == null)
+                               {
+                                       _menu.BackgroundColor = effectiveColor;
+                               }
+                       }
+               }
+
+               public Aspect BackgroundImageAspect
+               {
+                       get
+                       {
+                               return _bgImageAspect;
+                       }
+                       set
+                       {
+                               _bgImageAspect = value;
+                               if (_bg != null)
+                               {
+                                       _bg.ApplyAspect(_bgImageAspect);
+                               }
+                       }
+               }
+
+               public ImageSource BackgroundImageSource
+               {
+                       get
+                       {
+                               return _bgImageSource;
+                       }
+                       set
+                       {
+                               _bgImageSource = value;
+                               if (_bgImageSource != null)
+                               {
+                                       if (_bg == null)
+                                       {
+                                               _bg = new EImage(this);
+                                       }
+                                       _menu.BackgroundColor = EColor.Transparent;
+                                       SetPartContent("elm.swallow.background", _bg);
+                                       _bg.ApplyAspect(_bgImageAspect);
+                                       _ = _bg.LoadFromImageSourceAsync(_bgImageSource);
+                               }
+                               else
+                               {
+                                       EColor effectiveColor = _backgroundColor.IsDefault ? _defaultBackgroundColor : _backgroundColor;
+                                       _menu.BackgroundColor = effectiveColor;
+                                       SetPartContent("elm.swallow.background", null);
+                               }
                        }
                }
 
@@ -56,14 +108,14 @@ namespace Xamarin.Forms.Platform.Tizen
                        {
                                if (_header != null)
                                {
-                                       UnPack(_header);
+                                       _box.UnPack(_header);
                                        _header.Hide();
                                }
                                _header = value;
 
                                if (_header != null)
                                {
-                                       PackStart(_header);
+                                       _box.PackStart(_header);
                                        if (!_header.IsVisible)
                                        {
                                                _header.Show();
@@ -118,6 +170,15 @@ namespace Xamarin.Forms.Platform.Tizen
 
                void Initialize(EvasObject parent)
                {
+                       _box = new Native.Box(parent)
+                       {
+                               AlignmentX = -1,
+                               AlignmentY = -1,
+                               WeightX = 1,
+                               WeightY = 1
+                       };
+                       SetContent(_box);
+
                        _menu = new GenList(parent)
                        {
                                BackgroundColor = EColor.Transparent,
@@ -132,7 +193,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        };
 
                        _menu.Show();
-                       PackEnd(_menu);
+                       _box.PackEnd(_menu);
 
                        _defaultClass = new GenItemClass("double_label")
                        {
index 5bcb66a..b33ba7d 100644 (file)
@@ -1,18 +1,20 @@
 using System;
 using System.Reflection;
 using ElmSharp;
-using EColor = ElmSharp.Color;
 using Xamarin.Forms.Platform.Tizen.Native;
+using EColor = ElmSharp.Color;
+using EButton = ElmSharp.Button;
+using EImage = ElmSharp.Image;
 
 namespace Xamarin.Forms.Platform.Tizen
 {
        public class ShellNavBar : Native.Box
        {
-               Native.Image _menu = null;
+               EImage _menu = null;
+               EButton _menuButton = null;
                Native.Label _title = null;
                Native.SearchBar _nativeSearchHandler = null;
                EvasObject _nativeTitleView = null;
-               ShellSectionNavigation _shellSectionNavigation = null;
 
                SearchHandler _searchHandler = null;
                View _titleView = null;
@@ -29,15 +31,18 @@ namespace Xamarin.Forms.Platform.Tizen
 
                bool _hasBackButton = false;
 
-               public ShellNavBar(IFlyoutController flyoutController, ShellSectionNavigation shellSectionNavigation) : base(Forms.NativeParent)
+               public ShellNavBar(IFlyoutController flyoutController) : base(Forms.NativeParent)
                {
                        _flyoutController = flyoutController;
-                       _shellSectionNavigation = shellSectionNavigation;
 
-                       _menu = new Native.Image(Forms.NativeParent);
-                       _menu.Clicked += OnMenuClicked;
+                       _menuButton = new EButton(Forms.NativeParent);
+                       _menuButton.Clicked += OnMenuClicked;
+                       _menu = new EImage(Forms.NativeParent);
                        UpdateMenuIcon();
                        _menu.Show();
+                       _menuButton.Show();
+
+                       _menuButton.SetPartContent("icon", _menu);
 
                        _title = new Native.Label(Forms.NativeParent)
                        {
@@ -49,7 +54,8 @@ namespace Xamarin.Forms.Platform.Tizen
                        _title.Show();
 
                        BackgroundColor = _backgroudColor;
-                       PackEnd(_menu);
+                       _menuButton.BackgroundColor = _backgroudColor;
+                       PackEnd(_menuButton);
                        PackEnd(_title);
                        LayoutUpdated += OnLayoutUpdated;
                }
@@ -123,6 +129,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        set
                        {
                                _backgroudColor = value;
+                               _menuButton.BackgroundColor = _backgroudColor;
                                base.BackgroundColor = _backgroudColor;
                        }
                }
@@ -136,7 +143,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        set
                        {
                                _foregroudColor = value;
-                               _menu.Color = value;
+                               _menuButton.Color = value;
                        }
                }
 
@@ -164,11 +171,11 @@ namespace Xamarin.Forms.Platform.Tizen
                        }
                }
 
-               async void UpdateMenuIcon()
+               void UpdateMenuIcon()
                {
                        string file = _hasBackButton ? _backIcon : _menuIcon;
-                       ImageSource source = ImageSource.FromResource(file, typeof(ShellNavBar).GetTypeInfo().Assembly);
-                       bool ret = await _menu.LoadFromImageSourceAsync(source);
+                       var path = Assembly.GetExecutingAssembly().GetManifestResourceStream(file);
+                       _menu.Load(path);
                }
 
                void OnMenuClicked(object sender, EventArgs e)
@@ -180,7 +187,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        }
                        else if (_hasBackButton)
                        {
-                               _shellSectionNavigation.PopRequest(this, new Internals.NavigationRequestedEventArgs(_page, false));
+                               Shell.Current.CurrentItem.Navigation.PopAsync();
                        }
                        else
                        {
@@ -258,8 +265,8 @@ namespace Xamarin.Forms.Platform.Tizen
                        int titleLeftMargin = 40;
                        int titleViewTopMargin = 40;
 
-                       _menu.Move(e.Geometry.X + menuMargin, e.Geometry.Y + (e.Geometry.Height - menuSize) / 2);
-                       _menu.Resize(menuSize, menuSize);
+                       _menuButton.Move(e.Geometry.X + menuMargin, e.Geometry.Y + (e.Geometry.Height - menuSize) / 2);
+                       _menuButton.Resize(menuSize, menuSize);
 
                        if (_searchHandler != null)
                        {
index e84aa97..51221de 100644 (file)
@@ -1,5 +1,4 @@
 using System;
-using System.Collections.Generic;
 using ElmSharp;
 
 namespace Xamarin.Forms.Platform.Tizen
@@ -18,6 +17,8 @@ namespace Xamarin.Forms.Platform.Tizen
                {
                        RegisterPropertyHandler(Shell.CurrentItemProperty, UpdateCurrentItem);
                        RegisterPropertyHandler(Shell.FlyoutBackgroundColorProperty, UpdateFlyoutBackgroundColor);
+                       RegisterPropertyHandler(Shell.FlyoutBackgroundImageProperty, UpdateFlyoutBackgroundImage);
+                       RegisterPropertyHandler(Shell.FlyoutBackgroundImageAspectProperty, UpdateFlyoutBackgroundImageAspect);
                        RegisterPropertyHandler(Shell.FlyoutIsPresentedProperty, UpdateFlyoutIsPresented);
                }
 
@@ -119,6 +120,16 @@ namespace Xamarin.Forms.Platform.Tizen
                        _navigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNative();
                }
 
+               void UpdateFlyoutBackgroundImageAspect()
+               {
+                       _navigationView.BackgroundImageAspect = Element.FlyoutBackgroundImageAspect;
+               }
+
+               void UpdateFlyoutBackgroundImage()
+               {
+                       _navigationView.BackgroundImageSource = Element.FlyoutBackgroundImage;
+               }
+
                void UpdateFlyoutIsPresented()
                {
                        _native.IsOpen = Element.FlyoutIsPresented;
index f1a9a64..b30aad2 100644 (file)
@@ -30,7 +30,7 @@ namespace Xamarin.Forms.Platform.Tizen
                        _section.PropertyChanged += OnSectionPropertyChanged;
                        _rootPage = ((IShellContentController)_section.CurrentItem).GetOrCreateContent();
 
-                       _navBar = new ShellNavBar(flyoutController, this);
+                       _navBar = new ShellNavBar(flyoutController);
                        _navBar.Show();
 
                        var renderer = CreateShellSection(section);
index 4f7b3ce..800ec41 100644 (file)
@@ -66,7 +66,7 @@ namespace Xamarin.Forms.Platform.Tizen
 
                public static void RegisterHandlers(Dictionary<Type, Func<IRegisterable>> customHandlers)
                {
-                       ////Renderers
+                       //Renderers
                        Registered.Register(typeof(Layout), () => new LayoutRenderer());
                        Registered.Register(typeof(ScrollView), () => new ScrollViewRenderer());
                        Registered.Register(typeof(CarouselPage), () => new CarouselPageRenderer());
@@ -102,18 +102,21 @@ namespace Xamarin.Forms.Platform.Tizen
                        Registered.Register(typeof(SwipeView), () => new SwipeViewRenderer());
                        Registered.Register(typeof(RefreshView), () => new RefreshViewRenderer());
 
-                       ////ImageSourceHandlers
+                       //ImageSourceHandlers
                        Registered.Register(typeof(FileImageSource), () => new FileImageSourceHandler());
                        Registered.Register(typeof(StreamImageSource), () => new StreamImageSourceHandler());
                        Registered.Register(typeof(UriImageSource), () => new UriImageSourceHandler());
 
-                       ////Cell Renderers
+                       //Cell Renderers
                        Registered.Register(typeof(TextCell), () => new TextCellRenderer());
                        Registered.Register(typeof(ImageCell), () => new ImageCellRenderer());
                        Registered.Register(typeof(SwitchCell), () => new SwitchCellRenderer());
                        Registered.Register(typeof(EntryCell), () => new EntryCellRenderer());
                        Registered.Register(typeof(ViewCell), () => new ViewCellRenderer());
 
+                       //Font Loaders
+                       Registered.Register(typeof(EmbeddedFont), () => new EmbeddedFontLoader());
+
                        //Dependencies
                        DependencyService.Register<ISystemResourcesProvider, ResourcesProvider>();
                        DependencyService.Register<IDeserializer, Deserializer>();
index ce051ea..5616eca 100644 (file)
@@ -347,21 +347,21 @@ namespace Xamarin.Forms.Xaml
                        //If it's a BindableProberty, SetValue
                        if (xpe == null && TrySetValue(xamlelement, property, attached, value, lineInfo, serviceProvider, out xpe)) {
                                if (!(node is ValueNode) && value != null && !value.GetType().GetTypeInfo().IsValueType && XamlFilePathAttribute.GetFilePathForObject(context.RootElement) is string path)
-                                       VisualDiagnostics.RegisterSourceInfo(value, new Uri(path, UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
+                                       VisualDiagnostics.RegisterSourceInfo(value, new Uri($"{path};assembly={context.RootAssembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
                                return;
                        }
 
                        //If we can assign that value to a normal property, let's do it
                        if (xpe == null && TrySetProperty(xamlelement, localName, value, lineInfo, serviceProvider, context, out xpe)) {
                                if (!(node is ValueNode) && value != null && !value.GetType().GetTypeInfo().IsValueType && XamlFilePathAttribute.GetFilePathForObject(context.RootElement) is string path)
-                                       VisualDiagnostics.RegisterSourceInfo(value, new Uri(path, UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
+                                       VisualDiagnostics.RegisterSourceInfo(value, new Uri($"{path};assembly={context.RootAssembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
                                return;
                        }
 
                        //If it's an already initialized property, add to it
                        if (xpe == null && TryAddToProperty(xamlelement, propertyName, value, xKey, lineInfo, serviceProvider, context, out xpe)) {
                                if (!(node is ValueNode) && value != null && !value.GetType().GetTypeInfo().IsValueType && XamlFilePathAttribute.GetFilePathForObject(context.RootElement) is string path)
-                                       VisualDiagnostics.RegisterSourceInfo(value, new Uri(path, UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
+                                       VisualDiagnostics.RegisterSourceInfo(value, new Uri($"{path};assembly={context.RootAssembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
                                return;
                        }
 
index 8cf27cf..5dc7317 100644 (file)
@@ -119,26 +119,59 @@ namespace Xamarin.Forms.Xaml
                                if (serviceProvider.GetService(typeof(IXmlLineInfoProvider)) is IXmlLineInfoProvider xmlLineInfoProvider)
                                        xmlLineInfo = xmlLineInfoProvider.XmlLineInfo;
 
-                               var split = match.Split(':');
-                               if (split.Length > 2)
-                                       throw new ArgumentException();
+                               var (prefix, name) = ParseName(match);
+
+                               var namespaceuri = nsResolver.LookupNamespace(prefix) ?? "";
 
-                               string prefix; //, name;
-                               if (split.Length == 2) {
-                                       prefix = split[0];
-                                       //                                      name = split [1];
+                               IList<XmlType> typeArguments = null;
+                               var childnodes = new List<(XmlName, INode)>();
+                               var contentname = new XmlName(null, null);
+
+                               if (remaining.StartsWith("}", StringComparison.Ordinal)) {
+                                       remaining = remaining.Substring(1);
                                }
                                else {
-                                       prefix = "";
-                                       //                                      name = split [0];
+                                       Property parsed;
+                                       do {
+                                               parsed = ParseProperty(serviceProvider, ref remaining);
+                                               XmlName childname;
+
+                                               if (parsed.name == null) {
+                                                       childname = contentname;
+                                               }
+                                               else {
+                                                       var (propertyPrefix, propertyName) = ParseName(parsed.name);
+
+                                                       childname = XamlParser.ParsePropertyName(new XmlName(
+                                                               propertyPrefix == "" ? null : nsResolver.LookupNamespace(propertyPrefix),
+                                                               propertyName));
+
+                                                       if (childname.NamespaceURI == null && childname.LocalName == null)
+                                                               continue;
+                                               }
+
+                                               if (childname == XmlName.xTypeArguments) {
+                                                       typeArguments = TypeArgumentsParser.ParseExpression(parsed.strValue, nsResolver, xmlLineInfo);
+                                                       childnodes.Add((childname, new ValueNode(typeArguments, nsResolver)));
+                                               }
+                                               else {
+                                                       var childnode = parsed.value as INode ?? new ValueNode(parsed.strValue, nsResolver);
+                                                       childnodes.Add((childname, childnode));
+                                               }
+                                       }
+                                       while (!parsed.last);
                                }
 
-                               Type type;
-                               if (!(serviceProvider.GetService(typeof(IXamlTypeResolver)) is IXamlTypeResolver typeResolver))
-                                       type = null;
-                               else {
-                                       //The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
-                                       if (!typeResolver.TryResolve(match + "Extension", out type) && !typeResolver.TryResolve(match, out type)) {
+
+                               if (!(serviceProvider.GetService(typeof (IXamlTypeResolver)) is XamlTypeResolver typeResolver))
+                                       throw new NotSupportedException();
+
+                               var xmltype = new XmlType(namespaceuri, name + "Extension", typeArguments);
+
+                               //The order of lookup is to look for the Extension-suffixed class name first and then look for the class name without the Extension suffix.
+                               if (!typeResolver.TryResolve(xmltype, out _)) {
+                                       xmltype = new XmlType(namespaceuri, name, typeArguments);
+                                       if (!typeResolver.TryResolve(xmltype, out _)) {
                                                var ex = new XamlParseException($"MarkupExtension not found for {match}", serviceProvider);
                                                if (ExceptionHandler != null) {
                                                        ExceptionHandler(ex);
@@ -148,49 +181,23 @@ namespace Xamarin.Forms.Xaml
                                        }
                                }
 
-                               var namespaceuri = nsResolver.LookupNamespace(prefix) ?? "";
-                               var xmltype = new XmlType(namespaceuri, type.Name, null);
-
-                               if (type == null)
-                                       throw new NotSupportedException();
-
                                _node = xmlLineInfo == null
                                        ? new ElementNode(xmltype, null, nsResolver)
                                        : new ElementNode(xmltype, null, nsResolver, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition);
 
-                               if (remaining.StartsWith("}", StringComparison.Ordinal)) {
-                                       remaining = remaining.Substring(1);
-                                       return _node;
-                               }
-
-                               string piece;
-                               while ((piece = GetNextPiece(ref remaining, out var next)) != null)
-                                       HandleProperty(piece, serviceProvider, ref remaining, next != '=');
+                               foreach (var (childname, childnode) in childnodes) {
+                                       childnode.Parent = _node;
 
-                               return _node;
-                       }
-
-                       protected override void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
-                       {
-                               if (value == null && strValue == null) {
-                                       var xpe = new XamlParseException($"No value found for property '{prop}' in markup expression", serviceProvider);
-                                       if (ExceptionHandler != null) {
-                                               ExceptionHandler(xpe);
-                                               return;
+                                       if (childname == contentname) {
+                                               //ContentProperty
+                                               _node.CollectionItems.Add(childnode);
+                                       }
+                                       else {
+                                               _node.Properties[childname] = childnode;
                                        }
-                                       throw xpe;
                                }
 
-                               var nsResolver = serviceProvider.GetService(typeof (IXmlNamespaceResolver)) as IXmlNamespaceResolver;
-
-                               var childnode = value as INode ?? new ValueNode(strValue, nsResolver);
-                               childnode.Parent = _node;
-                               if (prop != null) {
-                                       var name = new XmlName(_node.NamespaceURI, prop);
-                                       _node.Properties[name] = childnode;
-                               }
-                               else //ContentProperty
-                                       _node.CollectionItems.Add(childnode);
+                               return _node;
                        }
                }
        }
index 9123a72..a0297ab 100644 (file)
@@ -38,6 +38,14 @@ namespace Xamarin.Forms.Xaml
 {
        abstract class MarkupExpressionParser
        {
+               protected struct Property
+               {
+                       public bool last;
+                       public string name;
+                       public string strValue;
+                       public object value;
+               }
+
                public object ParseExpression(ref string expression, IServiceProvider serviceProvider)
                {
                        if (serviceProvider == null)
@@ -46,7 +54,7 @@ namespace Xamarin.Forms.Xaml
                                return expression.Substring(2);
 
                        if (expression[expression.Length - 1] != '}')
-                               throw new Exception("Expression must end with '}'");
+                               throw new XamlParseException("Expression must end with '}'", serviceProvider);
 
                        int len;
                        string match;
@@ -54,7 +62,7 @@ namespace Xamarin.Forms.Xaml
                                return false;
                        expression = expression.Substring(len).TrimStart();
                        if (expression.Length == 0)
-                               throw new Exception("Expression did not end in '}'");
+                               throw new XamlParseException("Expression did not end in '}'", serviceProvider);
 
                        var parser = Activator.CreateInstance(GetType()) as IExpressionParser;
                        return parser.Parse(match, ref expression, serviceProvider);
@@ -112,49 +120,56 @@ namespace Xamarin.Forms.Xaml
                        return true;
                }
 
-               protected void HandleProperty(string prop, IServiceProvider serviceProvider, ref string remaining, bool isImplicit)
+               protected Property ParseProperty(IServiceProvider serviceProvider, ref string remaining)
                {
                        char next;
                        object value = null;
                        string str_value;
+                       string name;
 
-                       if (isImplicit)
-                       {
-                               SetPropertyValue(null, prop, null, serviceProvider);
-                               return;
-                       }
                        remaining = remaining.TrimStart();
-                       if (remaining.StartsWith("{", StringComparison.Ordinal))
-                       {
-                               value = ParseExpression(ref remaining, serviceProvider);
-                               remaining = remaining.TrimStart();
+                       if (remaining[0] == '{')
+                               return ParsePropertyExpression(null, serviceProvider, ref remaining);
 
-                               if (remaining.Length > 0 && remaining[0] == ',')
-                                       remaining = remaining.Substring(1);
-                               else if (remaining.Length > 0 && remaining[0] == '}')
-                                       remaining = remaining.Substring(1);
+                       str_value = GetNextPiece(serviceProvider, ref remaining, out next);
+                       if (next == '=') {
+                               remaining = remaining.TrimStart();
+                               if (remaining[0] == '{')
+                                       return ParsePropertyExpression(str_value, serviceProvider, ref remaining);
 
-                               str_value = value as string;
+                               name = str_value;
+                               str_value = GetNextPiece(serviceProvider, ref remaining, out next);
+                       }
+                       else {
+                               name = null;
                        }
-                       else
-                               str_value = GetNextPiece(ref remaining, out next);
 
-                       SetPropertyValue(prop, str_value, value, serviceProvider);
+                       return new Property { last = next == '}', name = name, strValue = str_value, value = value };
                }
 
-               protected abstract void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider);
+               private Property ParsePropertyExpression(string prop, IServiceProvider serviceProvider, ref string remaining)
+               {
+                       bool last;
+                       var value = ParseExpression(ref remaining, serviceProvider);
+                       remaining = remaining.TrimStart();
+                       if (remaining.Length <= 0)
+                               throw new XamlParseException("Unexpected end of markup expression", serviceProvider);
+                       if (remaining[0] == ',')
+                               last = false;
+                       else if (remaining[0] == '}')
+                               last = true;
+                       else
+                               throw new XamlParseException("Unexpected character following value string", serviceProvider);
+
+                       remaining = remaining.Substring(1);
+                       return new Property { last = last, name = prop, strValue = value as string, value = value };
+               }
 
-               protected string GetNextPiece(ref string remaining, out char next)
+               private string GetNextPiece(IServiceProvider serviceProvider, ref string remaining, out char next)
                {
                        bool inString = false;
                        int end = 0;
                        char stringTerminator = '\0';
-                       remaining = remaining.TrimStart();
-                       if (remaining.Length == 0)
-                       {
-                               next = Char.MaxValue;
-                               return null;
-                       }
 
                        var piece = new StringBuilder();
                        // If we're inside a quoted string we append all chars to our piece until we hit the ending quote.
@@ -193,16 +208,10 @@ namespace Xamarin.Forms.Xaml
                        }
 
                        if (inString && end == remaining.Length)
-                               throw new Exception("Unterminated quoted string");
-
-                       if (end == remaining.Length && !remaining.EndsWith("}", StringComparison.Ordinal))
-                               throw new Exception("Expression did not end with '}'");
+                               throw new XamlParseException("Unterminated quoted string", serviceProvider);
 
                        if (end == 0)
-                       {
-                               next = Char.MaxValue;
-                               return null;
-                       }
+                               throw new XamlParseException("Empty value string in markup expression", serviceProvider);
 
                        next = remaining[end];
                        remaining = remaining.Substring(end + 1);
@@ -225,5 +234,15 @@ namespace Xamarin.Forms.Xaml
 
                        return piece.ToString();
                }
+
+               protected static (string, string) ParseName(string name)
+               {
+                       var split = name.Split(':');
+
+                       if (split.Length > 2)
+                               throw new ArgumentException();
+
+                       return split.Length == 2 ? (split[0], split[1]) : ("", split[0]);
+               }
        }
 }
\ No newline at end of file
index 086c12b..ef203b2 100644 (file)
@@ -42,14 +42,17 @@ namespace Xamarin.Forms.Xaml
                        if (remaining == "}")
                                return markupExtension.ProvideValue(serviceProvider);
 
-                       string piece;
-                       while ((piece = GetNextPiece(ref remaining, out char next)) != null)
-                               HandleProperty(piece, serviceProvider, ref remaining, next != '=');
+                       Property value;
+                       do {
+                               value = ParseProperty(serviceProvider, ref remaining);
+                               SetPropertyValue(value.name, value.strValue, value.value, serviceProvider);
+                       }
+                       while (!value.last);
 
                        return markupExtension.ProvideValue(serviceProvider);
                }
 
-               protected override void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
+               private void SetPropertyValue(string prop, string strValue, object value, IServiceProvider serviceProvider)
                {
                        MethodInfo setter;
                        if (prop == null) {
index 042c0d2..5bd7cfc 100644 (file)
@@ -92,7 +92,7 @@ namespace Xamarin.Forms.Xaml
 
                                        var rootnode = new RuntimeRootNode(new XmlType(reader.NamespaceURI, reader.Name, null), view, (IXmlNamespaceResolver)reader) { LineNumber = ((IXmlLineInfo)reader).LineNumber, LinePosition = ((IXmlLineInfo)reader).LinePosition };
                                        if (XamlFilePathAttribute.GetFilePathForObject(view) is string path) {
-                                               VisualDiagnostics.RegisterSourceInfo(view, new Uri(path, UriKind.Relative), ((IXmlLineInfo)rootnode).LineNumber, ((IXmlLineInfo)rootnode).LinePosition);
+                                               VisualDiagnostics.RegisterSourceInfo(view, new Uri($"{path};assembly={view.GetType().GetTypeInfo().Assembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)rootnode).LineNumber, ((IXmlLineInfo)rootnode).LinePosition);
                                                VisualDiagnostics.SendVisualTreeChanged(null, view);
                                        }
                                        XamlParser.ParseXaml(rootnode, reader);
@@ -143,7 +143,7 @@ namespace Xamarin.Forms.Xaml
                                        inflatedView = rootnode.Root = visitorContext.Values[rootnode];
                                        if (XamlFilePathAttribute.GetFilePathForObject(inflatedView) is string path)
                                        {
-                                               VisualDiagnostics.RegisterSourceInfo(inflatedView, new Uri(path, UriKind.Relative), ((IXmlLineInfo)rootnode).LineNumber, ((IXmlLineInfo)rootnode).LinePosition);
+                                               VisualDiagnostics.RegisterSourceInfo(inflatedView, new Uri($"{path};assembly={inflatedView.GetType().GetTypeInfo().Assembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)rootnode).LineNumber, ((IXmlLineInfo)rootnode).LinePosition);
                                                VisualDiagnostics.SendVisualTreeChanged(null, inflatedView);
                                        }
                                        visitorContext.RootElement = inflatedView as BindableObject;
index 73fb60a..5fbed18 100644 (file)
@@ -219,58 +219,15 @@ namespace Xamarin.Forms.Xaml
                                var namespaceUri = reader.NamespaceURI;
                                if (reader.LocalName.Contains(".") && namespaceUri == "")
                                        namespaceUri = ((IXmlNamespaceResolver)reader).LookupNamespace("");
-                               var propertyName = new XmlName(namespaceUri, reader.LocalName);
+                               var propertyName = ParsePropertyName(new XmlName(namespaceUri, reader.LocalName));
 
-                               object value = reader.Value;
+                               if (propertyName.NamespaceURI == null && propertyName.LocalName == null)
+                                       continue;
 
-                               if (reader.NamespaceURI == X2006Uri)
-                               {
-                                       switch (reader.LocalName) {
-                                       case "Key":
-                                               propertyName = XmlName.xKey;
-                                               break;
-                                       case "Name":
-                                               propertyName = XmlName.xName;
-                                               break;
-                                       case "Class":
-                                       case "FieldModifier":
-                                               continue;
-                                       default:
-                                               Debug.WriteLine("Unhandled attribute {0}", reader.Name);
-                                               continue;
-                                       }
-                }
+                               object value = reader.Value;
 
-                               if (reader.NamespaceURI == X2009Uri)
-                               {
-                                       switch (reader.LocalName) {
-                                       case "Key":
-                                               propertyName = XmlName.xKey;
-                                               break;
-                                       case "Name":
-                                               propertyName = XmlName.xName;
-                                               break;
-                                       case "TypeArguments":
-                                               propertyName = XmlName.xTypeArguments;
-                                               value = TypeArgumentsParser.ParseExpression((string)value, (IXmlNamespaceResolver)reader, (IXmlLineInfo)reader);
-                                               break;
-                                       case "DataType":
-                                               propertyName = XmlName.xDataType;
-                                               break;
-                                       case "Class":
-                                       case "FieldModifier":
-                                               continue;
-                                       case "FactoryMethod":
-                                               propertyName = XmlName.xFactoryMethod;
-                                               break;
-                                       case "Arguments":
-                                               propertyName = XmlName.xArguments;
-                                               break;
-                                       default:
-                                               Debug.WriteLine("Unhandled attribute {0}", reader.Name);
-                                               continue;
-                                       }
-                               }
+                               if (propertyName == XmlName.xTypeArguments)
+                                       value = TypeArgumentsParser.ParseExpression((string)value, (IXmlNamespaceResolver)reader, (IXmlLineInfo)reader);
 
                                var propertyNode = GetValueNode(value, reader);
                                attributes.Add(new KeyValuePair<XmlName, INode>(propertyName, propertyNode));
@@ -279,6 +236,51 @@ namespace Xamarin.Forms.Xaml
                        return attributes;
                }
 
+               public static XmlName ParsePropertyName(XmlName name)
+               {
+                       if (name.NamespaceURI == X2006Uri)
+                       {
+                               switch (name.LocalName) {
+                               case "Key":
+                                       return XmlName.xKey;
+                               case "Name":
+                                       return XmlName.xName;
+                               case "Class":
+                               case "FieldModifier":
+                                       return new XmlName(null, null);
+                               default:
+                                       Debug.WriteLine("Unhandled attribute {0}", name);
+                                       return new XmlName(null, null);
+                               }
+            }
+
+                       if (name.NamespaceURI == X2009Uri)
+                       {
+                               switch (name.LocalName) {
+                               case "Key":
+                                       return XmlName.xKey;
+                               case "Name":
+                                       return XmlName.xName;
+                               case "TypeArguments":
+                                       return XmlName.xTypeArguments;
+                               case "DataType":
+                                       return XmlName.xDataType;
+                               case "Class":
+                               case "FieldModifier":
+                                       return new XmlName(null, null);
+                               case "FactoryMethod":
+                                       return XmlName.xFactoryMethod;
+                               case "Arguments":
+                                       return XmlName.xArguments;
+                               default:
+                                       Debug.WriteLine("Unhandled attribute {0}", name);
+                                       return new XmlName(null, null);
+                               }
+                       }
+
+                       return name;
+               }
+
                static IList<string> PrefixesToIgnore(IList<KeyValuePair<string, string>> xmlns)
                {
                        var prefixes = new List<string>();
index 8490857..9eed91d 100644 (file)
@@ -196,6 +196,13 @@ namespace Xamarin.Forms.Xaml.Internals
                        return exception == null;
                }
 
+               internal bool TryResolve(XmlType xmlType, out Type type)
+               {
+                       XamlParseException exception;
+                       type = getTypeFromXmlName(xmlType, null, currentAssembly, out exception);
+                       return exception == null;
+               }
+
                Type Resolve(string qualifiedTypeName, IServiceProvider serviceProvider, out XamlParseException exception)
                {
                        exception = null;