* [Android,iOS,UWP,WPF] MediaElement control (#3482)
* MediaElement and iOS/Android/UWP renderers.
* Added basic WPF renderer. Fixed sizing issue on UWP renderer. Added sample page to ControlGallery app
* MediaEnded event on UWP. Source Uri convert ms-appx to pack:// uri scheme on WPF
* code style fixes
* resolving conflicts
* Following discussion on PR made NaturalDuration a nullable TimeSpan so that can determine when value is not present.
Added CanSeek property to indicate when Position property can be set.
Added support for KeepScreenOn to WPF renderer.
Improved ControlGallery page with separate Play/Pause/Stop buttons and label to display current state
* coding style fixes
* coding style fixes 2
* Improved media controls on demo page. Renderer enhancements for Android and iOS
* Improved logic around media metadata in iOS renderer.
Switched to https video uri to allow playback in iOS. Increased media control button sizes
* Improved parsing of ms-appdata uris supporting both the local and temp folders
* Removed redundant Debug.WriteLines. Removed XML docs from MediaElement and MediaElementState types.
* Bug fix for iOS MediaElement.Source Uri escape handling
* Adding MediaElement fast renderer for Android
* Rearchitecting the Element/Renderer connectivity. New WPF,UAP and Android FastRenderer complete. Still requires Android classic renderer and iOS renderer...
* More MediaElement renderer development. Converted iOS to a FastRenderer
* Latest android renderer
* Android renderer fix for StopPlayback reusability. MetadataRetreiver now loaded async.
* Added an IMediaElementController property to Android renderers to simplify code.
* Simplified Aspect property
* Changed AreTransportControlsEnabled to ShowsPlaybackControls. Removed commented-out code.
* merging
* merging
* added mediaelementrenderer back
* support for polling the position property. Added timer to demopage to poll for position.
* Fixed scaling issue on iOS and background color
* Renderer code review issues
* Implementing MediaSource (ImageSource pattern) to replace Uris. Other code cleaning fixes.
* Hooked up MediaSource and kept uri custom schema logic
* Moved all the "callback" methods to the IMediaElementController interface so they are not publicly exposed on the MediaElement. Removed HttpHeaders and associated code for v1 release. Other code tidying.
* Code review feedback
* Integrated unit tests. Added support for inherited binding on MediaSource.
* Additional Windows file path test. Fix for UWP renderer to check if Control is released
* Fix for FormsVideoView metadata causing aspect issues. Simplified Android FastRenderer to correctly handle VideoView lifetime.
* fixed merge error
* fixed merge errors
* MediaElement and iOS/Android/UWP renderers.
* Added basic WPF renderer. Fixed sizing issue on UWP renderer. Added sample page to ControlGallery app
* MediaEnded event on UWP. Source Uri convert ms-appx to pack:// uri scheme on WPF
* code style fixes
* resolving conflicts
* Following discussion on PR made NaturalDuration a nullable TimeSpan so that can determine when value is not present.
Added CanSeek property to indicate when Position property can be set.
Added support for KeepScreenOn to WPF renderer.
Improved ControlGallery page with separate Play/Pause/Stop buttons and label to display current state
* coding style fixes
* coding style fixes 2
* Improved media controls on demo page. Renderer enhancements for Android and iOS
* Improved logic around media metadata in iOS renderer.
Switched to https video uri to allow playback in iOS. Increased media control button sizes
* Improved parsing of ms-appdata uris supporting both the local and temp folders
* Removed redundant Debug.WriteLines. Removed XML docs from MediaElement and MediaElementState types.
* Bug fix for iOS MediaElement.Source Uri escape handling
* Adding MediaElement fast renderer for Android
* Rearchitecting the Element/Renderer connectivity. New WPF,UAP and Android FastRenderer complete. Still requires Android classic renderer and iOS renderer...
* More MediaElement renderer development. Converted iOS to a FastRenderer
* Latest android renderer
* Android renderer fix for StopPlayback reusability. MetadataRetreiver now loaded async.
* Added an IMediaElementController property to Android renderers to simplify code.
* Simplified Aspect property
* Changed AreTransportControlsEnabled to ShowsPlaybackControls. Removed commented-out code.
* merging
* support for polling the position property. Added timer to demopage to poll for position.
* Fixed scaling issue on iOS and background color
* Renderer code review issues
* Implementing MediaSource (ImageSource pattern) to replace Uris. Other code cleaning fixes.
* Hooked up MediaSource and kept uri custom schema logic
* Moved all the "callback" methods to the IMediaElementController interface so they are not publicly exposed on the MediaElement. Removed HttpHeaders and associated code for v1 release. Other code tidying.
* Code review feedback
* Integrated unit tests. Added support for inherited binding on MediaSource.
* Additional Windows file path test. Fix for UWP renderer to check if Control is released
* Fix for FormsVideoView metadata causing aspect issues. Simplified Android FastRenderer to correctly handle VideoView lifetime.
* fixed merge error
* manual merge fixes
* Fixed missing FileMediaSourceConverter
* Fixed merge issue with iOS project file
* Clarification to WPF Source Uri exception message
* Changed iOS local path to the Library path as this is not user visible or synchronised with iCloud. Fix for breaking change in WeakEventManager
* Added Volume property
* Fixes for code-review feedback
* Optimised iOS renderer to remove a view layer
* fixes to resolve possible rotation bug
* Moved ms-appdata parsing to shared location.
* fixed codesign error
* Fix for issue on Android where control may show black screen prior to video playing - now respects backgroundcolor property
* Renamed conflicting extensions class
* Add experimental flag; remove slow renderer
* Update MediaElementDemoPage.cs
* Update MediaElementRenderer.cs
* Update MediaElementRenderer.cs
* Porting latest renderer updates. iOS re-written as a non-fast renderer to workaround issue with dispose when using on modal pages.
* Fixed protected member in sealed class issue
Co-authored-by: Rui Marinho <me@ruimarinho.net>
Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
fixes #1692
* propagate bindingcontext to ItemsLayout (#9221)
* [Android] Fix sizing and position on Android of the Indicators (#9343) fixes #8911 fixes #8934
* [Android] Center indicators by default
* [Core] Add an extra point to fix size issues on Android 10
* Update IndicatorView.cs
* Adds Cookies to the WebView fixing #Issue3262 (#8169)
* New branch of Master so that I can re-build the issue and fix by cherry-picking the files as needed. This is just Master with the issue3262.cs added at the moment...
* Added the UnitTest to the WebViewUnitTest.cs it currently fails...
* Added the BindableProperty to the WebView.cs in the XF core...
* Firsly forgot to add the Issue3262.cs to the project (Oppsie!) but this commit is for the Droid platform implementation...
* Adding the IOS platform renderer implementation...
* Adding the UWP platform renderer implementation...
* Added Cookies.Count null check as it was missing and caused a NullRef Exception... Need to check the other renderers...
* Added Cookies.Count null check as it was missing and could cause a NullRef Exception...
Co-authored-by: Rui Marinho <me@ruimarinho.net>
* Allow multiple selection updates with one notification in CollectionView (#8737) fixes #8203
* Repro and UI test
* Add method to allow renderers to update multiple selections with one changed notification;
Fixes #8203
* [Android,iOS] Add Visual States to CarouselView items (#8785) fixes #7904 fixes #7997 fixes #8671
* [iOS] Add CarouselView states
* [iOS] Update method naming UpdateVisualStates
* [Android] Add Visual states to CarouselView items
* Update ExampleTemplateCarousel.xaml
* [Android] Implement Visual states for carousel items
* [Android] Cleanup on CarouselViewRenderer
* [Android] Add default state to view
* [Android] Fix when we update vsm
* [Android] Rollback change on adapter
* Update CarouselViewRenderer.cs
* [Android] Update States also when ScrollTo is requested
* [Android] Fix Teardown CarouselViewRenderer
* working-fix7904
* Cleanup
* Update App.cs
* [iOS,Android] Set initial position
* [Controls] Update sample Carousel
* [Core] Update current position on scroll
* [Controls] Cleanup
* [Core] Don't update position if we are scrolling to
* [Android]Rename ScrollsToAction
* [ANdroid] Remove extra ScrollTo
* Update CarouselViewController.cs
* Cleanup
* [Android] Fix initilization Android CarouselView
* [iOS,Core] Add VisibleViews to CarouselView iOS
* [Android] Populate VisibleViews
* [Core,iOS,Droid] Move to a mroe explicit name for platform initialized
* [Core] Remove not used VisualState
* [Android] Fix scrollto when layout loads
* [Controls] Fix Carousel example gallery
* [Android]Better fix for ScrollTo when loading the RecyclerView
* [Android] Null check for eventhandler
* [iOS] Update if any ScrollToaActions exist
* [iOS, Android] Fix spelling CarouselViewRenderer
* [Android] Rename CarouselViewwOnGlobalLayoutListener
* Allow to create more easily SwipeItems (#8757) fixes #8753
* Added new constructors to SwipeItems to allow to create more easily using CSharp
* Fixed build error
* Changed SwipeItems constructor
* Removed SwipeItem parameter constructor
Renamed internal SwipeItems collection
* Chain the ctors
Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
* Fix UWP Nuspec to copy correct NS version (#9388)
* fix ns references
* disable layout files
* - preserve
* - 2017 workarounds
* - winui
* Implemented BindableLayout EmptyView (#7686) fixes #6932
* Update BindableLayoutGalleryPage.xaml
* Added demo
* Account for null and added tests
* Big cleanup
* Update BindableLayout.cs
* Update BindableLayoutGalleryPage.xaml
Co-authored-by: Rui Marinho <me@ruimarinho.net>
* Fix 8586 8587 - [Tizen] Added guard for CollectionView (#9367) fixes #8586 #8587
* [Tizen] Remove events on unbinding in CollectionView
- Resolves bug #8587
* [Tizen] Guard for sizeless layout requests in CollectionView
- Resolves bug #8586
* Improve animation behavior with regard to power saving mode (#8921)
* Return canceled value when animations are forced to complete because
they were disabled by the system
* Add IsEnabled to check whether animations are available
* Check animation status every time power save mode changes;
Use AreAnimatorsEnabled for newer API versions;
Fixes #8634; Fixes #7500;
* Add missing awaits
* Fix rebase issue
* fix HR on 4.4+ (#9433)
* [Enhancement] AdaptiveTriggers (#9422)
* Initial AdaptiveTriggers commit
* Updated adaptive triggers implementation
* Fixed AdaptiveTrigger conflicts issue
* Improved OrientationStateTrigger
* Updated AdaptiveTrigger sample
* Improved StateTriggers conflicts resolution
Added CompareStateTrigger
Propagate BindingContext from parent VisualElement to StateTriggers
* Propagate BindingContext correctly to StateTriggers.
* Added StateTriggers unit tests
* Fixed broken unit tests.
* Added more StateTriggers unit tests
* Added more AdaptiveTriggers samples
* Fixed wrong line break
* Moved PropagateBindingContextToStateTriggers from View to VisualElement
* Added DualScreenStateTrigger
* Changes based on PR feedback (modify AdaptiveTrigger class to be sealed)
* Added AdaptiveTriggersExperimental
* Changes in OrientationStateTrigger to use Device Info INPC
* More changes to cover PR feedback
* Updated DualScreenStateTrigger to use TwoPaneViewMode enum instead a bool
* Fixed problem with WeakEventListener, unsubscribe events correctly
* - call update state when attaching
* - IsTriggerActiveChanged
* Update Xamarin.Forms.Core/StateTrigger.cs
* Update Xamarin.Forms.DualScreen/SpanModeStateTrigger.shared.cs
* - isactive simplify
* - update state
* - toggle state tests
* - fix flag on unit tests
Co-authored-by: Shane Neuville <shane94@hotmail.com>
* Adding null check for picker items (#9478) fixes #2674
* Add default fontsize back to size BP (#9506)
- fixes #9371
* [Core] Allow CarouselView to specify IndicatorView (#9332) fixes #8992
* [Core] Add extension point to IndicatorView link to other controls
* [Core] Move ItemSourceBy from attached property to bindable property
* [Core] ItemsSourceBy just takes ItemsView
* [Core] Remane to LinkToItemsView
* [Core] Remove ItemsSourceBY and add to CarouselView IndicatorsView property
* Update Xamarin.Forms.ControlGallery.Android.csproj
* [Core] Don't use BP for IndicatorView reference
* RadioButton Implementation (#8910) fixes #2404 closes #5349
* RadioButton
* Removed unused files
* Rebase and make it run
* First round of feedback
* Revert AppCompatButton -> AButton
* Cleaned minor usings
* Fix unselecting radiobutton on iOS
* Fixed Mac OS grouping
* [Android] Fix API29 usages
Co-authored-by: Andrei Nitescu <nitescua@yahoo.com>
Co-authored-by: Rui Marinho <me@ruimarinho.net>
* Fixed the build issue
* [Android] Properly unsubscribe ToolbarItem on removal (#9485) fixes #9419
* [Android] Properly unsubscribe ToolbarItem on removal.
* New approach.
* Only add item if not present.
* [iOS,Android,Core] Implement MaximumVisible on iOS and Android (#9592) fixes #8912
* [Core] Update MaxVisible usage on IndicatorView
* [Android] Set MaxVisible usage on IndicatorViewRender
* [iOS] Update MaximumVisible count for IndicatorViewRenderer
* [Controls] Add sample for IndicatorMaximumVisible
* [Core] Reset indicators if IndicatorView MaximumVisible changes
* Fix ios url images (#9682)
* Generalize StreamWrapper Image Uri Loading Behavior
* - await stream retrieve opposed to returning task
* - generalize ui test for more platforms
* - fix ui tests
* - disable caching
* [Spec] CSharpForMarkup (#8342)
* - Add MarkupExtensions from https://github.com/VincentH-Net/CSharpForMarkup/blob/
061566282706de22d040e5b7b32bc3c2e3e323e3/src/XamarinFormsMarkupExtensions.cs
- Remove specific converters
- Fix .NETStandard 1 build errors by refactoring IConvertible to Enum generic constraints
- Add RegisterDefaultBindableProperties method
- Reformat source to comply with Forms contibuting guidelines
* - Move markup extensions to Xamarin.Forms.Markup namespace to prevent impact on existing code and to support clear separation of UI markup from UI logic
- Split up source file for better maintainability
* Cleanup:
- Names of generic types and variables to reflect where clauses
- Namespaces remove unused & sort
- Add whitespace
- Remove unused file
* Fix for incorrect type name in exception message
Remove RowCol helpers because compiler cannot infer its generic type parameters. Use Row and Col methods instead
* Add targetNullValue and fallbackValue parameters to binding helpers.
Equivalent to https://github.com/VincentH-Net/CSharpForMarkup/tree/
bd7f99957653f5e813ec805e3dad88aa487b9526
* rename binding helpers sourcePropertyName parameter to path
* Enable Style<T> on BindableObject instead of Element to resolve https://github.com/xamarin/Xamarin.Forms/pull/8342#discussion_r363088124
* Fix for Grid properties Row, Col, RowSpan, ColumnSpan not set when default value is specified https://github.com/VincentH-Net/CSharpForMarkup/issues/7
* Support DefaultBindableProperties for BindableObject (was limited to Element)
* .Font:
- Support any IFontElement (was limited to Button, Label, Entry and Picker)
- Fix for italic ignored when bold == true
* Fix for .FontSize, .Bold, .Italic return Label instead of type derived from Label
* Unit tests batch 1: for LabelExtensions, LayoutExtensions, ViewExtensions
* Add DefaultBindableProperties for more BindableObject types
* Remove .Menu() helper from ViewInFlexLayoutExtensions (bc no relation to FlexLayout)
* Fix for .Row and .Col without span specified still sets span
* Rename MarkupBaseTestFixture.TestPropertySet to TestPropertiesSet
* Add unit tests for ViewInFlexLayoutExtensions, ViewInGridExtensions and VisualElementExtensions
* Add default bindable property for all applicable bindable objects in core.
* Add unit tests for DefaultBindableProperties
* Support Bind, Assign and Invoke on BindableObject (was Element)
Add inline doc to clarify purpose of Bind overloads
* Add unit tests for BindableObjectExtensions
* Fix for attributes not set in .Font
Rename fontSize parameter to size
Enable .FontSize, .Bold and .Italic on IFontElement (was Label)
* Add unit tests for ElementExtensions
* Add unit tests for ElementExtensions
* Add assert of exception message content in GetDefaultBindablePropertyForUnsupportedType
* Add unit tests for EnumsForGridRowsAndColumns
* Add inline doc to clarify purpose of gesture recognizer binding overloads
Rename private constant bindingContextPropertyName to bindingContextPath for clarity
* Add unit tests for Style; cleanup namespaces in Style
* Simplify BindableObjectExtensions implementation (functionality unchanged)
* Add IGestureRecognizers to GestureElement and View, to avoid code duplication when accessing the existing GestureRecognizers property
* - Add specific helpers for more built-in gesture recognizers
- Add helpers to support initializing gesture recognizers
- Eliminate duplicate code for View and GestureElement by using new IGestureRecognizers
- Simplify implementation
- Rename ViewGesturesExtensions to ElementGesturesExtensions
* Extract BindingHelpers from BindableObjectExtensionsTests
* Add unit tests for ElementGesturesExtensions
* Add support for typed converter parameters
Add asserts for conversion values in unit tests
* Add unit tests for FuncConverter
* Add missing calls to Bind overloads in unit test for supporting derived types in BindableObject extensions fluent API
* Cleanup: remove unneeded Xamarin.Forms. prefix from static class references
* Add unit tests for PlatformSpecificsExtensions
* Reorder FuncConverter type params to optimize for most common usage and to remain backwards compatible with https://github.com/VincentH-Net/CSharpForMarkup
* Extend Bind*Gesture helpers to support all possible binding parameters
* Rename EnumsForGridRowsAndColumns to GridRowColEnums
* Add BindCommand helper to bind to default Command + CommandParameter properties
Add RegisterForCommand to register default Command + CommandParameter properties for custom bindable objects
* - Change Bind*Gesture helper parameters to match BindCommand parameters, to enable binding to Command + CommandParameter in a single compact call (removed parameters did not make sense for a command).
- Add inline doc to more ElementGesturesExtensions helpers where needed
- Improve ElementGesturesExtensions source formattting for consistency and readability
* Remove PlatformSpecificsExtensions
(only 2 iOS-only helpers; replace with guidance in PR description on how to use any platform specific with .Invoke)
* Fix inline doc spelling: it's -> its
* Rename TextLeft -> TextStart, TextRight -> TextEnd to support RTL
* Add Columns.Define(width, width ...) and Rows.Define(height, height ...) overloads for Grid
* Rename Grid rows & columns extensions class to GridRowsColumns
* Change gesture recognizer helpers implementation from reuse existing instance to always add new instance
* Change Bind*Gesture helpers default to not bind the commandparameter (is the more common scenario)
* Rename BoolNotConverter to NotConverter and make it a 2-way converter
* Add culture support to FuncConverter
* Rename helpers Left -> Start, Right -> End
Add opt-in LeftToRight and RightToLeft helpers
* Rename Col -> Column throughout
* Rename H -> Horizontal and V -> Vertical throughout
* Add Markup_Experimental flag for all API methods and update all unit tests to run with and without the flag
* Re-add "Markup_Experimental" flag
* Add default bindable properties / test exclusions for new BindableObject types in core
* Add constructorHint to FuncConverter and Style for more clear experimental flag message
* Add default bindable property for RadioButton - fixes test AllBindableElementsInCoreHaveDefaultBindablePropertyOrAreExcluded failure
* [Core,iOS] Invalidate size when IndicatorView collection/count changes (#9672) fixes #9666
* [Controls] Add sample for issue #9666
* [Core] If IndicatorView collection changes invalidate size
* [Tizen] MediaElement control support (#9634)
* [Bug] DisplayPromptAsync overload hurts usability (#9749)
- fixes #9171
* Update PromptArguments.cs
* Update Page.cs
* Revert "Update Page.cs"
This reverts commit
7272b8991a61aa8bfaf23be5b9fec64958c42851.
* Revert "Update PromptArguments.cs"
This reverts commit
5c3e3e8b3da4c64cc1036edfd9fff63c460c9cf7.
* Fix ambiguity
* Fix null SwipeItem Parent in SwipeView (#9743)
- fixes #9738
* Set SwipeItems Parent
* Direct cast bindable object in SwipeView
* Fix null SwipeItem Parent in SwipeView (#9743)
- fixes #9738
* Set SwipeItems Parent
* Direct cast bindable object in SwipeView
* [C] Avoid NRE in Fles.OnMeasure (#9891) fixes #9340
- fixes #9340
We don't have a repro project for that issue, but the fix has been tested by the OP, and it fixes the issue
* Fixed wrong StateTriggersExperimental registration (#9889)
* Create wearable_app_requirement.md
* Update templates from X.F
* Fixed the ambiguous issue
- We do not use the `private` keyword, as it is the default accessibility level in C#.
- We use hard tabs over spaces.
-Read and follow our [Pull Request template](https://github.com/xamarin/Xamarin.Forms/blob/master/PULL_REQUEST_TEMPLATE.md).
+Read and follow our [Pull Request template](PULL_REQUEST_TEMPLATE.md).
### Pull Request Requirements
- We use red-green-refactor test driven development. If you're planning to work on a bug fix, please be sure to create a test case in the UI tests suite (or unit tests, if you're working on Core/XAML code) that proves that the behavior is broken and then proves that the behavior was resolved after your changes. If at all possible, the test should be automated. If the test cannot be automated, then it should include manual testing instructions on screen.
+We use red-green-refactor test driven development. If you're planning to work on a bug fix, please be sure to create a test case in the UI tests suite (or unit tests, if you're working on Core/XAML code) that proves that the behavior is broken and then proves that the behavior was resolved after your changes. If at all possible, the test should be automated. If the test cannot be automated, then it should include manual testing instructions on screen.
+
+Please check the "Allow edits from maintainers" checkbox on your pull request. This allows us to quickly make minor fixes and resolve conflicts for you.
## Proposals/Enhancements/Suggestions
### Reproduction Link
<!-- Please upload or provide a link to a reproduction case -->
+
+### Workaround
+
+<!-- Did you find any workaround for this issue? This can unblock other people while waiting for this issue to be resolved -->
\ No newline at end of file
--- /dev/null
+---
+name: Wearable app requirements
+about: Create a requirement for wearable application
+title: "[UA/S-Health/3rd] YOUR IDEA!"
+labels: REQUIREMENT
+assignees: ''
+
+---
+
+## Summary
+Please provide a brief summary of the requirement. Two to three sentences is best here.
+
+## Intended Use Case
+Provide a detailed example of where it is used and for what purpose.
- If this is an enhancement or contains API changes or breaking changes, target master.
- If the issue you're working on has a milestone, target the corresponding branch.
- If this is a bug fix, target the branch of the latest stable version (unless the bug is only in a prerelease or master, of course!).
- See [Contributing](https://github.com/xamarin/Xamarin.Forms/blob/master/CONTRIBUTING.md) for more tips!
+ See [Contributing](https://github.com/xamarin/Xamarin.Forms/blob/master/.github/CONTRIBUTING.md) for more tips!
PLEASE DELETE THE ALL THESE COMMENTS BEFORE SUBMITTING! THANKS!!!
-->
using MMView = Tizen.Multimedia.MediaView;
-namespace Tizen.Wearable.CircularUI.Forms
+namespace Tizen.Wearable.CircularUI.Forms.Renderer
{
public interface IMediaViewProvider
{
using System.Threading.Tasks;
using Tizen.Multimedia;
+using CircularUI = Tizen.Wearable.CircularUI.Forms;
using Tizen.Wearable.CircularUI.Forms;
using Tizen.Wearable.CircularUI.Forms.Renderer;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Tizen;
-[assembly: ExportHandler(typeof(UriMediaSource), typeof(UriMediaSourceHandler))]
-[assembly: ExportHandler(typeof(FileMediaSource), typeof(FileMediaSourceHandler))]
+[assembly: ExportHandler(typeof(CircularUI.UriMediaSource), typeof(UriMediaSourceHandler))]
+[assembly: ExportHandler(typeof(CircularUI.FileMediaSource), typeof(FileMediaSourceHandler))]
namespace Tizen.Wearable.CircularUI.Forms.Renderer
{
[RenderWith(typeof(ImageButtonRenderer))]
internal class _ImageButtonRenderer { }
+#if __ANDROID__
+ [RenderWith(typeof(RadioButtonRenderer))]
+#elif !TIZEN4_0
+ [RenderWith(typeof(RadioButtonRenderer))]
+#endif
+ internal class _RadioButtonRenderer { }
+
[RenderWith (typeof (TableViewRenderer))]
internal class _TableViewRenderer { }
#endif
internal class _MasterDetailPageRenderer { }
+ [RenderWith (typeof(MediaElementRenderer))]
+ internal class _MediaElementRenderer { }
+
[RenderWith(typeof(RefreshViewRenderer))]
internal class _RefreshViewRenderer { }
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public sealed class AdaptiveTrigger : StateTriggerBase
+ {
+ public AdaptiveTrigger()
+ {
+ UpdateState();
+ }
+
+ public double MinWindowHeight
+ {
+ get => (double)GetValue(MinWindowHeightProperty);
+ set => SetValue(MinWindowHeightProperty, value);
+ }
+
+ public static readonly BindableProperty MinWindowHeightProperty =
+ BindableProperty.Create(nameof(MinWindowHeight), typeof(double), typeof(AdaptiveTrigger), -1d,
+ propertyChanged: OnMinWindowHeightChanged);
+
+ static void OnMinWindowHeightChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((AdaptiveTrigger)bindable).UpdateState();
+ }
+
+ public double MinWindowWidth
+ {
+ get => (double)GetValue(MinWindowWidthProperty);
+ set => SetValue(MinWindowWidthProperty, value);
+ }
+
+ public static readonly BindableProperty MinWindowWidthProperty =
+ BindableProperty.Create(nameof(MinWindowWidthProperty), typeof(double), typeof(AdaptiveTrigger), -1d,
+ propertyChanged: OnMinWindowWidthChanged);
+
+ static void OnMinWindowWidthChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((AdaptiveTrigger)bindable).UpdateState();
+ }
+
+ internal override void OnAttached()
+ {
+ base.OnAttached();
+
+ if (!DesignMode.IsDesignModeEnabled)
+ {
+ UpdateState();
+ Application.Current.MainPage.SizeChanged += OnSizeChanged;
+ }
+ }
+
+ internal override void OnDetached()
+ {
+ base.OnDetached();
+
+ Application.Current.MainPage.SizeChanged -= OnSizeChanged;
+ }
+
+ void OnSizeChanged(object sender, EventArgs e)
+ {
+ UpdateState();
+ }
+
+ void UpdateState()
+ {
+ var scaledScreenSize = Device.Info.ScaledScreenSize;
+
+ var w = scaledScreenSize.Width;
+ var h = scaledScreenSize.Height;
+ var mw = MinWindowWidth;
+ var mh = MinWindowHeight;
+
+ SetActive(w >= mw && h >= mh);
+ }
+ }
+}
\ No newline at end of file
_children.Add(child);
return this;
}
+
+ public static bool IsEnabled
+ {
+ get
+ {
+ return Internals.Ticker.Default.SystemEnabled;
+ }
+ }
}
}
\ No newline at end of file
var repeat = false;
// If the Ticker has been disabled (e.g., by power save mode), then don't repeat the animation
- if (info.Repeat != null && Ticker.Default.SystemEnabled)
+ var animationsEnabled = Ticker.Default.SystemEnabled;
+
+ if (info.Repeat != null && animationsEnabled)
repeat = info.Repeat();
if (!repeat)
tweener.Finished -= HandleTweenerFinished;
}
- info.Finished?.Invoke(tweener.Value, false);
+ info.Finished?.Invoke(tweener.Value, !animationsEnabled);
if (info.Owner.TryGetTarget(out owner))
owner.BatchCommit();
defaultValueCreator: (b) => new BindableLayoutController((Layout<View>)b),
propertyChanged: (b, o, n) => OnControllerChanged(b, (BindableLayoutController)o, (BindableLayoutController)n));
+ public static readonly BindableProperty EmptyViewProperty =
+ BindableProperty.Create("EmptyView", typeof(object), typeof(Layout<View>), null, propertyChanged: (b, o, n) => { GetBindableLayoutController(b).EmptyView = n; });
+
+ public static readonly BindableProperty EmptyViewTemplateProperty =
+ BindableProperty.Create("EmptyViewTemplate", typeof(DataTemplate), typeof(Layout<View>), null, propertyChanged: (b, o, n) => { GetBindableLayoutController(b).EmptyViewTemplate = (DataTemplate)n; });
+
public static void SetItemsSource(BindableObject b, IEnumerable value)
{
b.SetValue(ItemsSourceProperty, value);
return (DataTemplateSelector)b.GetValue(ItemTemplateSelectorProperty);
}
+ public static object GetEmptyView(BindableObject b)
+ {
+ return b.GetValue(EmptyViewProperty);
+ }
+
+ public static void SetEmptyView(BindableObject b, object value)
+ {
+ b.SetValue(EmptyViewProperty, value);
+ }
+
+ public static DataTemplate GetEmptyViewTemplate(BindableObject b)
+ {
+ return (DataTemplate)b.GetValue(EmptyViewTemplateProperty);
+ }
+
+ public static void SetEmptyViewTemplate(BindableObject b, DataTemplate value)
+ {
+ b.SetValue(EmptyViewProperty, value);
+ }
+
static BindableLayoutController GetBindableLayoutController(BindableObject b)
{
return (BindableLayoutController)b.GetValue(BindableLayoutControllerProperty);
newC.ItemsSource = GetItemsSource(b);
newC.ItemTemplate = GetItemTemplate(b);
newC.ItemTemplateSelector = GetItemTemplateSelector(b);
+ newC.EmptyView = GetEmptyView(b);
+ newC.EmptyViewTemplate = GetEmptyViewTemplate(b);
newC.EndBatchUpdate();
}
}
DataTemplate _itemTemplate;
DataTemplateSelector _itemTemplateSelector;
bool _isBatchUpdate;
+ object _emptyView;
+ DataTemplate _emptyViewTemplate;
+ View _currentEmptyView;
public IEnumerable ItemsSource { get => _itemsSource; set => SetItemsSource(value); }
public DataTemplate ItemTemplate { get => _itemTemplate; set => SetItemTemplate(value); }
public DataTemplateSelector ItemTemplateSelector { get => _itemTemplateSelector; set => SetItemTemplateSelector(value); }
+ public object EmptyView { get => _emptyView; set => SetEmptyView(value); }
+ public DataTemplate EmptyViewTemplate { get => _emptyViewTemplate; set => SetEmptyViewTemplate(value); }
public BindableLayoutController(Layout<View> layout)
{
}
}
+ void SetEmptyView(object emptyView)
+ {
+ _emptyView = emptyView;
+
+ _currentEmptyView = CreateEmptyView(_emptyView, _emptyViewTemplate);
+
+ if (!_isBatchUpdate)
+ {
+ CreateChildren();
+ }
+ }
+
+ void SetEmptyViewTemplate(DataTemplate emptyViewTemplate)
+ {
+ _emptyViewTemplate = emptyViewTemplate;
+
+ _currentEmptyView = CreateEmptyView(_emptyView, _emptyViewTemplate);
+
+ if (!_isBatchUpdate)
+ {
+ CreateChildren();
+ }
+ }
+
void CreateChildren()
{
- Layout<View> layout;
- if (!_layoutWeakReference.TryGetTarget(out layout))
+ if (!_layoutWeakReference.TryGetTarget(out Layout<View> layout))
{
return;
}
layout.Children.Clear();
+ UpdateEmptyView(layout);
+
if (_itemsSource == null)
- {
return;
- }
foreach (object item in _itemsSource)
{
}
}
+ void UpdateEmptyView(Layout<View> layout)
+ {
+ if (_currentEmptyView == null)
+ return;
+
+ if (!_itemsSource?.GetEnumerator().MoveNext() ?? true)
+ {
+ layout.Children.Add(_currentEmptyView);
+ return;
+ }
+
+ layout.Children.Remove(_currentEmptyView);
+ }
+
View CreateItemView(object item, Layout<View> layout)
{
return CreateItemView(item, _itemTemplate ?? _itemTemplateSelector?.SelectTemplate(item, layout));
}
else
{
- return new Label() { Text = item?.ToString() };
+ return new Label { Text = item?.ToString(), HorizontalTextAlignment = TextAlignment.Center };
}
}
+ View CreateEmptyView(object emptyView, DataTemplate dataTemplate)
+ {
+ if (!_layoutWeakReference.TryGetTarget(out Layout<View> layout))
+ {
+ return null;
+ }
+
+ if (dataTemplate != null)
+ {
+ var view = (View)dataTemplate.CreateContent();
+ view.BindingContext = layout.BindingContext;
+ return view;
+ }
+
+ if (emptyView is View emptyLayout)
+ {
+ return emptyLayout;
+ }
+
+ return new Label { Text = emptyView?.ToString(), HorizontalTextAlignment = TextAlignment.Center };
+ }
+
void ItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
- Layout<View> layout;
- if (!_layoutWeakReference.TryGetTarget(out layout))
+ if (!_layoutWeakReference.TryGetTarget(out Layout<View> layout))
{
return;
}
insert: (item, index, _) => layout.Children.Insert(index, CreateItemView(item, layout)),
removeAt: (item, index) => layout.Children.RemoveAt(index),
reset: CreateChildren);
+
+ // UpdateEmptyView is called from within CreateChildren, therefor skip it for Reset
+ if (e.Action != NotifyCollectionChangedAction.Reset)
+ UpdateEmptyView(layout);
}
}
}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Globalization;
+
+namespace Xamarin.Forms
+{
+ public sealed class CompareStateTrigger : StateTriggerBase
+ {
+ public CompareStateTrigger()
+ {
+ UpdateState();
+ }
+
+ public object Property
+ {
+ get => GetValue(PropertyProperty);
+ set => SetValue(PropertyProperty, value);
+ }
+
+ public static readonly BindableProperty PropertyProperty =
+ BindableProperty.Create(nameof(Property), typeof(object), typeof(CompareStateTrigger), null,
+ propertyChanged: OnPropertyChanged);
+
+ static void OnPropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((CompareStateTrigger)bindable).UpdateState();
+ }
+
+ public object Value
+ {
+ get => GetValue(ValueProperty);
+ set => SetValue(ValueProperty, value);
+ }
+
+ public static readonly BindableProperty ValueProperty =
+ BindableProperty.Create(nameof(Value), typeof(object), typeof(CompareStateTrigger), null,
+ propertyChanged: OnValueChanged);
+
+ static void OnValueChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((CompareStateTrigger)bindable).UpdateState();
+ }
+
+ internal override void OnAttached()
+ {
+ base.OnAttached();
+ UpdateState();
+ }
+
+ void UpdateState()
+ {
+ SetActive(AreEqual(Property, Value));
+ }
+
+ bool AreEqual(object value1, object value2)
+ {
+ if (value1 == value2)
+ return true;
+
+ if (value1 != null && value2 != null)
+ return AreEqualType(value1, value2) || AreEqualType(value2, value1);
+
+ return false;
+ }
+
+ bool AreEqualType(object value1, object value2)
+ {
+ if (value2 is Enum)
+ value1 = ConvertToEnum(value2.GetType(), value1);
+ else
+ value1 = Convert.ChangeType(value1, value2.GetType(), CultureInfo.InvariantCulture);
+
+ return value2.Equals(value1);
+ }
+
+ object ConvertToEnum(Type enumType, object value)
+ {
+ try
+ {
+ return Enum.IsDefined(enumType, value) ? Enum.ToObject(enumType, value) : null;
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using FormsDevice = Xamarin.Forms.Device;
+
+namespace Xamarin.Forms
+{
+ public sealed class DeviceStateTrigger : StateTriggerBase
+ {
+ public DeviceStateTrigger()
+ {
+ UpdateState();
+ }
+
+ public string Device
+ {
+ get => (string)GetValue(DeviceProperty);
+ set => SetValue(DeviceProperty, value);
+ }
+
+ public static readonly BindableProperty DeviceProperty =
+ BindableProperty.Create(nameof(Device), typeof(string), typeof(DeviceStateTrigger), string.Empty,
+ propertyChanged: OnDeviceChanged);
+
+ static void OnDeviceChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((DeviceStateTrigger)bindable).UpdateState();
+ }
+
+ void UpdateState()
+ {
+ switch (Device)
+ {
+ case FormsDevice.Android:
+ SetActive(FormsDevice.RuntimePlatform == FormsDevice.Android);
+ break;
+ case FormsDevice.iOS:
+ SetActive(FormsDevice.RuntimePlatform == FormsDevice.iOS);
+ break;
+ case FormsDevice.UWP:
+ SetActive(FormsDevice.RuntimePlatform == FormsDevice.UWP);
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
{
internal static class ExperimentalFlags
{
+ internal const string StateTriggersExperimental = "StateTriggers_Experimental";
internal const string IndicatorViewExperimental = "IndicatorView_Experimental";
internal const string ShellUWPExperimental = "Shell_UWP_Experimental";
internal const string CarouselViewExperimental = "CarouselView_Experimental";
internal const string SwipeViewExperimental = "SwipeView_Experimental";
+ internal const string MediaElementExperimental = "MediaElement_Experimental";
+ internal const string MarkupExperimental = "Markup_Experimental";
[EditorBrowsable(EditorBrowsableState.Never)]
public static void VerifyFlagEnabled(
using System;
namespace Xamarin.Forms
{
+ [Internals.Preserve(AllMembers = true)]
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ExportFontAttribute : Attribute
{
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(FileMediaSourceConverter))]
+ public sealed class FileMediaSource : MediaSource
+ {
+ public static readonly BindableProperty FileProperty = BindableProperty.Create(nameof(File), typeof(string), typeof(FileMediaSource), default(string));
+
+ public string File
+ {
+ get { return (string)GetValue(FileProperty); }
+ set { SetValue(FileProperty, value); }
+ }
+
+ public override string ToString()
+ {
+ return $"File: {File}";
+ }
+
+ public static implicit operator FileMediaSource(string file)
+ {
+ return (FileMediaSource)FromFile(file);
+ }
+
+ public static implicit operator string(FileMediaSource file)
+ {
+ return file != null ? file.File : null;
+ }
+
+ protected override void OnPropertyChanged(string propertyName = null)
+ {
+ if (propertyName == FileProperty.PropertyName)
+ OnSourceChanged();
+ base.OnPropertyChanged(propertyName);
+ }
+ }
+}
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ [Xaml.TypeConversion(typeof(FileMediaSource))]
+ public sealed class FileMediaSourceConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ return (FileMediaSource)MediaSource.FromFile(value);
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(FileMediaSource)));
+ }
+ }
+}
\ No newline at end of file
bool _measuring;
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
+ if (_root == null)
+ return new SizeRequest(new Size(widthConstraint, heightConstraint));
+
//All of this is a HACK as X.Flex doesn't supports measuring
if (!double.IsPositiveInfinity(widthConstraint) && !double.IsPositiveInfinity(heightConstraint))
return new SizeRequest(new Size(widthConstraint, heightConstraint));
//1. Set Shrink to 0, set align-self to start (to avoid stretching)
// Set Image.Aspect to Fill to get the value we expect in measuring
foreach (var child in Children) {
- var item = GetFlexItem(child);
- item.Shrink = 0;
- item.AlignSelf = Flex.AlignSelf.Start;
+ if (GetFlexItem(child) is Flex.Item item) {
+ item.Shrink = 0;
+ item.AlignSelf = Flex.AlignSelf.Start;
+ }
}
Layout(widthConstraint, heightConstraint);
//3. reset Shrink, algin-self, and image.aspect
foreach (var child in Children) {
- var item = GetFlexItem(child);
- item.Shrink = (float)child.GetValue(ShrinkProperty);
- item.AlignSelf = (Flex.AlignSelf)(FlexAlignSelf)child.GetValue(AlignSelfProperty);
+ if (GetFlexItem(child) is Flex.Item item) {
+ item.Shrink = (float)child.GetValue(ShrinkProperty);
+ item.AlignSelf = (Flex.AlignSelf)(FlexAlignSelf)child.GetValue(AlignSelfProperty);
+ }
}
_measuring = false;
return new SizeRequest(new Size(widthConstraint, heightConstraint));
set => SetValue(GlyphProperty, value);
}
- public static readonly BindableProperty SizeProperty = BindableProperty.Create(nameof(Size), typeof(double), typeof(FontImageSource), default(double),
+ public static readonly BindableProperty SizeProperty = BindableProperty.Create(nameof(Size), typeof(double), typeof(FontImageSource), 30d,
propertyChanged: (b, o, n) => ((FontImageSource)b).OnSourceChanged());
[TypeConverter(typeof(FontSizeConverter))]
namespace Xamarin.Forms
{
- public class GestureElement : Element, ISpatialElement
+ public class GestureElement : Element, ISpatialElement, IGestureRecognizers
{
readonly GestureRecognizerCollection _gestureRecognizers = new GestureRecognizerCollection();
internal event NotifyCollectionChangedEventHandler GestureRecognizersCollectionChanged;
--- /dev/null
+using System.Collections.Generic;
+
+namespace Xamarin.Forms
+{
+ public interface IGestureRecognizers
+ {
+ IList<IGestureRecognizer> GestureRecognizers { get; }
+ }
+}
\ No newline at end of file
}
}
- void ResetIndicators()
+ internal void ResetIndicators()
{
try
{
for (int index = 0; index < childrenCount; index++)
{
- Children[index].BackgroundColor = index == _indicatorView.Position
+ var maxVisible = _indicatorView.MaximumVisible;
+ var position = _indicatorView.Position;
+ var selectedIndex = position >= maxVisible ? maxVisible - 1 : position;
+ Children[index].BackgroundColor = index == selectedIndex
? GetColorOrDefault(_indicatorView.SelectedIndicatorColor, Color.Gray)
: GetColorOrDefault(_indicatorView.IndicatorColor, Color.Silver);
}
public static readonly BindableProperty CountProperty = BindableProperty.Create(nameof(Count), typeof(int), typeof(IndicatorView), default(int), propertyChanged: (bindable, oldValue, newValue)
=> (((IndicatorView)bindable).IndicatorLayout as IndicatorStackLayout)?.ResetIndicatorCount((int)oldValue));
- public static readonly BindableProperty MaximumVisibleProperty = BindableProperty.Create(nameof(MaximumVisible), typeof(int), typeof(IndicatorView), int.MaxValue);
+ public static readonly BindableProperty MaximumVisibleProperty = BindableProperty.Create(nameof(MaximumVisible), typeof(int), typeof(IndicatorView), int.MaxValue, propertyChanged: (bindable, oldValue, newValue)
+ => (((IndicatorView) bindable).IndicatorLayout as IndicatorStackLayout)?.ResetIndicators());
public static readonly BindableProperty IndicatorTemplateProperty = BindableProperty.Create(nameof(IndicatorTemplate), typeof(DataTemplate), typeof(IndicatorView), default(DataTemplate), propertyChanging: (bindable, oldValue, newValue)
=> UpdateIndicatorLayout((IndicatorView)bindable, newValue));
set => SetValue(ItemsSourceProperty, value);
}
- public static readonly BindableProperty ItemsSourceByProperty = BindableProperty.Create("ItemsSourceBy", typeof(VisualElement), typeof(IndicatorView), default(VisualElement), propertyChanged: (bindable, oldValue, newValue)
- => LinkToCarouselView(bindable as IndicatorView, newValue as CarouselView));
-
- [TypeConverter(typeof(ReferenceTypeConverter))]
- public static VisualElement GetItemsSourceBy(BindableObject bindable)
- {
- return (VisualElement)bindable.GetValue(ItemsSourceByProperty);
- }
-
- public static void SetItemsSourceBy(BindableObject bindable, VisualElement value)
- {
- bindable.SetValue(ItemsSourceByProperty, value);
- }
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
if (IndicatorTemplate != null)
return baseRequest;
- var defaultSize = IndicatorSize + DefaultPadding + DefaultPadding;
+ var defaultSize = IndicatorSize + DefaultPadding + DefaultPadding + 1;
var items = Count;
var sizeRequest = new SizeRequest(new Size(items * defaultSize, IndicatorSize), new Size(10, 10));
return sizeRequest;
}
}
- static void LinkToCarouselView(IndicatorView indicatorView, CarouselView carouselView)
- {
- if (carouselView == null || indicatorView == null)
- return;
-
- indicatorView.SetBinding(PositionProperty, new Binding
- {
- Path = nameof(CarouselView.Position),
- Source = carouselView
- });
-
- indicatorView.SetBinding(ItemsSourceProperty, new Binding
- {
- Path = nameof(ItemsView.ItemsSource),
- Source = carouselView
- });
- }
-
void ResetItemsSource(IEnumerable oldItemsSource)
{
if (oldItemsSource is INotifyCollectionChanged oldCollection)
collection.CollectionChanged += OnCollectionChanged;
OnCollectionChanged(ItemsSource, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+
+ InvalidateMeasureInternal(Internals.InvalidationTrigger.MeasureChanged);
}
void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-using System;
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
List<ToolbarItem> returnValue = GetCurrentToolbarItems(Target);
if (AdditionalTargets != null)
- foreach(var item in AdditionalTargets)
- foreach(var toolbarItem in item.ToolbarItems)
+ foreach (var item in AdditionalTargets)
+ foreach (var toolbarItem in item.ToolbarItems)
returnValue.Add(toolbarItem);
returnValue.Sort(_toolBarItemComparer);
using Xamarin.Forms.Platform;
using System.Linq;
using System.Runtime.CompilerServices;
+using System.Collections.ObjectModel;
namespace Xamarin.Forms
{
public const string CurrentItemVisualState = "CurrentItem";
public const string NextItemVisualState = "NextItem";
public const string PreviousItemVisualState = "PreviousItem";
- public const string VisibleItemVisualState = "VisibleItem";
public const string DefaultItemVisualState = "DefaultItem";
+ bool _isInitialized;
+ int _gotoPosition = -1;
+
public static readonly BindableProperty PeekAreaInsetsProperty = BindableProperty.Create(nameof(PeekAreaInsets), typeof(Thickness), typeof(CarouselView), default(Thickness));
public Thickness PeekAreaInsets
set { SetValue(PeekAreaInsetsProperty, value); }
}
- static readonly BindablePropertyKey VisibleViewsPropertyKey = BindableProperty.CreateReadOnly(nameof(VisibleViews), typeof(List<View>), typeof(CarouselView), null);
+ static readonly BindablePropertyKey VisibleViewsPropertyKey = BindableProperty.CreateReadOnly(nameof(VisibleViews), typeof(ObservableCollection<View>), typeof(CarouselView), new ObservableCollection<View>());
public static readonly BindableProperty VisibleViewsProperty = VisibleViewsPropertyKey.BindableProperty;
- public List<View> VisibleViews => (List<View>)GetValue(VisibleViewsProperty);
+ public ObservableCollection<View> VisibleViews => (ObservableCollection<View>)GetValue(VisibleViewsProperty);
static readonly BindablePropertyKey IsDraggingPropertyKey = BindableProperty.CreateReadOnly(nameof(IsDragging), typeof(bool), typeof(CarouselView), false);
}
public static readonly BindableProperty CurrentItemProperty =
- BindableProperty.Create(nameof(CurrentItem), typeof(object), typeof(CarouselView), default, BindingMode.TwoWay,
+ BindableProperty.Create(nameof(CurrentItem), typeof(object), typeof(CarouselView), default, BindingMode.TwoWay,
propertyChanged: CurrentItemPropertyChanged);
public static readonly BindableProperty CurrentItemChangedCommandProperty =
}
}
- carouselView.SetValueCore(PositionProperty, GetPositionForItem(carouselView, newValue));
+ var positionItem = GetPositionForItem(carouselView, newValue);
+ var gotoPosition = carouselView._gotoPosition;
+
+ if (positionItem == gotoPosition || gotoPosition == -1)
+ {
+ carouselView._gotoPosition = -1;
+ carouselView.SetValueCore(PositionProperty, positionItem);
+ }
carouselView.CurrentItemChanged?.Invoke(carouselView, args);
set => SetValue(ItemsLayoutProperty, value);
}
+ [TypeConverter(typeof(ReferenceTypeConverter))]
+ public IndicatorView IndicatorView
+ {
+ set => LinkToIndicatorView(this, value);
+ }
+
+ static void LinkToIndicatorView(CarouselView carouselView, IndicatorView indicatorView)
+ {
+ if (indicatorView == null)
+ return;
+
+ indicatorView.SetBinding(IndicatorView.PositionProperty, new Binding
+ {
+ Path = nameof(CarouselView.Position),
+ Source = carouselView
+ });
+
+ indicatorView.SetBinding(IndicatorView.ItemsSourceProperty, new Binding
+ {
+ Path = nameof(ItemsView.ItemsSource),
+ Source = carouselView
+ });
+ }
+
[EditorBrowsable(EditorBrowsableState.Never)]
public bool IsScrolling { get; set; }
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public Queue<Action> ScrollToActions = new Queue<Action>();
+
public event EventHandler<CurrentItemChangedEventArgs> CurrentItemChanged;
public event EventHandler<PositionChangedEventArgs> PositionChanged;
}
catch (InvalidOperationException)
{
-
+
}
}
protected override void OnScrolled(ItemsViewScrolledEventArgs e)
{
- CurrentItem = GetItemForPosition(this, e.CenterItemIndex);
+ SetCurrentItem(GetItemForPosition(this, e.CenterItemIndex));
base.OnScrolled(e);
}
static void PositionPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
+
var carousel = (CarouselView)bindable;
var args = new PositionChangedEventArgs((int)oldValue, (int)newValue);
carousel.PositionChanged?.Invoke(carousel, args);
+ if (args.CurrentPosition == carousel._gotoPosition)
+ carousel._gotoPosition = -1;
+
// User is interacting with the carousel we don't need to scroll to item
if (!carousel.IsDragging && !carousel.IsScrolling)
- carousel.ScrollTo(args.CurrentPosition, position: ScrollToPosition.Center, animate: carousel.IsScrollAnimated);
+ {
+ carousel._gotoPosition = args.CurrentPosition;
+
+ Action actionSCroll = () =>
+ {
+ carousel.ScrollTo(args.CurrentPosition, position: ScrollToPosition.Center, animate: carousel.IsScrollAnimated);
+ };
+
+ if (!carousel._isInitialized)
+ carousel.ScrollToActions.Enqueue(actionSCroll);
+ else
+ actionSCroll();
+ }
carousel.OnPositionChanged(args);
}
+
static object GetItemForPosition(CarouselView carouselView, int index)
{
if (!(carouselView?.ItemsSource is IList itemSource))
{
SetValue(IsDraggingPropertyKey, value);
}
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public void PlatformInitialized()
+ {
+ _isInitialized = true;
+ }
}
}
internal static readonly BindableProperty InternalItemsLayoutProperty =
BindableProperty.Create(nameof(ItemsLayout), typeof(IItemsLayout), typeof(ItemsView),
- LinearItemsLayout.Vertical);
+ LinearItemsLayout.Vertical, propertyChanged: OnInternalItemsLayoutPropertyChanged);
+
+ static void OnInternalItemsLayoutPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (oldValue is BindableObject boOld)
+ SetInheritedBindingContext(boOld, null);
+
+ if (newValue is BindableObject boNew)
+ SetInheritedBindingContext(boNew, bindable.BindingContext);
+ }
protected IItemsLayout InternalItemsLayout
{
{
}
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+ if (InternalItemsLayout is BindableObject bo)
+ SetInheritedBindingContext(bo, BindingContext);
+ }
}
}
\ No newline at end of file
static readonly IList<object> s_empty = new List<object>(0);
+ bool _suppressSelectionChangeNotification;
+
public SelectableItemsView()
{
}
public event EventHandler<SelectionChangedEventArgs> SelectionChanged;
+ public void UpdateSelectedItems(IList<object> newSelection)
+ {
+ var oldSelection = new List<object>(SelectedItems);
+
+ _suppressSelectionChangeNotification = true;
+
+ SelectedItems.Clear();
+
+ if (newSelection?.Count > 0)
+ {
+ for (int n = 0; n < newSelection.Count; n++)
+ {
+ SelectedItems.Add(newSelection[n]);
+ }
+ }
+
+ _suppressSelectionChangeNotification = false;
+
+ SelectedItemsPropertyChanged(oldSelection, newSelection);
+ }
+
protected virtual void OnSelectionChanged(SelectionChangedEventArgs args)
{
}
internal void SelectedItemsPropertyChanged(IList<object> oldSelection, IList<object> newSelection)
{
+ if (_suppressSelectionChangeNotification)
+ {
+ return;
+ }
+
SelectionPropertyChanged(this, new SelectionChangedEventArgs(oldSelection, newSelection));
OnPropertyChanged(SelectedItemsProperty.PropertyName);
command.Execute(commandParameter);
}
}
-
- selectableItemsView.SelectionChanged?.Invoke(selectableItemsView, args);
+ selectableItemsView.SelectionChanged?.Invoke(selectableItemsView, args);
selectableItemsView.OnSelectionChanged(args);
}
}
var args = new SelectionChangedEventArgs(previousSelection, newSelection);
-
SelectionPropertyChanged(selectableItemsView, args);
}
}
--- /dev/null
+using System;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class BindableObjectExtensions
+ {
+ const string bindingContextPath = Binding.SelfPath;
+
+ /// <summary>Bind to a specified property</summary>
+ public static TBindable Bind<TBindable>(
+ this TBindable bindable,
+ BindableProperty targetProperty,
+ string path = bindingContextPath,
+ BindingMode mode = BindingMode.Default,
+ IValueConverter converter = null,
+ object converterParameter = null,
+ string stringFormat = null,
+ object source = null,
+ object targetNullValue = null,
+ object fallbackValue = null
+ ) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ bindable.SetBinding(
+ targetProperty,
+ new Binding(path, mode, converter, converterParameter, stringFormat, source)
+ {
+ TargetNullValue = targetNullValue,
+ FallbackValue = fallbackValue
+ });
+ return bindable;
+ }
+
+ /// <summary>Bind to a specified property with inline conversion</summary>
+ public static TBindable Bind<TBindable, TSource, TDest>(
+ this TBindable bindable,
+ BindableProperty targetProperty,
+ string path = bindingContextPath,
+ BindingMode mode = BindingMode.Default,
+ Func<TSource, TDest> convert = null,
+ Func<TDest, TSource> convertBack = null,
+ object converterParameter = null,
+ string stringFormat = null,
+ object source = null,
+ object targetNullValue = null,
+ object fallbackValue = null
+ ) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ var converter = new FuncConverter<TSource, TDest, object>(convert, convertBack);
+ bindable.SetBinding(
+ targetProperty,
+ new Binding(path, mode, converter, converterParameter, stringFormat, source)
+ {
+ TargetNullValue = targetNullValue,
+ FallbackValue = fallbackValue
+ });
+ return bindable;
+ }
+
+ /// <summary>Bind to a specified property with inline conversion and conversion parameter</summary>
+ public static TBindable Bind<TBindable, TSource, TParam, TDest>(
+ this TBindable bindable,
+ BindableProperty targetProperty,
+ string path = bindingContextPath,
+ BindingMode mode = BindingMode.Default,
+ Func<TSource, TParam, TDest> convert = null,
+ Func<TDest, TParam, TSource> convertBack = null,
+ object converterParameter = null,
+ string stringFormat = null,
+ object source = null,
+ object targetNullValue = null,
+ object fallbackValue = null
+ ) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ var converter = new FuncConverter<TSource, TDest, TParam>(convert, convertBack);
+ bindable.SetBinding(
+ targetProperty,
+ new Binding(path, mode, converter, converterParameter, stringFormat, source)
+ {
+ TargetNullValue = targetNullValue,
+ FallbackValue = fallbackValue
+ });
+ return bindable;
+ }
+
+ /// <summary>Bind to the default property</summary>
+ public static TBindable Bind<TBindable>(
+ this TBindable bindable,
+ string path = bindingContextPath,
+ BindingMode mode = BindingMode.Default,
+ IValueConverter converter = null,
+ object converterParameter = null,
+ string stringFormat = null,
+ object source = null,
+ object targetNullValue = null,
+ object fallbackValue = null
+ ) where TBindable : BindableObject
+ {
+ bindable.Bind(
+ DefaultBindableProperties.GetFor(bindable),
+ path, mode, converter, converterParameter, stringFormat, source, targetNullValue, fallbackValue
+ );
+ return bindable;
+ }
+
+ /// <summary>Bind to the default property with inline conversion</summary>
+ public static TBindable Bind<TBindable, TSource, TDest>(
+ this TBindable bindable,
+ string path = bindingContextPath,
+ BindingMode mode = BindingMode.Default,
+ Func<TSource, TDest> convert = null,
+ Func<TDest, TSource> convertBack = null,
+ object converterParameter = null,
+ string stringFormat = null,
+ object source = null,
+ object targetNullValue = null,
+ object fallbackValue = null
+ ) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ var converter = new FuncConverter<TSource, TDest, object>(convert, convertBack);
+ bindable.Bind(
+ DefaultBindableProperties.GetFor(bindable),
+ path, mode, converter, converterParameter, stringFormat, source, targetNullValue, fallbackValue
+ );
+ return bindable;
+ }
+
+ /// <summary>Bind to the default property with inline conversion and conversion parameter</summary>
+ public static TBindable Bind<TBindable, TSource, TParam, TDest>(
+ this TBindable bindable,
+ string path = bindingContextPath,
+ BindingMode mode = BindingMode.Default,
+ Func<TSource, TParam, TDest> convert = null,
+ Func<TDest, TParam, TSource> convertBack = null,
+ object converterParameter = null,
+ string stringFormat = null,
+ object source = null,
+ object targetNullValue = null,
+ object fallbackValue = null
+ ) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ var converter = new FuncConverter<TSource, TDest, TParam>(convert, convertBack);
+ bindable.Bind(
+ DefaultBindableProperties.GetFor(bindable),
+ path, mode, converter, converterParameter, stringFormat, source, targetNullValue, fallbackValue
+ );
+ return bindable;
+ }
+
+ /// <summary>Bind to the <typeparamref name="TBindable"/>'s default Command and CommandParameter properties </summary>
+ /// <param name="parameterPath">If null, no binding is created for the CommandParameter property</param>
+ public static TBindable BindCommand<TBindable>(
+ this TBindable bindable,
+
+ string path = bindingContextPath,
+ object source = null,
+ string parameterPath = bindingContextPath,
+ object parameterSource = null
+ ) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ (var commandProperty, var parameterProperty) = DefaultBindableProperties.GetForCommand(bindable);
+
+ bindable.SetBinding(commandProperty, new Binding(path: path, source: source));
+
+ if (parameterPath != null)
+ bindable.SetBinding(parameterProperty, new Binding(path: parameterPath, source: parameterSource));
+
+ return bindable;
+ }
+
+ public static TBindable Assign<TBindable, TVariable>(this TBindable bindable, out TVariable variable)
+ where TBindable : BindableObject, TVariable
+ {
+ VerifyExperimental();
+ variable = bindable;
+ return bindable;
+ }
+
+ public static TBindable Invoke<TBindable>(this TBindable bindable, Action<TBindable> action) where TBindable : BindableObject
+ {
+ VerifyExperimental();
+ action?.Invoke(bindable);
+ return bindable;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class DefaultBindableProperties
+ {
+ static Dictionary<string, BindableProperty> bindableObjectTypeDefaultProperty = new Dictionary<string, BindableProperty>
+ { // Key: full type name of BindableObject, Value: the default BindableProperty
+ // Note that we don't specify default properties for unconstructed generic types
+ { "Xamarin.Forms.ActivityIndicator", ActivityIndicator.IsRunningProperty },
+ { "Xamarin.Forms.BackButtonBehavior", BackButtonBehavior.CommandProperty },
+ { "Xamarin.Forms.BoxView", BoxView.ColorProperty },
+ { "Xamarin.Forms.Button", Button.CommandProperty },
+ { "Xamarin.Forms.CarouselPage", Page.TitleProperty },
+ { "Xamarin.Forms.CheckBox", CheckBox.IsCheckedProperty },
+ { "Xamarin.Forms.ClickGestureRecognizer", ClickGestureRecognizer.CommandProperty },
+ { "Xamarin.Forms.CollectionView", CollectionView.ItemsSourceProperty },
+ { "Xamarin.Forms.ContentPage", Page.TitleProperty },
+ { "Xamarin.Forms.DatePicker", DatePicker.DateProperty },
+ { "Xamarin.Forms.Editor", Editor.TextProperty },
+ { "Xamarin.Forms.Entry", Entry.TextProperty },
+ { "Xamarin.Forms.EntryCell", EntryCell.TextProperty },
+ { "Xamarin.Forms.FileImageSource", FileImageSource.FileProperty },
+ { "Xamarin.Forms.FileMediaSource", FileMediaSource.FileProperty },
+ { "Xamarin.Forms.HtmlWebViewSource", HtmlWebViewSource.HtmlProperty },
+ { "Xamarin.Forms.Image", Image.SourceProperty },
+ { "Xamarin.Forms.ImageButton", ImageButton.CommandProperty },
+ { "Xamarin.Forms.ImageCell", ImageCell.ImageSourceProperty },
+ { "Xamarin.Forms.ItemsView", ItemsView.ItemsSourceProperty },
+ { "Xamarin.Forms.Label", Label.TextProperty },
+ { "Xamarin.Forms.ListView", ListView.ItemsSourceProperty },
+ { "Xamarin.Forms.MasterDetailPage", Page.TitleProperty },
+ { "Xamarin.Forms.MediaElement", MediaElement.SourceProperty },
+ { "Xamarin.Forms.MenuItem", MenuItem.CommandProperty },
+ { "Xamarin.Forms.MultiPage", Page.TitleProperty },
+ { "Xamarin.Forms.NavigationPage", Page.TitleProperty },
+ { "Xamarin.Forms.Page", Page.TitleProperty },
+ { "Xamarin.Forms.Picker", Picker.SelectedIndexProperty },
+ { "Xamarin.Forms.ProgressBar", ProgressBar.ProgressProperty },
+ { "Xamarin.Forms.RadioButton", RadioButton.IsCheckedProperty },
+ { "Xamarin.Forms.RefreshView", RefreshView.CommandProperty },
+ { "Xamarin.Forms.SearchBar", SearchBar.SearchCommandProperty },
+ { "Xamarin.Forms.SearchHandler", SearchHandler.CommandProperty },
+ { "Xamarin.Forms.Slider", Slider.ValueProperty },
+ { "Xamarin.Forms.Span", Span.TextProperty },
+ { "Xamarin.Forms.Stepper", Stepper.ValueProperty },
+ { "Xamarin.Forms.StreamImageSource", StreamImageSource.StreamProperty },
+ { "Xamarin.Forms.SwipeGestureRecognizer", SwipeGestureRecognizer.CommandProperty },
+ { "Xamarin.Forms.SwipeItem", SwipeItem.CommandProperty },
+ { "Xamarin.Forms.Switch", Switch.IsToggledProperty },
+ { "Xamarin.Forms.SwitchCell", SwitchCell.OnProperty },
+ { "Xamarin.Forms.TabbedPage", Page.TitleProperty },
+ { "Xamarin.Forms.TableRoot", TableRoot.TitleProperty },
+ { "Xamarin.Forms.TableSection", TableSection.TitleProperty },
+ { "Xamarin.Forms.TableSectionBase", TableSectionBase.TitleProperty },
+ { "Xamarin.Forms.TapGestureRecognizer", TapGestureRecognizer.CommandProperty },
+ { "Xamarin.Forms.TemplatedPage", Page.TitleProperty },
+ { "Xamarin.Forms.TextCell", TextCell.TextProperty },
+ { "Xamarin.Forms.TimePicker", TimePicker.TimeProperty },
+ { "Xamarin.Forms.ToolbarItem", ToolbarItem.CommandProperty },
+ { "Xamarin.Forms.UriImageSource", UriImageSource.UriProperty },
+ { "Xamarin.Forms.UriMediaSource", UriMediaSource.UriProperty },
+ { "Xamarin.Forms.UrlWebViewSource", UrlWebViewSource.UrlProperty },
+ { "Xamarin.Forms.WebView", WebView.SourceProperty }
+ };
+
+ static Dictionary<string, (BindableProperty, BindableProperty)> bindableObjectTypeDefaultCommandAndParameterProperties = new Dictionary<string, (BindableProperty, BindableProperty)>
+ { // Key: full type name of BindableObject, Value: command property and corresponding commandParameter property
+ { "Xamarin.Forms.Button", (Button.CommandProperty, Button.CommandParameterProperty) },
+ { "Xamarin.Forms.TextCell", (TextCell.CommandProperty, TextCell.CommandParameterProperty) },
+ { "Xamarin.Forms.ClickGestureRecognizer", (ClickGestureRecognizer.CommandProperty, ClickGestureRecognizer.CommandParameterProperty) },
+ { "Xamarin.Forms.ImageButton", (ImageButton.CommandProperty, ImageButton.CommandParameterProperty) },
+ { "Xamarin.Forms.MenuItem", (MenuItem.CommandProperty, MenuItem.CommandParameterProperty) },
+ { "Xamarin.Forms.RefreshView", (RefreshView.CommandProperty, RefreshView.CommandParameterProperty) },
+ { "Xamarin.Forms.SwipeGestureRecognizer", (SwipeGestureRecognizer.CommandProperty, SwipeGestureRecognizer.CommandParameterProperty) },
+ { "Xamarin.Forms.SwipeItemView", (SwipeItemView.CommandProperty, SwipeItemView.CommandParameterProperty) },
+ { "Xamarin.Forms.TapGestureRecognizer", (TapGestureRecognizer.CommandProperty, TapGestureRecognizer.CommandParameterProperty) }
+ };
+
+ public static void Register(params BindableProperty[] properties)
+ {
+ VerifyExperimental();
+ foreach (var property in properties)
+ bindableObjectTypeDefaultProperty.Add(property.DeclaringType.FullName, property);
+ }
+
+ public static void RegisterForCommand(params (BindableProperty commandProperty, BindableProperty parameterProperty)[] propertyPairs)
+ {
+ VerifyExperimental();
+ foreach (var propertyPair in propertyPairs)
+ bindableObjectTypeDefaultCommandAndParameterProperties.Add(propertyPair.commandProperty.DeclaringType.FullName, propertyPair);
+ }
+
+ internal static void Unregister(BindableProperty property)
+ => bindableObjectTypeDefaultProperty.Remove(property.DeclaringType.FullName);
+
+ internal static BindableProperty GetFor(BindableObject bindableObject)
+ {
+ var type = bindableObject.GetType();
+ var defaultProperty = GetFor(type);
+ if (defaultProperty == null)
+ throw new ArgumentException(
+ "No default bindable property is registered for BindableObject type " + type.FullName +
+ "\r\nEither specify a property when calling Bind() or register a default bindable property for this BindableObject type");
+ return defaultProperty;
+ }
+
+ internal static BindableProperty GetFor(Type bindableObjectType)
+ {
+ BindableProperty defaultProperty;
+
+ do
+ {
+ string bindableObjectTypeName = bindableObjectType.FullName;
+ if (bindableObjectTypeDefaultProperty.TryGetValue(bindableObjectTypeName, out defaultProperty))
+ break;
+ if (bindableObjectTypeName.StartsWith("Xamarin.Forms.", StringComparison.Ordinal))
+ break;
+
+ bindableObjectType = bindableObjectType.GetTypeInfo().BaseType;
+ } while (bindableObjectType != null);
+
+ return defaultProperty;
+ }
+
+ internal static void UnregisterForCommand(BindableProperty commandProperty)
+ => bindableObjectTypeDefaultCommandAndParameterProperties.Remove(commandProperty.DeclaringType.FullName);
+
+ internal static (BindableProperty, BindableProperty) GetForCommand(BindableObject bindableObject)
+ {
+ var type = bindableObject.GetType();
+ (var commandProperty, var parameterProperty) = GetForCommand(type);
+ if (commandProperty == null)
+ throw new ArgumentException(
+ "No command + command parameter properties are registered for BindableObject type " + type.FullName +
+ "\r\nRegister command + command parameter properties for this BindableObject type");
+ return (commandProperty, parameterProperty);
+ }
+
+ internal static (BindableProperty, BindableProperty) GetForCommand(Type bindableObjectType)
+ {
+ (BindableProperty, BindableProperty) commandAndParameterProperties;
+
+ do
+ {
+ string bindableObjectTypeName = bindableObjectType.FullName;
+ if (bindableObjectTypeDefaultCommandAndParameterProperties.TryGetValue(bindableObjectTypeName, out commandAndParameterProperties))
+ break;
+ if (bindableObjectTypeName.StartsWith("Xamarin.Forms.", StringComparison.Ordinal))
+ break;
+
+ bindableObjectType = bindableObjectType.GetTypeInfo().BaseType;
+ } while (bindableObjectType != null);
+
+ return commandAndParameterProperties;
+ }
+ }
+}
--- /dev/null
+using Xamarin.Forms.Internals;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class ElementExtensions
+ {
+ public static TElement Effects<TElement>(this TElement element, params Effect[] effects) where TElement : Element
+ {
+ VerifyExperimental();
+ for (int i = 0; i < effects.Length; i++)
+ element.Effects.Add(effects[i]);
+ return element;
+ }
+
+ public static TFontElement FontSize<TFontElement>(this TFontElement fontElement, double size) where TFontElement : Element, IFontElement
+ { VerifyExperimental(); fontElement.SetValue(FontElement.FontSizeProperty, size); return fontElement; }
+
+ public static TFontElement Bold<TFontElement>(this TFontElement fontElement) where TFontElement : Element, IFontElement
+ { VerifyExperimental(); fontElement.SetValue(FontElement.FontAttributesProperty, FontAttributes.Bold); return fontElement; }
+
+ public static TFontElement Italic<TFontElement>(this TFontElement fontElement) where TFontElement : Element, IFontElement
+ { VerifyExperimental(); fontElement.SetValue(FontElement.FontAttributesProperty, FontAttributes.Italic); return fontElement; }
+
+ public static TFontElement Font<TFontElement>(
+ this TFontElement fontElement,
+ double? size = null,
+ bool? bold = null,
+ bool? italic = null,
+ string family = null
+ ) where TFontElement : Element, IFontElement
+ {
+ VerifyExperimental();
+ if (size.HasValue)
+ fontElement.SetValue(FontElement.FontSizeProperty, size.Value);
+
+ if (bold.HasValue || italic.HasValue)
+ {
+ var attributes = FontAttributes.None;
+ if (bold == true)
+ attributes |= FontAttributes.Bold;
+ if (italic == true)
+ attributes |= FontAttributes.Italic;
+ fontElement.SetValue(FontElement.FontAttributesProperty, attributes);
+ }
+
+ if (family != null)
+ fontElement.SetValue(FontElement.FontFamilyProperty, family);
+
+ return fontElement;
+ }
+ }
+}
--- /dev/null
+using System;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class ElementGesturesExtensions
+ {
+ const string bindingContextPath = Binding.SelfPath;
+
+ /// <summary>Add a <see cref="ClickGestureRecognizer"/>,
+ /// and bind to its Command and (optionally) CommandParameter properties</summary>
+ /// <param name="parameterPath">If not specified or null, no binding is created for the CommandParameter property</param>
+ public static TGestureElement BindClickGesture<TGestureElement>(
+ this TGestureElement gestureElement,
+ string commandPath = bindingContextPath,
+ object commandSource = null,
+ string parameterPath = null,
+ object parameterSource = null
+ ) where TGestureElement : Element, IGestureRecognizers
+ => ClickGesture(gestureElement, g => g.BindCommand(commandPath, commandSource, parameterPath, parameterSource));
+
+ /// <summary>Add a <see cref="SwipeGestureRecognizer"/>,
+ /// and bind to its Command and (optionally) CommandParameter properties</summary>
+ /// <param name="parameterPath">If not specified or null, no binding is created for the CommandParameter property</param>
+ public static TGestureElement BindSwipeGesture<TGestureElement>(
+ this TGestureElement gestureElement,
+ string commandPath = bindingContextPath,
+ object commandSource = null,
+ string parameterPath = null,
+ object parameterSource = null
+ ) where TGestureElement : Element, IGestureRecognizers
+ => SwipeGesture(gestureElement, g => g.BindCommand(commandPath, commandSource, parameterPath, parameterSource));
+
+ /// <summary>Add a <see cref="TapGestureRecognizer"/>,
+ /// and bind to its Command and (optionally) CommandParameter properties</summary>
+ /// <param name="parameterPath">If not specified or null, no binding is created for the CommandParameter property</param>
+ public static TGestureElement BindTapGesture<TGestureElement>(
+ this TGestureElement gestureElement,
+ string commandPath = bindingContextPath,
+ object commandSource = null,
+ string parameterPath = null,
+ object parameterSource = null
+ ) where TGestureElement : Element, IGestureRecognizers
+ => TapGesture(gestureElement, g => g.BindCommand(commandPath, commandSource, parameterPath, parameterSource));
+
+ /// <summary>Add a <see cref="ClickGestureRecognizer"/>,
+ /// and pass it to the supplied <paramref name="init"/> Action</summary>
+ public static TGestureElement ClickGesture<TGestureElement>(this TGestureElement gestureElement, Action<ClickGestureRecognizer> init)
+ where TGestureElement : Element, IGestureRecognizers
+ => Gesture(gestureElement, init);
+
+ /// <summary>Add a <see cref="PanGestureRecognizer"/>,
+ /// and pass it to the supplied <paramref name="init"/> Action</summary>
+ public static TGestureElement PanGesture<TGestureElement>(this TGestureElement gestureElement, Action<PanGestureRecognizer> init)
+ where TGestureElement : Element, IGestureRecognizers
+ => Gesture(gestureElement, init);
+
+ /// <summary>Add a <see cref="PinchGestureRecognizer"/>,
+ /// and pass it to the supplied <paramref name="init"/> Action</summary>
+ public static TGestureElement PinchGesture<TGestureElement>(this TGestureElement gestureElement, Action<PinchGestureRecognizer> init)
+ where TGestureElement : Element, IGestureRecognizers
+ => Gesture(gestureElement, init);
+
+ /// <summary>Add a <see cref="SwipeGestureRecognizer"/>,
+ /// and pass it to the supplied <paramref name="init"/> Action</summary>
+ public static TGestureElement SwipeGesture<TGestureElement>(this TGestureElement gestureElement, Action<SwipeGestureRecognizer> init)
+ where TGestureElement : Element, IGestureRecognizers
+ => Gesture(gestureElement, init);
+
+ /// <summary>Add a <see cref="TapGestureRecognizer"/>,
+ /// and pass it to the supplied <paramref name="init"/> Action</summary>
+ public static TGestureElement TapGesture<TGestureElement>(this TGestureElement gestureElement, Action<TapGestureRecognizer> init)
+ where TGestureElement : Element, IGestureRecognizers
+ => Gesture(gestureElement, init);
+
+ /// <summary>Add a <typeparamref name="TGestureRecognizer"/>,
+ /// and pass it to the supplied <paramref name="init"/> Action</summary>
+ public static TGestureElement Gesture<TGestureElement, TGestureRecognizer>(
+ this TGestureElement gestureElement,
+ Action<TGestureRecognizer> init
+ ) where TGestureElement : Element, IGestureRecognizers
+ where TGestureRecognizer : GestureRecognizer, new()
+ {
+ VerifyExperimental();
+ var gestureRecognizer = new TGestureRecognizer();
+ init.Invoke(gestureRecognizer);
+
+ gestureElement.GestureRecognizers.Add(gestureRecognizer);
+
+ return gestureElement;
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Globalization;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public class FuncConverter<TSource, TDest, TParam> : IValueConverter
+ {
+ readonly Func<TSource, TDest> convert;
+ readonly Func<TDest, TSource> convertBack;
+
+ readonly Func<TSource, TParam, TDest> convertWithParam;
+ readonly Func<TDest, TParam, TSource> convertBackWithParam;
+
+ readonly Func<TSource, TParam, CultureInfo, TDest> convertWithParamAndCulture;
+ readonly Func<TDest, TParam, CultureInfo, TSource> convertBackWithParamAndCulture;
+
+ public FuncConverter(Func<TSource, TParam, CultureInfo, TDest> convertWithParamAndCulture = null, Func<TDest, TParam, CultureInfo, TSource> convertBackWithParamAndCulture = null)
+ { VerifyExperimental(constructorHint: nameof(FuncConverter)); this.convertWithParamAndCulture = convertWithParamAndCulture; this.convertBackWithParamAndCulture = convertBackWithParamAndCulture; }
+
+ public FuncConverter(Func<TSource, TParam, TDest> convertWithParam = null, Func<TDest, TParam, TSource> convertBackWithParam = null)
+ { VerifyExperimental(constructorHint: nameof(FuncConverter)); this.convertWithParam = convertWithParam; this.convertBackWithParam = convertBackWithParam; }
+
+ public FuncConverter(Func<TSource, TDest> convert = null, Func<TDest, TSource> convertBack = null)
+ { VerifyExperimental(constructorHint: nameof(FuncConverter)); this.convert = convert; this.convertBack = convertBack; }
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (convert != null)
+ return convert.Invoke(
+ value != null ? (TSource)value : default(TSource));
+
+ if (convertWithParam != null)
+ return convertWithParam.Invoke(
+ value != null ? (TSource)value : default(TSource),
+ parameter != null ? (TParam)parameter : default(TParam));
+
+ if (convertWithParamAndCulture != null)
+ return convertWithParamAndCulture.Invoke(
+ value != null ? (TSource)value : default(TSource),
+ parameter != null ? (TParam)parameter : default(TParam),
+ culture);
+
+ return default(TDest);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (convertBack != null)
+ return convertBack.Invoke(
+ value != null ? (TDest)value : default(TDest));
+
+ if (convertBackWithParam != null)
+ return convertBackWithParam.Invoke(
+ value != null ? (TDest)value : default(TDest),
+ parameter != null ? (TParam)parameter : default(TParam));
+
+ if (convertBackWithParamAndCulture != null)
+ return convertBackWithParamAndCulture.Invoke(
+ value != null ? (TDest)value : default(TDest),
+ parameter != null ? (TParam)parameter : default(TParam),
+ culture);
+
+ return default(TSource);
+ }
+ }
+
+ public class FuncConverter<TSource, TDest> : FuncConverter<TSource, TDest, object>
+ {
+ public FuncConverter(Func<TSource, TDest> convert = null, Func<TDest, TSource> convertBack = null)
+ : base(convert, convertBack) { }
+ }
+
+ public class FuncConverter<TSource> : FuncConverter<TSource, object, object>
+ {
+ public FuncConverter(Func<TSource, object> convert = null, Func<object, TSource> convertBack = null)
+ : base(convert, convertBack) { }
+ }
+
+ public class FuncConverter : FuncConverter<object, object, object>
+ {
+ public FuncConverter(Func<object, object> convert = null, Func<object, object> convertBack = null)
+ : base(convert, convertBack) { }
+ }
+
+ public class ToStringConverter : FuncConverter<object, string>
+ {
+ public ToStringConverter(string format = "{0}")
+ : base(o => string.Format(CultureInfo.InvariantCulture, format, o)) { }
+ }
+
+ public class NotConverter : FuncConverter<bool, bool>
+ {
+ static readonly Lazy<NotConverter> instance = new Lazy<NotConverter>(() => new NotConverter());
+ public static NotConverter Instance => instance.Value;
+ public NotConverter() : base(t => !t, t => !t) { }
+ }
+}
--- /dev/null
+using System;
+using System.Globalization;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class GridRowsColumns
+ {
+ public static GridLength Auto => GridLength.Auto;
+
+ public static GridLength Star => GridLength.Star;
+
+ public static class Columns
+ {
+ public static ColumnDefinitionCollection Define(params GridLength [] widths)
+ {
+ VerifyExperimental();
+ var columnDefinitions = new ColumnDefinitionCollection();
+
+ for (int i = 0; i < widths.Length; i++)
+ columnDefinitions.Add(new ColumnDefinition { Width = widths[i] });
+
+ return columnDefinitions;
+ }
+
+ public static ColumnDefinitionCollection Define<TEnum>(params (TEnum name, GridLength width)[] columns) where TEnum : Enum
+ {
+ VerifyExperimental();
+ var columnDefinitions = new ColumnDefinitionCollection();
+ for (int i = 0; i < columns.Length; i++)
+ {
+ if (i != columns[i].name.ToInt())
+ throw new ArgumentException(
+ $"Value of column name { columns[i].name } is not { i }. " +
+ "Columns must be defined with enum names whose values form the sequence 0,1,2,..."
+ );
+ columnDefinitions.Add(new ColumnDefinition { Width = columns[i].width });
+ }
+ return columnDefinitions;
+ }
+ }
+
+ public static class Rows
+ {
+ public static RowDefinitionCollection Define(params GridLength[] heights)
+ {
+ VerifyExperimental();
+ var rowDefinitions = new RowDefinitionCollection();
+
+ for (int i = 0; i < heights.Length; i++)
+ rowDefinitions.Add(new RowDefinition { Height = heights[i] });
+
+ return rowDefinitions;
+ }
+
+ public static RowDefinitionCollection Define<TEnum>(params (TEnum name, GridLength height)[] rows) where TEnum : Enum
+ {
+ VerifyExperimental();
+ var rowDefinitions = new RowDefinitionCollection();
+ for (int i = 0; i < rows.Length; i++)
+ {
+ if (i != rows[i].name.ToInt())
+ throw new ArgumentException(
+ $"Value of row name { rows[i].name } is not { i }. " +
+ "Rows must be defined with enum names whose values form the sequence 0,1,2,..."
+ );
+ rowDefinitions.Add(new RowDefinition { Height = rows[i].height });
+ }
+ return rowDefinitions;
+ }
+ }
+
+ public static int All<TEnum>() where TEnum : Enum
+ {
+ VerifyExperimental();
+ var values = Enum.GetValues(typeof(TEnum));
+ int span = (int)values.GetValue(values.Length - 1) + 1;
+ return span;
+ }
+
+ public static int Last<TEnum>() where TEnum : Enum
+ {
+ VerifyExperimental();
+ var values = Enum.GetValues(typeof(TEnum));
+ int last = (int)values.GetValue(values.Length - 1);
+ return last;
+ }
+
+ static int ToInt(this Enum enumValue) => Convert.ToInt32(enumValue, CultureInfo.InvariantCulture);
+ }
+}
--- /dev/null
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class LabelExtensions
+ {
+ public static TLabel TextStart<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.Start; return label; }
+
+ public static TLabel TextCenterHorizontal<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.Center; return label; }
+
+ public static TLabel TextEnd<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.End; return label; }
+
+ public static TLabel TextTop<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.VerticalTextAlignment = TextAlignment.Start; return label; }
+
+ public static TLabel TextCenterVertical<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.VerticalTextAlignment = TextAlignment.Center; return label; }
+
+ public static TLabel TextBottom<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.VerticalTextAlignment = TextAlignment.End; return label; }
+
+ public static TLabel TextCenter<TLabel>(this TLabel label) where TLabel : Label
+ => label.TextCenterHorizontal().TextCenterVertical();
+
+ public static TLabel FormattedText<TLabel>(this TLabel label, params Span[] spans) where TLabel : Label
+ {
+ VerifyExperimental();
+ label.FormattedText = new FormattedString();
+ foreach (var span in spans)
+ label.FormattedText.Spans.Add(span);
+ return label;
+ }
+ }
+
+ namespace LeftToRight
+ {
+ public static class LabelExtensions
+ {
+ public static TLabel TextLeft<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.Start; return label; }
+
+ public static TLabel TextRight<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.End; return label; }
+ }
+ }
+
+ namespace RightToLeft
+ {
+ public static class LabelExtensions
+ {
+ public static TLabel TextLeft<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.End; return label; }
+
+ public static TLabel TextRight<TLabel>(this TLabel label) where TLabel : Label
+ { VerifyExperimental(); label.HorizontalTextAlignment = TextAlignment.Start; return label; }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class LayoutExtensions
+ {
+ public static TLayout Padding<TLayout>(this TLayout layout, Thickness padding) where TLayout : Layout
+ { VerifyExperimental(); layout.Padding = padding; return layout; }
+
+ public static TLayout Padding<TLayout>(this TLayout layout, double horizontalSize, double verticalSize) where TLayout : Layout
+ { VerifyExperimental(); layout.Padding = new Thickness(horizontalSize, verticalSize); return layout; }
+
+ public static TLayout Paddings<TLayout>(this TLayout layout, double left = 0, double top = 0, double right = 0, double bottom = 0) where TLayout : Layout
+ { VerifyExperimental(); layout.Padding = new Thickness(left, top, right, bottom); return layout; }
+ }
+}
--- /dev/null
+using System.Runtime.CompilerServices;
+
+namespace Xamarin.Forms.Core.Markup
+{
+ internal static class Markup
+ {
+ static bool isExperimentalFlagSet = false;
+
+ internal static void VerifyExperimental([CallerMemberName] string memberName = "", string constructorHint = null)
+ {
+ if (isExperimentalFlagSet)
+ return;
+
+ ExperimentalFlags.VerifyFlagEnabled(nameof(Markup), ExperimentalFlags.MarkupExperimental, constructorHint, memberName);
+
+ isExperimentalFlagSet = true;
+ }
+
+ internal static void ClearExperimental() => isExperimentalFlagSet = false;
+ }
+}
--- /dev/null
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public class Style<T> where T : BindableObject
+ {
+ public static implicit operator Style(Style<T> style) => style?.FormsStyle;
+
+ public Style FormsStyle { get; }
+
+ public Style(params (BindableProperty Property, object Value)[] setters)
+ {
+ VerifyExperimental(constructorHint: nameof(Style));
+ FormsStyle = new Style(typeof(T)) { };
+ Add(setters);
+ }
+
+ public Style<T> ApplyToDerivedTypes(bool value)
+ {
+ FormsStyle.ApplyToDerivedTypes = value;
+ return this;
+ }
+
+ public Style<T> BasedOn(Style value)
+ {
+ FormsStyle.BasedOn = value;
+ return this;
+ }
+
+ public Style<T> Add(params (BindableProperty Property, object Value)[] setters)
+ {
+ foreach (var setter in setters)
+ FormsStyle.Setters.Add(setter.Property, setter.Value);
+ return this;
+ }
+
+ public Style<T> Add(params Behavior[] behaviors)
+ {
+ foreach (var behavior in behaviors)
+ FormsStyle.Behaviors.Add(behavior);
+ return this;
+ }
+
+ public Style<T> Add(params TriggerBase[] triggers)
+ {
+ foreach (var trigger in triggers)
+ FormsStyle.Triggers.Add(trigger);
+ return this;
+ }
+
+ public Style<T> CanCascade(bool value)
+ {
+ FormsStyle.CanCascade = value;
+ return this;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class ViewExtensions
+ {
+ public static TView Start<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.Start; return view; }
+
+ public static TView CenterHorizontal<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.Center; return view; }
+
+ public static TView FillHorizontal<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.Fill; return view; }
+
+ public static TView End<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.End; return view; }
+
+ public static TView StartExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.StartAndExpand; return view; }
+
+ public static TView CenterExpandHorizontal<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.CenterAndExpand; return view; }
+
+ public static TView FillExpandHorizontal<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.FillAndExpand; return view; }
+
+ public static TView EndExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.EndAndExpand; return view; }
+
+ public static TView Top<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.Start; return view; }
+
+ public static TView Bottom<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.End; return view; }
+
+ public static TView CenterVertical<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.Center; return view; }
+
+ public static TView FillVertical<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.Fill; return view; }
+
+ public static TView TopExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.StartAndExpand; return view; }
+
+ public static TView BottomExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.EndAndExpand; return view; }
+
+ public static TView CenterExpandVertical<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.CenterAndExpand; return view; }
+
+ public static TView FillExpandVertical<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.VerticalOptions = LayoutOptions.FillAndExpand; return view; }
+
+ public static TView Center<TView>(this TView view) where TView : View
+ => view.CenterHorizontal().CenterVertical();
+
+ public static TView Fill<TView>(this TView view) where TView : View
+ => view.FillHorizontal().FillVertical();
+
+ public static TView CenterExpand<TView>(this TView view) where TView : View
+ => view.CenterExpandHorizontal().CenterExpandVertical();
+
+ public static TView FillExpand<TView>(this TView view) where TView : View
+ => view.FillExpandHorizontal().FillExpandVertical();
+
+ public static TView Margin<TView>(this TView view, Thickness margin) where TView : View
+ { VerifyExperimental(); view.Margin = margin; return view; }
+
+ public static TView Margin<TView>(this TView view, double horizontal, double vertical) where TView : View
+ { VerifyExperimental(); view.Margin = new Thickness(horizontal, vertical); return view; }
+
+ public static TView Margins<TView>(this TView view, double left = 0, double top = 0, double right = 0, double bottom = 0) where TView : View
+ { VerifyExperimental(); view.Margin = new Thickness(left, top, right, bottom); return view; }
+ }
+
+ namespace LeftToRight
+ {
+ public static class ViewExtensions
+ {
+ public static TView Left<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.Start; return view; }
+
+ public static TView Right<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.End; return view; }
+
+ public static TView LeftExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.StartAndExpand; return view; }
+
+ public static TView RightExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.EndAndExpand; return view; }
+ }
+ }
+
+ namespace RightToLeft
+ {
+ public static class ViewExtensions
+ {
+ public static TView Left<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.End; return view; }
+
+ public static TView Right<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.Start; return view; }
+
+ public static TView LeftExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.EndAndExpand; return view; }
+
+ public static TView RightExpand<TView>(this TView view) where TView : View
+ { VerifyExperimental(); view.HorizontalOptions = LayoutOptions.StartAndExpand; return view; }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class ViewInFlexLayoutExtensions
+ {
+ public static TView AlignSelf<TView>(this TView view, FlexAlignSelf value) where TView : View
+ {
+ VerifyExperimental();
+ FlexLayout.SetAlignSelf(view, value);
+ return view;
+ }
+
+ public static TView Basis<TView>(this TView view, FlexBasis value) where TView : View
+ {
+ VerifyExperimental();
+ FlexLayout.SetBasis(view, value);
+ return view;
+ }
+
+ public static TView Grow<TView>(this TView view, float value) where TView : View
+ {
+ VerifyExperimental();
+ FlexLayout.SetGrow(view, value);
+ return view;
+ }
+
+ public static TView Order<TView>(this TView view, int value) where TView : View
+ {
+ VerifyExperimental();
+ FlexLayout.SetOrder(view, value);
+ return view;
+ }
+
+ public static TView Shrink<TView>(this TView view, float value) where TView : View
+ {
+ VerifyExperimental();
+ FlexLayout.SetShrink(view, value);
+ return view;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Globalization;
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class ViewInGridExtensions
+ {
+ public static TView Row<TView>(this TView view, int row) where TView : View
+ {
+ VerifyExperimental();
+ view.SetValue(Grid.RowProperty, row);
+ return view;
+ }
+
+ public static TView Row<TView>(this TView view, int row, int span) where TView : View
+ {
+ VerifyExperimental();
+ view.SetValue(Grid.RowProperty, row);
+ view.SetValue(Grid.RowSpanProperty, span);
+ return view;
+ }
+
+ public static TView RowSpan<TView>(this TView view, int span) where TView : View
+ {
+ VerifyExperimental();
+ view.SetValue(Grid.RowSpanProperty, span);
+ return view;
+ }
+
+ public static TView Column<TView>(this TView view, int column) where TView : View
+ {
+ VerifyExperimental();
+ view.SetValue(Grid.ColumnProperty, column);
+ return view;
+ }
+
+ public static TView Column<TView>(this TView view, int column, int span) where TView : View
+ {
+ VerifyExperimental();
+ view.SetValue(Grid.ColumnProperty, column);
+ view.SetValue(Grid.ColumnSpanProperty, span);
+ return view;
+ }
+
+ public static TView ColumnSpan<TView>(this TView view, int span) where TView : View
+ {
+ VerifyExperimental();
+ view.SetValue(Grid.ColumnSpanProperty, span);
+ return view;
+ }
+
+ public static TView Row<TView, TRow>(this TView view, TRow row) where TView : View where TRow : Enum
+ {
+ VerifyExperimental();
+ int rowIndex = row.ToInt();
+ view.SetValue(Grid.RowProperty, rowIndex);
+ return view;
+ }
+
+ public static TView Row<TView, TRow>(this TView view, TRow first, TRow last) where TView : View where TRow : Enum
+ {
+ VerifyExperimental();
+ int rowIndex = first.ToInt();
+ int span = last.ToInt() - rowIndex + 1;
+ view.SetValue(Grid.RowProperty, rowIndex);
+ view.SetValue(Grid.RowSpanProperty, span);
+ return view;
+ }
+
+ public static TView Column<TView, TColumn>(this TView view, TColumn column) where TView : View where TColumn : Enum
+ {
+ VerifyExperimental();
+ int columnIndex = column.ToInt();
+ view.SetValue(Grid.ColumnProperty, columnIndex);
+ return view;
+ }
+
+ public static TView Column<TView, TColumn>(this TView view, TColumn first, TColumn last) where TView : View where TColumn : Enum
+ {
+ VerifyExperimental();
+ int columnIndex = first.ToInt();
+ view.SetValue(Grid.ColumnProperty, columnIndex);
+
+ int span = last.ToInt() + 1 - columnIndex;
+ view.SetValue(Grid.ColumnSpanProperty, span);
+
+ return view;
+ }
+
+ static int ToInt(this Enum enumValue) => Convert.ToInt32(enumValue, CultureInfo.InvariantCulture);
+ }
+}
--- /dev/null
+using static Xamarin.Forms.Core.Markup.Markup;
+
+namespace Xamarin.Forms.Markup
+{
+ public static class VisualElementExtensions
+ {
+ public static TElement Height<TElement>(this TElement element, double request) where TElement : VisualElement
+ { VerifyExperimental(); element.HeightRequest = request; return element; }
+
+ public static TElement Width<TElement>(this TElement element, double request) where TElement : VisualElement
+ { VerifyExperimental(); element.WidthRequest = request; return element; }
+
+ public static TElement MinHeight<TElement>(this TElement element, double request) where TElement : VisualElement
+ { VerifyExperimental(); element.MinimumHeightRequest = request; return element; }
+
+ public static TElement MinWidth<TElement>(this TElement element, double request) where TElement : VisualElement
+ { VerifyExperimental(); element.MinimumWidthRequest = request; return element; }
+
+ public static TElement Size<TElement>(this TElement element, double widthRequest, double heightRequest) where TElement : VisualElement
+ => element.Width(widthRequest).Height(heightRequest);
+
+ public static TElement Size<TElement>(this TElement element, double sizeRequest) where TElement : VisualElement
+ => element.Width(sizeRequest).Height(sizeRequest);
+
+ public static TElement MinSize<TElement>(this TElement element, double widthRequest, double heightRequest) where TElement : VisualElement
+ => element.MinWidth(widthRequest).MinHeight(heightRequest);
+
+ public static TElement MinSize<TElement>(this TElement element, double sizeRequest) where TElement : VisualElement
+ => element.MinWidth(sizeRequest).MinHeight(sizeRequest);
+
+ public static T Style<T>(this T view, Style<T> style) where T : VisualElement
+ { VerifyExperimental(); view.Style = style; return view; }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_MediaElementRenderer))]
+ public sealed class MediaElement : View, IMediaElementController
+ {
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static void VerifyMediaElementFlagEnabled(
+ string constructorHint = null,
+ [CallerMemberName] string memberName = "")
+ {
+ ExperimentalFlags.VerifyFlagEnabled(nameof(MediaElement), ExperimentalFlags.MediaElementExperimental, memberName: memberName);
+ }
+
+ public static readonly BindableProperty AspectProperty =
+ BindableProperty.Create(nameof(Aspect), typeof(Aspect), typeof(MediaElement), Aspect.AspectFit);
+
+ public static readonly BindableProperty AutoPlayProperty =
+ BindableProperty.Create(nameof(AutoPlay), typeof(bool), typeof(MediaElement), true);
+
+ public static readonly BindableProperty BufferingProgressProperty =
+ BindableProperty.Create(nameof(BufferingProgress), typeof(double), typeof(MediaElement), 0.0);
+
+ public static readonly BindableProperty CurrentStateProperty =
+ BindableProperty.Create(nameof(CurrentState), typeof(MediaElementState), typeof(MediaElement), MediaElementState.Closed);
+
+ public static readonly BindableProperty DurationProperty =
+ BindableProperty.Create(nameof(Duration), typeof(TimeSpan?), typeof(MediaElement), null);
+
+ public static readonly BindableProperty IsLoopingProperty =
+ BindableProperty.Create(nameof(IsLooping), typeof(bool), typeof(MediaElement), false);
+
+ public static readonly BindableProperty KeepScreenOnProperty =
+ BindableProperty.Create(nameof(KeepScreenOn), typeof(bool), typeof(MediaElement), false);
+
+ public static readonly BindableProperty PositionProperty =
+ BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(MediaElement), TimeSpan.Zero);
+
+ public static readonly BindableProperty ShowsPlaybackControlsProperty =
+ BindableProperty.Create(nameof(ShowsPlaybackControls), typeof(bool), typeof(MediaElement), false);
+
+ public static readonly BindableProperty SourceProperty =
+ BindableProperty.Create(nameof(Source), typeof(MediaSource), typeof(MediaElement),
+ propertyChanging: OnSourcePropertyChanging, propertyChanged: OnSourcePropertyChanged);
+
+ public static readonly BindableProperty VideoHeightProperty =
+ BindableProperty.Create(nameof(VideoHeight), typeof(int), typeof(MediaElement));
+
+ public static readonly BindableProperty VideoWidthProperty =
+ BindableProperty.Create(nameof(VideoWidth), typeof(int), typeof(MediaElement));
+
+ public static readonly BindableProperty VolumeProperty =
+ BindableProperty.Create(nameof(Volume), typeof(double), typeof(MediaElement), 1.0, BindingMode.TwoWay, new BindableProperty.ValidateValueDelegate(ValidateVolume));
+
+ private static bool ValidateVolume(BindableObject o, object newValue)
+ {
+ double d = (double)newValue;
+
+ return d >= 0.0 && d <= 1.0;
+ }
+
+ public Aspect Aspect
+ {
+ get => (Aspect)GetValue(AspectProperty);
+ set => SetValue(AspectProperty, value);
+ }
+
+ public bool AutoPlay
+ {
+ get { return (bool)GetValue(AutoPlayProperty); }
+ set { SetValue(AutoPlayProperty, value); }
+ }
+
+ public double BufferingProgress
+ {
+ get { return (double)GetValue(BufferingProgressProperty); }
+ }
+
+ public bool CanSeek
+ {
+ get { return Source != null && Duration.HasValue; }
+ }
+
+ public MediaElementState CurrentState
+ {
+ get { return (MediaElementState)GetValue(CurrentStateProperty); }
+ }
+
+ public TimeSpan? Duration
+ {
+ get { return (TimeSpan?)GetValue(DurationProperty); }
+ }
+
+ public bool IsLooping
+ {
+ get { return (bool)GetValue(IsLoopingProperty); }
+ set { SetValue(IsLoopingProperty, value); }
+ }
+
+ public bool KeepScreenOn
+ {
+ get { return (bool)GetValue(KeepScreenOnProperty); }
+ set { SetValue(KeepScreenOnProperty, value); }
+ }
+
+ public bool ShowsPlaybackControls
+ {
+ get { return (bool)GetValue(ShowsPlaybackControlsProperty); }
+ set { SetValue(ShowsPlaybackControlsProperty, value); }
+ }
+
+ public TimeSpan Position
+ {
+ get
+ {
+ PositionRequested?.Invoke(this, EventArgs.Empty);
+ return (TimeSpan)GetValue(PositionProperty);
+ }
+
+ set
+ {
+ SeekRequested?.Invoke(this, new SeekRequested(value));
+ }
+ }
+
+ [TypeConverter(typeof(MediaSourceConverter))]
+ public MediaSource Source
+ {
+ get { return (MediaSource)GetValue(SourceProperty); }
+ set { SetValue(SourceProperty, value); }
+ }
+
+ public int VideoHeight
+ {
+ get { return (int)GetValue(VideoHeightProperty); }
+ }
+
+ public int VideoWidth
+ {
+ get { return (int)GetValue(VideoWidthProperty); }
+ }
+
+ public double Volume
+ {
+ get
+ {
+ VolumeRequested?.Invoke(this, EventArgs.Empty);
+ return (double)GetValue(VolumeProperty);
+ }
+ set
+ {
+ SetValue(VolumeProperty, value);
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<SeekRequested> SeekRequested;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler<StateRequested> StateRequested;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler PositionRequested;
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public event EventHandler VolumeRequested;
+
+
+ public void Play()
+ {
+ StateRequested?.Invoke(this, new StateRequested(MediaElementState.Playing));
+ }
+
+ public void Pause()
+ {
+ StateRequested?.Invoke(this, new StateRequested(MediaElementState.Paused));
+ }
+
+ public void Stop()
+ {
+ StateRequested?.Invoke(this, new StateRequested(MediaElementState.Stopped));
+ }
+
+ double IMediaElementController.BufferingProgress { get => (double)GetValue(BufferingProgressProperty); set => SetValue(BufferingProgressProperty, value); }
+ MediaElementState IMediaElementController.CurrentState { get => (MediaElementState)GetValue(CurrentStateProperty); set => SetValue(CurrentStateProperty, value); }
+ TimeSpan? IMediaElementController.Duration { get => (TimeSpan?)GetValue(DurationProperty); set => SetValue(DurationProperty, value); }
+ TimeSpan IMediaElementController.Position { get => (TimeSpan)GetValue(PositionProperty); set => SetValue(PositionProperty, value); }
+ int IMediaElementController.VideoHeight { get => (int)GetValue(VideoHeightProperty); set => SetValue(VideoHeightProperty, value); }
+ int IMediaElementController.VideoWidth { get => (int)GetValue(VideoWidthProperty); set => SetValue(VideoWidthProperty, value); }
+ double IMediaElementController.Volume { get => (double)GetValue(VolumeProperty); set => SetValue(VolumeProperty, value);
+ }
+ void IMediaElementController.OnMediaEnded()
+ {
+ SetValue(CurrentStateProperty, MediaElementState.Stopped);
+ MediaEnded?.Invoke(this, EventArgs.Empty);
+ }
+
+ public event EventHandler MediaEnded;
+
+ void IMediaElementController.OnMediaFailed()
+ {
+ MediaFailed?.Invoke(this, EventArgs.Empty);
+ }
+
+ public event EventHandler MediaFailed;
+
+ void IMediaElementController.OnMediaOpened()
+ {
+ MediaOpened?.Invoke(this, EventArgs.Empty);
+ }
+
+ public event EventHandler MediaOpened;
+
+ void IMediaElementController.OnSeekCompleted()
+ {
+ SeekCompleted?.Invoke(this, EventArgs.Empty);
+ }
+
+ public event EventHandler SeekCompleted;
+
+ protected override void OnBindingContextChanged()
+ {
+ if (Source != null)
+ SetInheritedBindingContext(Source, BindingContext);
+
+ base.OnBindingContextChanged();
+ }
+
+ void OnSourceChanged(object sender, EventArgs eventArgs)
+ {
+ OnPropertyChanged(SourceProperty.PropertyName);
+ InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnSourcePropertyChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((MediaElement)bindable).OnSourcePropertyChanged((MediaSource)oldvalue, (MediaSource)newvalue);
+ }
+
+ void OnSourcePropertyChanged(MediaSource oldvalue, MediaSource newvalue)
+ {
+ if (newvalue != null)
+ {
+ newvalue.SourceChanged += OnSourceChanged;
+ SetInheritedBindingContext(newvalue, BindingContext);
+ }
+
+ InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
+ }
+
+ static void OnSourcePropertyChanging(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((MediaElement)bindable).OnSourcePropertyChanging((MediaSource)oldvalue, (MediaSource)newvalue);
+ }
+
+ void OnSourcePropertyChanging(MediaSource oldvalue, MediaSource newvalue)
+ {
+ if (oldvalue == null)
+ return;
+
+ oldvalue.SourceChanged -= OnSourceChanged;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class SeekRequested : EventArgs
+ {
+ public TimeSpan Position { get; }
+
+ public SeekRequested(TimeSpan position)
+ {
+ Position = position;
+ }
+ }
+
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public class StateRequested : EventArgs
+ {
+ public MediaElementState State { get; }
+
+ public StateRequested(MediaElementState state)
+ {
+ State = state;
+ }
+ }
+
+ public interface IMediaElementController
+ {
+ double BufferingProgress { get; set; }
+ MediaElementState CurrentState { get; set; }
+ TimeSpan? Duration { get; set; }
+ TimeSpan Position { get; set; }
+ int VideoHeight { get; set; }
+ int VideoWidth { get; set; }
+ double Volume { get; set; }
+
+ void OnMediaEnded();
+ void OnMediaFailed();
+ void OnMediaOpened();
+ void OnSeekCompleted();
+
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms
+{
+ public enum MediaElementState
+ {
+ Closed,
+ Opening,
+ Buffering,
+ Playing,
+ Paused,
+ Stopped,
+ }
+}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xamarin.Forms
+{
+ [TypeConverter(typeof(MediaSourceConverter))]
+ public abstract class MediaSource : Element
+ {
+ readonly WeakEventManager _weakEventManager = new WeakEventManager();
+
+ public static MediaSource FromFile(string file)
+ {
+ return new FileMediaSource { File = file };
+ }
+
+ public static MediaSource FromUri(Uri uri)
+ {
+ if (!uri.IsAbsoluteUri)
+ throw new ArgumentException("uri is relative");
+ return new UriMediaSource { Uri = uri };
+ }
+
+ public static implicit operator MediaSource(string source)
+ {
+ Uri uri;
+ return Uri.TryCreate(source, UriKind.Absolute, out uri) && uri.Scheme != "file" ? FromUri(uri) : FromFile(source);
+ }
+
+ public static implicit operator MediaSource(Uri uri)
+ {
+ if (uri is null)
+ return null;
+
+ if (!uri.IsAbsoluteUri)
+ throw new ArgumentException("uri is relative");
+ return FromUri(uri);
+ }
+
+ protected void OnSourceChanged()
+ {
+ _weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(SourceChanged));
+ }
+
+ internal event EventHandler SourceChanged
+ {
+ add { _weakEventManager.AddEventHandler(value); }
+ remove { _weakEventManager.RemoveEventHandler(value); }
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xamarin.Forms
+{
+ [Xaml.TypeConversion(typeof(MediaSource))]
+ public sealed class MediaSourceConverter : TypeConverter
+ {
+ public override object ConvertFromInvariantString(string value)
+ {
+ if (value != null)
+ {
+ Uri uri;
+ return Uri.TryCreate(value, UriKind.Absolute, out uri) && uri.Scheme != "file" ? MediaSource.FromUri(uri) : MediaSource.FromFile(value);
+ }
+
+ throw new InvalidOperationException(string.Format("Cannot convert \"{0}\" into {1}", value, typeof(MediaSource)));
+ }
+ }
+}
--- /dev/null
+using System.ComponentModel;
+using Xamarin.Forms.Internals;
+using FormsDevice = Xamarin.Forms.Device;
+
+namespace Xamarin.Forms
+{
+ public sealed class OrientationStateTrigger : StateTriggerBase
+ {
+ public OrientationStateTrigger()
+ {
+ UpdateState();
+ }
+
+ public DeviceOrientation Orientation
+ {
+ get => (DeviceOrientation)GetValue(OrientationProperty);
+ set => SetValue(OrientationProperty, value);
+ }
+
+ public static readonly BindableProperty OrientationProperty =
+ BindableProperty.Create(nameof(Orientation), typeof(DeviceOrientation), typeof(OrientationStateTrigger), null,
+ propertyChanged: OnOrientationChanged);
+
+ static void OnOrientationChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ ((OrientationStateTrigger)bindable).UpdateState();
+ }
+
+ internal override void OnAttached()
+ {
+ base.OnAttached();
+
+ if (!DesignMode.IsDesignModeEnabled)
+ {
+ UpdateState();
+ FormsDevice.Info.PropertyChanged += OnInfoPropertyChanged;
+ }
+ }
+
+ internal override void OnDetached()
+ {
+ base.OnDetached();
+
+ FormsDevice.Info.PropertyChanged -= OnInfoPropertyChanged;
+ }
+
+ void OnInfoPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if(e.PropertyName == "CurrentOrientation")
+ UpdateState();
+ }
+
+ void UpdateState()
+ {
+ var currentOrientation = FormsDevice.Info.CurrentOrientation;
+
+ switch (Orientation)
+ {
+ case DeviceOrientation.Landscape:
+ case DeviceOrientation.LandscapeLeft:
+ case DeviceOrientation.LandscapeRight:
+ SetActive(
+ currentOrientation == DeviceOrientation.Landscape ||
+ currentOrientation == DeviceOrientation.LandscapeLeft ||
+ currentOrientation == DeviceOrientation.LandscapeRight);
+ break;
+ case DeviceOrientation.Portrait:
+ case DeviceOrientation.PortraitDown:
+ case DeviceOrientation.PortraitUp:
+ SetActive(
+ currentOrientation == DeviceOrientation.Portrait ||
+ currentOrientation == DeviceOrientation.PortraitDown ||
+ currentOrientation == DeviceOrientation.PortraitUp);
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
using System.Threading.Tasks;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform;
-using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
namespace Xamarin.Forms
{
public const string ActionSheetSignalName = "Xamarin.ShowActionSheet";
internal static readonly BindableProperty IgnoresContainerAreaProperty = BindableProperty.Create("IgnoresContainerArea", typeof(bool), typeof(Page), false);
-
+
public static readonly BindableProperty BackgroundImageSourceProperty = BindableProperty.Create(nameof(BackgroundImageSource), typeof(ImageSource), typeof(Page), default(ImageSource));
[Obsolete("BackgroundImageProperty is obsolete as of 4.0.0. Please use BackgroundImageSourceProperty instead.")]
{
get
{
- View titleviewPart1TheShell = null;
- if (OptionalFeatureValues.UseShell)
- {
- titleviewPart1TheShell = Shell.GetTitleView(this);
- }
-
+ var titleviewPart1TheShell = Shell.GetTitleView(this);
var titleViewPart2TheNavBar = NavigationPage.GetTitleView(this);
if (titleviewPart1TheShell != null)
}
}
- internal override ReadOnlyCollection<Element> LogicalChildrenInternal =>
+ internal override ReadOnlyCollection<Element> LogicalChildrenInternal =>
_logicalChildren ?? (_logicalChildren = new ReadOnlyCollection<Element>(InternalChildren));
public event EventHandler LayoutChanged;
MessagingCenter.Send(this, ActionSheetSignalName, args);
else
_pendingActions.Add(() => MessagingCenter.Send(this, ActionSheetSignalName, args));
-
+
return args.Result.Task;
}
[Obsolete("DisplayPromptAsync overload is obsolete as of version 4.5.0 and is no longer supported.")]
[EditorBrowsable(EditorBrowsableState.Never)]
- public Task<string> DisplayPromptAsync(string title, string message, string accept = "OK", string cancel = "Cancel", string placeholder = null, int maxLength = -1, Keyboard keyboard = default(Keyboard))
+ public Task<string> DisplayPromptAsync(string title, string message, string accept, string cancel, string placeholder, int maxLength, Keyboard keyboard)
{
return DisplayPromptAsync(title, message, accept, cancel, placeholder, maxLength, keyboard, "");
}
string GetDisplayMember(object item)
{
if (ItemDisplayBinding == null)
- return item.ToString();
+ return item == null ? string.Empty : item.ToString();
ItemDisplayBinding.Apply(item, this, s_displayProperty);
ItemDisplayBinding.Unapply();
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using XVisualElement = Xamarin.Forms.VisualElement;
+
+namespace Xamarin.Forms.PlatformConfiguration.TizenSpecific
+{
+ public interface IVideoOutput
+ {
+ XVisualElement MediaView { get; }
+ View Controller { get; set; }
+ VideoOuputType OuputType { get; }
+ }
+
+ public enum VideoOuputType
+ {
+ Overlay,
+ Buffer,
+ }
+
+ public enum DisplayAspectMode
+ {
+ Fill,
+ AspectFit,
+ AspectFill,
+ OrignalSize
+ }
+
+ public enum PlaybackState
+ {
+ Stopped,
+ Playing,
+ Paused
+ }
+
+ public interface IMediaPlayer
+ {
+ PlaybackState State { get; }
+
+ event EventHandler PlaybackPaused;
+ event EventHandler PlaybackStarted;
+ event EventHandler PlaybackStopped;
+
+ }
+
+ public interface IPlatformMediaPlayer : IDisposable
+ {
+ bool UsesEmbeddingControls { get; set; }
+ bool AutoPlay { get; set; }
+ bool AutoStop { get; set; }
+ double Volume { get; set; }
+ bool IsMuted { get; set; }
+ bool IsLooping { get; set; }
+ int Position { get; }
+ int Duration { get; }
+ DisplayAspectMode AspectMode { get; set; }
+
+ event EventHandler PlaybackCompleted;
+ event EventHandler PlaybackStarted;
+ event EventHandler PlaybackPaused;
+ event EventHandler PlaybackStopped;
+ event EventHandler UpdateStreamInfo;
+ event EventHandler<BufferingProgressUpdatedEventArgs> BufferingProgressUpdated;
+ event EventHandler ErrorOccurred;
+
+ void SetDisplay(IVideoOutput output);
+ void SetSource(MediaSource source);
+ Task<bool> Start();
+ void Stop();
+ void Pause();
+ Task<int> Seek(int ms);
+ Task<Stream> GetAlbumArts();
+ Task<IDictionary<string, string>> GetMetadata();
+ Task<Size> GetVideoSize();
+ View GetEmbeddingControlView(IMediaPlayer player);
+ }
+
+ public class BufferingProgressUpdatedEventArgs : EventArgs
+ {
+ public double Progress { get; set; }
+ }
+}
{
[Obsolete("PromptArguments overload is obsolete as of version 4.5.0 and is no longer supported.")]
[EditorBrowsable(EditorBrowsableState.Never)]
- public PromptArguments(string title, string message, string accept = "OK", string cancel = "Cancel", string placeholder = null, int maxLength = -1, Keyboard keyboard = default(Keyboard))
+ public PromptArguments(string title, string message, string accept, string cancel, string placeholder, int maxLength, Keyboard keyboard)
: this (title, message, accept, cancel, placeholder, maxLength, keyboard, "")
{ }
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Xamarin.Forms.Platform;
+
+namespace Xamarin.Forms
+{
+ [RenderWith(typeof(_RadioButtonRenderer))]
+ public class RadioButton : Button, IElementConfiguration<RadioButton>
+ {
+ readonly Lazy<PlatformConfigurationRegistry<RadioButton>> _platformConfigurationRegistry;
+
+ static Dictionary<string, List<WeakReference<RadioButton>>> _groupNameToElements;
+
+ public const string IsCheckedVisualState = "IsChecked";
+
+ public static readonly BindableProperty IsCheckedProperty = BindableProperty.Create(
+ nameof(IsChecked), typeof(bool), typeof(RadioButton), false, propertyChanged: (b, o, n) => ((RadioButton)b).OnIsCheckedPropertyChanged((bool)n), defaultBindingMode: BindingMode.TwoWay);
+
+ public static readonly BindableProperty GroupNameProperty = BindableProperty.Create(
+ nameof(GroupName), typeof(string), typeof(RadioButton), null, propertyChanged: (b, o, n) => ((RadioButton)b).OnGroupNamePropertyChanged((string)o, (string)n));
+
+ public static readonly BindableProperty ButtonSourceProperty = BindableProperty.Create(
+ nameof(ButtonSource), typeof(ImageSource), typeof(RadioButton), null);
+
+ public event EventHandler<CheckedChangedEventArgs> CheckedChanged;
+
+ public bool IsChecked
+ {
+ get { return (bool)GetValue(IsCheckedProperty); }
+ set { SetValue(IsCheckedProperty, value); }
+ }
+
+ public string GroupName
+ {
+ get { return (string)GetValue(GroupNameProperty); }
+ set { SetValue(GroupNameProperty, value); }
+ }
+
+ public ImageSource ButtonSource
+ {
+ get { return (ImageSource)GetValue(ButtonSourceProperty); }
+ set { SetValue(ButtonSourceProperty, value); }
+ }
+
+ public RadioButton()
+ {
+ _platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<RadioButton>>(() => new PlatformConfigurationRegistry<RadioButton>(this));
+ }
+
+ public new IPlatformElementConfiguration<T, RadioButton> On<T>() where T : IConfigPlatform
+ {
+ return _platformConfigurationRegistry.Value.On<T>();
+ }
+
+ protected internal override void ChangeVisualState()
+ {
+ if (IsEnabled && IsChecked)
+ VisualStateManager.GoToState(this, IsCheckedVisualState);
+ else
+ base.ChangeVisualState();
+ }
+
+ void OnIsCheckedPropertyChanged(bool isChecked)
+ {
+ if (isChecked)
+ UpdateRadioButtonGroup();
+
+ CheckedChanged?.Invoke(this, new CheckedChangedEventArgs(isChecked));
+ ChangeVisualState();
+ }
+
+ void OnGroupNamePropertyChanged(string oldGroupName, string newGroupName)
+ {
+ // Unregister the old group name if set
+ if (!string.IsNullOrEmpty(oldGroupName))
+ Unregister(this, oldGroupName);
+
+ // Register the new group name is set
+ if (!string.IsNullOrEmpty(newGroupName))
+ Register(this, newGroupName);
+ }
+
+ void UpdateRadioButtonGroup()
+ {
+ string groupName = GroupName;
+ if (!string.IsNullOrEmpty(groupName))
+ {
+ Element rootScope = GetVisualRoot(this);
+
+ if (_groupNameToElements == null)
+ _groupNameToElements = new Dictionary<string, List<WeakReference<RadioButton>>>(1);
+
+ // Get all elements bound to this key and remove this element
+ List<WeakReference<RadioButton>> elements = _groupNameToElements[groupName];
+ for (int i = 0; i < elements.Count;)
+ {
+ WeakReference<RadioButton> weakRef = elements[i];
+ if (weakRef.TryGetTarget(out RadioButton rb))
+ {
+ // Uncheck all checked RadioButtons different from the current one
+ if (rb != this && (rb.IsChecked == true) && rootScope == GetVisualRoot(rb))
+ rb.SetValueFromRenderer(IsCheckedProperty, false);
+
+ i++;
+ }
+ else
+ {
+ // Remove dead instances
+ elements.RemoveAt(i);
+ }
+ }
+ }
+ else // Logical parent should be the group
+ {
+ Element parent = Parent;
+ if (parent != null)
+ {
+ // Traverse logical children
+ IEnumerable children = parent.LogicalChildren;
+ IEnumerator itor = children.GetEnumerator();
+ while (itor.MoveNext())
+ {
+ var rb = itor.Current as RadioButton;
+ if (rb != null && rb != this && string.IsNullOrEmpty(rb.GroupName) && (rb.IsChecked == true))
+ rb.SetValueFromRenderer(IsCheckedProperty, false);
+ }
+ }
+ }
+ }
+
+ static void Register(RadioButton radioButton, string groupName)
+ {
+ if (_groupNameToElements == null)
+ _groupNameToElements = new Dictionary<string, List<WeakReference<RadioButton>>>(1);
+
+ if (_groupNameToElements.TryGetValue(groupName, out List<WeakReference<RadioButton>> elements))
+ {
+ // There were some elements there, remove dead ones
+ PurgeDead(elements, null);
+ }
+ else
+ {
+ elements = new List<WeakReference<RadioButton>>(1);
+ _groupNameToElements[groupName] = elements;
+ }
+
+ elements.Add(new WeakReference<RadioButton>(radioButton));
+ }
+
+ static void Unregister(RadioButton radioButton, string groupName)
+ {
+ if (_groupNameToElements == null)
+ return;
+
+ // Get all elements bound to this key and remove this element
+ if (_groupNameToElements.TryGetValue(groupName, out List<WeakReference<RadioButton>> elements))
+ {
+ PurgeDead(elements, radioButton);
+
+ if (elements.Count == 0)
+ _groupNameToElements.Remove(groupName);
+ }
+ }
+
+ static void PurgeDead(List<WeakReference<RadioButton>> elements, object elementToRemove)
+ {
+ for (int i = 0; i < elements.Count;)
+ {
+ if (elements[i].TryGetTarget(out RadioButton rb) && rb == elementToRemove)
+ elements.RemoveAt(i);
+ else
+ i++;
+ }
+ }
+
+ static Element GetVisualRoot(Element element)
+ {
+ Element parent = element.Parent;
+ while (parent != null && !(parent is Page))
+ parent = parent.Parent;
+ return parent;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+namespace Xamarin.Forms
+{
+ public sealed class StateTrigger : StateTriggerBase
+ {
+ public new bool IsActive
+ {
+ get => (bool)GetValue(IsActiveProperty);
+ set => SetValue(IsActiveProperty, value);
+ }
+
+ public static readonly BindableProperty IsActiveProperty =
+ BindableProperty.Create(nameof(IsActive), typeof(bool), typeof(StateTrigger), default(bool),
+ propertyChanged: OnIsActiveChanged);
+
+ static void OnIsActiveChanged(BindableObject bindable, object oldvalue, object newvalue)
+ {
+ if (newvalue is bool b)
+ {
+ ((StateTrigger)bindable).UpdateState();
+ }
+ }
+
+ internal override void OnAttached()
+ {
+ base.OnAttached();
+ UpdateState();
+ }
+
+ void UpdateState()
+ {
+ SetActive(IsActive);
+ }
+ }
+}
--- /dev/null
+using System;
+
+namespace Xamarin.Forms
+{
+ public abstract class StateTriggerBase : BindableObject
+ {
+ bool _isActive;
+ public event EventHandler IsActiveChanged;
+
+ public StateTriggerBase()
+ {
+ ExperimentalFlags.VerifyFlagEnabled(nameof(StateTriggerBase), ExperimentalFlags.StateTriggersExperimental);
+ }
+
+ public bool IsActive
+ {
+ get => _isActive;
+ private set
+ {
+ if (_isActive == value)
+ return;
+
+ _isActive = value;
+ IsActiveChanged?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ internal VisualState VisualState { get; set; }
+
+ protected void SetActive(bool active)
+ {
+ IsActive = active;
+
+ VisualState?.VisualStateGroup?.UpdateStateTriggers();
+ }
+
+ internal virtual void OnAttached()
+ {
+
+ }
+
+ internal virtual void OnDetached()
+ {
+
+ }
+ }
+}
\ No newline at end of file
using System;
using System.IO;
+#if !NETSTANDARD1_0
+using System.Net.Http;
+#endif
+
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms.Internals;
+
namespace Xamarin.Forms
{
internal class StreamWrapper : Stream
{
readonly Stream _wrapped;
+ IDisposable _additionalDisposable;
+
+ public StreamWrapper(Stream wrapped) : this(wrapped, null)
+ {
+ }
- public StreamWrapper(Stream wrapped)
+ public StreamWrapper(Stream wrapped, IDisposable additionalDisposable)
{
if (wrapped == null)
throw new ArgumentNullException("wrapped");
_wrapped = wrapped;
+ _additionalDisposable = additionalDisposable;
}
public override bool CanRead
protected override void Dispose(bool disposing)
{
_wrapped.Dispose();
- EventHandler eh = Disposed;
- if (eh != null)
- eh(this, EventArgs.Empty);
+ Disposed?.Invoke(this, EventArgs.Empty);
+ _additionalDisposable?.Dispose();
+ _additionalDisposable = null;
base.Dispose(disposing);
}
+
+#if !NETSTANDARD1_0
+
+ public static async Task<Stream> GetStreamAsync(Uri uri, CancellationToken cancellationToken, HttpClient client)
+ {
+ HttpResponseMessage response = await client.GetAsync(uri, cancellationToken).ConfigureAwait(false);
+ if (!response.IsSuccessStatusCode)
+ {
+ Internals.Log.Warning("HTTP Request", $"Could not retrieve {uri}, status code {response.StatusCode}");
+ return null;
+ }
+
+ // the HttpResponseMessage needs to be disposed of after the calling code is done with the stream
+ // otherwise the stream may get disposed before the caller can use it
+ return new StreamWrapper(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), response);
+ }
+#endif
+
}
}
\ No newline at end of file
-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
{
public class SwipeItems : Element, IList<ISwipeItem>, INotifyCollectionChanged
{
- readonly ObservableCollection<ISwipeItem> _internal;
+ readonly ObservableCollection<ISwipeItem> _swipeItems;
- public SwipeItems()
+ public SwipeItems(IEnumerable<ISwipeItem> swipeItems)
{
- _internal = new ObservableCollection<ISwipeItem>();
- _internal.CollectionChanged += OnSwipeItemsChanged;
+ _swipeItems = new ObservableCollection<ISwipeItem>(swipeItems) ?? throw new ArgumentNullException(nameof(swipeItems));
+ _swipeItems.CollectionChanged += OnSwipeItemsChanged;
+ }
+
+ public SwipeItems() : this(Enumerable.Empty<ISwipeItem>())
+ {
+
}
public static readonly BindableProperty ModeProperty = BindableProperty.Create(nameof(Mode), typeof(SwipeMode), typeof(SwipeItems), SwipeMode.Reveal);
public event NotifyCollectionChangedEventHandler CollectionChanged
{
- add { _internal.CollectionChanged += value; }
- remove { _internal.CollectionChanged -= value; }
+ add { _swipeItems.CollectionChanged += value; }
+ remove { _swipeItems.CollectionChanged -= value; }
}
public ISwipeItem this[int index]
{
- get => _internal.Count > index ? _internal[index] : null;
- set => _internal[index] = value;
+ get => _swipeItems.Count > index ? _swipeItems[index] : null;
+ set => _swipeItems[index] = value;
}
- public int Count => _internal.Count;
+ public int Count => _swipeItems.Count;
public bool IsReadOnly => false;
public void Add(ISwipeItem item)
{
- _internal.Add(item);
+ _swipeItems.Add(item);
}
public void Clear()
{
- _internal.Clear();
+ _swipeItems.Clear();
}
public bool Contains(ISwipeItem item)
{
- return _internal.Contains(item);
+ return _swipeItems.Contains(item);
}
public void CopyTo(ISwipeItem[] array, int arrayIndex)
{
- _internal.CopyTo(array, arrayIndex);
+ _swipeItems.CopyTo(array, arrayIndex);
}
public IEnumerator<ISwipeItem> GetEnumerator()
{
- return _internal.GetEnumerator();
+ return _swipeItems.GetEnumerator();
}
public int IndexOf(ISwipeItem item)
{
- return _internal.IndexOf(item);
+ return _swipeItems.IndexOf(item);
}
public void Insert(int index, ISwipeItem item)
{
- _internal.Insert(index, item);
+ _swipeItems.Insert(index, item);
}
public bool Remove(ISwipeItem item)
{
- return _internal.Remove(item);
+ return _swipeItems.Remove(item);
}
public void RemoveAt(int index)
{
- _internal.RemoveAt(index);
+ _swipeItems.RemoveAt(index);
}
protected override void OnBindingContextChanged()
object bc = BindingContext;
- foreach (BindableObject item in _internal)
+ foreach (BindableObject item in _swipeItems)
SetInheritedBindingContext(item, bc);
}
IEnumerator IEnumerable.GetEnumerator()
{
- return _internal.GetEnumerator();
+ return _swipeItems.GetEnumerator();
}
}
}
\ No newline at end of file
_platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<SwipeView>>(() => new PlatformConfigurationRegistry<SwipeView>(this));
}
-
[EditorBrowsable(EditorBrowsableState.Never)]
public static void VerifySwipeViewFlagEnabled(
string constructorHint = null,
ExperimentalFlags.VerifyFlagEnabled(nameof(SwipeView), ExperimentalFlags.SwipeViewExperimental, memberName: memberName);
}
- public static readonly BindableProperty LeftItemsProperty = BindableProperty.Create(nameof(LeftItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
- public static readonly BindableProperty RightItemsProperty = BindableProperty.Create(nameof(RightItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
- public static readonly BindableProperty TopItemsProperty = BindableProperty.Create(nameof(TopItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
- public static readonly BindableProperty BottomItemsProperty = BindableProperty.Create(nameof(BottomItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+ public static readonly BindableProperty LeftItemsProperty =
+ BindableProperty.Create(nameof(LeftItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator,
+ propertyChanged: OnSwipeItemsChanged);
+
+ public static readonly BindableProperty RightItemsProperty =
+ BindableProperty.Create(nameof(RightItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+
+ public static readonly BindableProperty TopItemsProperty =
+ BindableProperty.Create(nameof(TopItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
+
+ public static readonly BindableProperty BottomItemsProperty =
+ BindableProperty.Create(nameof(BottomItems), typeof(SwipeItems), typeof(SwipeView), null, BindingMode.OneWay, null, defaultValueCreator: SwipeItemsDefaultValueCreator);
public SwipeItems LeftItems
{
set { SetValue(BottomItemsProperty, value); }
}
+ static void OnSwipeItemsChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ ((SwipeView)bindable).UpdateSwipeItemsParent((SwipeItems)newValue);
+ }
+
public event EventHandler<SwipeStartedEventArgs> SwipeStarted;
public event EventHandler<SwipeChangingEventArgs> SwipeChanging;
public event EventHandler<SwipeEndedEventArgs> SwipeEnded;
{
return _platformConfigurationRegistry.Value.On<T>();
}
+
+ void UpdateSwipeItemsParent(SwipeItems swipeItems)
+ {
+ swipeItems.Parent = this;
+
+ foreach (var swipeItem in swipeItems)
+ ((VisualElement)swipeItem).Parent = swipeItems;
+ }
}
}
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms.Internals;
+
+namespace Xamarin.Forms
+{
+ public sealed class UriMediaSource : MediaSource
+ {
+ public static readonly BindableProperty UriProperty = BindableProperty.Create(nameof(Uri), typeof(Uri), typeof(UriMediaSource), default(Uri),
+ propertyChanged: (bindable, oldvalue, newvalue) => ((UriMediaSource)bindable).OnSourceChanged(), validateValue: (bindable, value) => value == null || ((Uri)value).IsAbsoluteUri);
+
+
+ [TypeConverter(typeof(UriTypeConverter))]
+ public Uri Uri
+ {
+ get { return (Uri)GetValue(UriProperty); }
+ set { SetValue(UriProperty, value); }
+ }
+
+ public override string ToString()
+ {
+ return $"Uri: {Uri}";
+ }
+ }
+}
\ No newline at end of file
namespace Xamarin.Forms
{
- public class View : VisualElement, IViewController, IGestureController
+ public class View : VisualElement, IViewController, IGestureController, IGestureRecognizers
{
protected internal IGestureController GestureController => this;
if (value && IsNativeStateConsistent)
InvalidateMeasureInternal(InvalidationTrigger.RendererReady);
+ InvalidateStateTriggers(IsPlatformEnabled);
+
OnIsPlatformEnabledChanged();
}
- }
+ }
internal LayoutConstraint SelfConstraint
{
InvalidateMeasureInternal(InvalidationTrigger.MeasureChanged);
}
+ protected override void OnBindingContextChanged()
+ {
+ PropagateBindingContextToStateTriggers();
+ base.OnBindingContextChanged();
+ }
+
protected override void OnChildAdded(Element child)
{
base.OnChildAdded(child);
InvalidateMeasureInternal(trigger);
}
+ internal void InvalidateStateTriggers(bool attach)
+ {
+ var groups = (IList<VisualStateGroup>)GetValue(VisualStateManager.VisualStateGroupsProperty);
+
+ if (groups.Count == 0)
+ return;
+
+ foreach (var group in groups)
+ foreach (var state in group.States)
+ foreach (var stateTrigger in state.StateTriggers)
+ {
+ if(attach)
+ stateTrigger.OnAttached();
+ else
+ stateTrigger.OnDetached();
+ }
+ }
+
internal void MockBounds(Rectangle bounds)
{
#if NETSTANDARD2_0
_mockX = _mockY = _mockWidth = _mockHeight = -1;
}
+ void PropagateBindingContextToStateTriggers()
+ {
+ var groups = (IList<VisualStateGroup>)GetValue(VisualStateManager.VisualStateGroupsProperty);
+
+ if (groups.Count == 0)
+ return;
+
+ foreach (var group in groups)
+ foreach (var state in group.States)
+ foreach (var stateTrigger in state.StateTriggers)
+ SetInheritedBindingContext(stateTrigger, BindingContext);
+ }
+
void OnFocused()
{
EventHandler<FocusEventArgs> focus = Focused;
}
public static readonly BindableProperty VisualStateGroupsProperty =
- BindableProperty.CreateAttached("VisualStateGroups", typeof(VisualStateGroupList), typeof(VisualElement),
- defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged,
- defaultValueCreator: bindable => new VisualStateGroupList {VisualElement = (VisualElement)bindable});
+ BindableProperty.CreateAttached("VisualStateGroups", typeof(VisualStateGroupList), typeof(VisualElement),
+ defaultValue: null, propertyChanged: VisualStateGroupsPropertyChanged,
+ defaultValueCreator: bindable => new VisualStateGroupList { VisualElement = (VisualElement)bindable });
static void VisualStateGroupsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
((VisualStateGroupList)newValue).VisualElement = visualElement;
visualElement.ChangeVisualState();
+
+ UpdateStateTriggers(visualElement);
}
public static IList<VisualStateGroup> GetVisualStateGroups(VisualElement visualElement)
{
return element.IsSet(VisualStateGroupsProperty);
}
+
+ internal static void UpdateStateTriggers(VisualElement visualElement)
+ {
+ var groups = (IList<VisualStateGroup>)visualElement.GetValue(VisualStateGroupsProperty);
+
+ foreach (VisualStateGroup group in groups)
+ {
+ group.VisualElement = visualElement;
+ group.UpdateStateTriggers();
+ }
+ }
}
public class VisualStateGroupList : IList<VisualStateGroup>
readonly HashSet<string> _names = new HashSet<string>();
void Validate(IList<VisualStateGroup> groups)
- {
+ {
var groupCount = groups.Count;
// If we only have 1 group, no need to worry about duplicate group names
{
// Cache the group lookup and states count; it's ugly, but it speeds things up a lot
var group = groups[groupIndex];
+ group.VisualElement = VisualElement;
+ group.UpdateStateTriggers();
+
var stateCount = group.States.Count;
for (int stateIndex = 0; stateIndex < stateCount; stateIndex++)
}
}
- public VisualStateGroupList()
+ public VisualStateGroupList()
{
_internalList = new WatchAddList<VisualStateGroup>(ValidateAndNotify);
}
set => _internalList[index] = value;
}
- internal VisualElement VisualElement { get; set; }
+ internal VisualElement VisualElement { get; set; }
- void OnStatesChanged()
+ void OnStatesChanged()
{
VisualElement?.ChangeVisualState();
}
[RuntimeNameProperty(nameof(Name))]
[ContentProperty(nameof(States))]
- public sealed class VisualStateGroup
+ public sealed class VisualStateGroup
{
public VisualStateGroup()
{
public string Name { get; set; }
public IList<VisualState> States { get; }
public VisualState CurrentState { get; internal set; }
+ internal VisualElement VisualElement { get; set; }
internal VisualState GetState(string name)
{
return null;
}
+ internal bool HasStateTriggers()
+ {
+ bool hasStateTriggers = false;
+
+ foreach (VisualState state in States)
+ {
+ if (state.StateTriggers.Count > 0)
+ {
+ hasStateTriggers = true;
+ break;
+ }
+ }
+
+ return hasStateTriggers;
+ }
+
+ internal VisualState GetActiveTrigger()
+ {
+ var defaultState = default(VisualState);
+ var visualState = defaultState;
+ var conflicts = new List<StateTriggerBase>();
+
+ for (var stateIndex = 0; stateIndex < States.Count; stateIndex++)
+ {
+ var state = States[stateIndex];
+ for (var triggerIndex = 0; triggerIndex < state.StateTriggers.Count; triggerIndex++)
+ {
+ var trigger = state.StateTriggers[triggerIndex];
+
+ if (trigger.IsActive)
+ {
+ if(visualState == defaultState)
+ visualState = state;
+
+ conflicts.Add(trigger);
+ }
+ }
+ }
+
+ if (conflicts.Count > 1)
+ visualState = ResolveStateTriggersConflict(conflicts);
+
+ return visualState;
+ }
+
+ VisualState ResolveStateTriggersConflict(List<StateTriggerBase> conflicts)
+ {
+ // When using StateTriggers to control visual states, the trigger engine uses the following rules to
+ // score triggers and determine which trigger, and the corresponding VisualState, will be active:
+ //
+ // 1. Custom trigger that derives from StateTriggerBase
+ // 2. AdaptiveTrigger activated due to MinWindowWidth
+ // 3. AdaptiveTrigger activated due to MinWindowHeight
+ //
+ // If there are multiple active triggers at a time that have a conflict in scoring (i.e.two custom
+ // triggers), then the first one declared in the markup file takes precedence.
+
+ var existCustomTriggers = conflicts.Where(c => !(c is AdaptiveTrigger));
+
+ if (existCustomTriggers.Count() > 1)
+ {
+ var firstExistCustomTrigger = existCustomTriggers.FirstOrDefault();
+ return firstExistCustomTrigger.VisualState;
+ }
+
+ var adaptiveTriggers = conflicts.Where(c => c is AdaptiveTrigger);
+
+ var minWindowWidthAdaptiveTriggers = adaptiveTriggers.Where(c => ((AdaptiveTrigger)c).MinWindowWidth != -1d).OrderByDescending(c => ((AdaptiveTrigger)c).MinWindowWidth);
+ var latestMinWindowWidthAdaptiveTrigger = minWindowWidthAdaptiveTriggers.FirstOrDefault();
+
+ if (latestMinWindowWidthAdaptiveTrigger != null)
+ return latestMinWindowWidthAdaptiveTrigger.VisualState;
+
+ var minWindowHeightAdaptiveTriggers = adaptiveTriggers.Where(c => ((AdaptiveTrigger)c).MinWindowHeight != -1d).OrderByDescending(c => ((AdaptiveTrigger)c).MinWindowHeight);
+ var latestMinWindowHeightAdaptiveTrigger = minWindowHeightAdaptiveTriggers.FirstOrDefault();
+
+ if (latestMinWindowHeightAdaptiveTrigger != null)
+ return latestMinWindowHeightAdaptiveTrigger.VisualState;
+
+ return default;
+ }
+
internal VisualStateGroup Clone()
{
- var clone = new VisualStateGroup {TargetType = TargetType, Name = Name, CurrentState = CurrentState};
+ var clone = new VisualStateGroup { TargetType = TargetType, Name = Name, CurrentState = CurrentState, VisualElement = VisualElement };
+
foreach (VisualState state in States)
{
+ state.VisualStateGroup = clone;
clone.States.Add(state.Clone());
}
return clone;
}
+ internal void UpdateStateTriggers()
+ {
+ if (VisualElement == null)
+ return;
+
+ bool hasStateTriggers = HasStateTriggers();
+
+ if (!hasStateTriggers)
+ return;
+
+ var newStateTrigger = GetActiveTrigger();
+
+ if (newStateTrigger == null)
+ return;
+
+ var oldStateTrigger = CurrentState;
+
+ if (newStateTrigger == oldStateTrigger)
+ return;
+
+ VisualStateManager.GoToState(VisualElement, newStateTrigger.Name);
+ }
+
internal event EventHandler StatesChanged;
- void OnStatesChanged(IList<VisualState> list)
+ void OnStatesChanged(IList<VisualState> states)
{
- if (list.Any(state => string.IsNullOrEmpty(state.Name)))
+ if (states.Any(state => string.IsNullOrEmpty(state.Name)))
{
throw new InvalidOperationException("State names may not be null or empty");
}
+ foreach(var state in states)
+ {
+ state.VisualStateGroup = this;
+ }
+
StatesChanged?.Invoke(this, EventArgs.Empty);
}
}
[RuntimeNameProperty(nameof(Name))]
- public sealed class VisualState
+ public sealed class VisualState
{
public VisualState()
{
Setters = new ObservableCollection<Setter>();
+ StateTriggers = new WatchAddList<StateTriggerBase>(OnStateTriggersChanged);
}
public string Name { get; set; }
- public IList<Setter> Setters { get;}
+ public IList<Setter> Setters { get; }
+ public IList<StateTriggerBase> StateTriggers { get; }
public Type TargetType { get; set; }
+ internal VisualStateGroup VisualStateGroup { get; set; }
internal VisualState Clone()
{
var clone = new VisualState { Name = Name, TargetType = TargetType };
+
foreach (var setter in Setters)
{
clone.Setters.Add(setter);
}
+ foreach (var stateTrigger in StateTriggers)
+ {
+ stateTrigger.VisualState = this;
+ clone.StateTriggers.Add(stateTrigger);
+ }
+
return clone;
}
+
+ void OnStateTriggersChanged(IList<StateTriggerBase> stateTriggers)
+ {
+ foreach(var stateTrigger in stateTriggers)
+ {
+ stateTrigger.VisualState = this;
+ }
+
+ VisualStateGroup?.UpdateStateTriggers();
+ }
}
internal static class VisualStateGroupListExtensions
internal static IList<VisualStateGroup> Clone(this IList<VisualStateGroup> groups)
{
var actual = new VisualStateGroupList();
+
foreach (var group in groups)
{
+ group.VisualElement = actual.VisualElement;
actual.Add(group.Clone());
}
set => _internalList[index] = value;
}
}
-}
+}
\ No newline at end of file
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
+using System.Net;
namespace Xamarin.Forms
{
public static readonly BindableProperty CanGoForwardProperty = CanGoForwardPropertyKey.BindableProperty;
+ public static readonly BindableProperty CookiesProperty = BindableProperty.Create(nameof(Cookies), typeof(CookieContainer), typeof(WebView), default(string));
+
readonly Lazy<PlatformConfigurationRegistry<WebView>> _platformConfigurationRegistry;
public WebView()
get { return (bool)GetValue(CanGoForwardProperty); }
}
+ public CookieContainer Cookies
+ {
+ get { return (CookieContainer)GetValue(CookiesProperty); }
+ set { SetValue(CookiesProperty, value); }
+ }
+
[TypeConverter(typeof(WebViewSourceTypeConverter))]
public WebViewSource Source
{
namespace Xamarin.Forms.CustomAttributes
{
- [Conditional("DEBUG")]
+
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Event |
Default
}
- [Conditional("DEBUG")]
+
[AttributeUsage(
AttributeTargets.Class |
AttributeTargets.Method,
: $"{IssueTracker.ToString().Substring(0, 1)}{IssueNumber} ({IssueTestNumber})";
}
- [Conditional("DEBUG")]
+
public class UiTestExemptAttribute : Attribute
{
// optional string reason
public string Description => "Description: " + _description;
}
- [Conditional("DEBUG")]
+
public class UiTestFragileAttribute : Attribute
{
// optional string reason
public string Description => "Description: " + _description;
}
- [Conditional("DEBUG")]
+
[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
public class UiTestBrokenAttribute : Attribute
{
Editor,
DatePicker,
CheckBox,
- SwipeView
+ SwipeView,
+ RadioButton
}
public enum Layouts
UncheckedColor
}
+ public enum RadioButton
+ {
+ IsChecked,
+ ButtonSource,
+ }
+
public enum TimePicker
{
Format,
IsScrollAnimated,
NumberOfSideItems,
PeekAreaInsets,
- Position
+ Position,
+ IsBounceEnabled
}
}
-}
-
+}
\ No newline at end of file
--- /dev/null
+using Tizen.Multimedia;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+using ERect = ElmSharp.Rect;
+using MRect = Tizen.Multimedia.Rectangle;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public static class MediaElementExtension
+ {
+ public static MRect ToMultimediaRectangle(this ERect rect)
+ {
+ return new MRect(rect.X, rect.Y, rect.Width, rect.Height);
+ }
+
+ public static PlayerDisplayMode ToNative(this Aspect aspect)
+ {
+ PlayerDisplayMode ret = PlayerDisplayMode.LetterBox;
+ switch (aspect)
+ {
+ case Aspect.AspectFill:
+ ret = PlayerDisplayMode.CroppedFull;
+ break;
+ case Aspect.AspectFit:
+ ret = PlayerDisplayMode.LetterBox;
+ break;
+ case Aspect.Fill:
+ ret = PlayerDisplayMode.FullScreen;
+ break;
+ }
+ return ret;
+ }
+
+ public static PlayerDisplayMode ToNative(this DisplayAspectMode mode)
+ {
+ PlayerDisplayMode ret = PlayerDisplayMode.LetterBox;
+ switch (mode)
+ {
+ case DisplayAspectMode.AspectFill:
+ ret = PlayerDisplayMode.CroppedFull;
+ break;
+ case DisplayAspectMode.AspectFit:
+ ret = PlayerDisplayMode.LetterBox;
+ break;
+ case DisplayAspectMode.Fill:
+ ret = PlayerDisplayMode.FullScreen;
+ break;
+ case DisplayAspectMode.OrignalSize:
+ ret = PlayerDisplayMode.OriginalOrFull;
+ break;
+ }
+ return ret;
+ }
+
+ public static DisplayAspectMode ToDisplayAspectMode(this Aspect aspect)
+ {
+ DisplayAspectMode ret = DisplayAspectMode.AspectFit;
+ switch (aspect)
+ {
+ case Aspect.AspectFill:
+ ret = DisplayAspectMode.AspectFill;
+ break;
+ case Aspect.AspectFit:
+ ret = DisplayAspectMode.AspectFit;
+ break;
+ case Aspect.Fill:
+ ret = DisplayAspectMode.Fill;
+ break;
+ }
+ return ret;
+ }
+ }
+}
void RequestLayoutItems()
{
+ if (AllocatedSize.Width <= 0 || AllocatedSize.Height <= 0)
+ return;
+
if (!_requestLayoutItems)
{
_requestLayoutItems = true;
public void LayoutItems(Rect bound, bool force)
{
+ if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+ return;
+
// TODO : need to optimization. it was frequently called with similar bound value.
if (!ShouldRearrange(bound) && !force)
{
public override void RemoveNativeView(EvasObject native)
{
+ UnBinding(native);
if (_nativeFormsTable.TryGetValue(native, out View view))
{
- ResetBindedView(view);
Platform.GetRenderer(view)?.Dispose();
_nativeFormsTable.Remove(native);
}
int index = GetItemIndex(data);
if (index != -1)
{
- CollectionView.ItemMeasureInvalidated(index);
+ CollectionView?.ItemMeasureInvalidated(index);
}
}
}
public void LayoutItems(Rect bound, bool force)
{
+ if (_allocatedSize.Width <= 0 || _allocatedSize.Height <= 0)
+ return;
+
// TODO : need to optimization. it was frequently called with similar bound value.
if (!ShouldRearrange(bound) && !force)
{
--- /dev/null
+using System;
+using System.Globalization;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+
+using XLabel = Xamarin.Forms.Label;
+using XTextAlignment = Xamarin.Forms.TextAlignment;
+using PlayerState = Xamarin.Forms.PlatformConfiguration.TizenSpecific.PlaybackState;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class EmbeddingControls : ContentView
+ {
+ static readonly string PlayImagePath = "Xamarin.Forms.Platform.Tizen.Resource.img_button_play.png";
+ static readonly string PauseImagePath = "Xamarin.Forms.Platform.Tizen.Resource.img_button_pause.png";
+
+ public ImageButton PlayImage { get; private set; }
+ public ImageButton PauseImage { get; private set; }
+
+ public EmbeddingControls()
+ {
+ PlayImage = new ImageButton
+ {
+ Source = ImageSource.FromResource(PlayImagePath, typeof(EmbeddingControls).Assembly),
+ IsVisible = false
+ };
+ PlayImage.Clicked += OnImageButtonClicked;
+ AbsoluteLayout.SetLayoutFlags(PlayImage, AbsoluteLayoutFlags.All);
+ AbsoluteLayout.SetLayoutBounds(PlayImage, new Rectangle(0.5, 0.5, 0.25, 0.25));
+
+ PauseImage = new ImageButton
+ {
+ Source = ImageSource.FromResource(PauseImagePath, typeof(EmbeddingControls).Assembly),
+ IsVisible = false
+ };
+ PauseImage.Clicked += OnImageButtonClicked;
+ AbsoluteLayout.SetLayoutFlags(PauseImage, AbsoluteLayoutFlags.All);
+ AbsoluteLayout.SetLayoutBounds(PauseImage, new Rectangle(0.5, 0.5, 0.25, 0.25));
+
+ var bufferingLabel = new XLabel
+ {
+ FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label), false),
+ HorizontalTextAlignment = XTextAlignment.Center,
+ TextColor = Color.FromHex("#eeeeeeee")
+ };
+ bufferingLabel.SetBinding(XLabel.TextProperty, new Binding
+ {
+ Path = "BufferingProgress",
+ StringFormat = "{0:0%}"
+ });
+ bufferingLabel.SetBinding(IsVisibleProperty, new Binding
+ {
+ Path = "IsBuffering",
+ });
+ AbsoluteLayout.SetLayoutFlags(bufferingLabel, AbsoluteLayoutFlags.All);
+ AbsoluteLayout.SetLayoutBounds(bufferingLabel, new Rectangle(0.5, 0.5, 0.25, 0.25));
+
+ var progressBoxView = new BoxView
+ {
+ Color = Color.FromHex($"#4286f4")
+ };
+ progressBoxView.SetBinding(AbsoluteLayout.LayoutBoundsProperty, new Binding
+ {
+ Path = "Progress",
+ Converter = new ProgressToBoundTextConverter()
+ });
+ AbsoluteLayout.SetLayoutFlags(progressBoxView, AbsoluteLayoutFlags.All);
+
+ var posLabel = new XLabel
+ {
+ Margin = new Thickness(10, 0, 0, 0),
+ FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(XLabel)),
+ HorizontalTextAlignment = XTextAlignment.Start
+ };
+ posLabel.SetBinding(XLabel.TextProperty, new Binding
+ {
+ Path = "Position",
+ Converter = new MillisecondToTextConverter()
+ });
+ AbsoluteLayout.SetLayoutFlags(posLabel, AbsoluteLayoutFlags.All);
+ AbsoluteLayout.SetLayoutBounds(posLabel, new Rectangle(0,0,1,1));
+
+ var durationLabel = new XLabel
+ {
+ Margin = new Thickness(0, 0, 10, 0),
+ FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(XLabel)),
+ HorizontalTextAlignment = XTextAlignment.End
+ };
+ durationLabel.SetBinding(XLabel.TextProperty, new Binding
+ {
+ Path = "Duration",
+ Converter = new MillisecondToTextConverter()
+ });
+ AbsoluteLayout.SetLayoutFlags(durationLabel, AbsoluteLayoutFlags.All);
+ AbsoluteLayout.SetLayoutBounds(durationLabel, new Rectangle(0, 0, 1, 1));
+
+ var progressInnerLayout = new AbsoluteLayout
+ {
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ HeightRequest = 23,
+ BackgroundColor = Color.FromHex("#80000000"),
+ Children =
+ {
+ progressBoxView,
+ posLabel,
+ durationLabel
+ }
+ };
+
+ var progressLayout = new StackLayout
+ {
+ Children =
+ {
+ new StackLayout { VerticalOptions = LayoutOptions.FillAndExpand },
+ new StackLayout
+ {
+ Margin = Device.Idiom == TargetIdiom.Watch ? new Thickness(80, 0, 80, 0) : 20,
+ VerticalOptions = LayoutOptions.End,
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ BackgroundColor = Color.FromHex("#50000000"),
+ Children = { progressInnerLayout }
+ }
+ }
+ };
+ AbsoluteLayout.SetLayoutFlags(progressLayout, AbsoluteLayoutFlags.All);
+ AbsoluteLayout.SetLayoutBounds(progressLayout, new Rectangle(0, 0, 1, 1));
+
+ Content = new AbsoluteLayout
+ {
+ HorizontalOptions = LayoutOptions.FillAndExpand,
+ VerticalOptions = LayoutOptions.FillAndExpand,
+ Children = {
+ progressLayout,
+ PlayImage,
+ PauseImage,
+ bufferingLabel
+ }
+ };
+ }
+
+ protected override void OnBindingContextChanged()
+ {
+ base.OnBindingContextChanged();
+ if (BindingContext is IMediaPlayer player)
+ {
+ player.PlaybackPaused += OnPlaybackStateChanged;
+ player.PlaybackStarted += OnPlaybackStateChanged;
+ player.PlaybackStopped += OnPlaybackStateChanged;
+ }
+ }
+
+ async void OnPlaybackStateChanged(object sender, EventArgs e)
+ {
+ if (BindingContext is IMediaPlayer player)
+ {
+ if (player.State == PlayerState.Playing)
+ {
+ var unused = PlayImage.FadeTo(0, 100);
+ await PlayImage.ScaleTo(3.0, 300);
+ PlayImage.IsVisible = false;
+ PlayImage.Scale = 1.0;
+
+ PauseImage.IsVisible = true;
+ unused = PauseImage.FadeTo(1, 50);
+ }
+ else
+ {
+ var unused = PauseImage.FadeTo(0, 100);
+ await PauseImage.ScaleTo(3.0, 300);
+ PauseImage.IsVisible = false;
+ PauseImage.Scale = 1.0;
+
+ PlayImage.IsVisible = true;
+ unused = PlayImage.FadeTo(1, 50);
+ }
+ }
+ }
+
+ async void OnImageButtonClicked(object sender, EventArgs e)
+ {
+ if (BindingContext is MediaPlayer player)
+ {
+ if (player.State == PlayerState.Playing)
+ {
+ player.Pause();
+ }
+ else
+ {
+ await player.Start();
+ }
+ }
+ }
+ }
+
+ public class ProgressToBoundTextConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ double progress = (double)value;
+ if (Double.IsNaN(progress))
+ {
+ progress = 0d;
+ }
+ return new Rectangle(0, 0, progress, 1);
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ Rectangle rect = (Rectangle)value;
+ return rect.Width;
+ }
+ }
+
+ public class MillisecondToTextConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ int millisecond = (int)value;
+ int second = (millisecond / 1000) % 60;
+ int min = (millisecond / 1000 / 60) % 60;
+ int hour = (millisecond / 1000 / 60 / 60);
+ if (hour > 0)
+ {
+ return string.Format("{0:d2}:{1:d2}:{2:d2}", hour, min, second);
+ }
+ else
+ {
+ return string.Format("{0:d2}:{1:d2}", min, second);
+ }
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
--- /dev/null
+using System;
+using ElmSharp;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+
+using ELayout = ElmSharp.Layout;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class LayoutCanvas : ELayout, IContainable<EvasObject>
+ {
+ readonly ObservableCollection<EvasObject> _children = new ObservableCollection<EvasObject>();
+ Box _box;
+
+ public LayoutCanvas(EvasObject parent) : base(parent)
+ {
+ SetTheme("layout", "elm_widget", "default");
+ _box = new Box(parent);
+ SetContent(_box);
+
+ _children.CollectionChanged += (o, e) =>
+ {
+ if (e.Action == NotifyCollectionChangedAction.Add)
+ {
+ foreach (var v in e.NewItems)
+ {
+ var view = v as EvasObject;
+ if (null != view)
+ {
+ OnAdd(view);
+ }
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Remove)
+ {
+ foreach (var v in e.OldItems)
+ {
+ if (v is EvasObject view)
+ {
+ OnRemove(view);
+ }
+ }
+ }
+ else if (e.Action == NotifyCollectionChangedAction.Reset)
+ {
+ OnRemoveAll();
+ }
+ };
+ }
+
+ public event EventHandler<LayoutEventArgs> LayoutUpdated
+ {
+ add { _box.LayoutUpdated += value; }
+ remove { _box.LayoutUpdated -= value; }
+ }
+
+ public new IList<EvasObject> Children
+ {
+ get
+ {
+ return _children;
+ }
+ }
+
+ protected override void OnUnrealize()
+ {
+ foreach (var child in _children)
+ {
+ child.Unrealize();
+ }
+
+ base.OnUnrealize();
+ }
+
+ void OnAdd(EvasObject view)
+ {
+ _box.PackEnd(view);
+ }
+
+ void OnRemove(EvasObject view)
+ {
+ _box.UnPack(view);
+ }
+
+ void OnRemoveAll()
+ {
+ _box.UnPackAll();
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Xamarin.Forms.Internals;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class MediaPlayer : Element, IMediaPlayer, IDisposable
+ {
+ public static readonly BindableProperty SourceProperty = BindableProperty.Create(nameof(Source), typeof(MediaSource), typeof(MediaPlayer), default(MediaSource), propertyChanged: OnSourceChanged);
+
+ public static readonly BindableProperty VideoOutputProperty = BindableProperty.Create(nameof(VideoOutput), typeof(IVideoOutput), typeof(MediaPlayer), null, propertyChanging: null, propertyChanged: (b, o, n) => ((MediaPlayer)b).OnVideoOutputChanged());
+
+ public static readonly BindableProperty UsesEmbeddingControlsProperty = BindableProperty.Create(nameof(UsesEmbeddingControls), typeof(bool), typeof(MediaPlayer), true, propertyChanged: (b, o, n) => ((MediaPlayer)b).OnUsesEmbeddingControlsChanged());
+
+ public static readonly BindableProperty VolumeProperty = BindableProperty.Create(nameof(Volume), typeof(double), typeof(MediaPlayer), 1d, coerceValue: (bindable, value) => ((double)value).Clamp(0, 1), propertyChanged: (b, o, n) => ((MediaPlayer)b).OnVolumeChanged());
+
+ public static readonly BindableProperty IsMutedProperty = BindableProperty.Create(nameof(IsMuted), typeof(bool), typeof(MediaPlayer), false, propertyChanged: (b, o, n) => ((MediaPlayer)b).UpdateIsMuted());
+
+ public static readonly BindableProperty AspectModeProperty = BindableProperty.Create(nameof(AspectMode), typeof(DisplayAspectMode), typeof(MediaPlayer), DisplayAspectMode.AspectFit, propertyChanged: (b, o, n) => ((MediaPlayer)b).OnAspectModeChanged());
+
+ public static readonly BindableProperty AutoPlayProperty = BindableProperty.Create(nameof(AutoPlay), typeof(bool), typeof(MediaPlayer), false, propertyChanged: (b, o, n) => ((MediaPlayer)b).UpdateAutoPlay());
+
+ public static readonly BindableProperty AutoStopProperty = BindableProperty.Create(nameof(AutoStop), typeof(bool), typeof(MediaPlayer), true, propertyChanged: (b, o, n) => ((MediaPlayer)b).UpdateAutoStop());
+
+ public static readonly BindableProperty IsLoopingProperty = BindableProperty.Create(nameof(IsLooping), typeof(bool), typeof(MediaPlayer), false, propertyChanged: (b, o, n) => ((MediaPlayer)b).UpdateIsLooping());
+
+ static readonly BindablePropertyKey DurationPropertyKey = BindableProperty.CreateReadOnly(nameof(Duration), typeof(int), typeof(MediaPlayer), 0);
+
+ public static readonly BindableProperty DurationProperty = DurationPropertyKey.BindableProperty;
+
+ static readonly BindablePropertyKey BufferingProgressPropertyKey = BindableProperty.CreateReadOnly(nameof(BufferingProgress), typeof(double), typeof(MediaPlayer), 0d);
+
+ public static readonly BindableProperty BufferingProgressProperty = BufferingProgressPropertyKey.BindableProperty;
+
+ static readonly BindablePropertyKey PositionPropertyKey = BindableProperty.CreateReadOnly(nameof(Position), typeof(int), typeof(MediaPlayer), 0);
+
+ public static readonly BindableProperty PositionProperty = PositionPropertyKey.BindableProperty;
+
+ static readonly BindablePropertyKey StatePropertyKey = BindableProperty.CreateReadOnly(nameof(State), typeof(PlaybackState), typeof(MediaPlayer), PlaybackState.Stopped);
+
+ public static readonly BindableProperty StateProperty = StatePropertyKey.BindableProperty;
+
+ public static readonly BindableProperty PositionUpdateIntervalProperty = BindableProperty.Create(nameof(PositionUpdateInterval), typeof(int), typeof(MediaPlayer), 500);
+
+ static readonly BindablePropertyKey IsBufferingPropertyKey = BindableProperty.CreateReadOnly(nameof(IsBuffering), typeof(bool), typeof(MediaPlayer), false);
+
+ public static readonly BindableProperty IsBufferingProperty = IsBufferingPropertyKey.BindableProperty;
+
+ bool _disposed = false;
+ bool _isPlaying;
+ bool _controlsAlwaysVisible;
+ IPlatformMediaPlayer _impl;
+ CancellationTokenSource _hideTimerCTS = new CancellationTokenSource();
+ Lazy<View> _controls;
+
+ public MediaPlayer()
+ {
+ _impl = new MediaPlayerImpl();
+ _impl.UpdateStreamInfo += OnUpdateStreamInfo;
+ _impl.PlaybackCompleted += SendPlaybackCompleted;
+ _impl.PlaybackStarted += SendPlaybackStarted;
+ _impl.PlaybackPaused += SendPlaybackPaused;
+ _impl.PlaybackStopped += SendPlaybackStopped;
+ _impl.BufferingProgressUpdated += OnUpdateBufferingProgress;
+ _impl.ErrorOccurred += OnErrorOccurred;
+ _impl.UsesEmbeddingControls = true;
+ _impl.Volume = 1d;
+ _impl.AspectMode = DisplayAspectMode.AspectFit;
+ _impl.AutoPlay = false;
+ _impl.AutoStop = true;
+
+ _controlsAlwaysVisible = false;
+ _controls = new Lazy<View>(() =>
+ {
+ return _impl.GetEmbeddingControlView(this);
+ });
+ }
+
+ ~MediaPlayer()
+ {
+ Dispose(false);
+ }
+
+ public DisplayAspectMode AspectMode
+ {
+ get { return (DisplayAspectMode)GetValue(AspectModeProperty); }
+ set { SetValue(AspectModeProperty, value); }
+ }
+
+ public bool AutoPlay
+ {
+ get
+ {
+ return (bool)GetValue(AutoPlayProperty);
+ }
+ set
+ {
+ SetValue(AutoPlayProperty, value);
+ }
+ }
+
+ public bool AutoStop
+ {
+ get
+ {
+ return (bool)GetValue(AutoStopProperty);
+ }
+ set
+ {
+ SetValue(AutoStopProperty, value);
+ }
+ }
+
+ public bool IsLooping
+ {
+ get
+ {
+ return (bool)GetValue(IsLoopingProperty);
+ }
+ set
+ {
+ SetValue(IsLoopingProperty, value);
+ }
+ }
+
+ public double BufferingProgress
+ {
+ get
+ {
+ return (double)GetValue(BufferingProgressProperty);
+ }
+ private set
+ {
+ SetValue(BufferingProgressPropertyKey, value);
+ }
+ }
+
+ public int Duration
+ {
+ get
+ {
+ return (int)GetValue(DurationProperty);
+ }
+ private set
+ {
+ SetValue(DurationPropertyKey, value);
+ }
+ }
+
+ [Xamarin.Forms.TypeConverter(typeof(MediaSourceConverter))]
+ public MediaSource Source
+ {
+ get { return (MediaSource)GetValue(SourceProperty); }
+ set { SetValue(SourceProperty, value); }
+ }
+
+ public IVideoOutput VideoOutput
+ {
+ get { return (IVideoOutput)GetValue(VideoOutputProperty); }
+ set { SetValue(VideoOutputProperty, value); }
+ }
+
+ public double Volume
+ {
+ get { return (double)GetValue(VolumeProperty); }
+ set { SetValue(VolumeProperty, value); }
+ }
+
+ public bool IsMuted
+ {
+ get { return (bool)GetValue(IsMutedProperty); }
+ set { SetValue(IsMutedProperty, value); }
+ }
+
+ public int PositionUpdateInterval
+ {
+ get { return (int)GetValue(PositionUpdateIntervalProperty); }
+ set { SetValue(PositionUpdateIntervalProperty, value); }
+ }
+
+ public bool UsesEmbeddingControls
+ {
+ get
+ {
+ return (bool)GetValue(UsesEmbeddingControlsProperty);
+ }
+ set
+ {
+ SetValue(UsesEmbeddingControlsProperty, value);
+ _impl.UsesEmbeddingControls = value;
+ }
+ }
+
+ public int Position
+ {
+ get
+ {
+ return _impl.Position;
+ }
+ private set
+ {
+ SetValue(PositionPropertyKey, value);
+ OnPropertyChanged(nameof(Progress));
+ }
+ }
+
+ public PlaybackState State
+ {
+ get
+ {
+ return (PlaybackState)GetValue(StateProperty);
+ }
+ private set
+ {
+ SetValue(StatePropertyKey, value);
+ }
+ }
+
+ public bool IsBuffering
+ {
+ get
+ {
+ return (bool)GetValue(IsBufferingProperty);
+ }
+ private set
+ {
+ SetValue(IsBufferingPropertyKey, value);
+ }
+ }
+
+ public double Progress
+ {
+ get
+ {
+ return Position / (double)Math.Max(Position, Duration);
+ }
+ }
+
+ public Command StartCommand => new Command(() =>
+ {
+ if (State == PlaybackState.Playing)
+ {
+ Pause();
+ }
+ else
+ {
+ Start();
+ }
+ });
+
+ public Command FastForwardCommand => new Command(() =>
+ {
+ if (State != PlaybackState.Stopped)
+ {
+ Seek(Math.Min(Position + 5000, Duration));
+ }
+ }, () => State != PlaybackState.Stopped);
+
+ public Command RewindCommand => new Command(() =>
+ {
+ if (State != PlaybackState.Stopped)
+ {
+ Seek(Math.Max(Position - 5000, 0));
+ }
+ }, () => State != PlaybackState.Stopped);
+
+ public event EventHandler PlaybackCompleted;
+ public event EventHandler PlaybackStarted;
+ public event EventHandler PlaybackPaused;
+ public event EventHandler PlaybackStopped;
+ public event EventHandler<BufferingProgressUpdatedEventArgs> BufferingProgressUpdated;
+ public event EventHandler BufferingStarted;
+ public event EventHandler BufferingCompleted;
+ public event EventHandler ErrorOccurred;
+ public event EventHandler MediaPrepared;
+
+ public void Pause()
+ {
+ _impl.Pause();
+ }
+
+ public Task<int> Seek(int ms)
+ {
+ ShowController();
+ return _impl.Seek(ms).ContinueWith((t) => Position = _impl.Position);
+ }
+
+ public Task<bool> Start()
+ {
+ return _impl.Start();
+ }
+
+ public void Stop()
+ {
+ _impl.Stop();
+ }
+
+ public Task<Stream> GetAlbumArts()
+ {
+ return _impl.GetAlbumArts();
+ }
+
+ public Task<IDictionary<string, string>> GetMetadata()
+ {
+ return _impl.GetMetadata();
+ }
+
+ public Task<Size> GetVideoSize()
+ {
+ return _impl.GetVideoSize();
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ _impl.UpdateStreamInfo -= OnUpdateStreamInfo;
+ _impl.PlaybackCompleted -= SendPlaybackCompleted;
+ _impl.PlaybackStarted -= SendPlaybackStarted;
+ _impl.PlaybackPaused -= SendPlaybackPaused;
+ _impl.PlaybackStopped -= SendPlaybackStopped;
+ _impl.BufferingProgressUpdated -= OnUpdateBufferingProgress;
+ _impl.ErrorOccurred -= OnErrorOccurred;
+ _impl.Dispose();
+ }
+
+ _disposed = true;
+ }
+
+ void UpdateAutoPlay()
+ {
+ _impl.AutoPlay = AutoPlay;
+ }
+
+ void UpdateAutoStop()
+ {
+ _impl.AutoStop = AutoStop;
+ }
+
+ void UpdateIsMuted()
+ {
+ _impl.IsMuted = IsMuted;
+ }
+
+ void UpdateIsLooping()
+ {
+ _impl.IsLooping = IsLooping;
+ }
+
+ void OnUpdateStreamInfo(object sender, EventArgs e)
+ {
+ Duration = _impl.Duration;
+ MediaPrepared?.Invoke(this, EventArgs.Empty);
+ }
+
+ void SendPlaybackCompleted(object sender, EventArgs e)
+ {
+ PlaybackCompleted?.Invoke(this, EventArgs.Empty);
+ }
+
+ void SendPlaybackStarted(object sender, EventArgs e)
+ {
+ _isPlaying = true;
+ State = PlaybackState.Playing;
+ StartPostionPollingTimer();
+ PlaybackStarted?.Invoke(this, EventArgs.Empty);
+ _controlsAlwaysVisible = false;
+ ShowController();
+ }
+
+ void SendPlaybackPaused(object sender, EventArgs e)
+ {
+ _isPlaying = false;
+ State = PlaybackState.Paused;
+ PlaybackPaused?.Invoke(this, EventArgs.Empty);
+ _controlsAlwaysVisible = true;
+ ShowController();
+ }
+
+ void SendPlaybackStopped(object sender, EventArgs e)
+ {
+ _isPlaying = false;
+ State = PlaybackState.Stopped;
+ Position = 0;
+ PlaybackStopped?.Invoke(this, EventArgs.Empty);
+ _controlsAlwaysVisible = true;
+ ShowController();
+ }
+
+ void StartPostionPollingTimer()
+ {
+ Device.StartTimer(TimeSpan.FromMilliseconds(PositionUpdateInterval), () =>
+ {
+ Position = _impl.Position;
+ return _isPlaying;
+ });
+ }
+
+ void OnSourceChanged(object sender, EventArgs e)
+ {
+ _impl.SetSource(Source);
+ }
+
+ void OnVideoOutputChanged()
+ {
+ if (VideoOutput != null)
+ {
+ if (UsesEmbeddingControls)
+ {
+ VideoOutput.Controller = _controls.Value;
+ }
+ VideoOutput.MediaView.Focused += OnVideoOutputFocused;
+ if (VideoOutput.MediaView is View outputView)
+ {
+ TapGestureRecognizer tapGesture = new TapGestureRecognizer();
+ tapGesture.Tapped += OnOutputTapped;
+ outputView.GestureRecognizers.Add(tapGesture);
+ }
+ }
+ _impl.SetDisplay(VideoOutput);
+ }
+
+ void OnOutputTapped(object sender, EventArgs e)
+ {
+ if (!UsesEmbeddingControls)
+ return;
+ if (!_controls.Value.IsVisible)
+ {
+ ShowController();
+ }
+ }
+
+ async void OnUsesEmbeddingControlsChanged()
+ {
+ if (UsesEmbeddingControls)
+ {
+ if (VideoOutput != null)
+ {
+ VideoOutput.Controller = _controls.Value;
+ ShowController();
+ }
+ }
+ else
+ {
+ if (VideoOutput != null)
+ {
+ HideController(0);
+ await Task.Delay(200);
+ VideoOutput.Controller = null;
+ }
+ }
+ }
+
+ void OnVideoOutputFocused(object sender, FocusEventArgs e)
+ {
+ if (UsesEmbeddingControls)
+ {
+ ShowController();
+ }
+ }
+
+ void OnVolumeChanged()
+ {
+ _impl.Volume = Volume;
+ }
+
+ void OnAspectModeChanged()
+ {
+ _impl.AspectMode = AspectMode;
+ }
+
+ void OnUpdateBufferingProgress(object sender, BufferingProgressUpdatedEventArgs e)
+ {
+ if (!IsBuffering && e.Progress >= 0)
+ {
+ IsBuffering = true;
+ BufferingStarted?.Invoke(this, EventArgs.Empty);
+ }
+ else if (IsBuffering && e.Progress == 1.0)
+ {
+ IsBuffering = false;
+ BufferingCompleted?.Invoke(this, EventArgs.Empty);
+ }
+ BufferingProgress = e.Progress;
+ BufferingProgressUpdated?.Invoke(this, new BufferingProgressUpdatedEventArgs { Progress = BufferingProgress });
+ }
+
+ void OnErrorOccurred(object sender, EventArgs e)
+ {
+ ErrorOccurred?.Invoke(this, EventArgs.Empty);
+ }
+
+ async void HideController(int after)
+ {
+ if (!_controls.IsValueCreated)
+ return;
+
+ _hideTimerCTS?.Cancel();
+ _hideTimerCTS?.Dispose();
+ _hideTimerCTS = new CancellationTokenSource();
+ try
+ {
+ await Task.Delay(after, _hideTimerCTS.Token);
+
+ if (!_controlsAlwaysVisible)
+ {
+ await _controls.Value.FadeTo(0, 200);
+ _controls.Value.IsVisible = false;
+ }
+ }
+ catch (Exception)
+ {
+ //Exception when canceled
+ }
+ }
+
+ void ShowController()
+ {
+ if (_controls.IsValueCreated)
+ {
+ _controls.Value.IsVisible = true;
+ _controls.Value.FadeTo(1.0, 200);
+ HideController(5000);
+ }
+ }
+
+ static void OnSourceChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ (bindable as MediaPlayer)?.OnSourceChanged(bindable, EventArgs.Empty);
+ }
+ }
+}
--- /dev/null
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.IO;
+using System.Threading.Tasks;
+using Tizen.Multimedia;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+
+[assembly: Xamarin.Forms.Dependency(typeof(IPlatformMediaPlayer))]
+namespace Xamarin.Forms.Platform.Tizen.Native
+{
+ public class MediaPlayerImpl : IPlatformMediaPlayer
+ {
+ bool _disposed = false;
+ bool _cancelToStart;
+ DisplayAspectMode _aspectMode = DisplayAspectMode.AspectFit;
+ Player _player;
+ Task _taskPrepare;
+ TaskCompletionSource<bool> _tcsForStreamInfo;
+ IVideoOutput _videoOutput;
+ MediaSource _source;
+
+ public MediaPlayerImpl()
+ {
+ _player = new Player();
+ _player.PlaybackCompleted += OnPlaybackCompleted;
+ _player.BufferingProgressChanged += OnBufferingProgressChanged;
+ _player.ErrorOccurred += OnErrorOccurred;
+ }
+
+ ~MediaPlayerImpl()
+ {
+ Dispose(false);
+ }
+
+ public bool UsesEmbeddingControls { get; set; }
+
+ public bool AutoPlay { get; set; }
+
+ public bool AutoStop { get; set; }
+
+ public double Volume
+ {
+ get => _player.Volume;
+ set => _player.Volume = (float)value;
+ }
+
+ public bool IsMuted
+ {
+ get => _player.Muted;
+ set => _player.Muted = value;
+ }
+
+ public bool IsLooping
+ {
+ get => _player.IsLooping;
+ set => _player.IsLooping = value;
+ }
+
+ public int Duration => _player.StreamInfo.GetDuration();
+
+ public int Position
+ {
+ get
+ {
+ if (_player.State == PlayerState.Idle || _player.State == PlayerState.Preparing)
+ return 0;
+ return _player.GetPlayPosition();
+ }
+ }
+
+ public DisplayAspectMode AspectMode
+ {
+ get { return _aspectMode; }
+ set
+ {
+ _aspectMode = value;
+ ApplyAspectMode();
+ }
+ }
+
+ bool HasSource => _source != null;
+
+ IVideoOutput VideoOutput
+ {
+ get { return _videoOutput; }
+ set
+ {
+ if (TargetView != null)
+ TargetView.PropertyChanged -= OnTargetViewPropertyChanged;
+
+ _videoOutput = value;
+
+ if (TargetView != null)
+ {
+ TargetView.PropertyChanged += OnTargetViewPropertyChanged;
+ }
+ }
+ }
+
+ VisualElement TargetView => VideoOutput?.MediaView;
+
+ Task TaskPrepare
+ {
+ get => _taskPrepare ?? Task.CompletedTask;
+ set => _taskPrepare = value;
+ }
+
+ public event EventHandler UpdateStreamInfo;
+ public event EventHandler PlaybackCompleted;
+ public event EventHandler PlaybackStarted;
+ public event EventHandler<BufferingProgressUpdatedEventArgs> BufferingProgressUpdated;
+ public event EventHandler PlaybackStopped;
+ public event EventHandler PlaybackPaused;
+ public event EventHandler ErrorOccurred;
+
+ public async Task<bool> Start()
+ {
+ _cancelToStart = false;
+ if (!HasSource)
+ return false;
+
+ if (_player.State == PlayerState.Idle)
+ {
+ await Prepare();
+ }
+
+ if (_cancelToStart)
+ return false;
+
+ try
+ {
+ _player.Start();
+ }
+ catch (Exception e)
+ {
+ Log.Error($"Error On Start : {e.Message}");
+ return false;
+ }
+ PlaybackStarted?.Invoke(this, EventArgs.Empty);
+ return true;
+ }
+
+ public void Pause()
+ {
+ try
+ {
+ _player.Pause();
+ PlaybackPaused?.Invoke(this, EventArgs.Empty);
+ }
+ catch (Exception e)
+ {
+ Log.Error($"Error on Pause : {e.Message}");
+ }
+ }
+
+ public void Stop()
+ {
+ _cancelToStart = true;
+ var unusedTask = ChangeToIdleState();
+ PlaybackStopped?.Invoke(this, EventArgs.Empty);
+ }
+
+ public void SetDisplay(IVideoOutput output)
+ {
+ VideoOutput = output;
+ }
+
+ public async Task<int> Seek(int ms)
+ {
+ try
+ {
+ await _player.SetPlayPositionAsync(ms, false);
+ }
+ catch (Exception e)
+ {
+ Log.Error($"Fail to seek : {e.Message}");
+ }
+ return Position;
+ }
+
+ public void SetSource(MediaSource source)
+ {
+ _source = source;
+ if (HasSource && AutoPlay)
+ {
+ var unusedTask = Start();
+ }
+ }
+
+ public async Task<Stream> GetAlbumArts()
+ {
+ if (_player.State == PlayerState.Idle)
+ {
+ if (_tcsForStreamInfo == null || _tcsForStreamInfo.Task.IsCompleted)
+ {
+ _tcsForStreamInfo = new TaskCompletionSource<bool>();
+ }
+ await _tcsForStreamInfo.Task;
+ }
+ await TaskPrepare;
+
+ var imageData = _player.StreamInfo.GetAlbumArt();
+ if (imageData == null)
+ return null;
+ return new MemoryStream(imageData);
+ }
+
+ public async Task<IDictionary<string, string>> GetMetadata()
+ {
+ if (_player.State == PlayerState.Idle)
+ {
+ if (_tcsForStreamInfo == null || _tcsForStreamInfo.Task.IsCompleted)
+ {
+ _tcsForStreamInfo = new TaskCompletionSource<bool>();
+ }
+ await _tcsForStreamInfo.Task;
+ }
+ await TaskPrepare;
+
+ Dictionary<string, string> metadata = new Dictionary<string, string>
+ {
+ [nameof(StreamMetadataKey.Album)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Album),
+ [nameof(StreamMetadataKey.Artist)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Artist),
+ [nameof(StreamMetadataKey.Author)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Author),
+ [nameof(StreamMetadataKey.Genre)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Genre),
+ [nameof(StreamMetadataKey.Title)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Title),
+ [nameof(StreamMetadataKey.Year)] = _player.StreamInfo.GetMetadata(StreamMetadataKey.Year)
+ };
+ return metadata;
+ }
+
+ public async Task<Size> GetVideoSize()
+ {
+ if (_player.State == PlayerState.Idle)
+ {
+ if (_tcsForStreamInfo == null || _tcsForStreamInfo.Task.IsCompleted)
+ {
+ _tcsForStreamInfo = new TaskCompletionSource<bool>();
+ }
+ await _tcsForStreamInfo.Task;
+ }
+ await TaskPrepare;
+
+ var videoSize = _player.StreamInfo.GetVideoProperties().Size;
+ return new Size(videoSize.Width, videoSize.Height);
+ }
+
+ public View GetEmbeddingControlView(IMediaPlayer player)
+ {
+ return new EmbeddingControls
+ {
+ BindingContext = player
+ };
+ }
+
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ protected virtual void Dispose(bool disposing)
+ {
+ if (_disposed)
+ return;
+
+ if (disposing)
+ {
+ _player.PlaybackCompleted -= OnPlaybackCompleted;
+ _player.BufferingProgressChanged -= OnBufferingProgressChanged;
+ _player.ErrorOccurred -= OnErrorOccurred;
+ _player.Dispose();
+ }
+
+ _disposed = true;
+ }
+
+ void ApplyDisplay()
+ {
+ if (VideoOutput == null)
+ {
+ _player.Display = null;
+ }
+ else
+ {
+ var renderer = Platform.GetRenderer(TargetView);
+ if (renderer is IMediaViewProvider provider && provider.GetMediaView() != null)
+ {
+ try
+ {
+ Display display = new Display(provider.GetMediaView());
+ _player.Display = display;
+ _player.DisplaySettings.Mode = _aspectMode.ToNative();
+ }
+ catch
+ {
+ Log.Error("Error on MediaView");
+ }
+ }
+ }
+ }
+
+ void ApplySource()
+ {
+ if (_source == null)
+ {
+ return;
+ }
+
+ if (_source is UriMediaSource uriSource)
+ {
+ var uri = uriSource.Uri;
+ _player.SetSource(new MediaUriSource(uri.IsFile ? uri.LocalPath : uri.AbsoluteUri));
+ }
+ else if (_source is FileMediaSource fileSource)
+ {
+ _player.SetSource(new MediaUriSource(ResourcePath.GetPath(fileSource.File)));
+ }
+ }
+
+ async void OnTargetViewPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ if (e.PropertyName == "Renderer")
+ {
+ if (Platform.GetRenderer(sender as BindableObject) != null && HasSource && AutoPlay)
+ {
+ await Start();
+ }
+ else if (Platform.GetRenderer(sender as BindableObject) == null && AutoStop)
+ {
+ Stop();
+ }
+ }
+ }
+
+ async Task Prepare()
+ {
+ TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
+ var prevTask = TaskPrepare;
+ TaskPrepare = tcs.Task;
+ await prevTask;
+
+ if (_player.State == PlayerState.Ready)
+ return;
+
+ ApplyDisplay();
+ ApplySource();
+
+ try
+ {
+ await _player.PrepareAsync();
+ UpdateStreamInfo?.Invoke(this, EventArgs.Empty);
+ _tcsForStreamInfo?.TrySetResult(true);
+ }
+ catch (Exception e)
+ {
+ Log.Error($"Error on prepare : {e.Message}");
+ }
+ tcs.SetResult(true);
+ }
+
+ async void ApplyAspectMode()
+ {
+ if (_player.State == PlayerState.Preparing)
+ {
+ await TaskPrepare;
+ }
+ _player.DisplaySettings.Mode = AspectMode.ToNative();
+ }
+
+ void OnBufferingProgressChanged(object sender, BufferingProgressChangedEventArgs e)
+ {
+ BufferingProgressUpdated?.Invoke(this, new BufferingProgressUpdatedEventArgs { Progress = e.Percent / 100.0 });
+ }
+
+ void OnPlaybackCompleted(object sender, EventArgs e)
+ {
+ PlaybackCompleted?.Invoke(this, EventArgs.Empty);
+ }
+
+ void OnErrorOccurred(object sender, PlayerErrorOccurredEventArgs e)
+ {
+ Log.Error($"Playback Error Occurred (code:{e.Error})-{e.ToString()}");
+ ErrorOccurred?.Invoke(this, EventArgs.Empty);
+ }
+
+ async Task ChangeToIdleState()
+ {
+ switch (_player.State)
+ {
+ case PlayerState.Playing:
+ case PlayerState.Paused:
+ _player.Stop();
+ _player.Unprepare();
+ break;
+ case PlayerState.Ready:
+ _player.Unprepare();
+ break;
+ case PlayerState.Preparing:
+ await TaskPrepare;
+ _player.Unprepare();
+ break;
+ }
+ }
+ }
+}
[assembly: ExportRenderer(typeof(CarouselView), typeof(CarouselViewRenderer))]
[assembly: ExportRenderer(typeof(SwipeView), typeof(SwipeViewRenderer))]
[assembly: ExportRenderer(typeof(RefreshView), typeof(RefreshViewRenderer))]
+[assembly: ExportRenderer(typeof(MediaElement), typeof(MediaElementRenderer))]
[assembly: ExportImageSourceHandler(typeof(FileImageSource), typeof(FileImageSourceHandler))]
[assembly: ExportImageSourceHandler(typeof(StreamImageSource), typeof(StreamImageSourceHandler))]
--- /dev/null
+using System;
+using System.Globalization;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
+using Xamarin.Forms.Platform.Tizen.Native;
+using ElmSharp;
+using Tizen.Multimedia;
+using XForms = Xamarin.Forms.Forms;
+
+namespace Xamarin.Forms.Platform.Tizen
+{
+ public class MediaElementRenderer : ViewRenderer<MediaElement, LayoutCanvas>, IMediaViewProvider, IVideoOutput
+ {
+ MediaPlayer _player;
+ MediaView _mediaView;
+ View _controller;
+ EvasObject _nativeController;
+
+ IMediaElementController Controller => Element as IMediaElementController;
+
+ VisualElement IVideoOutput.MediaView => Element;
+
+ View IVideoOutput.Controller
+ {
+ get
+ {
+ return _controller;
+ }
+ set
+ {
+ if (_controller != null)
+ {
+ _controller.Parent = null;
+ Control.Children.Remove(_nativeController);
+ _nativeController.Unrealize();
+ }
+
+ _controller = value;
+
+ if (_controller != null)
+ {
+ _controller.Parent = Element;
+ _nativeController = Platform.GetOrCreateRenderer(_controller).NativeView;
+ Control.Children.Add(_nativeController);
+ }
+ }
+ }
+
+ VideoOuputType IVideoOutput.OuputType => VideoOuputType.Buffer;
+
+ MediaView IMediaViewProvider.GetMediaView()
+ {
+ return _mediaView;
+ }
+
+ public MediaElementRenderer()
+ {
+ MediaElement.VerifyMediaElementFlagEnabled(nameof(MediaElementRenderer));
+ }
+
+ protected override void OnElementChanged(ElementChangedEventArgs<MediaElement> e)
+ {
+ if (e.OldElement != null)
+ {
+ e.OldElement.SeekRequested -= OnSeekRequested;
+ e.OldElement.StateRequested -= OnStateRequested;
+ e.OldElement.PositionRequested -= OnPositionRequested;
+ }
+
+ if (Control == null)
+ {
+ SetNativeControl(new LayoutCanvas(XForms.NativeParent));
+ _mediaView = new MediaView(XForms.NativeParent);
+ Control.LayoutUpdated += (s, evt) => OnLayout();
+ Control.Children.Add(_mediaView);
+ Control.AllowFocus(true);
+
+ _player = new MediaPlayer()
+ {
+ VideoOutput = this
+ };
+ _player.PlaybackStarted += OnPlaybackStarted;
+ _player.PlaybackPaused += OnPlaybackPaused;
+ _player.PlaybackStopped += OnPlaybackStopped;
+ _player.PlaybackCompleted += OnPlaybackCompleted;
+ _player.BufferingProgressUpdated += OnBufferingProgressUpdated;
+ _player.ErrorOccurred += OnErrorOccurred;
+ _player.MediaPrepared += OnMediaPrepared;
+ _player.BindingContext = Element;
+ _player.SetBinding(MediaPlayer.SourceProperty, "Source");
+ _player.SetBinding(MediaPlayer.UsesEmbeddingControlsProperty, "ShowsPlaybackControls");
+ _player.SetBinding(MediaPlayer.AutoPlayProperty, "AutoPlay");
+ _player.SetBinding(MediaPlayer.VolumeProperty, "Volume");
+ _player.SetBinding(MediaPlayer.IsLoopingProperty, "IsLooping");
+ _player.SetBinding(MediaPlayer.AspectModeProperty, new Binding
+ {
+ Path = "Aspect",
+ Converter = new AspectToDisplayAspectModeConverter()
+ });
+ BindableObject.SetInheritedBindingContext(_player, Element.BindingContext);
+
+ Element.SeekRequested += OnSeekRequested;
+ Element.StateRequested += OnStateRequested;
+ Element.PositionRequested += OnPositionRequested;
+ }
+ base.OnElementChanged(e);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ Element.SeekRequested -= OnSeekRequested;
+ Element.StateRequested -= OnStateRequested;
+ Element.PositionRequested -= OnPositionRequested;
+ _mediaView?.Unrealize();
+ if (_player != null)
+ {
+ _player.PlaybackStarted -= OnPlaybackStarted;
+ _player.PlaybackPaused -= OnPlaybackPaused;
+ _player.PlaybackStopped -= OnPlaybackStopped;
+ _player.PlaybackCompleted -= OnPlaybackCompleted;
+ _player.BufferingProgressUpdated -= OnBufferingProgressUpdated;
+ _player.ErrorOccurred -= OnErrorOccurred;
+ _player.MediaPrepared -= OnMediaPrepared;
+ _player.Dispose();
+ }
+ if (_controller != null )
+ {
+ _controller.Parent = null;
+ Platform.SetRenderer(_controller, null);
+ }
+ _nativeController?.Unrealize();
+ Control?.Unrealize();
+ }
+ base.Dispose(disposing);
+ }
+
+ protected void OnLayout()
+ {
+ _mediaView.Geometry = Control.Geometry;
+ _controller?.Layout(Element.Bounds);
+ if (_nativeController != null)
+ _nativeController.Geometry = Control.Geometry;
+ }
+
+ protected void OnSeekRequested(object sender, SeekRequested e)
+ {
+ _player.Seek((int)e.Position.TotalMilliseconds);
+ }
+
+ protected void OnStateRequested(object sender, StateRequested e)
+ {
+ switch (e.State)
+ {
+ case MediaElementState.Playing:
+ _player.Start();
+ break;
+ case MediaElementState.Paused:
+ _player.Pause();
+ break;
+ case MediaElementState.Stopped:
+ _player.Stop();
+ break;
+ }
+ }
+
+ protected void OnPositionRequested(object sender, EventArgs e)
+ {
+ Controller.Position = TimeSpan.FromMilliseconds(_player.Position);
+ }
+
+ protected void OnPlaybackStarted(object sender, EventArgs e)
+ {
+ Controller.CurrentState = MediaElementState.Playing;
+ }
+
+ protected void OnPlaybackPaused(object sender, EventArgs e)
+ {
+ Controller.CurrentState = MediaElementState.Paused;
+ }
+
+ protected void OnPlaybackStopped(object sender, EventArgs e)
+ {
+ Controller.CurrentState = MediaElementState.Stopped;
+ }
+
+ protected void OnPlaybackCompleted(object sender, EventArgs e)
+ {
+ Controller.OnMediaEnded();
+ }
+
+ protected void OnBufferingProgressUpdated(object sender, BufferingProgressUpdatedEventArgs e)
+ {
+ if (e.Progress == 1.0)
+ {
+ switch (_player.State)
+ {
+ case PlaybackState.Paused:
+ Controller.CurrentState = MediaElementState.Paused;
+ break;
+ case PlaybackState.Playing:
+ Controller.CurrentState = MediaElementState.Playing;
+ break;
+ case PlaybackState.Stopped:
+ Controller.CurrentState = MediaElementState.Stopped;
+ break;
+ }
+ }
+ else if (Controller.CurrentState != MediaElementState.Buffering && e.Progress >= 0)
+ {
+ Controller.CurrentState = MediaElementState.Buffering;
+ }
+ Controller.BufferingProgress = e.Progress;
+ }
+
+ protected void OnErrorOccurred(object sender, EventArgs e)
+ {
+ Controller.OnMediaFailed();
+ }
+
+ protected async void OnMediaPrepared(object sender, EventArgs e)
+ {
+ Controller.OnMediaOpened();
+ Controller.Duration = TimeSpan.FromMilliseconds(_player.Duration);
+ var videoSize = await _player.GetVideoSize();
+ Controller.VideoWidth = (int)videoSize.Width;
+ Controller.VideoHeight = (int)videoSize.Height;
+ }
+ }
+
+ public interface IMediaViewProvider
+ {
+ MediaView GetMediaView();
+ }
+
+ public class AspectToDisplayAspectModeConverter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ return ((Aspect)value).ToDisplayAspectMode();
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
using System.Collections.Generic;
using System.Reflection;
using Xamarin.Forms.Internals;
+using Xamarin.Forms.Platform.Tizen.Native;
+using Xamarin.Forms.PlatformConfiguration.TizenSpecific;
using Xamarin.Forms.Xaml.Internals;
namespace Xamarin.Forms.Platform.Tizen
Registered.Register(typeof(CarouselView), () => new CarouselViewRenderer());
Registered.Register(typeof(SwipeView), () => new SwipeViewRenderer());
Registered.Register(typeof(RefreshView), () => new RefreshViewRenderer());
+ Registered.Register(typeof(MediaElement), () => new MediaElementRenderer());
//ImageSourceHandlers
Registered.Register(typeof(FileImageSource), () => new FileImageSourceHandler());
DependencyService.Register<IDeserializer, Deserializer>();
DependencyService.Register<INativeBindingService, NativeBindingService>();
DependencyService.Register<INativeValueConverterService, NativeValueConverterService>();
+ DependencyService.Register<IPlatformMediaPlayer, MediaPlayerImpl>();
//Custom Handlers
if (customHandlers != null)
{
}
+ internal class _RadioButtonRenderer
+ {
+ }
+
internal class _TableViewRenderer
{
}
{
}
- internal class _SwipeViewRenderer
+ internal class _MediaElementRenderer
+ {
+ }
+
+ internal class _SwipeViewRenderer
{
}
}
\ No newline at end of file
if (xpe == null && TrySetBinding(xamlelement, property, localName, value, lineInfo, out xpe))
return;
+ var assemblyName = (context.RootAssembly ?? rootElement.GetType().GetTypeInfo().Assembly)?.GetName().Name;
//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};assembly={context.RootAssembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
+ VisualDiagnostics.RegisterSourceInfo(value, new Uri($"{path};assembly={assemblyName}", 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};assembly={context.RootAssembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
+ VisualDiagnostics.RegisterSourceInfo(value, new Uri($"{path};assembly={assemblyName}", 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};assembly={context.RootAssembly.GetName().Name}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
+ VisualDiagnostics.RegisterSourceInfo(value, new Uri($"{path};assembly={assemblyName}", UriKind.Relative), ((IXmlLineInfo)node).LineNumber, ((IXmlLineInfo)node).LinePosition);
return;
}