Repository: xamarin/SignaturePad Branch: master Commit: b32285178014 Files: 122 Total size: 369.9 KB Directory structure: gitextract_9f8fffgb/ ├── .editorconfig ├── .gitignore ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── build.cake ├── build.ps1 ├── build.sh ├── nuget/ │ ├── Xamarin.Controls.SignaturePad.Forms.nuspec │ └── Xamarin.Controls.SignaturePad.nuspec ├── samples/ │ ├── Sample.Android/ │ │ ├── MainActivity.cs │ │ ├── Properties/ │ │ │ ├── AndroidManifest.xml │ │ │ └── AssemblyInfo.cs │ │ ├── Resources/ │ │ │ ├── drawable/ │ │ │ │ └── background.xml │ │ │ ├── layout/ │ │ │ │ └── main.axml │ │ │ └── values/ │ │ │ └── strings.xml │ │ ├── Sample.Android.csproj │ │ └── Sample.Android.sln │ ├── Sample.Forms/ │ │ ├── Sample.Forms.Mac.sln │ │ ├── Sample.Forms.sln │ │ ├── Samples/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── BindingPage.xaml │ │ │ ├── BindingPage.xaml.cs │ │ │ ├── BindingPageViewModel.cs │ │ │ ├── EventsPage.xaml │ │ │ ├── EventsPage.xaml.cs │ │ │ ├── Helpers/ │ │ │ │ ├── CaptureSignatureBehaviorBase.cs │ │ │ │ ├── CaptureSignaturePointsBehavior.cs │ │ │ │ ├── CaptureSignatureStrokesBehavior.cs │ │ │ │ └── NotConverter.cs │ │ │ ├── MainPage.xaml │ │ │ ├── MainPage.xaml.cs │ │ │ └── Samples.csproj │ │ ├── Samples.Android/ │ │ │ ├── MainActivity.cs │ │ │ ├── Properties/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Resources/ │ │ │ │ ├── layout/ │ │ │ │ │ ├── tabs.xml │ │ │ │ │ └── toolbar.xml │ │ │ │ ├── values/ │ │ │ │ │ ├── colors.xml │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── style.xml │ │ │ │ └── values-v21/ │ │ │ │ └── style.xml │ │ │ └── Samples.Android.csproj │ │ ├── Samples.UWP/ │ │ │ ├── App.xaml │ │ │ ├── App.xaml.cs │ │ │ ├── MainPage.xaml │ │ │ ├── MainPage.xaml.cs │ │ │ ├── Package.appxmanifest │ │ │ ├── Properties/ │ │ │ │ ├── AssemblyInfo.cs │ │ │ │ └── Default.rd.xml │ │ │ ├── Samples.UWP.csproj │ │ │ └── Samples.UWP_TemporaryKey.pfx │ │ └── Samples.iOS/ │ │ ├── AppDelegate.cs │ │ ├── Entitlements.plist │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ ├── Main.cs │ │ ├── Properties/ │ │ │ └── AssemblyInfo.cs │ │ ├── Resources/ │ │ │ └── Assets.xcassets/ │ │ │ └── AppIcons.appiconset/ │ │ │ └── Contents.json │ │ └── Samples.iOS.csproj │ ├── Sample.UWP/ │ │ ├── App.xaml │ │ ├── App.xaml.cs │ │ ├── MainPage.xaml │ │ ├── MainPage.xaml.cs │ │ ├── Package.appxmanifest │ │ ├── Properties/ │ │ │ ├── AssemblyInfo.cs │ │ │ └── Default.rd.xml │ │ ├── Sample.UWP.csproj │ │ ├── Sample.UWP.sln │ │ └── Sample.UWP_TemporaryKey.pfx │ └── Sample.iOS/ │ ├── AppDelegate.cs │ ├── Entitlements.plist │ ├── Info.plist │ ├── LaunchScreen.storyboard │ ├── Main.cs │ ├── Main.storyboard │ ├── Resources/ │ │ └── Assets.xcassets/ │ │ └── AppIcons.appiconset/ │ │ └── Contents.json │ ├── Sample.iOS.csproj │ ├── Sample.iOS.sln │ ├── ViewController.cs │ ├── ViewController.designer.cs │ ├── iTunesArtwork │ └── iTunesArtwork@2x └── src/ ├── SignaturePad.Android/ │ ├── InkPresenter.cs │ ├── Resources/ │ │ ├── drawable/ │ │ │ └── signature_pad_background.xml │ │ ├── layout/ │ │ │ └── signature_pad_layout.axml │ │ └── values/ │ │ ├── attrs.xml │ │ └── signature_pad_defaults.xml │ ├── SignaturePad.Android.csproj │ ├── SignaturePadCanvasView.cs │ └── SignaturePadView.cs ├── SignaturePad.Forms/ │ ├── SignaturePad.Forms.csproj │ └── SignaturePadCanvasRenderer.cs ├── SignaturePad.Forms.Droid/ │ ├── SignaturePad.Forms.Droid.csproj │ └── SignaturePadViewRenderer.cs ├── SignaturePad.Forms.Platform.Shared/ │ ├── ColorExtensions.cs │ └── SignaturePadCanvasRenderer.cs ├── SignaturePad.Forms.Shared/ │ ├── ImageConstructionSettings.cs │ ├── SignatureImageFormat.cs │ ├── SignaturePadCanvasView.cs │ └── SignaturePadView.cs ├── SignaturePad.Forms.UWP/ │ └── SignaturePad.Forms.UWP.csproj ├── SignaturePad.Forms.iOS/ │ └── SignaturePad.Forms.iOS.csproj ├── SignaturePad.InkPresenter.Shared/ │ ├── InkPresenter.cs │ └── InkStroke.cs ├── SignaturePad.Mac.sln ├── SignaturePad.Shared/ │ ├── Extensions.cs │ ├── ImageConstructionSettings.cs │ ├── PathSmoothing.cs │ ├── SignatureImageFormat.cs │ ├── SignaturePadCanvasView.cs │ └── SignaturePadView.cs ├── SignaturePad.UWP/ │ ├── SignaturePad.UWP.csproj │ ├── SignaturePad.cs │ ├── SignaturePadCanvasView.cs │ └── Themes/ │ └── Generic.xaml ├── SignaturePad.iOS/ │ ├── InkPresenter.cs │ ├── SignaturePad.iOS.csproj │ ├── SignaturePadCanvasView.cs │ └── SignaturePadView.cs └── SignaturePad.sln ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig is awesome: http://EditorConfig.org # top-most EditorConfig file root = true # Don't use tabs for indentation. [*] indent_style = space # (Please don't specify an indent_size here; that has too many unintended consequences.) # Code files [*.{cs,csx,vb,vbx}] indent_style = tab indent_size = 4 insert_final_newline = true charset = utf-8-bom trim_trailing_whitespace = true # Xml project files [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] indent_size = 2 # Xml config files [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] indent_size = 2 # JSON files [*.json] indent_size = 2 # .NET code style settings: [*.{cs,vb}] # Sort using and Import directives with System.* appearing first dotnet_sort_system_directives_first = true # Avoid "this." and "Me." if not necessary dotnet_style_qualification_for_field = false:suggestion dotnet_style_qualification_for_property = false:suggestion dotnet_style_qualification_for_method = false:suggestion dotnet_style_qualification_for_event = false:suggestion # Use language keywords instead of framework type names for type references dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion # Modifier preferences dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion # Expression-level preferences dotnet_style_object_initializer = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion # Null-checking preferences dotnet_style_coalesce_expression = true:suggestion dotnet_style_null_propagation = true:suggestion # C# code style settings: [*.cs] # Modifier preferences csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async # Prefer "var" everywhere csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_elsewhere = true:suggestion # Prefer method-like constructs to have a block body csharp_style_expression_bodied_methods = false:suggestion csharp_style_expression_bodied_constructors = false:suggestion csharp_style_expression_bodied_operators = false:suggestion # Prefer property-like constructs to have an expression-body csharp_style_expression_bodied_properties = true:suggestion csharp_style_expression_bodied_indexers = true:suggestion csharp_style_expression_bodied_accessors = true:suggestion # Pattern matching csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_pattern_matching_over_as_with_null_check = true:suggestion # Inlined variable declarations csharp_style_inlined_variable_declaration = true:suggestion # Expression-level preferences csharp_prefer_simple_default_expression = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion csharp_style_pattern_local_over_anonymous_function = true:suggestion # "Null" checking preferences csharp_style_throw_expression = true:suggestion csharp_style_conditional_delegate_call = true:suggestion # Code block preferences csharp_prefer_braces = true:suggestion # Newline settings csharp_new_line_before_open_brace = all csharp_new_line_before_else = true csharp_new_line_before_catch = true csharp_new_line_before_finally = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_between_query_expression_clauses = true # Indentation options csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_switch_labels = true csharp_indent_labels = one_less_than_current # Spacing options csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = do_not_ignore csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = true csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = true csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false # Wrapping options csharp_preserve_single_line_statements = false csharp_preserve_single_line_blocks = true # C# naming rules: [*.cs] # symbols # const fields dotnet_naming_symbols.constant_fields.required_modifiers = const dotnet_naming_symbols.constant_fields.applicable_kinds = field # methods|properties dotnet_naming_symbols.methods_and_properties.applicable_kinds = delegate,event,enum,interface,struct,class,method,property # parameters|fields dotnet_naming_symbols.parameters_and_fields.applicable_kinds = field,parameter # private|internal fields dotnet_naming_symbols.private_internal_fields.applicable_kinds = field dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private,internal # rules # name all constant fields using PascalCase dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case # name all methods and properties using PascalCase dotnet_naming_rule.methods_and_properties_should_be_pascal_case.severity = suggestion dotnet_naming_rule.methods_and_properties_should_be_pascal_case.symbols = methods_and_properties dotnet_naming_rule.methods_and_properties_should_be_pascal_case.style = pascal_case # name all parameters and fields using camelCase dotnet_naming_rule.parameters_should_be_pascal_case.severity = suggestion dotnet_naming_rule.parameters_should_be_pascal_case.symbols = parameters_and_fields dotnet_naming_rule.parameters_should_be_pascal_case.style = camel_case # internal and private fields should be camelCase dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # Repo-specific files tools/ output/ **/Resources/Resource.Designer.cs component/*.xam # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio 2015 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json artifacts/ *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.publishsettings node_modules/ orleans.codegen.cs # Since there are multiple workflows, uncomment next line to ignore bower_components # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt # Visual Studio LightSwitch build output **/*.HTMLClient/GeneratedArtifacts **/*.DesktopClient/GeneratedArtifacts **/*.DesktopClient/ModelManifest.xml **/*.Server/GeneratedArtifacts **/*.Server/ModelManifest.xml _Pvt_Extensions # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml ================================================ FILE: ISSUE_TEMPLATE.md ================================================ ### Description ### Code ### Expected Behavior ### Actual Behavior ### Basic Information - Version with issue: - Last known good version: - IDE: - Platform Target Frameworks: - Android: - iOS: - UWP: - Xamarin.Forms: - Target Devices: - ### Screenshots ### Reproduction Link ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2017 Xamarin Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Signature Pad [![Build Status](https://jenkins.mono-project.com/buildStatus/icon?job=Components-SignaturePad-Windows)](https://jenkins.mono-project.com/view/Components/job/Components-SignaturePad-Windows/) [![Build Status](https://jenkins.mono-project.com/buildStatus/icon?job=Components-SignaturePad)](https://jenkins.mono-project.com/view/Components/job/Components-SignaturePad/) [![SignaturePad NuGet](https://img.shields.io/nuget/vpre/Xamarin.Controls.SignaturePad.svg?label=SignaturePad%20NuGet)](https://www.nuget.org/packages/Xamarin.Controls.SignaturePad) [![SignaturePad Xamairn.Forms NuGet](https://img.shields.io/nuget/vpre/Xamarin.Controls.SignaturePad.Forms.svg?label=SignaturePad.Forms%20NuGet)](https://www.nuget.org/packages/Xamarin.Controls.SignaturePad.Forms) Signature Pad makes capturing, saving, exporting, and displaying signatures extremely simple on Xamarin.iOS, Xamarin.Android and Windows. Not only is Signature Pad available for native apps, but also available in Xamarin.Forms apps. ![Screenshot](images/signature-ios.jpg) --- ## Using Signature Pad Signature Pad can be installed from [NuGet.org][nuget-link] for native Xamarin and Windows apps: ``` nuget install Xamarin.Controls.SignaturePad ``` And also for Xamarin.Forms apps: ``` nuget install Xamarin.Controls.SignaturePad.Forms ``` ### Using Signature Pad on iOS ```csharp using Xamarin.Controls; var signatureView = new SignaturePadView (View.Frame) { StrokeWidth = 3f, StrokeColor = UIColor.Black, BackgroundColor = UIColor.White, }; ``` ### Using Signature Pad on Android ```csharp using Xamarin.Controls; var signatureView = new SignaturePadView (this) { StrokeWidth = 3f, StrokeColor = Color.White, BackgroundColor = Color.Black }; ``` ### Using Signature Pad on Windows ```xml ``` ### Using Signature Pad on Xamarin.Forms ```xml ``` ### Obtaining a Signature Image The signature that was drawn on the canvas can be obtained as a image using the `GetImage(...)` method overloads. The resulting image will be in the native platform image type: ```csharp // iOS UIImage image = signatureView.GetImage (); // Android Bitmap image = signatureView.GetImage (); // Windows WriteableBitmap bitmap = signatureView.GetImage (); ``` For Xamarin.Forms, there is no "native" image format, but `GetImageStreamAsync` can be used instead to retrieve an encoded (jpeg or png) image stream: ```csharp Stream bitmap = await signatureView.GetImageStreamAsync (SignatureImageFormat.Png); ``` ### Obtaining the Signature Points In addition to retrieving the signature as an image, the signature can also be retrieved as as an array of points: ```csharp var strokes = signatureView.Strokes; ``` These strokes can be used to save and restore a signature: ```csharp // restore strokes (iOS, Android, Windows) signatureView.LoadStrokes (newStrokes); // restore strokes (Xamarin.Forms) signatureView.Strokes = newStrokes; ``` --- ## License The license for this repository is specified in [LICENSE](LICENSE). ## .NET Foundation This project is part of the [.NET Foundation](http://www.dotnetfoundation.org/projects). [nuget-link]: https://www.nuget.org/packages/Xamarin.Controls.SignaturePad ================================================ FILE: build.cake ================================================ /////////////////////////////////////////////////////////////////////////////// // ARGUMENTS /////////////////////////////////////////////////////////////////////////////// var CURRENT_PACKAGE_VERSION = "3.0.0"; var target = Argument("target", "Default"); var configuration = Argument("configuration", "Release"); var packageVersion = Argument("packageVersion", CURRENT_PACKAGE_VERSION); var majorVersion = $"{packageVersion.Substring(0, packageVersion.IndexOf("."))}.0.0.0"; var buildVersion = Argument("buildVersion", EnvironmentVariable("BUILD_NUMBER") ?? ""); if (!string.IsNullOrEmpty(buildVersion)) { buildVersion = $"-{buildVersion}"; } /////////////////////////////////////////////////////////////////////////////// // TASKS /////////////////////////////////////////////////////////////////////////////// Task("libs") .Does(() => { var sln = IsRunningOnWindows() ? "./src/SignaturePad.sln" : "./src/SignaturePad.Mac.sln"; MSBuild(sln, new MSBuildSettings { Verbosity = Verbosity.Minimal, Configuration = configuration, PlatformTarget = PlatformTarget.MSIL, MSBuildPlatform = MSBuildPlatform.x86, ArgumentCustomization = args => args.Append("/restore"), Properties = { { "AssemblyVersion", new [] { majorVersion } }, { "Version", new [] { packageVersion } }, }, }); EnsureDirectoryExists("./output/android/"); EnsureDirectoryExists("./output/ios/"); EnsureDirectoryExists("./output/uwp/"); EnsureDirectoryExists("./output/uwp/Themes"); EnsureDirectoryExists("./output/netstandard/"); CopyFiles($"./src/SignaturePad.Android/bin/{configuration}/SignaturePad.*", "./output/android/"); CopyFiles($"./src/SignaturePad.iOS/bin/{configuration}/SignaturePad.*", "./output/ios/"); CopyFiles($"./src/SignaturePad.UWP/bin/{configuration}/SignaturePad.*", "./output/uwp/"); CopyFiles($"./src/SignaturePad.UWP/bin/{configuration}/Themes/*", "./output/uwp/Themes"); CopyFiles($"./src/SignaturePad.Forms.Droid/bin/{configuration}/SignaturePad.Forms.*", "./output/android/"); CopyFiles($"./src/SignaturePad.Forms.iOS/bin/{configuration}/SignaturePad.Forms.*", "./output/ios/"); CopyFiles($"./src/SignaturePad.Forms.UWP/bin/{configuration}/SignaturePad.Forms.*", "./output/uwp/"); CopyFiles($"./src/SignaturePad.Forms.UWP/bin/{configuration}/Themes/*", "./output/uwp/Themes"); CopyFiles($"./src/SignaturePad.Forms/bin/{configuration}/SignaturePad.Forms.*", "./output/netstandard/"); }); Task("nuget") .IsDependentOn("libs") .WithCriteria(IsRunningOnWindows()) .Does(() => { var nuget = Context.Tools.Resolve("nuget.exe"); var nuspecs = GetFiles("./nuget/*.nuspec"); var settings = new NuGetPackSettings { BasePath = ".", OutputDirectory = "./output", Properties = new Dictionary { { "configuration", configuration }, { "version", packageVersion }, }, }; EnsureDirectoryExists("./output"); NuGetPack(nuspecs, settings); settings.Properties["version"] = $"{packageVersion}-preview{buildVersion}"; NuGetPack(nuspecs, settings); }); Task("samples") .IsDependentOn("libs") .Does(() => { var settings = new MSBuildSettings { Verbosity = Verbosity.Minimal, Configuration = configuration, PlatformTarget = PlatformTarget.MSIL, MSBuildPlatform = MSBuildPlatform.x86, ArgumentCustomization = args => args.Append("/restore"), }; if (IsRunningOnWindows()) { MSBuild("./samples/Sample.Android/Sample.Android.sln", settings); MSBuild("./samples/Sample.iOS/Sample.iOS.sln", settings); MSBuild("./samples/Sample.UWP/Sample.UWP.sln", settings); MSBuild("./samples/Sample.Forms/Sample.Forms.sln", settings); } else { MSBuild("./samples/Sample.Android/Sample.Android.sln", settings); MSBuild("./samples/Sample.iOS/Sample.iOS.sln", settings); MSBuild("./samples/Sample.Forms/Sample.Forms.Mac.sln", settings); } }); Task("Default") .IsDependentOn("libs") .IsDependentOn("nuget") .IsDependentOn("samples"); Task("CI") .IsDependentOn("Default"); RunTarget(target); ================================================ FILE: build.ps1 ================================================ ########################################################################## # This is the Cake bootstrapper script for PowerShell. # This file was downloaded from https://github.com/cake-build/resources # Feel free to change this file to fit your needs. ########################################################################## <# .SYNOPSIS This is a Powershell script to bootstrap a Cake build. .DESCRIPTION This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) and execute your Cake build script with the parameters you provide. .PARAMETER Script The build script to execute. .PARAMETER Target The build script target to run. .PARAMETER Configuration The build configuration to use. .PARAMETER Verbosity Specifies the amount of information to be displayed. .PARAMETER ShowDescription Shows description about tasks. .PARAMETER DryRun Performs a dry run. .PARAMETER Experimental Uses the nightly builds of the Roslyn script engine. .PARAMETER Mono Uses the Mono Compiler rather than the Roslyn script engine. .PARAMETER SkipToolPackageRestore Skips restoring of packages. .PARAMETER ScriptArgs Remaining arguments are added here. .LINK https://cakebuild.net #> [CmdletBinding()] Param( [string]$Script = "build.cake", [string]$Target, [string]$Configuration, [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] [string]$Verbosity, [switch]$ShowDescription, [Alias("WhatIf", "Noop")] [switch]$DryRun, [switch]$Experimental, [switch]$Mono, [switch]$SkipToolPackageRestore, [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] [string[]]$ScriptArgs ) [Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null function MD5HashFile([string] $filePath) { if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) { return $null } [System.IO.Stream] $file = $null; [System.Security.Cryptography.MD5] $md5 = $null; try { $md5 = [System.Security.Cryptography.MD5]::Create() $file = [System.IO.File]::OpenRead($filePath) return [System.BitConverter]::ToString($md5.ComputeHash($file)) } finally { if ($file -ne $null) { $file.Dispose() } } } function GetProxyEnabledWebClient { $wc = New-Object System.Net.WebClient $proxy = [System.Net.WebRequest]::GetSystemWebProxy() $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials $wc.Proxy = $proxy return $wc } Write-Host "Preparing to run build script..." if(!$PSScriptRoot){ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent } $TOOLS_DIR = Join-Path $PSScriptRoot "tools" $ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" $MODULES_DIR = Join-Path $TOOLS_DIR "Modules" $NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" $CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" $NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" $PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" $PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" $ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" $MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" # Make sure tools folder exists if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { Write-Verbose -Message "Creating tools directory..." New-Item -Path $TOOLS_DIR -Type directory | out-null } # Make sure that packages.config exist. if (!(Test-Path $PACKAGES_CONFIG)) { Write-Verbose -Message "Downloading packages.config..." try { $wc = GetProxyEnabledWebClient $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch { Throw "Could not download packages.config." } } # Try find NuGet.exe in path if not exists if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Trying to find nuget.exe in PATH..." $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName } } # Try download NuGet.exe if not exists if (!(Test-Path $NUGET_EXE)) { Write-Verbose -Message "Downloading NuGet.exe..." try { $wc = GetProxyEnabledWebClient $wc.DownloadFile($NUGET_URL, $NUGET_EXE) } catch { Throw "Could not download NuGet.exe." } } # Save nuget.exe path to environment to be available to child processed $ENV:NUGET_EXE = $NUGET_EXE # Restore tools from NuGet? if(-Not $SkipToolPackageRestore.IsPresent) { Push-Location Set-Location $TOOLS_DIR # Check for changes in packages.config and remove installed tools if true. [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { Write-Verbose -Message "Missing or changed package.config hash..." Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | Remove-Item -Recurse } Write-Verbose -Message "Restoring tools from NuGet..." $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet tools." } else { $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" } Write-Verbose -Message ($NuGetOutput | out-string) Pop-Location } # Restore addins from NuGet if (Test-Path $ADDINS_PACKAGES_CONFIG) { Push-Location Set-Location $ADDINS_DIR Write-Verbose -Message "Restoring addins from NuGet..." $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet addins." } Write-Verbose -Message ($NuGetOutput | out-string) Pop-Location } # Restore modules from NuGet if (Test-Path $MODULES_PACKAGES_CONFIG) { Push-Location Set-Location $MODULES_DIR Write-Verbose -Message "Restoring modules from NuGet..." $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" if ($LASTEXITCODE -ne 0) { Throw "An error occurred while restoring NuGet modules." } Write-Verbose -Message ($NuGetOutput | out-string) Pop-Location } # Make sure that Cake has been installed. if (!(Test-Path $CAKE_EXE)) { Throw "Could not find Cake.exe at $CAKE_EXE" } # Build Cake arguments $cakeArguments = @("$Script"); if ($Target) { $cakeArguments += "-target=$Target" } if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } if ($ShowDescription) { $cakeArguments += "-showdescription" } if ($DryRun) { $cakeArguments += "-dryrun" } if ($Experimental) { $cakeArguments += "-experimental" } if ($Mono) { $cakeArguments += "-mono" } $cakeArguments += $ScriptArgs # Start Cake Write-Host "Running build script..." &$CAKE_EXE $cakeArguments exit $LASTEXITCODE ================================================ FILE: build.sh ================================================ #!/usr/bin/env bash ########################################################################## # This is the Cake bootstrapper script for Linux and OS X. # This file was downloaded from https://github.com/cake-build/resources # Feel free to change this file to fit your needs. ########################################################################## # Define directories. SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) TOOLS_DIR=$SCRIPT_DIR/tools ADDINS_DIR=$TOOLS_DIR/Addins MODULES_DIR=$TOOLS_DIR/Modules NUGET_EXE=$TOOLS_DIR/nuget.exe CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe PACKAGES_CONFIG=$TOOLS_DIR/packages.config PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config # Define md5sum or md5 depending on Linux/OSX MD5_EXE= if [[ "$(uname -s)" == "Darwin" ]]; then MD5_EXE="md5 -r" else MD5_EXE="md5sum" fi # Define default arguments. SCRIPT="build.cake" CAKE_ARGUMENTS=() # Parse arguments. for i in "$@"; do case $1 in -s|--script) SCRIPT="$2"; shift ;; --) shift; CAKE_ARGUMENTS+=("$@"); break ;; *) CAKE_ARGUMENTS+=("$1") ;; esac shift done # Make sure the tools folder exist. if [ ! -d "$TOOLS_DIR" ]; then mkdir "$TOOLS_DIR" fi # Make sure that packages.config exist. if [ ! -f "$TOOLS_DIR/packages.config" ]; then echo "Downloading packages.config..." curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages if [ $? -ne 0 ]; then echo "An error occurred while downloading packages.config." exit 1 fi fi # Download NuGet if it does not exist. if [ ! -f "$NUGET_EXE" ]; then echo "Downloading NuGet..." curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe if [ $? -ne 0 ]; then echo "An error occurred while downloading nuget.exe." exit 1 fi fi # Restore tools from NuGet. pushd "$TOOLS_DIR" >/dev/null if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf fi mono "$NUGET_EXE" install -ExcludeVersion if [ $? -ne 0 ]; then echo "Could not restore NuGet tools." exit 1 fi $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" popd >/dev/null # Restore addins from NuGet. if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then pushd "$ADDINS_DIR" >/dev/null mono "$NUGET_EXE" install -ExcludeVersion if [ $? -ne 0 ]; then echo "Could not restore NuGet addins." exit 1 fi popd >/dev/null fi # Restore modules from NuGet. if [ -f "$MODULES_PACKAGES_CONFIG" ]; then pushd "$MODULES_DIR" >/dev/null mono "$NUGET_EXE" install -ExcludeVersion if [ $? -ne 0 ]; then echo "Could not restore NuGet modules." exit 1 fi popd >/dev/null fi # Make sure that Cake has been installed. if [ ! -f "$CAKE_EXE" ]; then echo "Could not find Cake.exe at '$CAKE_EXE'." exit 1 fi # Start Cake exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}" ================================================ FILE: nuget/Xamarin.Controls.SignaturePad.Forms.nuspec ================================================ Xamarin.Controls.SignaturePad.Forms SignaturePad for Xamarin.Forms $version$ Microsoft Microsoft true https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874510 Makes capturing, saving, and displaying signatures extremely simple. https://go.microsoft.com/fwlink/?linkid=874509 © Microsoft Corporation. All rights reserved. xamarin,signature,handwriting,windows,ios,android,uwp,xamarin.forms ================================================ FILE: nuget/Xamarin.Controls.SignaturePad.nuspec ================================================ Xamarin.Controls.SignaturePad SignaturePad for Xamarin and Windows $version$ Microsoft Microsoft true https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874510 Makes capturing, saving, and displaying signatures extremely simple. https://go.microsoft.com/fwlink/?linkid=874509 © Microsoft Corporation. All rights reserved. xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: samples/Sample.Android/MainActivity.cs ================================================ using System.IO; using Android.App; using Android.Graphics; using Android.OS; using Android.Support.V7.App; using Android.Widget; using Xamarin.Controls; namespace Sample.Android { [Activity (MainLauncher = true)] public class MainActivity : AppCompatActivity { private System.Drawing.PointF[] points; protected override void OnCreate (Bundle bundle) { base.OnCreate (bundle); SetContentView (Resource.Layout.main); var signatureView = FindViewById (Resource.Id.signatureView); var btnSave = FindViewById ================================================ FILE: samples/Sample.iOS/Resources/Assets.xcassets/AppIcons.appiconset/Contents.json ================================================ { "images": [ { "scale": "2x", "size": "20x20", "idiom": "iphone", "filename": "Icon-App-20x20@2x.png" }, { "scale": "3x", "size": "20x20", "idiom": "iphone", "filename": "Icon-App-20x20@3x.png" }, { "scale": "2x", "size": "29x29", "idiom": "iphone", "filename": "Icon-App-29x29@2x.png" }, { "scale": "3x", "size": "29x29", "idiom": "iphone", "filename": "Icon-App-29x29@3x.png" }, { "scale": "2x", "size": "40x40", "idiom": "iphone", "filename": "Icon-App-40x40@2x.png" }, { "scale": "3x", "size": "40x40", "idiom": "iphone", "filename": "Icon-App-40x40@3x.png" }, { "scale": "2x", "size": "60x60", "idiom": "iphone", "filename": "Icon-App-60x60@2x.png" }, { "scale": "3x", "size": "60x60", "idiom": "iphone", "filename": "Icon-App-60x60@3x.png" }, { "scale": "1x", "size": "20x20", "idiom": "ipad", "filename": "Icon-App-20x20@1x.png" }, { "scale": "2x", "size": "20x20", "idiom": "ipad", "filename": "Icon-App-20x20@2x.png" }, { "scale": "1x", "size": "29x29", "idiom": "ipad", "filename": "Icon-App-29x29@1x.png" }, { "scale": "2x", "size": "29x29", "idiom": "ipad", "filename": "Icon-App-29x29@2x.png" }, { "scale": "1x", "size": "40x40", "idiom": "ipad", "filename": "Icon-App-40x40@1x.png" }, { "scale": "2x", "size": "40x40", "idiom": "ipad", "filename": "Icon-App-40x40@2x.png" }, { "scale": "1x", "size": "76x76", "idiom": "ipad", "filename": "Icon-App-76x76@1x.png" }, { "scale": "2x", "size": "76x76", "idiom": "ipad", "filename": "Icon-App-76x76@2x.png" }, { "scale": "2x", "size": "83.5x83.5", "idiom": "ipad", "filename": "Icon-App-83.5x83.5@2x.png" }, { "scale": "1x", "size": "1024x1024", "idiom": "ios-marketing", "filename": "ItunesArtwork@2x.png" } ], "properties": {}, "info": { "version": 1, "author": "xcode" } } ================================================ FILE: samples/Sample.iOS/Sample.iOS.csproj ================================================  Debug iPhoneSimulator {101E3060-8799-4119-8A7A-4F86A01C0C84} {FEACFBD2-3405-455C-9665-78FE426C6842};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} {edc1b0fa-90cd-4038-8fad-98fe74adb368} Exe Sample.iOS SampleiOS Resources true full false bin\iPhoneSimulator\Debug DEBUG prompt 4 false x86_64 None true none true bin\iPhoneSimulator\Release prompt 4 Full x86_64 false iPhone Developer true full false bin\iPhone\Debug DEBUG prompt 4 false ARM64 Entitlements.plist iPhone Developer true none true bin\iPhone\Release prompt 4 Entitlements.plist ARM64 false iPhone Developer Full {bef71536-787b-431f-ac7f-a6469710d11f} SignaturePad.iOS false false ViewController.cs ================================================ FILE: samples/Sample.iOS/Sample.iOS.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26228.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample.iOS", "Sample.iOS.csproj", "{101E3060-8799-4119-8A7A-4F86A01C0C84}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.iOS", "..\..\src\SignaturePad.iOS\SignaturePad.iOS.csproj", "{BEF71536-787B-431F-AC7F-A6469710D11F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|iPhone = Debug|iPhone Debug|iPhoneSimulator = Debug|iPhoneSimulator Release|Any CPU = Release|Any CPU Release|iPhone = Release|iPhone Release|iPhoneSimulator = Release|iPhoneSimulator EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {101E3060-8799-4119-8A7A-4F86A01C0C84}.Debug|Any CPU.ActiveCfg = Debug|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Debug|Any CPU.Build.0 = Debug|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Debug|iPhone.ActiveCfg = Debug|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Debug|iPhone.Build.0 = Debug|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator {101E3060-8799-4119-8A7A-4F86A01C0C84}.Debug|iPhoneSimulator.Build.0 = Debug|iPhoneSimulator {101E3060-8799-4119-8A7A-4F86A01C0C84}.Release|Any CPU.ActiveCfg = Release|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Release|Any CPU.Build.0 = Release|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Release|iPhone.ActiveCfg = Release|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Release|iPhone.Build.0 = Release|iPhone {101E3060-8799-4119-8A7A-4F86A01C0C84}.Release|iPhoneSimulator.ActiveCfg = Release|iPhoneSimulator {101E3060-8799-4119-8A7A-4F86A01C0C84}.Release|iPhoneSimulator.Build.0 = Release|iPhoneSimulator {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|iPhone.ActiveCfg = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|iPhone.Build.0 = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|Any CPU.Build.0 = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|iPhone.ActiveCfg = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|iPhone.Build.0 = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|iPhoneSimulator.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C56208F5-7791-4E6D-AACD-288D3363B843} EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = Sample.iOS.csproj EndGlobalSection EndGlobal ================================================ FILE: samples/Sample.iOS/ViewController.cs ================================================ using System; using System.Threading.Tasks; using CoreGraphics; using Foundation; using Photos; using UIKit; using Xamarin.Controls; namespace Sample.iOS { public partial class ViewController : UIViewController { private CGPoint[] points; public ViewController (IntPtr handle) : base (handle) { } public override void ViewDidLoad () { base.ViewDidLoad (); signatureView.Layer.BorderColor = UIColor.FromRGBA (184, 134, 11, 255).CGColor; signatureView.Layer.BorderWidth = 1f; signatureView.StrokeCompleted += (sender, e) => UpdateControls (); signatureView.Cleared += (sender, e) => UpdateControls (); UpdateControls (); } private void UpdateControls () { btnSave.Enabled = !signatureView.IsBlank; btnSaveImage.Enabled = !signatureView.IsBlank; btnLoad.Enabled = points != null; } partial void SaveVectorClicked (UIButton sender) { points = signatureView.Points; UpdateControls (); ShowToast ("Vector signature saved to memory."); } partial void LoadVectorClicked (UIButton sender) { signatureView.LoadPoints (points); } async partial void SaveImageClicked (UIButton sender) { UIImage image; using (var bitmap = await signatureView.GetImageStreamAsync (SignatureImageFormat.Png, UIColor.Black, UIColor.White, 1f)) using (var data = NSData.FromStream (bitmap)) { image = UIImage.LoadFromData (data); } var status = await PHPhotoLibrary.RequestAuthorizationAsync (); if (status == PHAuthorizationStatus.Authorized) { image.SaveToPhotosAlbum ((i, error) => { image.Dispose (); if (error == null) ShowToast ("Raster signature saved to the photo library."); else ShowToast ("There was an error saving the signature: " + error.LocalizedDescription); }); } else { ShowToast ("Permission to save to the photo library was denied."); } } private async void ShowToast (string message) { var toast = UIAlertController.Create (null, message, UIAlertControllerStyle.Alert); await PresentViewControllerAsync (toast, true); await Task.Delay (1000); await toast.DismissViewControllerAsync (true); } } } ================================================ FILE: samples/Sample.iOS/ViewController.designer.cs ================================================ // WARNING // // This file has been generated automatically by Visual Studio from the outlets and // actions declared in your storyboard file. // Manual changes to this file will not be maintained. // using Foundation; using System; using System.CodeDom.Compiler; using UIKit; namespace Sample.iOS { [Register ("ViewController")] partial class ViewController { [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIButton btnLoad { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIButton btnSave { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] UIKit.UIButton btnSaveImage { get; set; } [Outlet] [GeneratedCode ("iOS Designer", "1.0")] Xamarin.Controls.SignaturePadView signatureView { get; set; } [Action ("LoadVectorClicked:")] [GeneratedCode ("iOS Designer", "1.0")] partial void LoadVectorClicked (UIKit.UIButton sender); [Action ("SaveImageClicked:")] [GeneratedCode ("iOS Designer", "1.0")] partial void SaveImageClicked (UIKit.UIButton sender); [Action ("SaveVectorClicked:")] [GeneratedCode ("iOS Designer", "1.0")] partial void SaveVectorClicked (UIKit.UIButton sender); void ReleaseDesignerOutlets () { if (btnLoad != null) { btnLoad.Dispose (); btnLoad = null; } if (btnSave != null) { btnSave.Dispose (); btnSave = null; } if (btnSaveImage != null) { btnSaveImage.Dispose (); btnSaveImage = null; } if (signatureView != null) { signatureView.Dispose (); signatureView = null; } } } } ================================================ FILE: src/SignaturePad.Android/InkPresenter.cs ================================================ using System.Collections.Generic; using System.Drawing; using System.Linq; using Android.App; using Android.Content; using Android.Graphics; using Android.Runtime; using Android.Util; using Android.Views; namespace Xamarin.Controls { partial class InkPresenter : View { static InkPresenter () { // we may be in a designer if (Application.Context == null) { ScreenDensity = 1f; return; } var wndMngr = Application.Context.GetSystemService (Context.WindowService).JavaCast (); var dm = new DisplayMetrics (); wndMngr.DefaultDisplay.GetMetrics (dm); ScreenDensity = (float)dm.Density; } public InkPresenter (Context context) : base (context) { Initialize (); } private void Initialize () { } public override bool OnTouchEvent (MotionEvent e) { switch (e.Action) { case MotionEventActions.Down: TouchesBegan (e); return true; case MotionEventActions.Move: TouchesMoved (e); return true; case MotionEventActions.Up: TouchesEnded (e); return true; } return false; } private void TouchesBegan (MotionEvent e) { // don't allow the event to propagate because we're handling it here Parent?.RequestDisallowInterceptTouchEvent (true); // create a new path and set the options currentPath = new InkStroke (new Path (), new List (), StrokeColor, StrokeWidth); // obtain the location of the touch float touchX = e.GetX (); float touchY = e.GetY (); // move to the touched point currentPath.Path.MoveTo (touchX, touchY); currentPath.GetPoints ().Add (new System.Drawing.PointF (touchX, touchY)); // update the dirty rectangle ResetBounds (touchX, touchY); Invalidate (DirtyRect); } private void TouchesMoved (MotionEvent e, bool update = true) { // something may have happened (clear) so start the stroke again if (currentPath == null) { TouchesBegan (e); } var hasMoved = false; for (var i = 0; i < e.HistorySize; i++) { float historicalX = e.GetHistoricalX (i); float historicalY = e.GetHistoricalY (i); if (HasMovedFarEnough (currentPath, historicalX, historicalY)) { // update the dirty rectangle UpdateBounds (historicalX, historicalY); hasMoved = true; // add it to the current path currentPath.Path.LineTo (historicalX, historicalY); currentPath.GetPoints ().Add (new System.Drawing.PointF (historicalX, historicalY)); } } float touchX = e.GetX (); float touchY = e.GetY (); if (HasMovedFarEnough (currentPath, touchX, touchY)) { // add it to the current path currentPath.Path.LineTo (touchX, touchY); currentPath.GetPoints ().Add (new System.Drawing.PointF (touchX, touchY)); // update the dirty rectangle UpdateBounds (touchX, touchY); hasMoved = true; } if (update && hasMoved) { Invalidate (DirtyRect); } } private void TouchesEnded (MotionEvent e) { // something may have happened (clear) during the stroke if (currentPath != null) { TouchesMoved (e, false); // add the current path and points to their respective lists. var smoothed = PathSmoothing.SmoothedPathWithGranularity (currentPath, 2); paths.Add (smoothed); } // reset the drawing currentPath = null; // update the dirty rectangle Invalidate (DirtyRect); // we are done with drawing OnStrokeCompleted (); // allow the event to propagate Parent?.RequestDisallowInterceptTouchEvent (false); } private void Invalidate (RectangleF dirtyRect) { using (var rect = new Rect ( (int)(dirtyRect.Left - 0.5f), (int)(dirtyRect.Top - 0.5f), (int)(dirtyRect.Right + 0.5f), (int)(dirtyRect.Bottom + 0.5f))) { Invalidate (rect); } } protected override void OnDraw (Canvas canvas) { base.OnDraw (canvas); // destroy an old bitmap if (bitmapBuffer != null && ShouldRedrawBufferImage) { var temp = bitmapBuffer; bitmapBuffer = null; temp.Recycle (); temp.Dispose (); temp = null; } // re-create if (bitmapBuffer == null) { bitmapBuffer = CreateBufferImage (); } // if there are no lines, the the bitmap will be null if (bitmapBuffer != null) { canvas.DrawBitmap (bitmapBuffer, 0, 0, null); } // draw the current path over the old paths if (currentPath != null) { using (var paint = new Paint ()) { paint.StrokeJoin = Paint.Join.Round; paint.StrokeCap = Paint.Cap.Round; paint.AntiAlias = true; paint.SetStyle (Paint.Style.Stroke); paint.Color = currentPath.Color; paint.StrokeWidth = currentPath.Width * ScreenDensity; canvas.DrawPath (currentPath.Path, paint); } } } private Bitmap CreateBufferImage () { if (paths == null || paths.Count == 0) { return null; } var size = new SizeF (Width, Height); var image = Bitmap.CreateBitmap ((int)size.Width, (int)size.Height, Bitmap.Config.Argb8888); using (var canvas = new Canvas (image)) using (var paint = new Paint ()) { paint.StrokeJoin = Paint.Join.Round; paint.StrokeCap = Paint.Cap.Round; paint.AntiAlias = true; paint.SetStyle (Paint.Style.Stroke); foreach (var path in paths) { paint.Color = path.Color; paint.StrokeWidth = path.Width * ScreenDensity; canvas.DrawPath (path.Path, paint); path.IsDirty = false; } } return image; } } } ================================================ FILE: src/SignaturePad.Android/Resources/drawable/signature_pad_background.xml ================================================  ================================================ FILE: src/SignaturePad.Android/Resources/layout/signature_pad_layout.axml ================================================ ================================================ FILE: src/SignaturePad.Android/Resources/values/attrs.xml ================================================  ================================================ FILE: src/SignaturePad.Android/Resources/values/signature_pad_defaults.xml ================================================  clear sign above the line #FFFFFF #000000 12dip 3dip 1dip 15dip ================================================ FILE: src/SignaturePad.Android/SignaturePad.Android.csproj ================================================  monoandroid4.0.3 Xamarin.Controls SignaturePad False True False bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad SignaturePad for Xamarin and Windows $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.Android/SignaturePadCanvasView.cs ================================================ using System.IO; using System.Threading.Tasks; using Android.Content; using Android.Graphics; using Android.Util; using Android.Views; using Android.Widget; namespace Xamarin.Controls { public partial class SignaturePadCanvasView : FrameLayout { private InkPresenter inkPresenter; public SignaturePadCanvasView (Context context) : base (context) { Initialize (); } public SignaturePadCanvasView (Context context, IAttributeSet attrs) : base (context, attrs) { Initialize (); } public SignaturePadCanvasView (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle) { Initialize (); } private void Initialize () { inkPresenter = new InkPresenter (Context) { LayoutParameters = new FrameLayout.LayoutParams (FrameLayout.LayoutParams.MatchParent, FrameLayout.LayoutParams.MatchParent) }; inkPresenter.StrokeCompleted += OnStrokeCompleted; AddView (inkPresenter); StrokeWidth = ImageConstructionSettings.DefaultStrokeWidth; StrokeColor = ImageConstructionSettings.DefaultStrokeColor; } /// /// Gets or sets the color of the strokes for the signature. /// /// The color of the stroke. public Color StrokeColor { get { return inkPresenter.StrokeColor; } set { inkPresenter.StrokeColor = value; foreach (var stroke in inkPresenter.GetStrokes ()) { stroke.Color = value; } inkPresenter.Invalidate (); } } /// /// Gets or sets the width in pixels of the strokes for the signature. /// /// The width of the line. public float StrokeWidth { get { return inkPresenter.StrokeWidth; } set { inkPresenter.StrokeWidth = value; foreach (var stroke in inkPresenter.GetStrokes ()) { stroke.Width = value; } inkPresenter.Invalidate (); } } public void Clear () { inkPresenter.Clear (); OnCleared (); } private Bitmap GetImageInternal (System.Drawing.SizeF scale, System.Drawing.RectangleF signatureBounds, System.Drawing.SizeF imageSize, float strokeWidth, Color strokeColor, Color backgroundColor) { // create bitmap and set the desired options var image = Bitmap.CreateBitmap ((int)imageSize.Width, (int)imageSize.Height, Bitmap.Config.Argb8888); using (var canvas = new Canvas (image)) { // background canvas.DrawColor (backgroundColor); // cropping / scaling canvas.Scale (scale.Width, scale.Height); canvas.Translate (-signatureBounds.Left, -signatureBounds.Top); // strokes using (var paint = new Paint ()) { paint.Color = strokeColor; paint.StrokeWidth = strokeWidth * InkPresenter.ScreenDensity; paint.StrokeJoin = Paint.Join.Round; paint.StrokeCap = Paint.Cap.Round; paint.AntiAlias = true; paint.SetStyle (Paint.Style.Stroke); foreach (var path in inkPresenter.GetStrokes ()) { canvas.DrawPath (path.Path, paint); } } } // get the image return image; } private async Task GetImageStreamInternal (SignatureImageFormat format, System.Drawing.SizeF scale, System.Drawing.RectangleF signatureBounds, System.Drawing.SizeF imageSize, float strokeWidth, Color strokeColor, Color backgroundColor) { Bitmap.CompressFormat bcf; if (format == SignatureImageFormat.Jpeg) { bcf = Bitmap.CompressFormat.Jpeg; } else if (format == SignatureImageFormat.Png) { bcf = Bitmap.CompressFormat.Png; } else { return null; } var image = GetImageInternal (scale, signatureBounds, imageSize, strokeWidth, strokeColor, backgroundColor); if (image != null) { using (image) { var stream = new MemoryStream (); var result = await image.CompressAsync (bcf, 100, stream); image.Recycle (); if (result) { stream.Position = 0; return stream; } } } return null; } public override bool OnInterceptTouchEvent (MotionEvent ev) { // don't accept touch when the view is disabled if (!Enabled) return true; return base.OnInterceptTouchEvent (ev); } } } ================================================ FILE: src/SignaturePad.Android/SignaturePadView.cs ================================================ using System; using Android.Content; using Android.Graphics; using Android.Graphics.Drawables; using Android.Util; using Android.Views; using Android.Widget; namespace Xamarin.Controls { public partial class SignaturePadView : RelativeLayout { private static Random rnd = new Random (); public SignaturePadView (Context context) : base (context) { Initialize (null); } public SignaturePadView (Context context, IAttributeSet attrs) : base (context, attrs) { Initialize (attrs); } public SignaturePadView (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle) { Initialize (attrs); } private void Initialize (IAttributeSet attrs) { Inflate (Context, Resource.Layout.signature_pad_layout, this); // find the views BackgroundImageView = FindViewById (Resource.Id.background_image); SignaturePadCanvas = FindViewById (Resource.Id.signature_canvas); Caption = FindViewById (Resource.Id.caption); SignatureLine = FindViewById (Resource.Id.signature_line); SignaturePrompt = FindViewById (Resource.Id.signature_prompt); ClearLabel = FindViewById (Resource.Id.clear_label); // set the properties from the attributes if (attrs != null) { using (var a = Context.Theme.ObtainStyledAttributes (attrs, Resource.Styleable.SignaturePadView, 0, 0)) { if (a.HasValue (Resource.Styleable.SignaturePadView_strokeColor)) StrokeColor = a.GetColor (Resource.Styleable.SignaturePadView_strokeColor, ImageConstructionSettings.DefaultStrokeColor); if (a.HasValue (Resource.Styleable.SignaturePadView_strokeWidth)) StrokeWidth = a.GetDimension (Resource.Styleable.SignaturePadView_strokeWidth, ImageConstructionSettings.DefaultStrokeWidth); if (a.HasValue (Resource.Styleable.SignaturePadView_captionText)) CaptionText = a.GetString (Resource.Styleable.SignaturePadView_captionText); if (a.HasValue (Resource.Styleable.SignaturePadView_captionTextColor)) CaptionTextColor = a.GetColor (Resource.Styleable.SignaturePadView_captionTextColor, SignaturePadDarkColor); if (a.HasValue (Resource.Styleable.SignaturePadView_captionTextSize)) CaptionTextSize = a.GetDimension (Resource.Styleable.SignaturePadView_captionTextSize, DefaultFontSize); if (a.HasValue (Resource.Styleable.SignaturePadView_clearLabelText)) ClearLabelText = a.GetString (Resource.Styleable.SignaturePadView_clearLabelText); if (a.HasValue (Resource.Styleable.SignaturePadView_clearLabelTextColor)) ClearLabelTextColor = a.GetColor (Resource.Styleable.SignaturePadView_clearLabelTextColor, SignaturePadDarkColor); if (a.HasValue (Resource.Styleable.SignaturePadView_clearLabelTextSize)) ClearLabelTextSize = a.GetDimension (Resource.Styleable.SignaturePadView_clearLabelTextSize, DefaultFontSize); if (a.HasValue (Resource.Styleable.SignaturePadView_signaturePromptText)) SignaturePromptText = a.GetString (Resource.Styleable.SignaturePadView_signaturePromptText); if (a.HasValue (Resource.Styleable.SignaturePadView_signaturePromptTextColor)) SignaturePromptTextColor = a.GetColor (Resource.Styleable.SignaturePadView_signaturePromptTextColor, SignaturePadDarkColor); if (a.HasValue (Resource.Styleable.SignaturePadView_signaturePromptTextSize)) SignaturePromptTextSize = a.GetDimension (Resource.Styleable.SignaturePadView_signaturePromptTextSize, DefaultFontSize); if (a.HasValue (Resource.Styleable.SignaturePadView_signatureLineColor)) SignatureLineColor = a.GetColor (Resource.Styleable.SignaturePadView_signatureLineColor, SignaturePadDarkColor); if (a.HasValue (Resource.Styleable.SignaturePadView_signatureLineSpacing)) SignatureLineSpacing = a.GetInt (Resource.Styleable.SignaturePadView_signatureLineSpacing, (int)DefaultNarrowSpacing); if (a.HasValue (Resource.Styleable.SignaturePadView_signatureLineWidth)) SignatureLineWidth = a.GetInt (Resource.Styleable.SignaturePadView_signatureLineWidth, (int)DefaultLineThickness); a.Recycle (); } } // attach the events SignaturePadCanvas.StrokeCompleted += (sender, e) => OnSignatureStrokeCompleted (); SignaturePadCanvas.Cleared += (sender, e) => OnSignatureCleared (); ClearLabel.Click += (sender, e) => OnClearTapped (); // initialize the view UpdateUi (); } public SignaturePadCanvasView SignaturePadCanvas { get; private set; } public View SignatureLine { get; private set; } public TextView Caption { get; private set; } public TextView SignaturePrompt { get; private set; } public TextView ClearLabel { get; private set; } [Obsolete ("Set the background instead.")] public ImageView BackgroundImageView { get; private set; } public Color StrokeColor { get => SignaturePadCanvas.StrokeColor; set => SignaturePadCanvas.StrokeColor = value; } public float StrokeWidth { get => SignaturePadCanvas.StrokeWidth; set => SignaturePadCanvas.StrokeWidth = value; } public Color SignatureLineColor { get => (SignatureLine.Background as ColorDrawable)?.Color ?? Color.Transparent; set => SignatureLine.SetBackgroundColor (value); } public int SignatureLineWidth { get => SignatureLine.Height; set { var param = SignatureLine.LayoutParameters; param.Height = value; SignatureLine.LayoutParameters = param; } } public int SignatureLineSpacing { get => SignatureLine.PaddingBottom; set => SignatureLine.SetPadding (PaddingLeft, value, PaddingRight, value); } public string CaptionText { get => Caption.Text; set => Caption.Text = value; } public float CaptionTextSize { get => Caption.TextSize; set => Caption.TextSize = value; } public Color CaptionTextColor { get => new Color (Caption.CurrentTextColor); set => Caption.SetTextColor (value); } public string SignaturePromptText { get => SignaturePrompt.Text; set => SignaturePrompt.Text = value; } public float SignaturePromptTextSize { get => SignaturePrompt.TextSize; set => SignaturePrompt.TextSize = value; } public Color SignaturePromptTextColor { get => new Color (SignaturePrompt.CurrentTextColor); set => SignaturePrompt.SetTextColor (value); } public string ClearLabelText { get => ClearLabel.Text; set => ClearLabel.Text = value; } public float ClearLabelTextSize { get => ClearLabel.TextSize; set => ClearLabel.TextSize = value; } public Color ClearLabelTextColor { get => new Color (ClearLabel.CurrentTextColor); set => ClearLabel.SetTextColor (value); } [Obsolete ("Set the background instead.")] public Color BackgroundColor { get => (Background as ColorDrawable)?.Color ?? Color.Transparent; set => SetBackgroundColor (value); } private void UpdateUi () { ClearLabel.Visibility = IsBlank ? ViewStates.Invisible : ViewStates.Visible; } public override bool OnInterceptTouchEvent (MotionEvent ev) { // don't accept touch when the view is disabled if (!Enabled) return true; return base.OnInterceptTouchEvent (ev); } } } ================================================ FILE: src/SignaturePad.Forms/SignaturePad.Forms.csproj ================================================  netstandard1.0 SignaturePad.Forms SignaturePad.Forms False bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad.Forms SignaturePad for Xamarin.Forms $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.Forms/SignaturePadCanvasRenderer.cs ================================================ namespace SignaturePad.Forms { internal class SignaturePadCanvasRenderer { } } ================================================ FILE: src/SignaturePad.Forms.Droid/SignaturePad.Forms.Droid.csproj ================================================  monoandroid7.1 SignaturePad.Forms SignaturePad.Forms False true bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad.Forms SignaturePad for Xamarin.Forms $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.Forms.Droid/SignaturePadViewRenderer.cs ================================================ using System; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: ExportRenderer (typeof (SignaturePad.Forms.SignaturePadView), typeof (SignaturePad.Forms.SignaturePadViewRenderer))] namespace SignaturePad.Forms { // TODO: Remove this whole logic once Xamarin.Forms v2.4 is released. // This is a fix for issue #94 // https://github.com/xamarin/SignaturePad/issues/94 [RenderWith (typeof (SignaturePadViewRenderer))] partial class SignaturePadView { } internal class SignaturePadViewRenderer : VisualElementRenderer { [Obsolete ("This constructor is obsolete as of version 2.5. Please use 'SignaturePadCanvasRenderer (Context)' instead.")] public SignaturePadViewRenderer () { } public SignaturePadViewRenderer (Android.Content.Context context) : base (context) { } public override bool OnInterceptTouchEvent (Android.Views.MotionEvent ev) { if (!Enabled || Element?.IsEnabled == false) return true; return base.OnInterceptTouchEvent (ev); } } } ================================================ FILE: src/SignaturePad.Forms.Platform.Shared/ColorExtensions.cs ================================================ using Color = Xamarin.Forms.Color; #if WINDOWS_PHONE using System.Windows.Controls; using System.Windows.Media; using NativeColor = System.Windows.Media.Color; #elif WINDOWS_UWP || WINDOWS_PHONE_APP || WINDOWS_APP using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; using NativeColor = Windows.UI.Color; #elif __IOS__ using UIKit; using Xamarin.Forms.Platform.iOS; using NativeColor = UIKit.UIColor; #elif __ANDROID__ using Android.Widget; using Xamarin.Forms.Platform.Android; using NativeColor = Android.Graphics.Color; #endif namespace SignaturePad.Forms { public static class ColorExtensions { #if WINDOWS_PHONE || WINDOWS_UWP || WINDOWS_PHONE_APP || WINDOWS_APP public static NativeColor ToWindows (this Color color) { return NativeColor.FromArgb ( (byte)(color.A * 255), (byte)(color.R * 255), (byte)(color.G * 255), (byte)(color.B * 255)); } #endif public static NativeColor ToNative (this Color color) { #if WINDOWS_PHONE || WINDOWS_UWP || WINDOWS_PHONE_APP || WINDOWS_APP return color.ToWindows (); #elif __IOS__ return color.ToUIColor (); #elif __ANDROID__ return color.ToAndroid (); #endif } #if WINDOWS_PHONE || WINDOWS_UWP || WINDOWS_PHONE_APP || WINDOWS_APP public static void SetTextColor (this TextBlock textBlock, Color color) { textBlock.Foreground = new SolidColorBrush (color.ToNative ()); } #elif __IOS__ public static void SetTextColor (this UILabel label, Color color) { label.TextColor = color.ToNative (); } public static void SetTextColor (this UIButton button, Color color) { button.SetTitleColor (color.ToNative (), UIControlState.Normal); } #elif __ANDROID__ public static void SetTextColor (this TextView label, Color color) { label.SetTextColor (color.ToNative ()); } #endif } } ================================================ FILE: src/SignaturePad.Forms.Platform.Shared/SignaturePadCanvasRenderer.cs ================================================ using System; using System.ComponentModel; using System.Linq; using Xamarin.Forms; using SignaturePad.Forms; using Color = Xamarin.Forms.Color; using Point = Xamarin.Forms.Point; #if WINDOWS_PHONE using Xamarin.Forms.Platform.WinPhone; using NativeSignaturePadCanvasView = Xamarin.Controls.SignaturePadCanvasView; using NativePoint = System.Windows.Point; #elif WINDOWS_UWP using Xamarin.Forms.Platform.UWP; using NativeSignaturePadCanvasView = Xamarin.Controls.SignaturePadCanvasView; using NativePoint = Windows.Foundation.Point; #elif WINDOWS_PHONE_APP || WINDOWS_APP using Xamarin.Forms.Platform.WinRT; using NativeSignaturePadCanvasView = Xamarin.Controls.SignaturePadCanvasView; using NativePoint = Windows.Foundation.Point; #elif __IOS__ using Xamarin.Forms.Platform.iOS; using NativeSignaturePadCanvasView = Xamarin.Controls.SignaturePadCanvasView; using NativePoint = CoreGraphics.CGPoint; #elif __ANDROID__ using Xamarin.Forms.Platform.Android; using NativeSignaturePadCanvasView = Xamarin.Controls.SignaturePadCanvasView; using NativePoint = System.Drawing.PointF; #endif [assembly: ExportRenderer (typeof (SignaturePadCanvasView), typeof (SignaturePadCanvasRenderer))] namespace SignaturePad.Forms { public class SignaturePadCanvasRenderer : ViewRenderer { #if __ANDROID__ [Obsolete ("This constructor is obsolete as of version 2.5. Please use 'SignaturePadCanvasRenderer (Context)' instead.")] #endif public SignaturePadCanvasRenderer () { } #if __ANDROID__ public SignaturePadCanvasRenderer (Android.Content.Context context) : base (context) { } #endif protected override void OnElementChanged (ElementChangedEventArgs e) { base.OnElementChanged (e); if (Control == null && e.NewElement != null) { // Instantiate the native control and assign it to the Control property #if __ANDROID__ var native = new NativeSignaturePadCanvasView (Context); #else var native = new NativeSignaturePadCanvasView (); #endif native.StrokeCompleted += OnStrokeCompleted; native.Cleared += OnCleared; SetNativeControl (native); } if (e.OldElement != null) { // Unsubscribe from event handlers and cleanup any resources e.OldElement.ImageStreamRequested -= OnImageStreamRequested; e.OldElement.IsBlankRequested -= OnIsBlankRequested; e.OldElement.PointsRequested -= OnPointsRequested; e.OldElement.PointsSpecified -= OnPointsSpecified; e.OldElement.StrokesRequested -= OnStrokesRequested; e.OldElement.StrokesSpecified -= OnStrokesSpecified; e.OldElement.ClearRequested -= OnClearRequested; } if (e.NewElement != null) { // Configure the control and subscribe to event handlers e.NewElement.ImageStreamRequested += OnImageStreamRequested; e.NewElement.IsBlankRequested += OnIsBlankRequested; e.NewElement.PointsRequested += OnPointsRequested; e.NewElement.PointsSpecified += OnPointsSpecified; e.NewElement.StrokesRequested += OnStrokesRequested; e.NewElement.StrokesSpecified += OnStrokesSpecified; e.NewElement.ClearRequested += OnClearRequested; UpdateAll (); } } protected override void OnElementPropertyChanged (object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged (sender, e); Update (e.PropertyName); } private void OnStrokeCompleted (object sender, EventArgs e) { Element?.OnStrokeCompleted (); } private void OnCleared (object sender, EventArgs e) { Element?.OnCleared (); } private void OnImageStreamRequested (object sender, SignaturePadCanvasView.ImageStreamRequestedEventArgs e) { var ctrl = Control; if (ctrl != null) { var format = e.ImageFormat == SignatureImageFormat.Png ? Xamarin.Controls.SignatureImageFormat.Png : Xamarin.Controls.SignatureImageFormat.Jpeg; var settings = new Xamarin.Controls.ImageConstructionSettings (); if (e.Settings.BackgroundColor.HasValue) { settings.BackgroundColor = e.Settings.BackgroundColor.Value.ToNative (); } if (e.Settings.DesiredSizeOrScale.HasValue) { var val = e.Settings.DesiredSizeOrScale.Value; settings.DesiredSizeOrScale = new Xamarin.Controls.SizeOrScale (val.X, val.Y, (Xamarin.Controls.SizeOrScaleType)(int)val.Type, val.KeepAspectRatio); } settings.ShouldCrop = e.Settings.ShouldCrop; if (e.Settings.StrokeColor.HasValue) { settings.StrokeColor = e.Settings.StrokeColor.Value.ToNative (); } settings.StrokeWidth = e.Settings.StrokeWidth; settings.Padding = e.Settings.Padding; e.ImageStreamTask = ctrl.GetImageStreamAsync (format, settings); } } private void OnIsBlankRequested (object sender, SignaturePadCanvasView.IsBlankRequestedEventArgs e) { var ctrl = Control; if (ctrl != null) { e.IsBlank = ctrl.IsBlank; } } private void OnPointsRequested (object sender, SignaturePadCanvasView.PointsEventArgs e) { var ctrl = Control; if (ctrl != null) { e.Points = ctrl.Points.Select (p => new Point (p.X, p.Y)); } } private void OnPointsSpecified (object sender, SignaturePadCanvasView.PointsEventArgs e) { var ctrl = Control; if (ctrl != null) { ctrl.LoadPoints (e.Points.Select (p => new NativePoint ((float)p.X, (float)p.Y)).ToArray ()); } } private void OnStrokesRequested (object sender, SignaturePadCanvasView.StrokesEventArgs e) { var ctrl = Control; if (ctrl != null) { e.Strokes = ctrl.Strokes.Select (s => s.Select (p => new Point (p.X, p.Y))); } } private void OnStrokesSpecified (object sender, SignaturePadCanvasView.StrokesEventArgs e) { var ctrl = Control; if (ctrl != null) { ctrl.LoadStrokes (e.Strokes.Select (s => s.Select (p => new NativePoint ((float)p.X, (float)p.Y)).ToArray ()).ToArray ()); } } private void OnClearRequested (object sender, EventArgs e) { var ctrl = Control; if (ctrl != null) { ctrl.Clear (); } } /// /// Update all the properties on the native view. /// private void UpdateAll () { if (Control == null || Element == null) { return; } if (Element.StrokeColor != Color.Default) { Control.StrokeColor = Element.StrokeColor.ToNative (); } if (Element.StrokeWidth > 0) { Control.StrokeWidth = Element.StrokeWidth; } } /// /// Update a specific property on the native view. /// private void Update (string property) { if (Control == null || Element == null) { return; } if (property == SignaturePadCanvasView.StrokeColorProperty.PropertyName) { Control.StrokeColor = Element.StrokeColor.ToNative (); } else if (property == SignaturePadCanvasView.StrokeWidthProperty.PropertyName) { Control.StrokeWidth = Element.StrokeWidth; } } } } ================================================ FILE: src/SignaturePad.Forms.Shared/ImageConstructionSettings.cs ================================================ using Xamarin.Forms; namespace SignaturePad.Forms { public enum SizeOrScaleType { Size, Scale } public struct SizeOrScale { public SizeOrScale (float xy, SizeOrScaleType type) { X = xy; Y = xy; Type = type; KeepAspectRatio = true; } public SizeOrScale (float xy, SizeOrScaleType type, bool keepAspectRatio) { X = xy; Y = xy; Type = type; KeepAspectRatio = keepAspectRatio; } public SizeOrScale (Size size, SizeOrScaleType type) { X = (float)size.Width; Y = (float)size.Height; Type = type; KeepAspectRatio = true; } public SizeOrScale (Size size, SizeOrScaleType type, bool keepAspectRatio) { X = (float)size.Width; Y = (float)size.Height; Type = type; KeepAspectRatio = keepAspectRatio; } public SizeOrScale (float x, float y, SizeOrScaleType type) { X = x; Y = y; Type = type; KeepAspectRatio = true; } public SizeOrScale (float x, float y, SizeOrScaleType type, bool keepAspectRatio) { X = x; Y = y; Type = type; KeepAspectRatio = keepAspectRatio; } public float X { get; set; } public float Y { get; set; } public SizeOrScaleType Type { get; set; } public bool KeepAspectRatio { get; set; } public bool IsValid => X > 0 && Y > 0; public Size GetScale (float width, float height) { if (Type == SizeOrScaleType.Scale) { return new Size (X, Y); } else { return new Size (X / width, Y / height); } } public Size GetSize (float width, float height) { if (Type == SizeOrScaleType.Scale) { return new Size (width * X, height * Y); } else { return new Size (X, Y); } } public static implicit operator SizeOrScale (float scale) { return new SizeOrScale (scale, SizeOrScaleType.Scale); } public static implicit operator SizeOrScale (Size size) { return new SizeOrScale ((float)size.Width, (float)size.Height, SizeOrScaleType.Size); } } public struct ImageConstructionSettings { public static readonly bool DefaultShouldCrop = true; public static readonly SizeOrScale DefaultSizeOrScale = 1f; public static readonly Color DefaultStrokeColor = Color.Black; public static readonly Color DefaultBackgroundColor = Color.Transparent; public static readonly float DefaultStrokeWidth = 2f; public static readonly float DefaultPadding = 5f; public bool? ShouldCrop { get; set; } public SizeOrScale? DesiredSizeOrScale { get; set; } public Color? StrokeColor { get; set; } public Color? BackgroundColor { get; set; } public float? StrokeWidth { get; set; } public float? Padding { get; set; } } } ================================================ FILE: src/SignaturePad.Forms.Shared/SignatureImageFormat.cs ================================================ using System; namespace SignaturePad.Forms { public enum SignatureImageFormat { Png, Jpeg, [Obsolete ("Use Jpeg instead.")] Jpg = Jpeg } } ================================================ FILE: src/SignaturePad.Forms.Shared/SignaturePadCanvasView.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using System.Windows.Input; using Xamarin.Forms; namespace SignaturePad.Forms { [RenderWith (typeof (SignaturePadCanvasRenderer))] public class SignaturePadCanvasView : View { public static readonly BindableProperty StrokeColorProperty = BindableProperty.Create ( nameof (StrokeColor), typeof (Color), typeof (SignaturePadView), ImageConstructionSettings.DefaultStrokeColor); public static readonly BindableProperty StrokeWidthProperty = BindableProperty.Create ( nameof (StrokeWidth), typeof (float), typeof (SignaturePadView), ImageConstructionSettings.DefaultStrokeWidth); public static readonly BindableProperty ClearedCommandProperty = BindableProperty.Create ( nameof (ClearedCommand), typeof (ICommand), typeof (SignaturePadView), default (ICommand)); public static readonly BindableProperty StrokeCompletedCommandProperty = BindableProperty.Create ( nameof (StrokeCompletedCommand), typeof (ICommand), typeof (SignaturePadView), default (ICommand)); internal static readonly BindablePropertyKey IsBlankPropertyKey = BindableProperty.CreateReadOnly ( nameof (IsBlank), typeof (bool), typeof (SignaturePadView), true); public static readonly BindableProperty IsBlankProperty = IsBlankPropertyKey.BindableProperty; public bool IsBlank { get { return RequestIsBlank (); } } public float StrokeWidth { get { return (float)GetValue (StrokeWidthProperty); } set { SetValue (StrokeWidthProperty, value); } } public Color StrokeColor { get { return (Color)GetValue (StrokeColorProperty); } set { SetValue (StrokeColorProperty, value); } } public IEnumerable Points { get { return GetSignaturePoints (); } set { SetSignaturePoints (value); } } public IEnumerable> Strokes { get { return GetSignatureStrokes (); } set { SetSignatureStrokes (value); } } public ICommand ClearedCommand { get => (ICommand)GetValue (ClearedCommandProperty); set => SetValue (ClearedCommandProperty, value); } public ICommand StrokeCompletedCommand { get => (ICommand)GetValue (StrokeCompletedCommandProperty); set => SetValue (StrokeCompletedCommandProperty, value); } /// /// Create an encoded image stream of the currently drawn signature. /// public Task GetImageStreamAsync (SignatureImageFormat format, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified size. /// public Task GetImageStreamAsync (SignatureImageFormat format, Size size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified scale. /// public Task GetImageStreamAsync (SignatureImageFormat format, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio), StrokeColor = strokeColor }); } /// /// Create an encoded image stream of the currently drawn signature at the specified size with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Size size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified scale with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Color fillColor, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio), StrokeColor = strokeColor, BackgroundColor = fillColor }); } /// /// Create an encoded image stream of the currently drawn signature at the specified size with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Color fillColor, Size size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, BackgroundColor = fillColor, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified scale with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Color fillColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, BackgroundColor = fillColor, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature using the specified settings. /// public Task GetImageStreamAsync (SignatureImageFormat imageFormat, ImageConstructionSettings settings) { var args = new ImageStreamRequestedEventArgs (imageFormat, settings); ImageStreamRequested?.Invoke (this, args); return args.ImageStreamTask; } public void Clear () { ClearRequested?.Invoke (this, null); } private IEnumerable GetSignaturePoints () { var args = new PointsEventArgs (); PointsRequested?.Invoke (this, args); return args.Points; } private void SetSignaturePoints (IEnumerable points) { PointsSpecified?.Invoke (this, new PointsEventArgs { Points = points }); } private IEnumerable> GetSignatureStrokes () { var args = new StrokesEventArgs (); StrokesRequested?.Invoke (this, args); return args.Strokes; } private void SetSignatureStrokes (IEnumerable> strokes) { StrokesSpecified?.Invoke (this, new StrokesEventArgs { Strokes = strokes }); } private bool RequestIsBlank () { var args = new IsBlankRequestedEventArgs (); IsBlankRequested?.Invoke (this, args); return args.IsBlank; } internal void OnStrokeCompleted () { UpdateBindableProperties (); StrokeCompleted?.Invoke (this, EventArgs.Empty); if (StrokeCompletedCommand != null && StrokeCompletedCommand.CanExecute (null)) { StrokeCompletedCommand.Execute (null); } } internal void OnCleared () { UpdateBindableProperties (); Cleared?.Invoke (this, EventArgs.Empty); if (ClearedCommand != null && ClearedCommand.CanExecute (null)) { ClearedCommand.Execute (null); } } private void UpdateBindableProperties () { SetValue (IsBlankPropertyKey, IsBlank); } public event EventHandler StrokeCompleted; public event EventHandler Cleared; internal event EventHandler ImageStreamRequested; internal event EventHandler IsBlankRequested; internal event EventHandler PointsRequested; internal event EventHandler PointsSpecified; internal event EventHandler StrokesRequested; internal event EventHandler StrokesSpecified; internal event EventHandler ClearRequested; internal class ImageStreamRequestedEventArgs : EventArgs { public ImageStreamRequestedEventArgs (SignatureImageFormat imageFormat, ImageConstructionSettings settings) { ImageFormat = imageFormat; Settings = settings; } public SignatureImageFormat ImageFormat { get; private set; } public ImageConstructionSettings Settings { get; private set; } public Task ImageStreamTask { get; set; } = Task.FromResult (null); } internal class IsBlankRequestedEventArgs : EventArgs { public bool IsBlank { get; set; } = true; } internal class PointsEventArgs : EventArgs { public IEnumerable Points { get; set; } = new Point[0]; } internal class StrokesEventArgs : EventArgs { public IEnumerable> Strokes { get; set; } = new Point[0][]; } } } ================================================ FILE: src/SignaturePad.Forms.Shared/SignaturePadView.cs ================================================ using System; using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows.Input; using Xamarin.Forms; namespace SignaturePad.Forms { public partial class SignaturePadView : Grid { private const double DefaultWideSpacing = 12.0; private const double DefaultNarrowSpacing = 3.0; private const double DefaultLineWidth = 1.0; private const double DefaultFontSize = 15.0; private const string DefaultClearLabelText = "clear"; private const string DefaultPromptText = "▶"; private const string DefaultCaptionText = "sign above the line"; private static readonly Color SignaturePadDarkColor = Color.Black; private static readonly Color SignaturePadLightColor = Color.White; public static readonly BindableProperty StrokeColorProperty; public static readonly BindableProperty StrokeWidthProperty; public static readonly BindableProperty SignatureLineColorProperty; public static readonly BindableProperty SignatureLineWidthProperty; public static readonly BindableProperty SignatureLineSpacingProperty; public static readonly BindableProperty CaptionTextProperty; public static readonly BindableProperty CaptionFontSizeProperty; public static readonly BindableProperty CaptionTextColorProperty; public static readonly BindableProperty PromptTextProperty; public static readonly BindableProperty PromptFontSizeProperty; public static readonly BindableProperty PromptTextColorProperty; public static readonly BindableProperty ClearTextProperty; public static readonly BindableProperty ClearFontSizeProperty; public static readonly BindableProperty ClearTextColorProperty; public static readonly BindableProperty BackgroundImageProperty; public static readonly BindableProperty BackgroundImageAspectProperty; public static readonly BindableProperty BackgroundImageOpacityProperty; public static readonly BindableProperty ClearedCommandProperty; public static readonly BindableProperty StrokeCompletedCommandProperty; internal static readonly BindablePropertyKey IsBlankPropertyKey; public static readonly BindableProperty IsBlankProperty; private readonly TapGestureRecognizer clearLabelTap; static SignaturePadView () { StrokeColorProperty = BindableProperty.Create ( nameof (StrokeColor), typeof (Color), typeof (SignaturePadView), ImageConstructionSettings.DefaultStrokeColor, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignaturePadCanvas.StrokeColor = (Color)newValue); StrokeWidthProperty = BindableProperty.Create ( nameof (StrokeWidth), typeof (float), typeof (SignaturePadView), ImageConstructionSettings.DefaultStrokeWidth, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignaturePadCanvas.StrokeWidth = (float)newValue); SignatureLineColorProperty = BindableProperty.Create ( nameof (SignatureLineColor), typeof (Color), typeof (SignaturePadView), SignaturePadDarkColor, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignatureLine.Color = (Color)newValue); SignatureLineWidthProperty = BindableProperty.Create ( nameof (SignatureLineWidth), typeof (double), typeof (SignaturePadView), DefaultLineWidth, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignatureLine.HeightRequest = (double)newValue); SignatureLineSpacingProperty = BindableProperty.Create ( nameof (SignatureLineSpacing), typeof (double), typeof (SignaturePadView), DefaultNarrowSpacing, propertyChanged: OnPaddingChanged); CaptionTextProperty = BindableProperty.Create ( nameof (CaptionText), typeof (string), typeof (SignaturePadView), DefaultCaptionText, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).CaptionLabel.Text = (string)newValue); CaptionFontSizeProperty = BindableProperty.Create ( nameof (CaptionFontSize), typeof (double), typeof (SignaturePadView), DefaultFontSize, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).CaptionLabel.FontSize = (double)newValue); CaptionTextColorProperty = BindableProperty.Create ( nameof (CaptionTextColor), typeof (Color), typeof (SignaturePadView), SignaturePadDarkColor, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).CaptionLabel.TextColor = (Color)newValue); PromptTextProperty = BindableProperty.Create ( nameof (PromptText), typeof (string), typeof (SignaturePadView), DefaultPromptText, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignaturePrompt.Text = (string)newValue); PromptFontSizeProperty = BindableProperty.Create ( nameof (PromptFontSize), typeof (double), typeof (SignaturePadView), DefaultFontSize, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignaturePrompt.FontSize = (double)newValue); PromptTextColorProperty = BindableProperty.Create ( nameof (PromptTextColor), typeof (Color), typeof (SignaturePadView), SignaturePadDarkColor, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).SignaturePrompt.TextColor = (Color)newValue); ClearTextProperty = BindableProperty.Create ( nameof (ClearText), typeof (string), typeof (SignaturePadView), DefaultClearLabelText, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).ClearLabel.Text = (string)newValue); ClearFontSizeProperty = BindableProperty.Create ( nameof (ClearFontSize), typeof (double), typeof (SignaturePadView), DefaultFontSize, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).ClearLabel.FontSize = (double)newValue); ClearTextColorProperty = BindableProperty.Create ( nameof (ClearTextColor), typeof (Color), typeof (SignaturePadView), SignaturePadDarkColor, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).ClearLabel.TextColor = (Color)newValue); BackgroundImageProperty = BindableProperty.Create ( nameof (BackgroundImage), typeof (ImageSource), typeof (SignaturePadView), default (ImageSource), propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).BackgroundImageView.Source = (ImageSource)newValue); BackgroundImageAspectProperty = BindableProperty.Create ( nameof (BackgroundImageAspect), typeof (Aspect), typeof (SignaturePadView), Aspect.AspectFit, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).BackgroundImageView.Aspect = (Aspect)newValue); BackgroundImageOpacityProperty = BindableProperty.Create ( nameof (BackgroundImageOpacity), typeof (double), typeof (SignaturePadView), 1.0, propertyChanged: (bindable, oldValue, newValue) => ((SignaturePadView)bindable).BackgroundImageView.Opacity = (double)newValue); ClearedCommandProperty = BindableProperty.Create ( nameof (ClearedCommand), typeof (ICommand), typeof (SignaturePadView), default (ICommand)); StrokeCompletedCommandProperty = BindableProperty.Create ( nameof (StrokeCompletedCommand), typeof (ICommand), typeof (SignaturePadView), default (ICommand)); IsBlankPropertyKey = BindableProperty.CreateReadOnly ( nameof (IsBlank), typeof (bool), typeof (SignaturePadView), true); IsBlankProperty = IsBlankPropertyKey.BindableProperty; } public SignaturePadView () { // add the background view BackgroundImageView = new Image { Source = BackgroundImage, Aspect = BackgroundImageAspect, Opacity = BackgroundImageOpacity, InputTransparent = true, HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill, }; Children.Add (BackgroundImageView); // add the main signature view SignaturePadCanvas = new SignaturePadCanvasView { StrokeColor = StrokeColor, StrokeWidth = StrokeWidth, HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.Fill }; Children.Add (SignaturePadCanvas); // add the clear label ClearLabel = new Label { Text = ClearText, FontSize = ClearFontSize, TextColor = ClearTextColor, FontAttributes = FontAttributes.Bold, IsVisible = false, HorizontalOptions = LayoutOptions.End, VerticalOptions = LayoutOptions.Start }; Children.Add (ClearLabel); // add the footer bit var footer = new StackLayout { Spacing = 0, Padding = 0, Margin = 0, InputTransparent = true, HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.End, }; Children.Add (footer); // add the prompt SignaturePrompt = new Label { Text = PromptText, FontSize = PromptFontSize, TextColor = PromptTextColor, FontAttributes = FontAttributes.Bold, InputTransparent = true, HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.End }; footer.Children.Add (SignaturePrompt); // add the signature line SignatureLine = new BoxView { Color = SignatureLineColor, HeightRequest = SignatureLineWidth, InputTransparent = true, HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.End }; footer.Children.Add (SignatureLine); // add the caption CaptionLabel = new Label { Text = CaptionText, FontSize = CaptionFontSize, TextColor = CaptionTextColor, HorizontalTextAlignment = TextAlignment.Center, InputTransparent = true, HorizontalOptions = LayoutOptions.Fill, VerticalOptions = LayoutOptions.End }; footer.Children.Add (CaptionLabel); // set up the main control RowSpacing = 0; ColumnSpacing = 0; Padding = new Thickness (DefaultWideSpacing, DefaultWideSpacing, DefaultWideSpacing, DefaultNarrowSpacing); BackgroundColor = SignaturePadLightColor; // set up events from the controls SignaturePadCanvas.StrokeCompleted += delegate { OnSignatureStrokeCompleted (); }; SignaturePadCanvas.Cleared += delegate { OnSignatureCleared (); }; clearLabelTap = new TapGestureRecognizer { Command = new Command (() => OnClearTapped ()) }; ClearLabel.GestureRecognizers.Add (clearLabelTap); OnPaddingChanged (); UpdateUi (); } protected override void OnPropertyChanged ([CallerMemberName] string propertyName = null) { base.OnPropertyChanged (propertyName); if (propertyName == IsEnabledProperty.PropertyName) { SignaturePadCanvas.IsEnabled = IsEnabled; if (IsEnabled) ClearLabel.GestureRecognizers.Add (clearLabelTap); else ClearLabel.GestureRecognizers.Remove (clearLabelTap); } else if (propertyName == PaddingProperty.PropertyName) { OnPaddingChanged (); } } public IEnumerable> Strokes { get { return SignaturePadCanvas.Strokes; } set { SignaturePadCanvas.Strokes = value; UpdateUi (); } } public IEnumerable Points { get { return SignaturePadCanvas.Points; } set { SignaturePadCanvas.Points = value; UpdateUi (); } } public bool IsBlank => (bool)GetValue (IsBlankProperty); /// /// Gets the underlying control that handles the signatures. /// public SignaturePadCanvasView SignaturePadCanvas { get; private set; } /// /// Gets the horizontal line that goes in the lower part of the pad. /// public BoxView SignatureLine { get; private set; } /// /// The caption displayed under the signature line. /// public Label CaptionLabel { get; private set; } /// /// The prompt displayed at the beginning of the signature line. /// public Label SignaturePrompt { get; private set; } /// /// Gets the label that clears the pad when clicked. /// public Label ClearLabel { get; private set; } /// /// Gets the image view that handles the background image. /// public Image BackgroundImageView { get; private set; } /// /// Gets or sets the color of the signature strokes. /// public Color StrokeColor { get => (Color)GetValue (StrokeColorProperty); set => SetValue (StrokeColorProperty, value); } /// /// Gets or sets the width of the signature strokes. /// public float StrokeWidth { get => (float)GetValue (StrokeWidthProperty); set => SetValue (StrokeWidthProperty, value); } /// /// Gets or sets the color of the signature line. /// public Color SignatureLineColor { get => (Color)GetValue (SignatureLineColorProperty); set => SetValue (SignatureLineColorProperty, value); } /// /// Gets or sets the width of the signature line. /// public double SignatureLineWidth { get => (double)GetValue (SignatureLineWidthProperty); set => SetValue (SignatureLineWidthProperty, value); } /// /// Gets or sets the size of the spacing between the signature line and the caption. /// public double SignatureLineSpacing { get => (double)GetValue (SignatureLineSpacingProperty); set => SetValue (SignatureLineSpacingProperty, value); } /// /// Gets or sets the text for the caption displayed under the signature line. /// public string CaptionText { get => (string)GetValue (CaptionTextProperty); set => SetValue (CaptionTextProperty, value); } /// /// Gets or sets the font size of the caption. /// public double CaptionFontSize { get => (double)GetValue (CaptionFontSizeProperty); set => SetValue (CaptionFontSizeProperty, value); } /// /// Gets or sets the color of the caption text. /// public Color CaptionTextColor { get => (Color)GetValue (CaptionTextColorProperty); set => SetValue (CaptionTextColorProperty, value); } /// /// Gets or sets the text for the prompt displayed at the beginning of the signature line. /// public string PromptText { get => (string)GetValue (PromptTextProperty); set => SetValue (PromptTextProperty, value); } /// /// Gets or sets the font size of the prompt displayed at the beginning of the signature line. /// public double PromptFontSize { get => (double)GetValue (PromptFontSizeProperty); set => SetValue (PromptFontSizeProperty, value); } /// /// Gets or sets the text color of the prompt displayed at the beginning of the signature line. /// public Color PromptTextColor { get => (Color)GetValue (PromptTextColorProperty); set => SetValue (PromptTextColorProperty, value); } /// /// Gets or sets the text for the label that clears the pad when clicked. /// public string ClearText { get => (string)GetValue (ClearTextProperty); set => SetValue (ClearTextProperty, value); } /// /// Gets or sets the font size of the label that clears the pad when clicked. /// public double ClearFontSize { get => (double)GetValue (ClearFontSizeProperty); set => SetValue (ClearFontSizeProperty, value); } /// /// Gets or sets the color of the label that clears the pad when clicked. /// public Color ClearTextColor { get => (Color)GetValue (ClearTextColorProperty); set => SetValue (ClearTextColorProperty, value); } /// /// Gets or sets the watermark image. /// public ImageSource BackgroundImage { get => (ImageSource)GetValue (BackgroundImageProperty); set => SetValue (BackgroundImageProperty, value); } /// /// Gets or sets the aspect for the watermark image. /// public Aspect BackgroundImageAspect { get => (Aspect)GetValue (BackgroundImageAspectProperty); set => SetValue (BackgroundImageAspectProperty, value); } /// /// Gets or sets the transparency of the watermark. /// public double BackgroundImageOpacity { get => (double)GetValue (BackgroundImageOpacityProperty); set => SetValue (BackgroundImageOpacityProperty, value); } public ICommand ClearedCommand { get => (ICommand)GetValue (ClearedCommandProperty); set => SetValue (ClearedCommandProperty, value); } public ICommand StrokeCompletedCommand { get => (ICommand)GetValue (StrokeCompletedCommandProperty); set => SetValue (StrokeCompletedCommandProperty, value); } public event EventHandler StrokeCompleted; public event EventHandler Cleared; public void Clear () { SignaturePadCanvas.Clear (); UpdateUi (); } /// /// Create an encoded image of the currently drawn signature. /// public Task GetImageStreamAsync (SignatureImageFormat format, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified size. /// public Task GetImageStreamAsync (SignatureImageFormat format, Size size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, size, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified scale. /// public Task GetImageStreamAsync (SignatureImageFormat format, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, scale, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified size with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Size size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, size, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified scale with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, scale, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Color fillColor, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, fillColor, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified size with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Color fillColor, Size size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, fillColor, size, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified scale with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, Color strokeColor, Color fillColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, fillColor, scale, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature using the specified settings. /// public Task GetImageStreamAsync (SignatureImageFormat format, ImageConstructionSettings settings) { return SignaturePadCanvas.GetImageStreamAsync (format, settings); } private void OnClearTapped () { Clear (); } private void OnSignatureCleared () { UpdateBindableProperties (); UpdateUi (); Cleared?.Invoke (this, EventArgs.Empty); if (ClearedCommand != null && ClearedCommand.CanExecute (null)) { ClearedCommand.Execute (null); } } private void OnSignatureStrokeCompleted () { UpdateBindableProperties (); UpdateUi (); StrokeCompleted?.Invoke (this, EventArgs.Empty); if (StrokeCompletedCommand != null && StrokeCompletedCommand.CanExecute (null)) { StrokeCompletedCommand.Execute (null); } } private void UpdateBindableProperties () { SetValue (IsBlankPropertyKey, SignaturePadCanvas.IsBlank); } private void UpdateUi () { ClearLabel.IsVisible = !IsBlank; } private static void OnPaddingChanged (BindableObject bindable, object oldValue, object newValue) { ((SignaturePadView)bindable).OnPaddingChanged (); } private void OnPaddingChanged () { var padding = Padding; var spacing = SignatureLineSpacing; var ignorePadding = new Thickness (-padding.Left, -padding.Top, -padding.Right, -padding.Bottom); SignatureLine.Margin = new Thickness (0, spacing, 0, spacing); BackgroundImageView.Margin = ignorePadding; SignaturePadCanvas.Margin = ignorePadding; } } } ================================================ FILE: src/SignaturePad.Forms.UWP/SignaturePad.Forms.UWP.csproj ================================================  uap10.0.10240 SignaturePad.Forms SignaturePad.Forms False bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad.Forms SignaturePad for Xamarin.Forms $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.Forms.iOS/SignaturePad.Forms.iOS.csproj ================================================  xamarin.ios1.0 SignaturePad.Forms SignaturePad.Forms False $(MSBuildWarningsAsMessages);VSX1000 bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad.Forms SignaturePad for Xamarin.Forms $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.InkPresenter.Shared/InkPresenter.cs ================================================ using System; using System.Collections.Generic; using System.Linq; #if __ANDROID__ using NativeRect = System.Drawing.RectangleF; using NativeSize = System.Drawing.SizeF; using NativePoint = System.Drawing.PointF; using NativeColor = Android.Graphics.Color; using NativeImage = Android.Graphics.Bitmap; using NativePath = Android.Graphics.Path; #elif __IOS__ using NativeRect = CoreGraphics.CGRect; using NativeSize = CoreGraphics.CGSize; using NativePoint = CoreGraphics.CGPoint; using NativeColor = UIKit.UIColor; using NativeImage = UIKit.UIImage; using NativePath = UIKit.UIBezierPath; #elif WINDOWS_PHONE_APP using NativeRect = Windows.Foundation.Rect; using NativeSize = Windows.Foundation.Size; using NativePoint = Windows.Foundation.Point; using NativeColor = Windows.UI.Color; using NativeImage = Windows.UI.Xaml.Media.Imaging.WriteableBitmap; using NativePath = Windows.UI.Xaml.Media.PathGeometry; #endif namespace Xamarin.Controls { internal partial class InkPresenter { private const float MinimumPointDistance = 2.0f; public static float ScreenDensity; private readonly List paths = new List (); private InkStroke currentPath; // used to determine rectangle that needs to be redrawn private float dirtyRectLeft; private float dirtyRectTop; private float dirtyRectRight; private float dirtyRectBottom; private NativeImage bitmapBuffer; // public properties public NativeColor StrokeColor { get; set; } = ImageConstructionSettings.Black; public float StrokeWidth { get; set; } = 1f; // private properties #if __IOS__ private float Width => (float)Bounds.Width; private float Height => (float)Bounds.Height; #endif private bool ShouldRedrawBufferImage { get { var sizeChanged = false; if (bitmapBuffer != null) { var s = bitmapBuffer.GetSize (); sizeChanged = s.Width != Width || s.Height != Height; } return sizeChanged || (bitmapBuffer != null && paths.Count == 0) || paths.Any (p => p.IsDirty); } } private NativeRect DirtyRect { get { var x = Math.Min (dirtyRectLeft, dirtyRectRight); var y = Math.Min (dirtyRectTop, dirtyRectBottom); var w = Math.Abs (dirtyRectRight - dirtyRectLeft); var h = Math.Abs (dirtyRectBottom - dirtyRectTop); var half = StrokeWidth / 2f; return new NativeRect (x - half, y - half, w + StrokeWidth, h + StrokeWidth); } } // public events public event EventHandler StrokeCompleted; // public methods public IReadOnlyList GetStrokes () { return paths; } public void Clear () { paths.Clear (); currentPath = null; this.Invalidate (); } public void AddStroke (NativePoint[] strokePoints, NativeColor color, float width) { if (AddStrokeInternal (strokePoints, color, width)) { this.Invalidate (); } } public void AddStrokes (IEnumerable strokes, NativeColor color, float width) { var changed = false; foreach (var stroke in strokes) { if (AddStrokeInternal (stroke, color, width)) { changed = true; } } if (changed) { this.Invalidate (); } } private bool AddStrokeInternal (IEnumerable points, NativeColor color, float width) { var strokePoints = points?.ToList (); if (strokePoints == null || strokePoints.Count == 0) { return false; } var newpath = new NativePath (); newpath.MoveTo (strokePoints[0].X, strokePoints[0].Y); foreach (var point in strokePoints.Skip (1)) { newpath.LineTo (point.X, point.Y); } paths.Add (new InkStroke (newpath, strokePoints, color, width)); return true; } // private methods private bool HasMovedFarEnough (InkStroke stroke, double touchX, double touchY) { var lastPoint = stroke.GetPoints ().LastOrDefault (); var deltaX = touchX - lastPoint.X; var deltaY = touchY - lastPoint.Y; var distance = Math.Sqrt (Math.Pow (deltaX, 2) + Math.Pow (deltaY, 2)); return distance >= MinimumPointDistance; } /// /// Update the bounds for the rectangle to be redrawn if necessary for the given point. /// private void UpdateBounds (NativePoint touch) { UpdateBounds ((float)touch.X, (float)touch.Y); } /// /// Update the bounds for the rectangle to be redrawn if necessary for the given point. /// private void UpdateBounds (float touchX, float touchY) { if (touchX < dirtyRectLeft) dirtyRectLeft = touchX; else if (touchX > dirtyRectRight) dirtyRectRight = touchX; if (touchY < dirtyRectTop) dirtyRectTop = touchY; else if (touchY > dirtyRectBottom) dirtyRectBottom = touchY; } /// /// Set the bounds for the rectangle that will need to be redrawn to show the drawn path. /// private void ResetBounds (NativePoint touch) { ResetBounds ((float)touch.X, (float)touch.Y); } /// /// Set the bounds for the rectangle that will need to be redrawn to show the drawn path. /// private void ResetBounds (float touchX, float touchY) { dirtyRectLeft = touchX; dirtyRectRight = touchX; dirtyRectTop = touchY; dirtyRectBottom = touchY; } private void OnStrokeCompleted () { StrokeCompleted?.Invoke (this, EventArgs.Empty); } } } ================================================ FILE: src/SignaturePad.InkPresenter.Shared/InkStroke.cs ================================================ using System.Collections.Generic; #if __ANDROID__ using NativePoint = System.Drawing.PointF; using NativeColor = Android.Graphics.Color; using NativePath = Android.Graphics.Path; #elif __IOS__ using NativePoint = CoreGraphics.CGPoint; using NativeColor = UIKit.UIColor; using NativePath = UIKit.UIBezierPath; #elif WINDOWS_PHONE_APP using NativePoint = Windows.Foundation.Point; using NativeColor = Windows.UI.Color; using NativePath = Windows.UI.Xaml.Media.PathGeometry; #endif namespace Xamarin.Controls { internal class InkStroke { private NativeColor color; private float width; private IList inkPoints; public InkStroke (NativePath path, IList points, NativeColor color, float width) { Path = path; inkPoints = points; Color = color; Width = width; } public NativePath Path { get; private set; } public IList GetPoints () { return inkPoints; } public NativeColor Color { get { return color; } set { color = value; IsDirty = true; } } public float Width { get { return width; } set { width = value; IsDirty = true; } } internal bool IsDirty { get; set; } } } ================================================ FILE: src/SignaturePad.Mac.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26228.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.iOS", "SignaturePad.iOS\SignaturePad.iOS.csproj", "{BEF71536-787B-431F-AC7F-A6469710D11F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Android", "SignaturePad.Android\SignaturePad.Android.csproj", "{F1A16CB9-A759-42C8-8F0B-3C9698A55336}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms", "SignaturePad.Forms\SignaturePad.Forms.csproj", "{B2AF970D-D640-451C-95AF-92AF531B8C1E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms.Droid", "SignaturePad.Forms.Droid\SignaturePad.Forms.Droid.csproj", "{1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms.iOS", "SignaturePad.Forms.iOS\SignaturePad.Forms.iOS.csproj", "{B12D20AA-0EDF-4903-B385-BB8090848532}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|Any CPU.Build.0 = Release|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Debug|Any CPU.Build.0 = Debug|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Release|Any CPU.Build.0 = Release|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Release|Any CPU.Build.0 = Release|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Release|Any CPU.Build.0 = Release|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Debug|Any CPU.Build.0 = Debug|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Release|Any CPU.ActiveCfg = Release|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C1DC0EAF-7D66-46D7-A4A8-6F84D8E299E3} EndGlobalSection EndGlobal ================================================ FILE: src/SignaturePad.Shared/Extensions.cs ================================================ using System; using System.Collections.Generic; using System.Linq; #if __ANDROID__ using Android.Graphics; using Android.Views; #elif __IOS__ using CoreGraphics; using UIKit; #elif WINDOWS_PHONE using System.Windows; using System.Windows.Controls; using System.Windows.Ink; using System.Windows.Input; using System.Windows.Media; #elif WINDOWS_UWP || WINDOWS_APP using Windows.Foundation; using System.Numerics; using Windows.UI; using Windows.UI.Input.Inking; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; #elif WINDOWS_PHONE_APP using Windows.Foundation; using Windows.UI.Xaml; using Windows.UI.Xaml.Media; using Windows.UI.Xaml.Media.Imaging; #endif namespace Xamarin.Controls { internal static class Extensions { #if __ANDROID__ public static System.Drawing.SizeF GetSize (this Bitmap image) { return new System.Drawing.SizeF (image.Width, image.Height); } public static System.Drawing.SizeF GetSize (this View view) { return new System.Drawing.SizeF (view.Width, view.Height); } #elif __IOS__ public static CGSize GetSize (this UIImage image) { return image.Size; } public static void Invalidate (this UIView view) { view.SetNeedsDisplay (); } public static void MoveTo (this UIBezierPath path, nfloat x, nfloat y) { path.MoveTo (new CGPoint (x, y)); } public static void LineTo (this UIBezierPath path, nfloat x, nfloat y) { path.AddLineTo (new CGPoint (x, y)); } public static CGSize GetSize (this UIView view) { return view.Bounds.Size; } #elif WINDOWS_PHONE public static void MoveTo (this Stroke stroke, double x, double y) { stroke.StylusPoints.Add (new StylusPoint (x, y)); } public static void LineTo (this Stroke stroke, double x, double y) { stroke.StylusPoints.Add (new StylusPoint (x, y)); } public static void AddStrokes (this InkPresenter inkPresenter, IList strokes, Color color, float width) { var strokeCollection = new StrokeCollection (); foreach (var stroke in strokes.Where(s => s.Length > 0)) { var pointCollection = new StylusPointCollection (); foreach (var point in stroke) { pointCollection.Add (new StylusPoint (point.X, point.Y)); } var newStroke = new Stroke (pointCollection); strokeCollection.Add (newStroke); newStroke.DrawingAttributes = new DrawingAttributes { Color = color, OutlineColor = color, Width = width, Height = width }; } inkPresenter.Strokes = strokeCollection; } public static IList GetStrokes (this InkPresenter inkPresenter) { return inkPresenter.Strokes; } public static IEnumerable GetPoints (this Stroke stroke) { return stroke.StylusPoints.Select (p => new Point (p.X, p.Y)); } public static Size GetSize (this FrameworkElement element) { return new Size (element.ActualWidth, element.ActualHeight); } #elif WINDOWS_PHONE_APP || WINDOWS_APP public static void MoveTo (this List stroke, double x, double y) { stroke.Add (new Point (x, y)); } public static void LineTo (this List stroke, double x, double y) { stroke.Add (new Point (x, y)); } public static void MoveTo (this PathGeometry stroke, double x, double y) { var figure = new PathFigure (); figure.StartPoint = new Point (x, y); var segment = new PolyLineSegment (); segment.Points.Add (new Point (x, y)); figure.Segments.Add (segment); stroke.Figures.Add (figure); } public static void LineTo (this PathGeometry stroke, double x, double y) { var figure = stroke.Figures.LastOrDefault (); if (figure == null) { figure = new PathFigure (); stroke.Figures.Add (figure); } var segment = figure.Segments.LastOrDefault () as PolyLineSegment; if (segment == null) { segment = new PolyLineSegment (); figure.Segments.Add (segment); } segment.Points.Add (new Point (x, y)); } public static Size GetSize (this FrameworkElement element) { return new Size (element.ActualWidth, element.ActualHeight); } public static Size GetSize (this WriteableBitmap image) { return new Size (image.PixelWidth, image.PixelHeight); } #if WINDOWS_APP public static IEnumerable GetPoints (this InkStroke stroke) { var empty = new Point (); var segments = stroke.GetRenderingSegments (); if (segments.Any (s => s.BezierControlPoint1 != empty || s.BezierControlPoint2 != empty)) { // we assume strokes with bezier controls are generated by the inking // so we know that the detail will be low - thus we smooth return PathSmoothing.BezierToLinear (segments, 0.1f); } return segments.Select (p => p.Position); } #endif #elif WINDOWS_UWP private const float DefaultPressure = 0.5f; public static void MoveTo (this List stroke, double x, double y) { stroke.Add (new Point (x, y)); } public static void LineTo (this List stroke, double x, double y) { stroke.Add (new Point (x, y)); } public static IReadOnlyList GetStrokes (this InkPresenter inkPresenter) { return inkPresenter.StrokeContainer.GetStrokes (); } public static void AddStrokes (this InkPresenter inkPresenter, IList strokes, Color color, float width) { var strokeBuilder = new InkStrokeBuilder (); var da = inkPresenter.CopyDefaultDrawingAttributes (); da.Color = color; da.Size = new Size (width, width); strokeBuilder.SetDefaultDrawingAttributes (da); var newStrokes = strokes.Where (s => s.Length > 0).Select (s => { var points = s.Select (p => new InkPoint (p, DefaultPressure)); return strokeBuilder.CreateStrokeFromInkPoints (points, Matrix3x2.Identity); }); inkPresenter.StrokeContainer.AddStrokes (newStrokes); } public static IEnumerable GetPoints (this InkStroke stroke) { return stroke.GetInkPoints ().Select (p => p.Position); } public static Size GetSize (this FrameworkElement element) { return new Size (element.ActualWidth, element.ActualHeight); } #endif } } ================================================ FILE: src/SignaturePad.Shared/ImageConstructionSettings.cs ================================================ using System; #if __ANDROID__ using NativeSize = System.Drawing.SizeF; using NativeColor = Android.Graphics.Color; using NativeNullableColor = System.Nullable; #elif __IOS__ using NativeSize = CoreGraphics.CGSize; using NativeColor = UIKit.UIColor; using NativeNullableColor = UIKit.UIColor; #elif WINDOWS_PHONE using NativeSize = System.Windows.Size; using NativeColor = System.Windows.Media.Color; using NativeNullableColor = System.Nullable; #elif WINDOWS_UWP || WINDOWS_APP using NativeSize = Windows.Foundation.Size; using NativeColor = Windows.UI.Color; using NativeNullableColor = System.Nullable; #elif WINDOWS_PHONE_APP using NativeSize = Windows.Foundation.Size; using NativeColor = Windows.UI.Color; using NativeNullableColor = System.Nullable; #endif namespace Xamarin.Controls { public enum SizeOrScaleType { Size, Scale } public struct SizeOrScale { public SizeOrScale (float xy, SizeOrScaleType type) { X = xy; Y = xy; Type = type; KeepAspectRatio = true; } public SizeOrScale (float xy, SizeOrScaleType type, bool keepAspectRatio) { X = xy; Y = xy; Type = type; KeepAspectRatio = keepAspectRatio; } public SizeOrScale (NativeSize size, SizeOrScaleType type) { X = (float)size.Width; Y = (float)size.Height; Type = type; KeepAspectRatio = true; } public SizeOrScale (NativeSize size, SizeOrScaleType type, bool keepAspectRatio) { X = (float)size.Width; Y = (float)size.Height; Type = type; KeepAspectRatio = keepAspectRatio; } public SizeOrScale (float x, float y, SizeOrScaleType type) { X = x; Y = y; Type = type; KeepAspectRatio = true; } public SizeOrScale (float x, float y, SizeOrScaleType type, bool keepAspectRatio) { X = x; Y = y; Type = type; KeepAspectRatio = keepAspectRatio; } public float X { get; set; } public float Y { get; set; } public SizeOrScaleType Type { get; set; } public bool KeepAspectRatio { get; set; } public bool IsValid => X > 0 && Y > 0; public NativeSize GetScale (float width, float height) { if (Type == SizeOrScaleType.Scale) { return new NativeSize (X, Y); } else { return new NativeSize (X / width, Y / height); } } public NativeSize GetSize (float width, float height) { if (Type == SizeOrScaleType.Scale) { return new NativeSize (width * X, height * Y); } else { return new NativeSize (X, Y); } } public static implicit operator SizeOrScale (float scale) { return new SizeOrScale (scale, SizeOrScaleType.Scale); } public static implicit operator SizeOrScale (NativeSize size) { return new SizeOrScale ((float)size.Width, (float)size.Height, SizeOrScaleType.Size); } } public struct ImageConstructionSettings { #if __IOS__ || __ANDROID__ internal static readonly NativeColor Black = NativeColor.Black; internal static readonly NativeColor Transparent = new NativeColor (0, 0, 0, 0); #elif WINDOWS_PHONE || WINDOWS_UWP || WINDOWS_PHONE_APP || WINDOWS_APP internal static readonly NativeColor Black = NativeColor.FromArgb (255, 0, 0, 0); internal static readonly NativeColor Transparent = NativeColor.FromArgb (0, 0, 0, 0); #endif public static readonly bool DefaultShouldCrop = true; public static readonly SizeOrScale DefaultSizeOrScale = 1f; public static readonly NativeColor DefaultStrokeColor = Black; public static readonly NativeColor DefaultBackgroundColor = Transparent; public static readonly float DefaultStrokeWidth = 2f; public static readonly float DefaultPadding = 5f; public bool? ShouldCrop { get; set; } public SizeOrScale? DesiredSizeOrScale { get; set; } public NativeNullableColor StrokeColor { get; set; } public NativeNullableColor BackgroundColor { get; set; } public float? StrokeWidth { get; set; } public float? Padding { get; set; } internal void ApplyDefaults () { ApplyDefaults (DefaultStrokeWidth, DefaultStrokeColor); } internal void ApplyDefaults (float strokeWidth, NativeColor strokeColor) { ShouldCrop = ShouldCrop ?? DefaultShouldCrop; DesiredSizeOrScale = DesiredSizeOrScale ?? DefaultSizeOrScale; StrokeColor = StrokeColor ?? strokeColor; BackgroundColor = BackgroundColor ?? DefaultBackgroundColor; StrokeWidth = StrokeWidth ?? strokeWidth; Padding = Padding ?? DefaultPadding; } } } ================================================ FILE: src/SignaturePad.Shared/PathSmoothing.cs ================================================ using System.Collections.Generic; using System.Linq; #if __ANDROID__ using NativePoint = System.Drawing.PointF; using NativePath = Android.Graphics.Path; #elif __IOS__ using NativePoint = CoreGraphics.CGPoint; using NativePath = UIKit.UIBezierPath; #elif WINDOWS_PHONE using System.Windows.Ink; using NativePoint = System.Windows.Point; using NativePath = System.Windows.Ink.Stroke; using InkStroke = System.Windows.Ink.Stroke; #elif WINDOWS_UWP || WINDOWS_APP using Windows.UI.Input.Inking; using NativePoint = Windows.Foundation.Point; using NativePath = System.Collections.Generic.List; using InkStroke = Windows.UI.Input.Inking.InkStroke; #elif WINDOWS_PHONE_APP using NativePoint = Windows.Foundation.Point; using NativePath = Windows.UI.Xaml.Media.PathGeometry; #endif namespace Xamarin.Controls { internal static class PathSmoothing { /// /// Obtain a smoothed path with the specified granularity from the current path using Catmull-Rom spline. /// Also outputs a List of the points corresponding to the smoothed path. /// /// /// Implemented using a modified version of the code in the solution at /// http://stackoverflow.com/questions/8702696/drawing-smooth-curves-methods-needed /// public static InkStroke SmoothedPathWithGranularity (InkStroke currentPath, int granularity) { var currentPoints = currentPath.GetPoints ().ToList (); NativePath smoothedPath; List smoothedPoints; SmoothedPathWithGranularity (currentPoints, granularity, out smoothedPath, out smoothedPoints); if (smoothedPath == null) { return currentPath; } // create the new path with the old attributes #if __ANDROID__ || __IOS__ || WINDOWS_PHONE_APP return new InkStroke (smoothedPath, smoothedPoints.ToList (), currentPath.Color, currentPath.Width); #elif WINDOWS_PHONE var da = currentPath.DrawingAttributes; smoothedPath.DrawingAttributes = new DrawingAttributes { Color = da.Color, OutlineColor = da.OutlineColor, Width = da.Width, Height = da.Height, }; return smoothedPath; #elif WINDOWS_UWP || WINDOWS_APP var da = currentPath.DrawingAttributes; var builder = new InkStrokeBuilder (); builder.SetDefaultDrawingAttributes (new InkDrawingAttributes { Color = da.Color, Size = da.Size }); return builder.CreateStroke (smoothedPath); #endif } public static void SmoothedPathWithGranularity (List currentPoints, int granularity, out NativePath smoothedPath, out List smoothedPoints) { // not enough points to smooth effectively, so return the original path and points. if (currentPoints.Count < 4) { smoothedPath = null; smoothedPoints = null; return; } // create a new bezier path to hold the smoothed path. smoothedPath = new NativePath (); smoothedPoints = new List (); // duplicate the first and last points as control points. currentPoints.Insert (0, currentPoints[0]); currentPoints.Add (currentPoints[currentPoints.Count - 1]); // add the first point smoothedPath.MoveTo (currentPoints[0].X, currentPoints[0].Y); smoothedPoints.Add (currentPoints[0]); for (var index = 1; index < currentPoints.Count - 2; index++) { var p0 = currentPoints[index - 1]; var p1 = currentPoints[index]; var p2 = currentPoints[index + 1]; var p3 = currentPoints[index + 2]; // add n points starting at p1 + dx/dy up until p2 using Catmull-Rom splines for (var i = 1; i < granularity; i++) { var t = (float)i * (1f / (float)granularity); var tt = t * t; var ttt = tt * t; // intermediate point var mid = new NativePoint { X = 0.5f * (2f * p1.X + (p2.X - p0.X) * t + (2f * p0.X - 5f * p1.X + 4f * p2.X - p3.X) * tt + (3f * p1.X - p0.X - 3f * p2.X + p3.X) * ttt), Y = 0.5f * (2 * p1.Y + (p2.Y - p0.Y) * t + (2 * p0.Y - 5 * p1.Y + 4 * p2.Y - p3.Y) * tt + (3 * p1.Y - p0.Y - 3 * p2.Y + p3.Y) * ttt) }; smoothedPath.LineTo (mid.X, mid.Y); smoothedPoints.Add (mid); } // add p2 smoothedPath.LineTo (p2.X, p2.Y); smoothedPoints.Add (p2); } // add the last point var last = currentPoints[currentPoints.Count - 1]; smoothedPath.LineTo (last.X, last.Y); smoothedPoints.Add (last); } #if WINDOWS_APP public static List BezierToLinear (IReadOnlyList segments, float step) { var linear = new List (); linear.Add (segments[0].Position); var prev = segments[0].Position; foreach (var segment in segments.Skip (1)) { var t = 0f; while (t < 1.0f) { t += step; var p1 = prev; var c1 = segment.BezierControlPoint1; var c2 = segment.BezierControlPoint2; var p2 = segment.Position; linear.Add (Qubic (t, p1, c1, c2, p2)); } prev = segment.Position; } return linear; } private static NativePoint Quadratic (float t, NativePoint p1, NativePoint p2, NativePoint p3) { return new NativePoint { X = (1 - t) * (1 - t) * p1.X + 2 * (1 - t) * t * p2.X + t * t * p3.X, Y = (1 - t) * (1 - t) * p1.Y + 2 * (1 - t) * t * p2.Y + t * t * p3.Y }; } private static NativePoint Qubic (float t, NativePoint p0, NativePoint p1, NativePoint p2, NativePoint p3) { return new NativePoint { X = (1 - t) * (1 - t) * (1 - t) * p0.X + 3 * (1 - t) * (1 - t) * t * p1.X + 3 * (1 - t) * t * t * p2.X + t * t * t * p3.X, Y = (1 - t) * (1 - t) * (1 - t) * p0.Y + 3 * (1 - t) * (1 - t) * t * p1.Y + 3 * (1 - t) * t * t * p2.Y + t * t * t * p3.Y }; } // P = (1-t)•A + t•B = (1-t)2•P1 + 2•(1-t) •t•C + t2•P2 #endif } } ================================================ FILE: src/SignaturePad.Shared/SignatureImageFormat.cs ================================================ namespace Xamarin.Controls { public enum SignatureImageFormat { Png, Jpeg } } ================================================ FILE: src/SignaturePad.Shared/SignaturePadCanvasView.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using System.IO; #if __ANDROID__ using NativeRect = System.Drawing.RectangleF; using NativePoint = System.Drawing.PointF; using NativeSize = System.Drawing.SizeF; using NativeColor = Android.Graphics.Color; using NativeImage = Android.Graphics.Bitmap; #elif __IOS__ using NativeRect = CoreGraphics.CGRect; using NativePoint = CoreGraphics.CGPoint; using NativeSize = CoreGraphics.CGSize; using NativeColor = UIKit.UIColor; using NativeImage = UIKit.UIImage; #elif WINDOWS_PHONE using NativeRect = System.Windows.Rect; using NativePoint = System.Windows.Point; using NativeSize = System.Windows.Size; using NativeColor = System.Windows.Media.Color; using NativeImage = System.Windows.Media.Imaging.WriteableBitmap; #elif WINDOWS_UWP || WINDOWS_APP using NativeRect = Windows.Foundation.Rect; using NativePoint = Windows.Foundation.Point; using NativeSize = Windows.Foundation.Size; using NativeColor = Windows.UI.Color; using NativeImage = Windows.UI.Xaml.Media.Imaging.WriteableBitmap; #elif WINDOWS_PHONE_APP using NativeRect = Windows.Foundation.Rect; using NativeSize = Windows.Foundation.Size; using NativePoint = Windows.Foundation.Point; using NativeColor = Windows.UI.Color; using NativeImage = Windows.UI.Xaml.Media.Imaging.WriteableBitmap; #endif namespace Xamarin.Controls { partial class SignaturePadCanvasView { public event EventHandler StrokeCompleted; public event EventHandler Cleared; public bool IsBlank => inkPresenter == null ? true : inkPresenter.GetStrokes ().Count == 0; public NativePoint[] Points { get { if (IsBlank) { return new NativePoint[0]; } // make a deep copy, with { 0, 0 } line starter return inkPresenter.GetStrokes () .SelectMany (s => new[] { new NativePoint (0, 0) }.Concat (s.GetPoints ())) .Skip (1) // skip the first empty .ToArray (); } } public NativePoint[][] Strokes { get { if (IsBlank) { return new NativePoint[0][]; } // make a deep copy return inkPresenter.GetStrokes ().Select (s => s.GetPoints ().ToArray ()).ToArray (); } } public NativeRect GetSignatureBounds (float padding = 5f) { if (IsBlank) { return NativeRect.Empty; } var size = this.GetSize (); double xMin = size.Width, xMax = 0, yMin = size.Height, yMax = 0; foreach (var point in inkPresenter.GetStrokes ().SelectMany (stroke => stroke.GetPoints ())) { xMin = point.X <= 0 ? 0 : Math.Min (xMin, point.X); yMin = point.Y <= 0 ? 0 : Math.Min (yMin, point.Y); xMax = point.X >= size.Width ? size.Width : Math.Max (xMax, point.X); yMax = point.Y >= size.Height ? size.Height : Math.Max (yMax, point.Y); } var spacing = (StrokeWidth / 2f) + padding; xMin = Math.Max (0, xMin - spacing); yMin = Math.Max (0, yMin - spacing); xMax = Math.Min (size.Width, xMax + spacing); yMax = Math.Min (size.Height, yMax + spacing); return new NativeRect ( (float)xMin, (float)yMin, (float)xMax - (float)xMin, (float)yMax - (float)yMin); } /// /// Create an image of the currently drawn signature. /// public NativeImage GetImage (bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature at the specified size. /// public NativeImage GetImage (NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature at the specified scale. /// public NativeImage GetImage (float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature with the specified stroke color. /// public NativeImage GetImage (NativeColor strokeColor, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio), StrokeColor = strokeColor, }); } /// /// Create an image of the currently drawn signature at the specified size with the specified stroke color. /// public NativeImage GetImage (NativeColor strokeColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature at the specified scale with the specified stroke color. /// public NativeImage GetImage (NativeColor strokeColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature with the specified stroke and background colors. /// public NativeImage GetImage (NativeColor strokeColor, NativeColor fillColor, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio), StrokeColor = strokeColor, BackgroundColor = fillColor, }); } /// /// Create an image of the currently drawn signature at the specified size with the specified stroke and background colors. /// public NativeImage GetImage (NativeColor strokeColor, NativeColor fillColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, BackgroundColor = fillColor, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature at the specified scale with the specified stroke and background colors. /// public NativeImage GetImage (NativeColor strokeColor, NativeColor fillColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImage (new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, BackgroundColor = fillColor, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an image of the currently drawn signature using the specified settings. /// public NativeImage GetImage (ImageConstructionSettings settings) { NativeSize scale; NativeRect signatureBounds; NativeSize imageSize; float strokeWidth; NativeColor strokeColor; NativeColor backgroundColor; if (GetImageConstructionArguments (settings, out scale, out signatureBounds, out imageSize, out strokeWidth, out strokeColor, out backgroundColor)) { return GetImageInternal (scale, signatureBounds, imageSize, strokeWidth, strokeColor, backgroundColor); } return null; } /// /// Create an encoded image stream of the currently drawn signature. /// public Task GetImageStreamAsync (SignatureImageFormat format, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified size. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified scale. /// public Task GetImageStreamAsync (SignatureImageFormat format, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio), StrokeColor = strokeColor }); } /// /// Create an encoded image stream of the currently drawn signature at the specified size with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified scale with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeColor fillColor, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, DesiredSizeOrScale = new SizeOrScale (1f, SizeOrScaleType.Scale, keepAspectRatio), StrokeColor = strokeColor, BackgroundColor = fillColor }); } /// /// Create an encoded image stream of the currently drawn signature at the specified size with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeColor fillColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, BackgroundColor = fillColor, DesiredSizeOrScale = new SizeOrScale (size, SizeOrScaleType.Size, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature at the specified scale with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeColor fillColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return GetImageStreamAsync (format, new ImageConstructionSettings { ShouldCrop = shouldCrop, StrokeColor = strokeColor, BackgroundColor = fillColor, DesiredSizeOrScale = new SizeOrScale (scale, SizeOrScaleType.Scale, keepAspectRatio) }); } /// /// Create an encoded image stream of the currently drawn signature using the specified settings. /// public Task GetImageStreamAsync (SignatureImageFormat format, ImageConstructionSettings settings) { NativeSize scale; NativeRect signatureBounds; NativeSize imageSize; float strokeWidth; NativeColor strokeColor; NativeColor backgroundColor; if (GetImageConstructionArguments (settings, out scale, out signatureBounds, out imageSize, out strokeWidth, out strokeColor, out backgroundColor)) { return GetImageStreamInternal (format, scale, signatureBounds, imageSize, strokeWidth, strokeColor, backgroundColor); } return Task.FromResult (null); } private bool GetImageConstructionArguments (ImageConstructionSettings settings, out NativeSize scale, out NativeRect signatureBounds, out NativeSize imageSize, out float strokeWidth, out NativeColor strokeColor, out NativeColor backgroundColor) { settings.ApplyDefaults ((float)StrokeWidth, StrokeColor); if (IsBlank || settings.DesiredSizeOrScale?.IsValid != true) { scale = default (NativeSize); signatureBounds = default (NativeRect); imageSize = default (NativeSize); strokeWidth = default (float); strokeColor = default (NativeColor); backgroundColor = default (NativeColor); return false; } var sizeOrScale = settings.DesiredSizeOrScale.Value; var viewSize = this.GetSize (); imageSize = sizeOrScale.GetSize ((float)viewSize.Width, (float)viewSize.Height); scale = sizeOrScale.GetScale ((float)imageSize.Width, (float)imageSize.Height); if (settings.ShouldCrop == true) { signatureBounds = GetSignatureBounds (settings.Padding.Value); if (sizeOrScale.Type == SizeOrScaleType.Size) { // if a specific size was set, scale to that var scaleX = imageSize.Width / (float)signatureBounds.Width; var scaleY = imageSize.Height / (float)signatureBounds.Height; if (sizeOrScale.KeepAspectRatio) { scaleX = scaleY = Math.Min ((float)scaleX, (float)scaleY); } scale = new NativeSize ((float)scaleX, (float)scaleY); } else if (sizeOrScale.Type == SizeOrScaleType.Scale) { imageSize.Width = signatureBounds.Width * scale.Width; imageSize.Height = signatureBounds.Height * scale.Height; } } else { signatureBounds = new NativeRect (0, 0, viewSize.Width, viewSize.Height); } strokeWidth = settings.StrokeWidth.Value; strokeColor = (NativeColor)settings.StrokeColor; backgroundColor = (NativeColor)settings.BackgroundColor; return true; } public void LoadStrokes (NativePoint[][] loadedStrokes) { // clear any existing paths or points. Clear (); // there is nothing if (loadedStrokes == null || loadedStrokes.Length == 0) { return; } inkPresenter.AddStrokes (loadedStrokes, StrokeColor, (float)StrokeWidth); if (!IsBlank) { OnStrokeCompleted (); } } /// /// Allow the user to import an array of points to be used to draw a signature in the view, with new /// lines indicated by a { 0, 0 } point in the array. /// public void LoadPoints (NativePoint[] loadedPoints) { // clear any existing paths or points. Clear (); // there is nothing if (loadedPoints == null || loadedPoints.Length == 0) { return; } var startIndex = 0; var emptyIndex = Array.IndexOf (loadedPoints, new NativePoint (0, 0)); if (emptyIndex == -1) { emptyIndex = loadedPoints.Length; } var strokes = new List (); do { // add a stroke to the ink presenter var currentStroke = new NativePoint[emptyIndex - startIndex]; strokes.Add (currentStroke); Array.Copy (loadedPoints, startIndex, currentStroke, 0, currentStroke.Length); // obtain the indices for the next line to be drawn. startIndex = emptyIndex + 1; if (startIndex < loadedPoints.Length - 1) { emptyIndex = Array.IndexOf (loadedPoints, new NativePoint (0, 0), startIndex); if (emptyIndex == -1) { emptyIndex = loadedPoints.Length; } } else { emptyIndex = startIndex; } } while (startIndex < emptyIndex); inkPresenter.AddStrokes (strokes, StrokeColor, (float)StrokeWidth); if (!IsBlank) { OnStrokeCompleted (); } } private void OnCleared () { Cleared?.Invoke (this, EventArgs.Empty); } private void OnStrokeCompleted () { OnStrokeCompleted (this, EventArgs.Empty); } private void OnStrokeCompleted (object sender, EventArgs e) { StrokeCompleted?.Invoke (this, e); } } } ================================================ FILE: src/SignaturePad.Shared/SignaturePadView.cs ================================================ using System; using System.IO; using System.Threading.Tasks; #if __ANDROID__ using NativeRect = System.Drawing.RectangleF; using NativePoint = System.Drawing.PointF; using NativeSize = System.Drawing.SizeF; using NativeColor = Android.Graphics.Color; using NativeImage = Android.Graphics.Bitmap; #elif __IOS__ using NativeRect = CoreGraphics.CGRect; using NativePoint = CoreGraphics.CGPoint; using NativeSize = CoreGraphics.CGSize; using NativeColor = UIKit.UIColor; using NativeImage = UIKit.UIImage; #elif WINDOWS_UWP using NativeRect = Windows.Foundation.Rect; using NativePoint = Windows.Foundation.Point; using NativeSize = Windows.Foundation.Size; using NativeColor = Windows.UI.Color; using NativeImage = Windows.UI.Xaml.Media.Imaging.WriteableBitmap; #endif namespace Xamarin.Controls { #if WINDOWS_UWP partial class SignaturePad #else partial class SignaturePadView #endif { private const float DefaultWideSpacing = 12.0f; private const float DefaultNarrowSpacing = 3.0f; private const float DefaultLineThickness = 1.0f; private const float DefaultFontSize = 15.0f; private const string DefaultClearLabelText = "clear"; private const string DefaultPromptText = "▶"; private const string DefaultCaptionText = "sign above the line"; #if __IOS__ private static readonly NativeColor SignaturePadDarkColor = NativeColor.Black; private static readonly NativeColor SignaturePadLightColor = NativeColor.White; #elif __ANDROID__ private static readonly NativeColor SignaturePadDarkColor = NativeColor.Black; private static readonly NativeColor SignaturePadLightColor = NativeColor.White; #elif WINDOWS_UWP private static readonly NativeColor SignaturePadDarkColor = Windows.UI.Colors.Black; private static readonly NativeColor SignaturePadLightColor = Windows.UI.Colors.White; #endif public NativePoint[][] Strokes => SignaturePadCanvas.Strokes; public NativePoint[] Points => SignaturePadCanvas.Points; public bool IsBlank => SignaturePadCanvas?.IsBlank ?? true; public event EventHandler StrokeCompleted; public event EventHandler Cleared; public void Clear () { SignaturePadCanvas.Clear (); UpdateUi (); } public void LoadPoints (NativePoint[] points) { SignaturePadCanvas.LoadPoints (points); UpdateUi (); } public void LoadStrokes (NativePoint[][] strokes) { SignaturePadCanvas.LoadStrokes (strokes); UpdateUi (); } /// /// Create an image of the currently drawn signature. /// public NativeImage GetImage (bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature at the specified size. /// public NativeImage GetImage (NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (size, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature at the specified scale. /// public NativeImage GetImage (float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (scale, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature with the specified stroke color. /// public NativeImage GetImage (NativeColor strokeColor, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (strokeColor, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature at the specified size with the specified stroke color. /// public NativeImage GetImage (NativeColor strokeColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (strokeColor, size, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature at the specified scale with the specified stroke color. /// public NativeImage GetImage (NativeColor strokeColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (strokeColor, scale, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature with the specified stroke and background colors. /// public NativeImage GetImage (NativeColor strokeColor, NativeColor fillColor, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (strokeColor, fillColor, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature at the specified size with the specified stroke and background colors. /// public NativeImage GetImage (NativeColor strokeColor, NativeColor fillColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (strokeColor, fillColor, size, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature at the specified scale with the specified stroke and background colors. /// public NativeImage GetImage (NativeColor strokeColor, NativeColor fillColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImage (strokeColor, fillColor, scale, shouldCrop, keepAspectRatio); } /// /// Create an image of the currently drawn signature using the specified settings. /// public NativeImage GetImage (ImageConstructionSettings settings) { return SignaturePadCanvas.GetImage (settings); } /// /// Create an encoded image of the currently drawn signature. /// public Task GetImageStreamAsync (SignatureImageFormat format, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified size. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, size, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified scale. /// public Task GetImageStreamAsync (SignatureImageFormat format, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, scale, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified size with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, size, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified scale with the specified stroke color. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, scale, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeColor fillColor, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, fillColor, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified size with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeColor fillColor, NativeSize size, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, fillColor, size, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature at the specified scale with the specified stroke and background colors. /// public Task GetImageStreamAsync (SignatureImageFormat format, NativeColor strokeColor, NativeColor fillColor, float scale, bool shouldCrop = true, bool keepAspectRatio = true) { return SignaturePadCanvas.GetImageStreamAsync (format, strokeColor, fillColor, scale, shouldCrop, keepAspectRatio); } /// /// Create an encoded image of the currently drawn signature using the specified settings. /// public Task GetImageStreamAsync (SignatureImageFormat format, ImageConstructionSettings settings) { return SignaturePadCanvas.GetImageStreamAsync (format, settings); } private void OnClearTapped () { Clear (); } private void OnSignatureCleared () { UpdateUi (); Cleared?.Invoke (this, EventArgs.Empty); } private void OnSignatureStrokeCompleted () { UpdateUi (); StrokeCompleted?.Invoke (this, EventArgs.Empty); } } } ================================================ FILE: src/SignaturePad.UWP/SignaturePad.UWP.csproj ================================================  uap10.0.10240 Xamarin.Controls SignaturePad False bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad SignaturePad for Xamarin and Windows $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.UWP/SignaturePad.cs ================================================ using System; using System.ComponentModel; using Windows.UI; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media; namespace Xamarin.Controls { [TemplatePart (Name = PartBackgroundImageView, Type = typeof (Image))] [TemplatePart (Name = PartSignaturePadCanvas, Type = typeof (SignaturePadCanvasView))] [TemplatePart (Name = PartCaption, Type = typeof (TextBlock))] [TemplatePart (Name = PartSignatureLine, Type = typeof (Border))] [TemplatePart (Name = PartSignaturePrompt, Type = typeof (TextBlock))] [TemplatePart (Name = PartClearLabel, Type = typeof (TextBlock))] public partial class SignaturePad : Control { private const string PartBackgroundImageView = "BackgroundImageView"; private const string PartSignaturePadCanvas = "SignaturePadCanvas"; private const string PartCaption = "Caption"; private const string PartSignatureLine = "SignatureLine"; private const string PartSignaturePrompt = "SignaturePrompt"; private const string PartClearLabel = "ClearLabel"; public static readonly DependencyProperty StrokeColorProperty; public static readonly DependencyProperty StrokeWidthProperty; public static readonly DependencyProperty SignatureLineBrushProperty; public static readonly DependencyProperty SignatureLineThicknessProperty; public static readonly DependencyProperty SignatureLineSpacingProperty; public static readonly DependencyProperty CaptionTextProperty; public static readonly DependencyProperty CaptionFontSizeProperty; public static readonly DependencyProperty CaptionForegroundProperty; public static readonly DependencyProperty SignaturePromptTextProperty; public static readonly DependencyProperty SignaturePromptFontSizeProperty; public static readonly DependencyProperty SignaturePromptForegroundProperty; public static readonly DependencyProperty ClearLabelTextProperty; public static readonly DependencyProperty ClearLabelFontSizeProperty; public static readonly DependencyProperty ClearLabelForegroundProperty; public static readonly DependencyProperty BackgroundImageProperty; public static readonly DependencyProperty BackgroundImageStretchProperty; public static readonly DependencyProperty BackgroundImageOpacityProperty; static SignaturePad () { StrokeColorProperty = DependencyProperty.Register ( nameof (StrokeColor), typeof (Color), typeof (SignaturePad), new PropertyMetadata (ImageConstructionSettings.DefaultStrokeColor)); StrokeWidthProperty = DependencyProperty.Register ( nameof (StrokeWidth), typeof (double), typeof (SignaturePad), new PropertyMetadata ((double)ImageConstructionSettings.DefaultStrokeWidth)); SignatureLineBrushProperty = DependencyProperty.Register ( nameof (SignatureLineBrush), typeof (Brush), typeof (SignaturePad), new PropertyMetadata (new SolidColorBrush (SignaturePadDarkColor))); SignatureLineThicknessProperty = DependencyProperty.Register ( nameof (SignatureLineThickness), typeof (double), typeof (SignaturePad), new PropertyMetadata ((double)DefaultLineThickness)); SignatureLineSpacingProperty = DependencyProperty.Register ( nameof (SignatureLineSpacing), typeof (double), typeof (SignaturePad), new PropertyMetadata ((double)DefaultNarrowSpacing, OnPaddingChanged)); CaptionTextProperty = DependencyProperty.Register ( nameof (CaptionText), typeof (string), typeof (SignaturePad), new PropertyMetadata (DefaultCaptionText)); CaptionFontSizeProperty = DependencyProperty.Register ( nameof (CaptionFontSize), typeof (double), typeof (SignaturePad), new PropertyMetadata ((double)DefaultFontSize)); CaptionForegroundProperty = DependencyProperty.Register ( nameof (CaptionForeground), typeof (Brush), typeof (SignaturePad), new PropertyMetadata (new SolidColorBrush (SignaturePadDarkColor))); SignaturePromptTextProperty = DependencyProperty.Register ( nameof (SignaturePromptText), typeof (string), typeof (SignaturePad), new PropertyMetadata (DefaultPromptText)); SignaturePromptFontSizeProperty = DependencyProperty.Register ( nameof (SignaturePromptFontSize), typeof (double), typeof (SignaturePad), new PropertyMetadata ((double)DefaultFontSize)); SignaturePromptForegroundProperty = DependencyProperty.Register ( nameof (SignaturePromptForeground), typeof (Brush), typeof (SignaturePad), new PropertyMetadata (new SolidColorBrush (SignaturePadDarkColor))); ClearLabelTextProperty = DependencyProperty.Register ( nameof (ClearLabelText), typeof (string), typeof (SignaturePad), new PropertyMetadata (DefaultClearLabelText)); ClearLabelFontSizeProperty = DependencyProperty.Register ( nameof (ClearLabelFontSize), typeof (double), typeof (SignaturePad), new PropertyMetadata ((double)DefaultFontSize)); ClearLabelForegroundProperty = DependencyProperty.Register ( nameof (ClearLabelForeground), typeof (Brush), typeof (SignaturePad), new PropertyMetadata (new SolidColorBrush (SignaturePadDarkColor))); BackgroundImageProperty = DependencyProperty.Register ( nameof (BackgroundImage), typeof (ImageSource), typeof (SignaturePad), new PropertyMetadata (null)); BackgroundImageStretchProperty = DependencyProperty.Register ( nameof (BackgroundImageStretch), typeof (Stretch), typeof (SignaturePad), new PropertyMetadata (Stretch.None)); BackgroundImageOpacityProperty = DependencyProperty.Register ( nameof (BackgroundImageOpacity), typeof (double), typeof (SignaturePad), new PropertyMetadata (1.0)); } public SignaturePad () { DefaultStyleKey = typeof (SignaturePad); RegisterPropertyChangedCallback (PaddingProperty, OnPaddingChanged); Padding = new Thickness (DefaultWideSpacing, DefaultWideSpacing, DefaultWideSpacing, DefaultNarrowSpacing); } protected override void OnApplyTemplate () { SignaturePadCanvas.StrokeCompleted += delegate { OnSignatureStrokeCompleted (); }; SignaturePadCanvas.Cleared += delegate { OnSignatureCleared (); }; ClearLabel.Tapped += delegate { OnClearTapped (); }; OnPaddingChanged (this, PaddingProperty); UpdateUi (); } /// /// The real signature canvas view /// public SignaturePadCanvasView SignaturePadCanvas => GetTemplateChild (PartSignaturePadCanvas) as SignaturePadCanvasView; /// /// The prompt displayed at the beginning of the signature line. /// public TextBlock SignaturePrompt => GetTemplateChild (PartSignaturePrompt) as TextBlock; /// /// The caption displayed under the signature line. /// public TextBlock Caption => GetTemplateChild (PartCaption) as TextBlock; /// /// An image view that may be used as a watermark or as a texture for the signature pad. /// public Image BackgroundImageView => GetTemplateChild (PartBackgroundImageView) as Image; /// /// Gets the label that clears the pad when clicked. /// public TextBlock ClearLabel => GetTemplateChild (PartClearLabel) as TextBlock; /// /// Gets the horizontal line that goes in the lower part of the pad. /// public Border SignatureLine => GetTemplateChild (PartSignatureLine) as Border; public Color StrokeColor { get { return (Color)GetValue (StrokeColorProperty); } set { SetValue (StrokeColorProperty, value); } } public double StrokeWidth { get { return (double)GetValue (StrokeWidthProperty); } set { SetValue (StrokeWidthProperty, value); } } public Brush SignatureLineBrush { get { return (Brush)GetValue (SignatureLineBrushProperty); } set { SetValue (SignatureLineBrushProperty, value); } } public Thickness SignatureLineThickness { get { return (Thickness)GetValue (SignatureLineThicknessProperty); } set { SetValue (SignatureLineThicknessProperty, value); } } public double SignatureLineSpacing { get { return (double)GetValue (SignatureLineSpacingProperty); } set { SetValue (SignatureLineSpacingProperty, value); } } public string CaptionText { get { return (string)GetValue (CaptionTextProperty); } set { SetValue (CaptionTextProperty, value); } } public double CaptionFontSize { get { return (double)GetValue (CaptionFontSizeProperty); } set { SetValue (CaptionFontSizeProperty, value); } } public Brush CaptionForeground { get { return (Brush)GetValue (CaptionForegroundProperty); } set { SetValue (CaptionForegroundProperty, value); } } public string SignaturePromptText { get { return (string)GetValue (SignaturePromptTextProperty); } set { SetValue (SignaturePromptTextProperty, value); } } public double SignaturePromptFontSize { get { return (double)GetValue (SignaturePromptFontSizeProperty); } set { SetValue (SignaturePromptFontSizeProperty, value); } } public Brush SignaturePromptForeground { get { return (Brush)GetValue (SignaturePromptForegroundProperty); } set { SetValue (SignaturePromptForegroundProperty, value); } } public string ClearLabelText { get { return (string)GetValue (ClearLabelTextProperty); } set { SetValue (ClearLabelTextProperty, value); } } public double ClearLabelFontSize { get { return (double)GetValue (ClearLabelFontSizeProperty); } set { SetValue (ClearLabelFontSizeProperty, value); } } public Brush ClearLabelForeground { get { return (Brush)GetValue (ClearLabelForegroundProperty); } set { SetValue (ClearLabelForegroundProperty, value); } } public ImageSource BackgroundImage { get { return (ImageSource)GetValue (BackgroundImageProperty); } set { SetValue (BackgroundImageProperty, value); } } public Stretch BackgroundImageStretch { get { return (Stretch)GetValue (BackgroundImageStretchProperty); } set { SetValue (BackgroundImageStretchProperty, value); } } public double BackgroundImageOpacity { get { return (double)GetValue (BackgroundImageOpacityProperty); } set { SetValue (BackgroundImageOpacityProperty, value); } } [EditorBrowsable (EditorBrowsableState.Never)] [Obsolete ("Use Background instead.")] public Color BackgroundColor { get { var scb = Background as SolidColorBrush; return scb == null ? Colors.Transparent : scb.Color; } set { Background = new SolidColorBrush (value); } } [EditorBrowsable (EditorBrowsableState.Never)] [Obsolete ("Use SignatureLineBrush instead.")] public Color SignatureLineColor { get { var scb = SignatureLineBrush as SolidColorBrush; return scb == null ? Colors.Transparent : scb.Color; } set { SignatureLineBrush = new SolidColorBrush (value); } } private void UpdateUi () { ClearLabel.Visibility = IsBlank ? Visibility.Collapsed : Visibility.Visible; } private void OnPaddingChanged (DependencyObject sender, DependencyProperty dp) { var padding = Padding; var spacing = SignatureLineSpacing; if (SignatureLine != null) { SignatureLine.Margin = new Thickness (padding.Left, 0, padding.Right, 0); } if (Caption != null) { Caption.Margin = new Thickness (0, spacing, 0, padding.Bottom); } if (ClearLabel != null) { ClearLabel.Margin = new Thickness (0, padding.Top, padding.Right, 0); } if (SignaturePrompt != null) { SignaturePrompt.Margin = new Thickness (padding.Left, 0, 0, spacing); } } private static void OnPaddingChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { ((SignaturePad)d).OnPaddingChanged (d, e.Property); } } } ================================================ FILE: src/SignaturePad.UWP/SignaturePadCanvasView.cs ================================================ using System; using System.IO; using System.Linq; using System.Numerics; using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using Windows.Foundation; using Windows.Storage.Streams; using Windows.UI; using Windows.UI.Core; using Windows.UI.Input.Inking; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Media.Imaging; using Microsoft.Graphics.Canvas; namespace Xamarin.Controls { [TemplatePart (Name = PartInkCanvas, Type = typeof (InkCanvas))] public partial class SignaturePadCanvasView : Control { public static readonly DependencyProperty StrokeColorProperty; public static readonly DependencyProperty StrokeWidthProperty; private const string PartInkCanvas = "InkCanvas"; private InkPresenter inkPresenter; static SignaturePadCanvasView () { StrokeColorProperty = DependencyProperty.Register ( nameof (StrokeColor), typeof (Color), typeof (SignaturePadCanvasView), new PropertyMetadata (ImageConstructionSettings.DefaultStrokeColor, OnStrokePropertiesChanged)); StrokeWidthProperty = DependencyProperty.Register ( nameof (StrokeWidth), typeof (double), typeof (SignaturePadCanvasView), new PropertyMetadata ((double)ImageConstructionSettings.DefaultStrokeWidth, OnStrokePropertiesChanged)); } public SignaturePadCanvasView () { DefaultStyleKey = typeof (SignaturePadCanvasView); IsEnabledChanged += delegate { var ip = inkPresenter; if (ip != null) ip.IsInputEnabled = IsEnabled; }; } protected override void OnApplyTemplate () { inkPresenter = InkCanvas?.InkPresenter; inkPresenter.StrokesCollected += (sender, e) => OnStrokeCompleted (); inkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Touch | CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse; OnStrokePropertiesChanged (this, null); } private InkCanvas InkCanvas => GetTemplateChild (PartInkCanvas) as InkCanvas; public Color StrokeColor { get { return (Color)GetValue (StrokeColorProperty); } set { SetValue (StrokeColorProperty, value); } } public double StrokeWidth { get { return (double)GetValue (StrokeWidthProperty); } set { SetValue (StrokeWidthProperty, value); } } public void Clear () { if (inkPresenter != null) { inkPresenter.StrokeContainer.Clear (); OnCleared (); } } private async Task GetImageStreamInternal (SignatureImageFormat format, Size scale, Rect signatureBounds, Size imageSize, float strokeWidth, Color strokeColor, Color backgroundColor) { CanvasBitmapFileFormat cbff; if (format == SignatureImageFormat.Jpeg) { cbff = CanvasBitmapFileFormat.Jpeg; } else if (format == SignatureImageFormat.Png) { cbff = CanvasBitmapFileFormat.Png; } else { return null; } using (var offscreen = GetRenderTarget (scale, signatureBounds, imageSize, strokeWidth, strokeColor, backgroundColor)) { var fileStream = new InMemoryRandomAccessStream (); await offscreen.SaveAsync (fileStream, cbff); var stream = fileStream.AsStream (); stream.Position = 0; return stream; } } private WriteableBitmap GetImageInternal (Size scale, Rect signatureBounds, Size imageSize, float strokeWidth, Color strokeColor, Color backgroundColor) { using (var offscreen = GetRenderTarget (scale, signatureBounds, imageSize, strokeWidth, strokeColor, backgroundColor)) { var bitmap = new WriteableBitmap ((int)offscreen.SizeInPixels.Width, (int)offscreen.SizeInPixels.Height); offscreen.GetPixelBytes (bitmap.PixelBuffer); return bitmap; } } private CanvasRenderTarget GetRenderTarget (Size scale, Rect signatureBounds, Size imageSize, float strokeWidth, Color strokeColor, Color backgroundColor) { var device = CanvasDevice.GetSharedDevice (); var offscreen = new CanvasRenderTarget (device, (int)imageSize.Width, (int)imageSize.Height, 96); using (var session = offscreen.CreateDrawingSession ()) { session.Clear (backgroundColor); session.Transform = Matrix3x2.Multiply ( Matrix3x2.CreateTranslation ((float)-signatureBounds.X, (float)-signatureBounds.Y), Matrix3x2.CreateScale ((float)scale.Width, (float)scale.Height)); // apply the specified colors/style var strokes = inkPresenter.StrokeContainer.GetStrokes ().Select (s => { // clone first, since this will change the UI if we don't s = s.Clone (); var attr = s.DrawingAttributes; attr.Color = strokeColor; attr.Size = new Size (StrokeWidth, StrokeWidth); s.DrawingAttributes = attr; return s; }); session.DrawInk (strokes.ToArray ()); } return offscreen; } private static void OnStrokePropertiesChanged (DependencyObject d, DependencyPropertyChangedEventArgs e) { var signaturePad = d as SignaturePadCanvasView; var inkPresenter = signaturePad.inkPresenter; if (inkPresenter != null) { var da = inkPresenter.CopyDefaultDrawingAttributes (); da.Color = signaturePad.StrokeColor; da.Size = new Size (signaturePad.StrokeWidth, signaturePad.StrokeWidth); inkPresenter.UpdateDefaultDrawingAttributes (da); foreach (var stroke in inkPresenter.StrokeContainer.GetStrokes ()) { stroke.DrawingAttributes = da; } } } } } ================================================ FILE: src/SignaturePad.UWP/Themes/Generic.xaml ================================================ ================================================ FILE: src/SignaturePad.iOS/InkPresenter.cs ================================================ using System.Collections.Generic; using CoreGraphics; using Foundation; using UIKit; namespace Xamarin.Controls { partial class InkPresenter : UIView { static InkPresenter () { ScreenDensity = (float)UIScreen.MainScreen.Scale; } public InkPresenter () : base () { Initialize (); } public InkPresenter (CGRect frame) : base (frame) { Initialize (); } private void Initialize () { Opaque = false; } // If you put SignaturePad inside a ScrollView, this line of code prevent that the gesture inside // an InkPresenter are dispatched to the ScrollView below public override bool GestureRecognizerShouldBegin (UIGestureRecognizer gestureRecognizer) => false; public override void TouchesBegan (NSSet touches, UIEvent evt) { // create a new path and set the options currentPath = new InkStroke (UIBezierPath.Create (), new List (), StrokeColor, StrokeWidth); // obtain the location of the touch var touch = touches.AnyObject as UITouch; var touchLocation = touch.LocationInView (this); // move the path to that position currentPath.Path.MoveTo (touchLocation); currentPath.GetPoints ().Add (touchLocation); // update the dirty rectangle ResetBounds (touchLocation); } public override void TouchesMoved (NSSet touches, UIEvent evt) { // something may have happened (clear) so start the stroke again if (currentPath == null) { TouchesBegan (touches, evt); } // obtain the location of the touch var touch = touches.AnyObject as UITouch; var touchLocation = touch.LocationInView (this); if (HasMovedFarEnough (currentPath, touchLocation.X, touchLocation.Y)) { // add it to the current path currentPath.Path.AddLineTo (touchLocation); currentPath.GetPoints ().Add (touchLocation); // update the dirty rectangle UpdateBounds (touchLocation); SetNeedsDisplayInRect (DirtyRect); } } public override void TouchesCancelled (NSSet touches, UIEvent evt) { TouchesEnded (touches, evt); } public override void TouchesEnded (NSSet touches, UIEvent evt) { // obtain the location of the touch var touch = touches.AnyObject as UITouch; var touchLocation = touch.LocationInView (this); // something may have happened (clear) during the stroke if (currentPath != null) { if (HasMovedFarEnough (currentPath, touchLocation.X, touchLocation.Y)) { // add it to the current path currentPath.Path.AddLineTo (touchLocation); currentPath.GetPoints ().Add (touchLocation); } // obtain the smoothed path, and add it to the old paths var smoothed = PathSmoothing.SmoothedPathWithGranularity (currentPath, 4); paths.Add (smoothed); } // clear the current path currentPath = null; // update the dirty rectangle UpdateBounds (touchLocation); SetNeedsDisplay (); // we are done with drawing OnStrokeCompleted (); } public override void Draw (CGRect rect) { base.Draw (rect); // destroy an old bitmap if (bitmapBuffer != null && ShouldRedrawBufferImage) { var temp = bitmapBuffer; bitmapBuffer = null; temp.Dispose (); temp = null; } // re-create if (bitmapBuffer == null) { bitmapBuffer = CreateBufferImage (); } // if there are no lines, the the bitmap will be null if (bitmapBuffer != null) { bitmapBuffer.Draw (CGPoint.Empty); } // draw the current path over the old paths if (currentPath != null) { var context = UIGraphics.GetCurrentContext (); context.SetLineCap (CGLineCap.Round); context.SetLineJoin (CGLineJoin.Round); context.SetStrokeColor (currentPath.Color.CGColor); context.SetLineWidth (currentPath.Width); context.AddPath (currentPath.Path.CGPath); context.StrokePath (); } } private UIImage CreateBufferImage () { if (paths == null || paths.Count == 0) { return null; } var size = Bounds.Size; UIGraphics.BeginImageContextWithOptions (size, false, ScreenDensity); var context = UIGraphics.GetCurrentContext (); context.SetLineCap (CGLineCap.Round); context.SetLineJoin (CGLineJoin.Round); foreach (var path in paths) { context.SetStrokeColor (path.Color.CGColor); context.SetLineWidth (path.Width); context.AddPath (path.Path.CGPath); context.StrokePath (); path.IsDirty = false; } var image = UIGraphics.GetImageFromCurrentImageContext (); UIGraphics.EndImageContext (); return image; } public override void LayoutSubviews () { base.LayoutSubviews (); SetNeedsDisplay (); } } } ================================================ FILE: src/SignaturePad.iOS/SignaturePad.iOS.csproj ================================================  xamarin.ios1.0 Xamarin.Controls SignaturePad False $(MSBuildWarningsAsMessages);VSX1000 bin\$(Configuration)\$(AssemblyName).xml 1.0.0 1.0.0 Xamarin.Controls.SignaturePad SignaturePad for Xamarin and Windows $(AssemblyName) ($(TargetFramework)) Microsoft Microsoft true Makes capturing, saving, and displaying signatures extremely simple. Makes capturing, saving, and displaying signatures extremely simple. © Microsoft Corporation. All rights reserved. en https://go.microsoft.com/fwlink/?linkid=874510 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874507 https://go.microsoft.com/fwlink/?linkid=874508 https://go.microsoft.com/fwlink/?linkid=874509 xamarin,signature,handwriting,windows,ios,android,uwp ================================================ FILE: src/SignaturePad.iOS/SignaturePadCanvasView.cs ================================================ using System; using System.ComponentModel; using System.IO; using System.Threading.Tasks; using CoreGraphics; using Foundation; using UIKit; namespace Xamarin.Controls { [Register ("SignaturePadCanvasView")] [DesignTimeVisible (true)] public partial class SignaturePadCanvasView : UIView { private InkPresenter inkPresenter; public SignaturePadCanvasView () { Initialize (); } public SignaturePadCanvasView (NSCoder coder) : base (coder) { Initialize (/* ? baseProperties: false ? */); } protected SignaturePadCanvasView (IntPtr ptr) : base (ptr) { Initialize (false); } public SignaturePadCanvasView (CGRect frame) : base (frame) { Initialize (); } private void Initialize (bool baseProperties = true) { inkPresenter = new InkPresenter (Bounds) { AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight, }; inkPresenter.StrokeCompleted += OnStrokeCompleted; AddSubview (inkPresenter); StrokeWidth = ImageConstructionSettings.DefaultStrokeWidth; StrokeColor = ImageConstructionSettings.DefaultStrokeColor; } [Export ("StrokeColor"), Browsable (true)] public UIColor StrokeColor { get { return inkPresenter.StrokeColor; } set { inkPresenter.StrokeColor = value; foreach (var stroke in inkPresenter.GetStrokes ()) { stroke.Color = value; } inkPresenter.SetNeedsDisplay (); } } [Export ("StrokeWidth"), Browsable (true)] public float StrokeWidth { get { return inkPresenter.StrokeWidth; } set { inkPresenter.StrokeWidth = value; foreach (var stroke in inkPresenter.GetStrokes ()) { stroke.Width = value; } inkPresenter.SetNeedsDisplay (); } } public void Clear () { inkPresenter.Clear (); OnCleared (); } private UIImage GetImageInternal (CGSize scale, CGRect signatureBounds, CGSize imageSize, float strokeWidth, UIColor strokeColor, UIColor backgroundColor) { UIGraphics.BeginImageContextWithOptions (imageSize, false, InkPresenter.ScreenDensity); // create context and set the desired options var context = UIGraphics.GetCurrentContext (); // background context.SetFillColor (backgroundColor.CGColor); context.FillRect (new CGRect (CGPoint.Empty, imageSize)); // cropping / scaling context.ScaleCTM (scale.Width, scale.Height); context.TranslateCTM (-signatureBounds.Left, -signatureBounds.Top); // strokes context.SetStrokeColor (strokeColor.CGColor); context.SetLineWidth (strokeWidth); context.SetLineCap (CGLineCap.Round); context.SetLineJoin (CGLineJoin.Round); foreach (var path in inkPresenter.GetStrokes ()) { context.AddPath (path.Path.CGPath); } context.StrokePath (); // get the image var image = UIGraphics.GetImageFromCurrentImageContext (); UIGraphics.EndImageContext (); return image; } private Task GetImageStreamInternal (SignatureImageFormat format, CGSize scale, CGRect signatureBounds, CGSize imageSize, float strokeWidth, UIColor strokeColor, UIColor backgroundColor) { var image = GetImageInternal (scale, signatureBounds, imageSize, strokeWidth, strokeColor, backgroundColor); if (image != null) { if (format == SignatureImageFormat.Jpeg) { return Task.Run (() => image.AsJPEG ().AsStream ()); } else if (format == SignatureImageFormat.Png) { return Task.Run (() => image.AsPNG ().AsStream ()); } } return Task.FromResult (null); } } } ================================================ FILE: src/SignaturePad.iOS/SignaturePadView.cs ================================================ using System; using System.ComponentModel; using CoreGraphics; using Foundation; using UIKit; namespace Xamarin.Controls { [Register ("SignaturePadView")] [DesignTimeVisible (true)] public partial class SignaturePadView : UIView { private UIEdgeInsets padding; public SignaturePadView () { Initialize (); } public SignaturePadView (NSCoder coder) : base (coder) { Initialize (/* ? baseProperties: false ? */); } public SignaturePadView (IntPtr ptr) : base (ptr) { Initialize (false); } public SignaturePadView (CGRect frame) : base (frame) { Initialize (); } private void Initialize (bool baseProperties = true) { // add the background view { BackgroundImageView = new UIImageView (); AddSubview (BackgroundImageView); } // add the main signature view { SignaturePadCanvas = new SignaturePadCanvasView (); SignaturePadCanvas.StrokeCompleted += delegate { OnSignatureStrokeCompleted (); }; SignaturePadCanvas.Cleared += delegate { OnSignatureCleared (); }; AddSubview (SignaturePadCanvas); } // add the caption { Caption = new UILabel () { BackgroundColor = UIColor.Clear, TextAlignment = UITextAlignment.Center, Font = UIFont.SystemFontOfSize (DefaultFontSize), Text = DefaultCaptionText, TextColor = SignaturePadDarkColor, }; AddSubview (Caption); } // add the signature line { SignatureLine = new UIView () { BackgroundColor = SignaturePadDarkColor, }; SignatureLineWidth = DefaultLineThickness; SignatureLineSpacing = DefaultNarrowSpacing; AddSubview (SignatureLine); } // add the prompt { SignaturePrompt = new UILabel () { BackgroundColor = UIColor.Clear, Font = UIFont.BoldSystemFontOfSize (DefaultFontSize), Text = DefaultPromptText, TextColor = SignaturePadDarkColor, }; AddSubview (SignaturePrompt); } // add the clear label { ClearLabel = UIButton.FromType (UIButtonType.Custom); ClearLabel.BackgroundColor = UIColor.Clear; ClearLabel.Font = UIFont.BoldSystemFontOfSize (DefaultFontSize); ClearLabel.SetTitle (DefaultClearLabelText, UIControlState.Normal); ClearLabel.SetTitleColor (SignaturePadDarkColor, UIControlState.Normal); AddSubview (ClearLabel); // attach the "clear" command ClearLabel.TouchUpInside += delegate { OnClearTapped (); }; } Padding = new UIEdgeInsets (DefaultWideSpacing, DefaultWideSpacing, DefaultNarrowSpacing, DefaultWideSpacing); // clear / initialize the view UpdateUi (); } public SignaturePadCanvasView SignaturePadCanvas { get; private set; } /// /// Gets the horizontal line that goes in the lower part of the pad. /// public UIView SignatureLine { get; private set; } /// /// The caption displayed under the signature line. /// public UILabel Caption { get; private set; } /// /// The prompt displayed at the beginning of the signature line. /// public UILabel SignaturePrompt { get; private set; } /// /// Gets the label that clears the pad when clicked. /// public UIButton ClearLabel { get; private set; } /// /// Gets the image view that may be used as a watermark or as a texture /// for the signature pad. /// public UIImageView BackgroundImageView { get; private set; } [Export ("StrokeColor"), Browsable (true)] public UIColor StrokeColor { get => SignaturePadCanvas.StrokeColor; set => SignaturePadCanvas.StrokeColor = value; } [Export ("StrokeWidth"), Browsable (true)] public float StrokeWidth { get => SignaturePadCanvas.StrokeWidth; set => SignaturePadCanvas.StrokeWidth = value; } /// /// Gets or sets the color of the signature line. /// [Export ("SignatureLineColor"), Browsable (true)] public UIColor SignatureLineColor { get => SignatureLine.BackgroundColor; set => SignatureLine.BackgroundColor = value; } /// /// Gets or sets the width of the signature line. /// [Export ("SignatureLineWidth"), Browsable (true)] public nfloat SignatureLineWidth { get => SignatureLine.Bounds.Height; set { var bounds = SignatureLine.Bounds; bounds.Height = value; SignatureLine.Bounds = bounds; SetNeedsLayout (); } } /// /// Gets or sets the spacing between the signature line and the caption and prompt. /// [Export ("SignatureLineSpacing"), Browsable (true)] public nfloat SignatureLineSpacing { get => SignatureLine.LayoutMargins.Bottom; set { var margins = SignatureLine.LayoutMargins; margins.Top = value; margins.Bottom = value; SignatureLine.LayoutMargins = margins; SetNeedsLayout (); } } /// /// Gets or sets the text for the caption displayed under the signature line. /// [Export ("CaptionText"), Browsable (true)] public string CaptionText { get => Caption.Text; set { Caption.Text = value; SetNeedsLayout (); } } /// /// Gets or sets the font size text for the caption displayed under the signature line. /// [Export ("CaptionFontSize"), Browsable (true)] public nfloat CaptionFontSize { get => Caption.Font.PointSize; set { Caption.Font = Caption.Font.WithSize (value); SetNeedsLayout (); } } /// /// Gets or sets the text color for the caption displayed under the signature line. /// [Export ("CaptionTextColor"), Browsable (true)] public UIColor CaptionTextColor { get => Caption.TextColor; set => Caption.TextColor = value; } /// /// Gets or sets the text for the prompt displayed at the beginning of the signature line. /// [Export ("SignaturePromptText"), Browsable (true)] public string SignaturePromptText { get => SignaturePrompt.Text; set { SignaturePrompt.Text = value; SetNeedsLayout (); } } /// /// Gets or sets the font size the prompt displayed at the beginning of the signature line. /// [Export ("SignaturePromptFontSize"), Browsable (true)] public nfloat SignaturePromptFontSize { get => SignaturePrompt.Font.PointSize; set { SignaturePrompt.Font = SignaturePrompt.Font.WithSize (value); SetNeedsLayout (); } } /// /// Gets or sets the text color for the prompt displayed at the beginning of the signature line. /// [Export ("SignaturePromptTextColor"), Browsable (true)] public UIColor SignaturePromptTextColor { get => SignaturePrompt.TextColor; set => SignaturePrompt.TextColor = value; } /// /// Gets or sets the text for the label that clears the pad when clicked. /// [Export ("ClearLabelText"), Browsable (true)] public string ClearLabelText { get => ClearLabel.Title (UIControlState.Normal); set { ClearLabel.SetTitle (value, UIControlState.Normal); SetNeedsLayout (); } } /// /// Gets or sets the font size the label that clears the pad when clicked. /// [Export ("ClearLabelFontSize"), Browsable (true)] public nfloat ClearLabelFontSize { get => ClearLabel.Font.PointSize; set { ClearLabel.Font = ClearLabel.Font.WithSize (value); SetNeedsLayout (); } } /// /// Gets or sets the text color for the label that clears the pad when clicked. /// [Export ("ClearLabelTextColor"), Browsable (true)] public UIColor ClearLabelTextColor { get => ClearLabel.TitleColor (UIControlState.Normal); set => ClearLabel.SetTitleColor (value, UIControlState.Normal); } [Export ("BackgroundImage"), Browsable (true)] public UIImage BackgroundImage { get => BackgroundImageView.Image; set => BackgroundImageView.Image = value; } [Export ("BackgroundImageContentMode"), Browsable (true)] public UIViewContentMode BackgroundImageContentMode { get => BackgroundImageView.ContentMode; set => BackgroundImageView.ContentMode = value; } [Export ("BackgroundImageAlpha"), Browsable (true)] public nfloat BackgroundImageAlpha { get => BackgroundImageView.Alpha; set => BackgroundImageView.Alpha = value; } [Export ("Padding"), Browsable (true)] public UIEdgeInsets Padding { get => padding; set { padding = value; SetNeedsLayout (); } } private void UpdateUi () { ClearLabel.Hidden = IsBlank; } public override void LayoutSubviews () { var w = Frame.Width; var h = Frame.Height; var currentY = h; SignaturePrompt.SizeToFit (); ClearLabel.SizeToFit (); var captionHeight = Caption.SizeThatFits (Caption.Frame.Size).Height; var clearButtonHeight = (int)ClearLabel.Font.LineHeight + 1; var rect = new CGRect (0, 0, w, h); SignaturePadCanvas.Frame = rect; BackgroundImageView.Frame = rect; currentY = currentY - Padding.Bottom - captionHeight; Caption.Frame = new CGRect (Padding.Left, currentY, w - Padding.Left - Padding.Right, captionHeight); currentY = currentY - SignatureLine.LayoutMargins.Bottom - SignatureLine.Frame.Height; SignatureLine.Frame = new CGRect (Padding.Left, currentY, w - Padding.Left - Padding.Right, SignatureLine.Frame.Height); currentY = currentY - SignatureLine.LayoutMargins.Top - SignaturePrompt.Frame.Height; SignaturePrompt.Frame = new CGRect (Padding.Left, currentY, SignaturePrompt.Frame.Width, SignaturePrompt.Frame.Height); ClearLabel.Frame = new CGRect (w - Padding.Right - ClearLabel.Frame.Width, Padding.Top, ClearLabel.Frame.Width, clearButtonHeight); } } } ================================================ FILE: src/SignaturePad.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.iOS", "SignaturePad.iOS\SignaturePad.iOS.csproj", "{BEF71536-787B-431F-AC7F-A6469710D11F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Android", "SignaturePad.Android\SignaturePad.Android.csproj", "{F1A16CB9-A759-42C8-8F0B-3C9698A55336}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms", "SignaturePad.Forms\SignaturePad.Forms.csproj", "{B2AF970D-D640-451C-95AF-92AF531B8C1E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms.Droid", "SignaturePad.Forms.Droid\SignaturePad.Forms.Droid.csproj", "{1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms.iOS", "SignaturePad.Forms.iOS\SignaturePad.Forms.iOS.csproj", "{B12D20AA-0EDF-4903-B385-BB8090848532}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.UWP", "SignaturePad.UWP\SignaturePad.UWP.csproj", "{16131FDC-D50B-4EF2-8ECC-661184FF80DB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SignaturePad.Forms.UWP", "SignaturePad.Forms.UWP\SignaturePad.Forms.UWP.csproj", "{6FC62387-6717-4577-A48B-D15848741F08}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Debug|Any CPU.Build.0 = Debug|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|Any CPU.ActiveCfg = Release|Any CPU {BEF71536-787B-431F-AC7F-A6469710D11F}.Release|Any CPU.Build.0 = Release|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Debug|Any CPU.Build.0 = Debug|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Release|Any CPU.ActiveCfg = Release|Any CPU {F1A16CB9-A759-42C8-8F0B-3C9698A55336}.Release|Any CPU.Build.0 = Release|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2AF970D-D640-451C-95AF-92AF531B8C1E}.Release|Any CPU.Build.0 = Release|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU {1D5482F6-AA89-422C-9E34-88A8A1AD8AB6}.Release|Any CPU.Build.0 = Release|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Debug|Any CPU.Build.0 = Debug|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Release|Any CPU.ActiveCfg = Release|Any CPU {B12D20AA-0EDF-4903-B385-BB8090848532}.Release|Any CPU.Build.0 = Release|Any CPU {16131FDC-D50B-4EF2-8ECC-661184FF80DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {16131FDC-D50B-4EF2-8ECC-661184FF80DB}.Debug|Any CPU.Build.0 = Debug|Any CPU {16131FDC-D50B-4EF2-8ECC-661184FF80DB}.Release|Any CPU.ActiveCfg = Release|Any CPU {16131FDC-D50B-4EF2-8ECC-661184FF80DB}.Release|Any CPU.Build.0 = Release|Any CPU {6FC62387-6717-4577-A48B-D15848741F08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6FC62387-6717-4577-A48B-D15848741F08}.Debug|Any CPU.Build.0 = Debug|Any CPU {6FC62387-6717-4577-A48B-D15848741F08}.Release|Any CPU.ActiveCfg = Release|Any CPU {6FC62387-6717-4577-A48B-D15848741F08}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {151CF82C-49C4-42D5-8023-2684A3D42EF8} EndGlobalSection EndGlobal