* [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)
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)
? 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
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;
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);
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;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Linq;
using System.Threading.Tasks;
namespace Xamarin.Forms.Internals
Title = title;
Cancel = cancel;
Destruction = destruction;
- Buttons = buttons;
+ Buttons = buttons?.Where(c => c != null);
Result = new TaskCompletionSource<string>();
}
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,
SynchronizedCollections.Add(collection, new CollectionSynchronizationContext(context, callback));
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected void ThrowIfApplied()
{
if (IsApplied)
-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;
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>();
for (var i = 0; i < _parts.Count; i++)
{
part = _parts[i];
- bool isLast = i + 1 == _parts.Count;
if (!part.IsSelf && current != null)
{
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);
}
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
}
}
- 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();
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;
}
}
}
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;
}
}
}
+ 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)
{
}
#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
}
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))
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)
--- /dev/null
+using System;
+using System.IO;
+
+namespace Xamarin.Forms
+{
+ public class EmbeddedFont
+ {
+ public string FontName { get; set; }
+ public Stream ResourceStream { get; set; }
+ }
+}
--- /dev/null
+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; }
+ }
+}
--- /dev/null
+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();
+ }
+ }
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+using System;
+namespace Xamarin.Forms
+{
+ public interface IEmbeddedFontLoader
+ {
+ (bool success, string filePath) LoadFont(EmbeddedFont font);
+ }
+}
using System.Collections;
-using System.Collections.Generic;
using System.Collections.Specialized;
-using System.Runtime.CompilerServices;
using Xamarin.Forms.Platform;
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);
}
Count = count;
}
-
}
}
\ No newline at end of file
return;
}
- _associatedObjects.RemoveAll(t => !t.IsAlive);
+ _associatedObjects.RemoveAll(t => t == null || !t.IsAlive);
_cleanupThreshold = _associatedObjects.Count + CleanupTrigger;
}
}
{
int _masterDetails;
Page _target;
+ ToolBarItemComparer _toolBarItemComparer;
+ public ToolbarTracker()
+ {
+ _toolBarItemComparer = new ToolBarItemComparer();
+ }
public IEnumerable<Page> AdditionalTargets { get; set; }
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;
}
}
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);
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
if (_styleClass == value)
return;
- if (_styleClass != null && _classStyles != null)
+ if (_styleClass != null && _classStyleProperties != null)
foreach (var classStyleProperty in _classStyleProperties)
Target.RemoveDynamicResource(classStyleProperty);
_classStyleProperties.Add (classStyleProperty);
Target.OnSetDynamicResource (classStyleProperty, Forms.Style.StyleClassPrefix + styleClass);
}
+
+ //reapply the css stylesheets
+ if (Target is Element targetelement)
+ targetelement.ApplyStyleSheetsOnParentSet();
}
}
}
[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);
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);
bindable.SetValue(TitleViewProperty, value);
}
+ public static void SetIconColor(BindableObject bindable, Color value)
+ {
+ bindable.SetValue(IconColorProperty, value);
+ }
+
protected override bool OnBackButtonPressed()
{
if (CurrentPage.SendBackButtonPressed())
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
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;
}
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)
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)
//typeof(ExportRendererAttribute);
//typeof(ExportCellAttribute);
//typeof(ExportImageSourceHandlerAttribute);
+ //TODO this is no longer used?
public static void RegisterRenderers(HandlerAttribute[] attributes)
{
var length = attributes.Length;
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);
-namespace Xamarin.Forms
+using System;
+
+namespace Xamarin.Forms
{
public interface IShellContentController : IElementController
{
void RecyclePage(Page page);
Page Page { get; }
+ event EventHandler IsPageVisibleChanged;
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
using System.Threading.Tasks;
namespace Xamarin.Forms
bool RemoveFlyoutBehaviorObserver(IFlyoutBehaviorObserver observer);
void UpdateCurrentState(ShellNavigationSource source);
+
+ ReadOnlyCollection<ShellItem> GetItems();
+
+ event NotifyCollectionChangedEventHandler ItemsCollectionChanged;
}
}
\ No newline at end of file
-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
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
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
+}
using System.Collections.Generic;
using System.ComponentModel;
using Xamarin.Forms.Internals;
-using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
namespace Xamarin.Forms
{
internal NavigableElement()
{
Navigation = new NavigationProxy();
-
- if (OptionalFeatureValues.UseStyle)
- {
- _mergedStyle = new MergedStyle(GetType(), this);
- }
+ _mergedStyle = new MergedStyle(GetType(), this);
}
public INavigation Navigation {
public Style Style {
get { return (Style)GetValue(StyleProperty); }
- set {
- if (OptionalFeatureValues.UseStyle)
- SetValue(StyleProperty, value);
- }
+ set { SetValue(StyleProperty, value); }
}
[TypeConverter(typeof(ListStringTypeConverter))]
[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)]
--- /dev/null
+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
+ }
+}
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
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);
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);
View IShellController.FlyoutHeader => FlyoutHeaderView;
+ IShellController ShellController => this;
+
void IShellController.AddAppearanceObserver(IAppearanceObserver observer, Element pivot)
{
_appearanceObservers.Add((observer, pivot));
// 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());
}
}
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)
{
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);
}
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;
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);
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)
{
if (shellSection.CurrentItem != shellContent)
{
shellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, shellContent);
+ navigatedToNewShellElement = true;
}
}
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);
});
}
}
//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;
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>();
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]));
+ }
+ }
+ }
}
}
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
}
}
- 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)
{
}
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);
}
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
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)
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);
}
}
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++)
{
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++)
{
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++)
{
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);
}
if (ModalStack.Count == 0)
_shell.CurrentItem.SendDisappearing();
- modal.SendAppearing();
+ if(!_shell.CurrentItem.CurrentItem.IsPushingModalStack)
+ modal.SendAppearing();
+
return base.OnPushModal(modal, animated);
}
}
#endif
using System.Reflection;
+using System.Runtime.CompilerServices;
using Xamarin.Forms.Internals;
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;
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;
((ShellSection)Parent).UpdateDisplayedPage();
}
}
-
+
public static implicit operator ShellContent(TemplatedPage page)
{
var shellContent = new ShellContent();
-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;
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);
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;
}
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)
#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);
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
public ShellItem()
{
- ((INotifyCollectionChanged)Items).CollectionChanged += ItemsCollectionChanged;
+ ShellItemController.ItemsCollectionChanged += (_, __) => SendStructureChanged();
+ (Items as INotifyCollectionChanged).CollectionChanged += ItemsCollectionChanged;
+
_platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<ShellItem>>(() => new PlatformConfigurationRegistry<ShellItem>(this));
}
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]);
}
}
foreach (Element element in e.OldItems)
OnChildRemoved(element);
}
-
- SendStructureChanged();
}
internal override void SendAppearing()
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]
{
)
{
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;
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);
#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>();
{
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;
ShellContent shellContent = request.Request.Content;
if (shellContent == null)
- shellContent = Items[0];
+ shellContent = ShellSectionController.GetItems()[0];
if (request.Request.GlobalRoutes.Count > 0)
{
_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)
SendUpdateCurrentState(ShellNavigationSource.Pop);
}
+ ReadOnlyCollection<ShellContent> IShellSectionController.GetItems() => ((ShellContentCollection)Items).VisibleItems;
+
+ [Obsolete]
+ [EditorBrowsable(EditorBrowsableState.Never)]
void IShellSectionController.SendPopping(Page page)
{
if (_navStack.Count <= 1)
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
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); }
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;
}
}
else
{
IShellContentController currentItem = CurrentItem;
- if (currentItem.Page != null)
- DisplayedPage = currentItem.Page;
+ DisplayedPage = currentItem?.Page;
}
if (previousPage != DisplayedPage)
{
+ previousPage?.SendDisappearing();
PresentedPageAppearing();
SendAppearanceChanged();
}
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)
base.OnChildRemoved(child);
if (CurrentItem == child)
{
- if (Items.Count == 0)
+ var items = ShellSectionController.GetItems();
+ if (items.Count == 0)
ClearValue(CurrentItemProperty);
else
{
Device.BeginInvokeOnMainThread(() =>
{
if (CurrentItem == null)
- SetValueFromRenderer(CurrentItemProperty, Items[0]);
+ SetValueFromRenderer(CurrentItemProperty, items[0]);
});
}
}
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))
protected override Task<Page> OnPopModal(bool animated)
{
- if (ModalStack.Count > 0)
- ModalStack[ModalStack.Count - 1].SendDisappearing();
-
if(ModalStack.Count == 1)
{
_owner.PresentedPageAppearing();
return base.OnPopModal(animated);
}
+
protected override Task OnPushModal(Page modal, bool animated)
{
if (ModalStack.Count == 0)
_owner.PresentedPageDisappearing();
}
- modal.SendAppearing();
return base.OnPushModal(modal, animated);
}
}
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]
{
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);
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];
{
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();
protected override void LayoutChildren(double x, double y, double width, double height)
{
- if (!HasVisibileChildren())
+ if (!HasVisibleChildren())
{
return;
}
[EditorBrowsable(EditorBrowsableState.Never)]
protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint)
{
- if (!HasVisibileChildren())
+ if (!HasVisibleChildren())
{
return new SizeRequest();
}
}
}
- bool HasVisibileChildren()
+ bool HasVisibleChildren()
{
for (var index = 0; index < InternalChildren.Count; index++)
{
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;
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;
Maximum = max;
}
- Value = val.Clamp(min, max);
+ StepperPosition = (int)((val - min) / increment);
Increment = increment;
+ Value = val.Clamp(min, max);
}
public double Increment
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
+}
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>>();
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;
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)
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)
<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>
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";
// 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;
// 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
--- /dev/null
+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
--- /dev/null
+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;
+ }
+ }
+}
+using System.Threading.Tasks;
using EImage = ElmSharp.Image;
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;
}
}
Elementary.Initialize();
Elementary.ThemeOverlay();
+ Utility.AppendGlobalFontPath(@"/usr/share/fonts");
Device.PlatformServices = new TizenPlatformServices();
if (Device.info != null)
typeof(ExportRendererAttribute),
typeof(ExportImageSourceHandlerAttribute),
typeof(ExportCellAttribute),
- typeof(ExportHandlerAttribute)
+ typeof(ExportHandlerAttribute),
+ typeof(ExportFontAttribute)
});
}
}
typeof(ExportRendererAttribute),
typeof(ExportImageSourceHandlerAttribute),
typeof(ExportCellAttribute),
- typeof(ExportHandlerAttribute)
+ typeof(ExportHandlerAttribute),
+ typeof(ExportFontAttribute)
});
}
}
typeof(ExportRendererAttribute),
typeof(ExportImageSourceHandlerAttribute),
typeof(ExportCellAttribute),
- typeof(ExportHandlerAttribute)
+ typeof(ExportHandlerAttribute),
+ typeof(ExportFontAttribute)
});
}
}
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();
+ }
+ }
}
}
/// </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>
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")]
EvasObject GetRootNativeView();
bool HasAlpha { get; set; }
event EventHandler<RootNativeViewChangedEventArgs> RootNativeViewChanged;
+ bool PageIsChildOfPlatform(Page page);
}
public class RootNativeViewChangedEventArgs : EventArgs
{
NavigationModel _navModel = new NavigationModel();
bool _disposed;
- Native.Dialog _pageBusyDialog;
- int _pageBusyCount;
readonly Naviframe _internalNaviframe;
+ readonly PopupManager _popupManager;
readonly HashSet<EvasObject> _alerts = new HashSet<EvasObject>();
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)
{
_internalNaviframe.SetWeight(1.0, 1.0);
_internalNaviframe.Show();
_internalNaviframe.AnimationFinished += NaviAnimationFinished;
+
+ if (Forms.UseMessagingCenter)
+ {
+ _popupManager = new PopupManager(this);
+ }
}
~DefaultPlatform()
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();
}
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("&", "&").Replace("<", "<").Replace(">", ">").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);
--- /dev/null
+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("&", "&").Replace("<", "<").Replace(">", ">").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
[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))]
{
if (Control is IButton ib)
{
- ib.FontFamily = Element.FontFamily;
+ ib.FontFamily = Element.FontFamily.ToNativeFontFamily();
}
}
void UpdateFontFamily()
{
- Control.FontFamily = Element.FontFamily;
+ Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
}
void UpdateFontAttributes()
void UpdateFontFamily()
{
- Control.FontFamily = Element.FontFamily;
+ Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
}
void UpdateFontAttributes()
{
if (Control is IEntry ie)
{
- ie.FontFamily = Element.FontFamily;
+ ie.FontFamily = Element.FontFamily.ToNativeFontFamily();
}
}
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;
RegisterPropertyHandler(Image.SourceProperty, UpdateSource);
RegisterPropertyHandler(Image.AspectProperty, UpdateAspect);
RegisterPropertyHandler(Image.IsOpaqueProperty, UpdateIsOpaque);
+ RegisterPropertyHandler(Image.IsAnimationPlayingProperty, UpdateIsAnimationPlaying);
RegisterPropertyHandler(Specific.BlendColorProperty, UpdateBlendColor);
RegisterPropertyHandler(Specific.FileProperty, UpdateFile);
}
{
UpdateIsOpaque(initialize);
UpdateBlendColor(initialize);
+ UpdateIsAnimationPlaying(initialize);
}
void UpdateAspect(bool initialize)
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)
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)
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)
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)
Control.FontSize = Element.FontSize;
Control.FontAttributes = Element.FontAttributes;
- Control.FontFamily = Element.FontFamily;
+ Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
Control.BatchCommit();
}
/// </summary>
void FontFamilyPropertyHandler()
{
- Control.FontFamily = Element.FontFamily;
+ Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
}
/// <summary>
void UpdateFontFamily()
{
- Control.FontFamily = Element.FontFamily;
+ Control.FontFamily = Element.FontFamily.ToNativeFontFamily();
}
void UpdateFontAttributes()
using System;
-using System.Collections;
using System.Collections.Generic;
using ElmSharp;
using EColor = ElmSharp.Color;
-using Xamarin.Forms;
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;
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();
};
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);
+ }
}
}
{
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();
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,
};
_menu.Show();
- PackEnd(_menu);
+ _box.PackEnd(_menu);
_defaultClass = new GenItemClass("double_label")
{
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;
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)
{
_title.Show();
BackgroundColor = _backgroudColor;
- PackEnd(_menu);
+ _menuButton.BackgroundColor = _backgroudColor;
+ PackEnd(_menuButton);
PackEnd(_title);
LayoutUpdated += OnLayoutUpdated;
}
set
{
_backgroudColor = value;
+ _menuButton.BackgroundColor = _backgroudColor;
base.BackgroundColor = _backgroudColor;
}
}
set
{
_foregroudColor = value;
- _menu.Color = value;
+ _menuButton.Color = value;
}
}
}
}
- 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)
}
else if (_hasBackButton)
{
- _shellSectionNavigation.PopRequest(this, new Internals.NavigationRequestedEventArgs(_page, false));
+ Shell.Current.CurrentItem.Navigation.PopAsync();
}
else
{
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)
{
using System;
-using System.Collections.Generic;
using ElmSharp;
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);
}
_navigationView.BackgroundColor = Element.FlyoutBackgroundColor.ToNative();
}
+ void UpdateFlyoutBackgroundImageAspect()
+ {
+ _navigationView.BackgroundImageAspect = Element.FlyoutBackgroundImageAspect;
+ }
+
+ void UpdateFlyoutBackgroundImage()
+ {
+ _navigationView.BackgroundImageSource = Element.FlyoutBackgroundImage;
+ }
+
void UpdateFlyoutIsPresented()
{
_native.IsOpen = Element.FlyoutIsPresented;
_section.PropertyChanged += OnSectionPropertyChanged;
_rootPage = ((IShellContentController)_section.CurrentItem).GetOrCreateContent();
- _navBar = new ShellNavBar(flyoutController, this);
+ _navBar = new ShellNavBar(flyoutController);
_navBar.Show();
var renderer = CreateShellSection(section);
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());
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>();
//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;
}
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);
}
}
- 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;
}
}
}
{
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)
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;
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);
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.
}
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);
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
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) {
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);
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;
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));
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>();
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;