-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Conversation
…Markup/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
… impact on existing code and to support clear separation of UI markup from UI logic - Split up source file for better maintainability
Remove RowCol helpers because compiler cannot infer its generic type parameters. Use Row and Col methods instead
I'm getting a full-circle feeling from this, after starting my Xamarin journey with @migueldeicaza https://github.com/migueldeicaza/MonoTouch.Dialog in 2013 (look at the sample code in the README). Shameless plug for an MVU prototype: anyone interested should checkout @Clancey https://github.com/Clancey/Comet |
I just updated the spec doc to include all helpers and explain intended usage |
Maintainability and Codegen
I would like to address these concerns by demonstrating:
Light MaintenanceI have been maintaining CSharpForMarkup for over 2 years and it has been very little work. To illustrate, in 2019 only 6 small commits were done to the helpers (note that in this PR the single helpers file already has been split up in multiple files for optimal maintainability) When new UI will be added to Forms - e.g. a new control or a feature like Shell, there is only a need to add helpers for those properties (or property combinations) that are overly verbose and most often used. A helper is essentialy just one assignment statement, or a combination of helpers: public static TLabel TextTop<TLabel>(this TLabel label) where TLabel : Label
{
label.VerticalTextAlignment = TextAlignment.Start;
return label;
}
public static TLabel TextCenter<TLabel>(this TLabel label) where TLabel : Label => label.TextCenterH().TextCenterV(); This allready small effort can be even further reduced: initially a minimal amount of helpers can be added for a new control/feature. After a period where the control is used in apps, app developers will have added common convenience methods (it is trivially easy as shown above) which can be simply contributed in a PR. It can be a process of small steps for gradual-but optimal- improvement, which would grow a standard body of helpers. The vast majority of existing helpers are for base classes (Element, View, VisualElement, Layout) and for layout classes that provide attached properties (Grid, FlexLayout). Additions to Forms of this type are relatively rare and even then would only require a handful of very similar methods. The PR only accesses public Forms properties and methods. These are highly unlikely to change but even if they were, e.g. a rename would automatically be done since the PR is in the Core project (which is a benefit as compared to putting it in a separate NuGet). So there is no risk of changes in Forms causing maintenane in this PR. The implementation in this PR has been used during more than 2 years in production apps; it is battle tested for stability and usability. It is already documented. All this value can be included in Forms for free. In summary, future maintainance for the current implementation is very light. CodeGen API would have little valueWhile automatically generating a full fluent API would prevent the above (light) maintenance, it would not improve much for the developer. C# markup is improved by intelligently choosing shorter names for the most often used (combinations of) overly verbose property setters. Simply automatically converting the vanilla Forms UI API to equivalent fluent methods would:
The codegen Fluent api approach has already been tried e.g. here is the most starred (17 stars) fluent Xamarin UI API: To illustrate why, compare below C# markup fragment in 4 different API's: // Vanilla Forms:
new Label { Text = "Hi", HorizontalTextAlignment = TextAlignment.Center, VerticalTextAlignment = TextAlignment.Center },
// Generated 1:
new Label("Hi", horizontalTextAlignment: TextAlignment.Center, verticalTextAlignment: TextAlignment.Center),
// Generated fluent 2:
new Label("Hi") .HorizontalTextAlignment(TextAlignment.Center) .VerticalTextAlignment (TextAlignment.Center),
// CSharpForMarkup:
new Label { Text = "Hi" } .TextCenter (), A fully fluent API is not a goal, it is only a means to be used selectively and intelligently where it improves on the readability and conciseness of vanilla Forms C# markup. As can be seen in above examples, no real improvement is achieved when automatically generating an API. Of course you can manually add attributes for the codegen, but that would mean equivalent or more effort and more complexity. In short, building a fluent UI API + generator would be a lot of extra effort for a lot less value. Getting it shippedIf the concerns are adequately addressed, possibly add an experimental flag to communicate that syntax may still be finetuned for a time, and it can be shipped! PS Follow up possibilities:
Thank you all for reading all this; please like if you agree or comment if you have additional concerns or questions. I believe that doing this right is important for the future of Forms (attracting more devs). Let's make this happen! |
@VincentH-Net I would love to see a performance comparison for pages created using XAML vs C# For instance, I have a very very complex page that has lot's of sub controls, which based on dynamic condition should be loaded, right now since Conditional XAML is not available, we use Visibility to meet the needs, but as the page grown in size and complexity, the page loading is severely slow (takes 1-2 seconds for rendering the UI in good phones). |
I like this feature. Sometimes I need to make page layout from code, and now this is not very convenient. |
@muhaym CSharpForMarkup is by definition faster than XAML, even when compared to XAML with precompiled XAML and compiled bindings. At it's core XAML is just an object serialization format, built on top of the UI API that you directly access from C# when you use CSharpForMarkup. You simply bypass the XAML layer, and the compiler optimizes your C# markup better than XAML. Also when using CSharpForMarkup you don't need to use any XAML to C# language bridging mechanisms (e.g. styles, resource dictionaries, behaviours, triggers, markup extensions), some of which can also be slowdowns. I have used CSharpForMarkup to build a production app with some quite complex pages. I never experienced the type of slowness you describe. Actually people watching my video walkthrough asked if I speeded it up (I didn't). You can check for yourself; download my app and select "or start demo" - you can navigate the full app UI with demo data to check the speed. You can check the screen below, I use the same approach there that you do (all controls on the page, set visibility on the top tab select). I never needed to create the controls dynamically; it was already fast enough. |
Just for reference, since xamarin forms does UWP. Not all code is faster! |
@Depechie Don't the view renderers instantiate its controls via code anyway? Xamarin doesn't use UWP Xaml, so this shouldn't matter |
@SuNNjek yeah, that was my point exactly. But the statement that coded UI is faster is floating around and if you should do 'native' UWP dev, that statement is not true. But indeed because of the Xamarin Forms framework we can't benefit from that speed gain. |
I also wanted to know, will it code support the Shell? |
@KSemenenko Yes, I have Shell support planned, see the PR description:
I might even get it included before review of this PR starts (it has been 3 weeks now, no idea how long that takes though). Do you have some example pain points of working with Shell in C#? |
Today I tried to create a Shell from the code. var shell = new Shell();
shell.Items.Add(new TabBar()
{
Items = { new Tab
{
Items = { new ShellContent()
{
Content = new StatusBarGalleryPage()
}}
}}
});
Application.Current.MainPage = shell; |
I updated the PR description with the name changes from @hartez 's review: I also added description of LTR / RTL support: The PR description is now fully aligned with source again (except for some screenshot details). |
…ental flag message
…eElementsInCoreHaveDefaultBindablePropertyOrAreExcluded failure
Hmm, I don't have a stage or a live streaming event to do this on....but...here goes.... |
So happy to see this getting merged! |
👏 |
Thanks @samhouts, @davidortinau, @hartez and everyone who supported this! Very happy to see this become a part of Forms. |
This is so awesome team! |
Awesome, so the next step is to be able to make components with a state like React and Live code reload with state preservation for the ultimate developer experience! |
Next Step is HotRestart for Xamarin.Forms Android projects |
Thanks! LiveSharp already does that, stateful hot reload of C# Markup (and XAML) for any pattern, MVVM etc. MVU is not needed for that. But if you like MVU as a pattern you can check out Comet. |
Yes, LiveSharp is exactly what I had in mind. I also monitor Fabulous because I tend to prefer F# whenever I can, but it would be cool to have something working great out of the box! I'll check out comet, it looks interesting! |
The CSharpForMarkup Part 2 PR (#11428) is up! :-) It adds support for:
Plus some minor improvements. If you would like to see additions or changes in C# Markup, or just have general feedback or questions, please add them in the comments on #11428. Thanks! |
@VincentH-Net is there any reason this couldn't be abstracted a little more so that it could partially be moved up to WinUI (UWP) as well? |
@MelbourneDeveloper The C# Markup approach can be applied to any .NET programmable UI framework. Including UWP WinUI and Blazor. However the commonality would be syntax, because UI object hierarchies and the update patterns differ. So not a code abstraction but a pattern |
* - 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 xamarin/Xamarin.Forms#8342 (comment) * Fix for Grid properties Row, Col, RowSpan, ColumnSpan not set when default value is specified VincentH-Net/CSharpForMarkup#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
* [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 xamarin/Xamarin.Forms#8342 (comment) * Fix for Grid properties Row, Col, RowSpan, ColumnSpan not set when default value is specified VincentH-Net/CSharpForMarkup#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 * Cross-Platform OS Theme APIs (#9958) * Implement OnAppTheme * Implement AppThemeColor * Added tests * Finishing up * Implement UWP * Xcode 11 and additional test * Update Forms.cs * Update ViewRenderer.cs * Update ViewRenderer.cs * Remove IAppThemeProvider * Implemented RequestedThemeChanged * Update WindowsBasePlatformServices.cs * Bindable props and Android invalidate * Update DefaultBindableProperties.cs * Updates at runtime * Review feedback part 1 * Implement VisualElement.OnRequestedThemeChanged * UWP Dispatcher * Add experimental flag * ControlGallery restructure * Update ViewRenderer.cs * Update WindowsBasePlatformServices.cs * iOS pre-13 fix * AppTheme fix NRE (#10327) * Various fixes * Back to basics * [Enhancement] Shapes (#9218) * Added Shapes to Core and Core Gallery samples Implemented basic Shapes on iOS * Added basic shapes Android implementation * Fixed UWP namespace conflicts * Fixed WPF namespace conflicts * Implemented basic shapes on UWP * Fixed UWP Ellipse and Rectangle size issue * Fixed Tizen Build * Changes to fix the build errors * Exclude shapes renderers from Tizen compilation * Implemented LineCap and LineJoin in Android and iOS * Implemented LineCap and LineJoin on UWP * Fixed build error * Updated Polygon sample * Fixed UWP Build error (namespace conflicts) * Fixed namespaces collision build error * Added "Shapes_Experimental" flag * Update UWP ShapeRenderer size logic * Updated iOS and Android ShapeRenderer size logic * Fixed UWP Build error * Added WPF implementation * Fixed Tizen Build error * Added Shapes macOS implementation * Fixed broken unit tests * Use the same Shapes classes between UWP and WPF backends * Fixed flipper macOS shape issue * Changed Shape class to be abstract * Added Polygon and Polyline unit tests * Added more Shapes Unit Tests * Moved Shapes to Xamarin.Forms.Shapes in Android, iOS and macOS * Moved Shapes to Xamarin.Forms.Shapes namespace in Windows (UWP and WPF) fixes #2452 (partially) fixes #9178 * [Enhancement] Shapes (Path) (#9264) * Added Path definition in Core (Shapes) Implemented Path in Android and iOS * Fixed Android build errors * Fixed UWP Build * Fixed WPF Build * Implemented PathRenderer on UWP * Added unit tests * Fixed namespaces conflicts in Platform projects * Changes to fix the build errors * Implemented Path Transformations in Android and iOS * Fixed Build error in WPF and UWP * Implemented Path Transformations in UWP * Fixed iOS Build error * Changes to fix the Build (Path namespace conflict) * More changes to fix the build error * Fixed Windows Build errors * Fixed Path size issue on UWP * Added Shapes_Experimental flag * Updated path sample * Updated Android ShapeRenderer size logic * Added Shape Aspect sample in Core Gallery * Added more Shapes samples * Updated UWP PathRenderer size logic * Updated droid and iOS pathRenderer size logic (same behavior in all the platforms) * Updated UWP ShapeRenderer * Implemented Path in WPF Backend * Fixed build error * Initial Clip implementation in WPF and UWP (work in progress) * Added Path implementation on macOS * Added Clip implementation in Android, iOS and macOS * Fixed broken unit tests * Notify the change of Geometry if any of the child properties changed * Added new sample clipping different views * Fixed flipped shape issue on macOS * Added support to Clip using EllipseGeometry, LineGeometry and RectangleGeometry in UWP * Changed Shape class to be abstract * Moved Shapes to Xamarin.Forms.Shapes in Android, iOS and macOS * Moved Shapes to Xamarin.Forms.Shapes namespace in Windows (UWP and WPF) * Fixed wrong property in LineGeometry * Fixed build error * Added Clip Performance sample in Core Gallery * Update Matrix.cs * Update RectangleGeometry.cs * Update Xamarin.Forms.Platform.macOS.csproj * Some duplicate classes * Update PointCollectionTests.cs * Update ImageButtonRenderer.cs * Update Xamarin.Forms.Platform.iOS.csproj * Update Xamarin.Forms.Platform.iOS.csproj * Fixed tabs error Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com> fixes #2452 (partially) fixes #9178 * Add fluent DynamicResource markup helpers * Add support for all IPaddingElement Elements to Padding markup helpers (was only Layouts) * Make family first parameter of Font markup helper (reduces the need to use named parameters across all FontElement extensions) * Add Stars(double) markup helper for Grids * Add RelativeLayout markup helpers * wip Add RelativeLayoutTests * Add RelativeLayout markup helpers done * Add BindableLayout markup helpers * wip Add MultiBind and MultiConverter support to markup helpers * - Add multiconverter markup helpers - Improve Bind markup helpers: add multibinding, make more strongly typed, remove converterParameter where unused - wip Add unit tests for multiconverter and multibind * wip Add unit tests for multibind * Move MultiBind helpers + tests to separate files wip Complete MultiBind unit tests * wip Add unit tests for MultiBind markup helpers * Add unit tests for MultiBind and MultiConvert markup helpers * Add overloads for Bindable Layout markup helpers that take a Func<object> instead of a DataTemplate * move C# Markup out of forms with internalsvisibleto, eliminate reflection * Markup API: build against Forms 5 NuGet, remove experimental checks, change namespace to Xamarin.CommunityToolkit.Markup, add TODO comments for inaccessible IPaddingElement * Build against Forms 5 NuGet, change namespace to Xamarin.CommunityToolkit.Markup.UnitTests, remove experimental flag testing. add TODO comments for inacccessible API's * Add PR link to TODO comment * Update to Forms NuGets that have public IPaddingElement Get unit tests working and succeeding except for issue in Forms * Bring unit tests to 100% code coverage Cleanup mocks and remove all unused code from them Remove all unnecessary namespace usings * prefix Markup top-level folders with Xamarin.CommunityToolkit. * add Xamarin.CommunityToolkit.Markup.sln, eliminate Markup subfolders, fix notimplemented exception in MockPlatformServices * include stylecop.json in Markup project * add comment with Forms NuGets download url to project files * add prefix Xamarin.CommunityToolkit. to namespaces and assembly names where missing * add package settings, license and icon to markup project * add MarkupSample solution * fix android build errro due to max path length (remove solution folder) complete MarkupSample * remove dependency on internal Forms PaddingElement and FontElement classes * add temporary msbuild target for downloading NuGets from Forms PR * add markup to ci * fix markup project folder names in ci * fix markup temporary NuGet download in ci * fix markup unit test project for ci * use NuGets from PR as merged into Forms 5.5.0 branch fix tests that fail due to Forms PR 12689 not yet merged * remove experimental wrapper from markup unit tests align markup unit test project packagereferences to xct unit test project * fix all code formatting warnings where applicable suppress remaining code formatting warnings with justification where not applicable * add warning codes from GlobalSuppressions.cs to NoWarn in project files * fix Forms version warning revert adding warning codes in project files * ci run all tests in single task * align markup csproj to XCT csproj for same type of debug info in release package * update forms version in markup sample * update to Forms 5.0.0.1709-pre4 remove custom Forms NuGet download targets update unit tests for Forms fix fix xamarin/Xamarin.Forms#12689 * Markup package version to 1.0.0-pre4 Markup sample to Markup 1.0.0-pre4 and Forms 5.0.0.1709-pre4 Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com> Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com> Co-authored-by: Vincent Hoogendoorn <dev@vincenth.net> Co-authored-by: Vincent Hoogendoorn <vincenth@workstreampeople.com>
* [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 xamarin/Xamarin.Forms#8342 (comment) * Fix for Grid properties Row, Col, RowSpan, ColumnSpan not set when default value is specified VincentH-Net/CSharpForMarkup#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 * Cross-Platform OS Theme APIs (#9958) * Implement OnAppTheme * Implement AppThemeColor * Added tests * Finishing up * Implement UWP * Xcode 11 and additional test * Update Forms.cs * Update ViewRenderer.cs * Update ViewRenderer.cs * Remove IAppThemeProvider * Implemented RequestedThemeChanged * Update WindowsBasePlatformServices.cs * Bindable props and Android invalidate * Update DefaultBindableProperties.cs * Updates at runtime * Review feedback part 1 * Implement VisualElement.OnRequestedThemeChanged * UWP Dispatcher * Add experimental flag * ControlGallery restructure * Update ViewRenderer.cs * Update WindowsBasePlatformServices.cs * iOS pre-13 fix * AppTheme fix NRE (#10327) * Various fixes * Back to basics * [Enhancement] Shapes (#9218) * Added Shapes to Core and Core Gallery samples Implemented basic Shapes on iOS * Added basic shapes Android implementation * Fixed UWP namespace conflicts * Fixed WPF namespace conflicts * Implemented basic shapes on UWP * Fixed UWP Ellipse and Rectangle size issue * Fixed Tizen Build * Changes to fix the build errors * Exclude shapes renderers from Tizen compilation * Implemented LineCap and LineJoin in Android and iOS * Implemented LineCap and LineJoin on UWP * Fixed build error * Updated Polygon sample * Fixed UWP Build error (namespace conflicts) * Fixed namespaces collision build error * Added "Shapes_Experimental" flag * Update UWP ShapeRenderer size logic * Updated iOS and Android ShapeRenderer size logic * Fixed UWP Build error * Added WPF implementation * Fixed Tizen Build error * Added Shapes macOS implementation * Fixed broken unit tests * Use the same Shapes classes between UWP and WPF backends * Fixed flipper macOS shape issue * Changed Shape class to be abstract * Added Polygon and Polyline unit tests * Added more Shapes Unit Tests * Moved Shapes to Xamarin.Forms.Shapes in Android, iOS and macOS * Moved Shapes to Xamarin.Forms.Shapes namespace in Windows (UWP and WPF) fixes #2452 (partially) fixes #9178 * [Enhancement] Shapes (Path) (#9264) * Added Path definition in Core (Shapes) Implemented Path in Android and iOS * Fixed Android build errors * Fixed UWP Build * Fixed WPF Build * Implemented PathRenderer on UWP * Added unit tests * Fixed namespaces conflicts in Platform projects * Changes to fix the build errors * Implemented Path Transformations in Android and iOS * Fixed Build error in WPF and UWP * Implemented Path Transformations in UWP * Fixed iOS Build error * Changes to fix the Build (Path namespace conflict) * More changes to fix the build error * Fixed Windows Build errors * Fixed Path size issue on UWP * Added Shapes_Experimental flag * Updated path sample * Updated Android ShapeRenderer size logic * Added Shape Aspect sample in Core Gallery * Added more Shapes samples * Updated UWP PathRenderer size logic * Updated droid and iOS pathRenderer size logic (same behavior in all the platforms) * Updated UWP ShapeRenderer * Implemented Path in WPF Backend * Fixed build error * Initial Clip implementation in WPF and UWP (work in progress) * Added Path implementation on macOS * Added Clip implementation in Android, iOS and macOS * Fixed broken unit tests * Notify the change of Geometry if any of the child properties changed * Added new sample clipping different views * Fixed flipped shape issue on macOS * Added support to Clip using EllipseGeometry, LineGeometry and RectangleGeometry in UWP * Changed Shape class to be abstract * Moved Shapes to Xamarin.Forms.Shapes in Android, iOS and macOS * Moved Shapes to Xamarin.Forms.Shapes namespace in Windows (UWP and WPF) * Fixed wrong property in LineGeometry * Fixed build error * Added Clip Performance sample in Core Gallery * Update Matrix.cs * Update RectangleGeometry.cs * Update Xamarin.Forms.Platform.macOS.csproj * Some duplicate classes * Update PointCollectionTests.cs * Update ImageButtonRenderer.cs * Update Xamarin.Forms.Platform.iOS.csproj * Update Xamarin.Forms.Platform.iOS.csproj * Fixed tabs error Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com> fixes #2452 (partially) fixes #9178 * Add fluent DynamicResource markup helpers * Add support for all IPaddingElement Elements to Padding markup helpers (was only Layouts) * Make family first parameter of Font markup helper (reduces the need to use named parameters across all FontElement extensions) * Add Stars(double) markup helper for Grids * Add RelativeLayout markup helpers * wip Add RelativeLayoutTests * Add RelativeLayout markup helpers done * Add BindableLayout markup helpers * wip Add MultiBind and MultiConverter support to markup helpers * - Add multiconverter markup helpers - Improve Bind markup helpers: add multibinding, make more strongly typed, remove converterParameter where unused - wip Add unit tests for multiconverter and multibind * wip Add unit tests for multibind * Move MultiBind helpers + tests to separate files wip Complete MultiBind unit tests * wip Add unit tests for MultiBind markup helpers * Add unit tests for MultiBind and MultiConvert markup helpers * Add overloads for Bindable Layout markup helpers that take a Func<object> instead of a DataTemplate * move C# Markup out of forms with internalsvisibleto, eliminate reflection * Markup API: build against Forms 5 NuGet, remove experimental checks, change namespace to Xamarin.CommunityToolkit.Markup, add TODO comments for inaccessible IPaddingElement * Build against Forms 5 NuGet, change namespace to Xamarin.CommunityToolkit.Markup.UnitTests, remove experimental flag testing. add TODO comments for inacccessible API's * Add PR link to TODO comment * Update to Forms NuGets that have public IPaddingElement Get unit tests working and succeeding except for issue in Forms * Bring unit tests to 100% code coverage Cleanup mocks and remove all unused code from them Remove all unnecessary namespace usings * prefix Markup top-level folders with Xamarin.CommunityToolkit. * add Xamarin.CommunityToolkit.Markup.sln, eliminate Markup subfolders, fix notimplemented exception in MockPlatformServices * include stylecop.json in Markup project * add comment with Forms NuGets download url to project files * add prefix Xamarin.CommunityToolkit. to namespaces and assembly names where missing * add package settings, license and icon to markup project * add MarkupSample solution * fix android build errro due to max path length (remove solution folder) complete MarkupSample * remove dependency on internal Forms PaddingElement and FontElement classes * add temporary msbuild target for downloading NuGets from Forms PR * add markup to ci * fix markup project folder names in ci * fix markup temporary NuGet download in ci * fix markup unit test project for ci * use NuGets from PR as merged into Forms 5.5.0 branch fix tests that fail due to Forms PR 12689 not yet merged * remove experimental wrapper from markup unit tests align markup unit test project packagereferences to xct unit test project * fix all code formatting warnings where applicable suppress remaining code formatting warnings with justification where not applicable * add warning codes from GlobalSuppressions.cs to NoWarn in project files * fix Forms version warning revert adding warning codes in project files * ci run all tests in single task * align markup csproj to XCT csproj for same type of debug info in release package * update forms version in markup sample * update to Forms 5.0.0.1709-pre4 remove custom Forms NuGet download targets update unit tests for Forms fix fix xamarin/Xamarin.Forms#12689 * Markup package version to 1.0.0-pre4 Markup sample to Markup 1.0.0-pre4 and Forms 5.0.0.1709-pre4 * MarkupSample: update Markup to 1.0.0-pre5, update Fody, update docs link to XCT content, fix iOS like button, suppress stylecop rules that do not apply to declarative markup * MarkupSample: fix StyleCop warnings * MarkupSample UWP set launch view size Co-authored-by: Gerald Versluis <gerald.versluis@microsoft.com> Co-authored-by: Javier Suárez <javiersuarezruiz@hotmail.com> Co-authored-by: Vincent Hoogendoorn <dev@vincenth.net> Co-authored-by: Vincent Hoogendoorn <vincenth@workstreampeople.com>
CSharpForMarkup
Fluent helpers and classes to make building Xamarin Forms UI in declarative style C# a joy.
Modern UI frameworks such as Flutter and SwiftUI offer declarative UI.
These helpers offer a similar approach for Xamarin Forms (the UI pattern remains MVVM though, this feature does not add MVU).
The goal is to make Xamarin Forms more attractive for additional developer audiences that don't have XAML skills / preference. Developers with XAML skills can also benefit; declarative C# can be very similar to XAML and can offer increased productivity (due to excellent IDE support for C# and no need for language bridging mechanisms).
This feature is based on CSharpForMarkup (226 stars atm).
This PR code is unit tested with 100% code coverage, and has been tested against this Production App by replacing CSharpForMarkup with it.
API
The API is an opt-in set of fluent helpers and classes that extend
BindableObject
,Element
,Layout
,Style
and some specific view types. No existing Forms code is touched. There is no impact on existing apps code.Developers opt in by calling
Forms.SetFlags("Markup_Experimental")
before callingForms.Init
, and adding these usings to a markup .cs file:The separate
Markup
namespace promotes a clean separation between UI markup and UI logic. It is recommended to split C# markup and UI logic into separate partial class files, e.g:Developers who do not include the above namespaces will have nothing of this code in their linked app or in their intellisense; zero impact.
The helpers offer a fluent API with Bind, Gesture, Effects, Invoke, Assign, Row, Column, FormattedText, Style, Font, inline converters, support for using enums for Grid rows + columns and more.
Simple to add to in app code; developers can also easily create their own markup DSL on top of this,
as in this example in David Ortinau's Xappy.
C# Examples
Just an
Entry
withStyle
,Row
,Margins
andBind
:A few more layout helpers
ColumnSpan
,Right
,FillExpandHorizontal
,CenterVertical
,Bottom
,Margin
,Height
,TextCenterHorizontal
:Methods
Extension methods to set select view properties. The helpers are not meant to replace all property setters; they are added when they improve readability. They are meant to be used in combination with normal property setters. It is recommended to always use a helper when one exists for a property, but developers can choose a balance they prefer (e.g. to keep the markup more or less similar to XAML).
Binding, commands and converters
Use
Bind
as in the above C# examples.Note that
Bind
knows the default bindable property for most built-in Forms view types; you can omit the target property in most cases. You can also register the default bindable property for additional controls:You can bind to any bindable property like this:
Use
BindCommand
to bind to a view's defaultCommand
andCommandParameter
properties in a single call:By default the
CommandParameter
is bound to the binding context (e.g. the item in a list). You can specify the binding path and source for theCommand
and theCommandParameter
bindings:If you only need to bind to
Command
, you can passnull
toBindCommand
'sparameterPath
parameter, or simply useBind
(for most views that have aCommand
parameter it is the default bindable property; usingBind
then is the most concise).You can also register the default
Command
andCommandParameter
properties for additional controls:Pass inline converter code with
convert
andconvertBack
parameters (type-safe):Type-safe converter parameters are also supported:
Re-use converter code and instances with
FuncConverter
:FuncConverter<TSource, TDest, TParam>
also supportsCultureInfo
:Use
FormattedText
together with binding toSpans
:Gesture recognizers
To bind
Command
and (optionally)CommandParameter
toGestureElement
(e.g.Span
) andView
types using gesture recognizers, useBindClickGesture
,BindSwipeGesture
andBindTapGesture
:This creates a gesture recognizer of the specified type and adds it to the view.
The
Bind*Gesture
helpers offer the same parameters asBindCommand
, however by default Bind*Gesture does not bindCommandParameter
, whileBindCommand
does.To initialize a gesture recognizer (e.g. parameters), use
ClickGesture
,PanGesture
,PinchGesture
,SwipeGesture
andTapGesture
:Note that since a gesture recognizer is a
BindableObject
, you can useBind
andBindCommand
when you initialize it (as shown above).You can also initialize custom gesture recognizer types with
Gesture<TGestureElement, TGestureRecognizer>
Layout
Use layout helpers for positioning views in layouts and content in views:
Grid
:Row
,Column
,RowSpan
,ColumnSpan
FlexLayout
:AlignSelf
,Basis
,Grow
,Menu
,Order
,Shrink
LayoutOptions
:Left
,CenterHorizontal
,FillHorizontal
,Right
LeftExpand
,CenterExpandHorizontal
,FillExpandHorizontal
,RightExpand
Top
,Bottom
,CenterVertical
,FillVertical
TopExpand
,BottomExpand
,CenterExpandVertical
,FillExpandVertical
Center
,Fill
,CenterExpand
,FillExpand
Margin
,Margins
Height
,Width
,MinHeight
,MinWidth
,Size
,MinSize
Padding
,Paddings
Label
:TextLeft
,TextCenterHorizontal
,TextRight
TextTop
,TextCenterVertical
,TextBottom
TextCenter
LTR and RTL support
For markup that is designed to support either left-to-right or right-to-left flow direction the helpers listed above offer the most intuitive set of names:
Left
,Right
,Top
&Bottom
.To make the correct set of
Left
&Right
helpers available - and in the process make explicit which flow direction the markup is designed for - include either:using Xamarin.Forms.Markup.LeftToRight
, orusing Xamarin.Forms.Markup.RightToLeft
.For markup that is designed to support both left-to-right and right-to-left flow direction, it is recommended not to include either one of above namespaces, and instead use the
Start
&End
set of helpers from theXamarin.Forms.Markup
namespace - their names are flow direction agnostic by design, and fit this scenario best:LayoutOptions
:Start
,End
StartExpand
,EndExpand
Label
:TextStart
,TextEnd
Layout line convention
The recommended convention is to put all helpers from above set for a view on a single line, in the order that they are listed above. This creates a layout line that visually zooms in on the view. content:
Consistently applying this convention enables developers to quickly visually scan and zoom in on markup to build a mental map of where the view content is located.
Enums for Grid rows and columns
By adding
using static Xamarin.Forms.Markup.GridRowsColumns;
you can use enums for Grid rows and columns instead of numbers, so you don't have to renumber manually when you add or remove rows or columns. Readability and intent of the layout is also improved:You can also concisely define rows and columns without enums:
Fonts
Element
that implementsIFontElement
(Button
,DatePicker
,Editor
,Entry
,Label
,Picker
,SearchBar
,Span
,TimePicker
) :FontSize
,Bold
,Italic
,Font
Effects
Logic integration
Use
Invoke
to execute code inline in your declarative markup:Use
Assign
if you need to access a control from outside the UI markup (in UI logic):Styles
Use
Style
to create type-safe, declarative coded styles:Note that instead of using
Style
, you can simply create your own extension methods to set styling even more type-safe:Platform Specifics
Use
Invoke
to apply platform specifics. To avoid ambiguity errors, don't include theXamarin.Forms.PlatformConfiguration.*Specific
namespaces directly; instead, create a namespace alias:and then you can use platform specifics like this:
If you use certain platform specifics often, you can create fluent helpers for them in your own extensions class, e.g. like this:
and then you can use them like this:
Conventions
Recommended markup formatting conventions for every control:
Bound properties are last, one per line. The default bindable property, if any, should be the very last line.
The line before the bound properties is about layout, ordered inward: rows / columns, layout options, margin, size, padding, content alignment.
Consistently applying these conventions allows to visually scan / zoom markup and build a mental map of the layout.
Before that are the other properties; any property that identifies the control's purpose should be on the very first line (e.g. Text or Placeholder)
Backward Compatibility
There are no breaking changes. All platforms are supported.
Area's where functionality still can be added:
Difficulty : [low]
Because it is fairly complete and thoroughly battle-tested.
Platforms Affected
Behavioral/Visual Changes
None
Testing Procedure
This PR includes comprehensive unit tests - 100% code coverage.
This PR code also has been tested against this Production App by replacing CSharpForMarkup with it.
If preferred, some example pages can be added to the
PagesGallery
project for testing.