├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── NuGet.Config ├── README.md ├── doc ├── Effects.gif ├── FluentTransitions.png ├── FluentTransitions.svg ├── Source FluentTransitions.url ├── button.gif ├── itv1.gif ├── itv2.gif ├── ripple.gif └── text.gif └── src ├── Directory.Build.props ├── FluentTransitions.sln ├── FluentTransitions.snk ├── FluentTransitions ├── FluentTransitions.csproj ├── InterpolationMethod.cs ├── ManagedTypes │ ├── IManagedType.cs │ ├── ManagedColor.cs │ ├── ManagedDouble.cs │ ├── ManagedFloat.cs │ ├── ManagedInt.cs │ └── ManagedString.cs ├── Methods │ ├── Acceleration.cs │ ├── Bounce.cs │ ├── CriticalDamping.cs │ ├── Deceleration.cs │ ├── EaseInEaseOut.cs │ ├── EaseWithFunction.cs │ ├── EasingFunctions.cs │ ├── Flash.cs │ ├── IMethod.cs │ ├── Linear.cs │ ├── Rubberband.cs │ ├── Sequence.cs │ ├── Spring.cs │ └── ThrowAndCatch.cs ├── Transition.cs ├── TransitionChain.cs ├── TransitionDefinition.cs ├── TransitionElement.cs ├── TransitionManager.cs └── Utility.cs └── TestApp ├── DemoForm.Designer.cs ├── DemoForm.cs ├── DemoForm.resx ├── KittenPuppyControl.Designer.cs ├── KittenPuppyControl.cs ├── KittenPuppyControl.resx ├── Program.cs ├── Properties ├── Resources.Designer.cs └── Resources.resx ├── Resources ├── kitten.jpg └── puppy.jpg ├── RippleControl.Designer.cs ├── RippleControl.cs ├── RippleControl.resx ├── TestApp.csproj └── app.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: .NET Build, Test, and Publish Nuget Package 2 | 3 | on: 4 | push: 5 | branches: [ "main", "master" ] 6 | pull_request: 7 | branches: [ "main", "master" ] 8 | 9 | jobs: 10 | 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v4 17 | 18 | - name: Allow unsecure commands 19 | run: echo 'ACTIONS_ALLOW_UNSECURE_COMMANDS=true' >> $GITHUB_ENV 20 | 21 | - name: Fetch all history for all tags and branches 22 | run: | 23 | git config remote.origin.url https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} 24 | git fetch --prune --unshallow 25 | 26 | - name: Install GitVersion 27 | uses: gittools/actions/gitversion/setup@v0.9.7 28 | with: 29 | versionSpec: '5.x' 30 | 31 | - name: Use GitVersion 32 | id: gitversion # step id used as reference for output values 33 | uses: gittools/actions/gitversion/execute@v0.9.7 34 | 35 | - name: Setup .NET 36 | uses: actions/setup-dotnet@v2 37 | with: 38 | dotnet-version: | 39 | 6.0.x 40 | 8.0.x 41 | 42 | - name: Restore dependencies 43 | run: dotnet restore src/FluentTransitions.sln 44 | 45 | - name: Build 46 | run: dotnet build src --no-restore --configuration=Release /p:Version=${{steps.gitversion.outputs.semVer}} 47 | 48 | - name: Test 49 | run: dotnet test src --no-build --configuration=Release --verbosity normal 50 | 51 | - name: pack nuget packages 52 | run: dotnet pack src --output nupkgs --configuration=Release --no-restore --no-build /p:PackageVersion=${{steps.gitversion.outputs.semVer}} 53 | 54 | - name: Create a GitHub release 55 | uses: ncipollo/release-action@v1 56 | with: 57 | tag: ${{steps.gitversion.outputs.semVer}} 58 | name: Release ${{steps.gitversion.outputs.semVer}} 59 | if: github.event_name != 'pull_request' 60 | 61 | - name: upload nuget package 62 | if: github.event_name != 'pull_request' 63 | run: dotnet nuget push nupkgs/FluentTransitions*.nupkg -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | **/.vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | project.fragment.lock.json 47 | artifacts/ 48 | 49 | *_i.c 50 | *_p.c 51 | *_i.h 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.tmp_proj 66 | *.log 67 | *.vspscc 68 | *.vssscc 69 | .builds 70 | *.pidb 71 | *.svclog 72 | *.scc 73 | 74 | # Chutzpah Test files 75 | _Chutzpah* 76 | 77 | # Visual C++ cache files 78 | ipch/ 79 | *.aps 80 | *.ncb 81 | *.opendb 82 | *.opensdf 83 | *.sdf 84 | *.cachefile 85 | *.VC.db 86 | *.VC.VC.opendb 87 | 88 | # Visual Studio profiler 89 | *.psess 90 | *.vsp 91 | *.vspx 92 | *.sap 93 | 94 | # TFS 2012 Local Workspace 95 | $tf/ 96 | 97 | # Guidance Automation Toolkit 98 | *.gpState 99 | 100 | # ReSharper is a .NET coding add-in 101 | _ReSharper*/ 102 | *.[Rr]e[Ss]harper 103 | *.DotSettings.user 104 | 105 | # JustCode is a .NET coding add-in 106 | .JustCode 107 | 108 | # TeamCity is a build add-in 109 | _TeamCity* 110 | 111 | # DotCover is a Code Coverage Tool 112 | *.dotCover 113 | 114 | # NCrunch 115 | _NCrunch_* 116 | .*crunch*.local.xml 117 | nCrunchTemp_* 118 | 119 | # MightyMoose 120 | *.mm.* 121 | AutoTest.Net/ 122 | 123 | # Web workbench (sass) 124 | .sass-cache/ 125 | 126 | # Installshield output folder 127 | [Ee]xpress/ 128 | 129 | # DocProject is a documentation generator add-in 130 | DocProject/buildhelp/ 131 | DocProject/Help/*.HxT 132 | DocProject/Help/*.HxC 133 | DocProject/Help/*.hhc 134 | DocProject/Help/*.hhk 135 | DocProject/Help/*.hhp 136 | DocProject/Help/Html2 137 | DocProject/Help/html 138 | 139 | # Click-Once directory 140 | publish/ 141 | 142 | # Publish Web Output 143 | *.[Pp]ublish.xml 144 | *.azurePubxml 145 | # TODO: Comment the next line if you want to checkin your web deploy settings 146 | # but database connection strings (with potential passwords) will be unencrypted 147 | #*.pubxml 148 | *.publishproj 149 | 150 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 151 | # checkin your Azure Web App publish settings, but sensitive information contained 152 | # in these scripts will be unencrypted 153 | PublishScripts/ 154 | 155 | # NuGet Packages 156 | *.nupkg 157 | # The packages folder can be ignored because of Package Restore 158 | **/packages/* 159 | # except build/, which is used as an MSBuild target. 160 | !**/packages/build/ 161 | # Uncomment if necessary however generally it will be regenerated when needed 162 | #!**/packages/repositories.config 163 | # NuGet v3's project.json files produces more ignoreable files 164 | *.nuget.props 165 | *.nuget.targets 166 | 167 | # Microsoft Azure Build Output 168 | csx/ 169 | *.build.csdef 170 | 171 | # Microsoft Azure Emulator 172 | ecf/ 173 | rcf/ 174 | 175 | # Windows Store app package directories and files 176 | AppPackages/ 177 | BundleArtifacts/ 178 | Package.StoreAssociation.xml 179 | _pkginfo.txt 180 | 181 | # Visual Studio cache files 182 | # files ending in .cache can be ignored 183 | *.[Cc]ache 184 | # but keep track of directories ending in .cache 185 | !*.[Cc]ache/ 186 | 187 | # Others 188 | ClientBin/ 189 | ~$* 190 | *~ 191 | *.dbmdl 192 | *.dbproj.schemaview 193 | *.jfm 194 | *.pfx 195 | *.publishsettings 196 | node_modules/ 197 | orleans.codegen.cs 198 | 199 | # Since there are multiple workflows, uncomment next line to ignore bower_components 200 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 201 | #bower_components/ 202 | 203 | # RIA/Silverlight projects 204 | Generated_Code/ 205 | 206 | # Backup & report files from converting an old project file 207 | # to a newer Visual Studio version. Backup files are not needed, 208 | # because we have git ;-) 209 | _UpgradeReport_Files/ 210 | Backup*/ 211 | UpgradeLog*.XML 212 | UpgradeLog*.htm 213 | 214 | # SQL Server files 215 | *.mdf 216 | *.ldf 217 | 218 | # Business Intelligence projects 219 | *.rdl.data 220 | *.bim.layout 221 | *.bim_*.settings 222 | 223 | # Microsoft Fakes 224 | FakesAssemblies/ 225 | 226 | # GhostDoc plugin setting file 227 | *.GhostDoc.xml 228 | 229 | # Node.js Tools for Visual Studio 230 | .ntvs_analysis.dat 231 | 232 | # Visual Studio 6 build log 233 | *.plg 234 | 235 | # Visual Studio 6 workspace options file 236 | *.opt 237 | 238 | # Visual Studio LightSwitch build output 239 | **/*.HTMLClient/GeneratedArtifacts 240 | **/*.DesktopClient/GeneratedArtifacts 241 | **/*.DesktopClient/ModelManifest.xml 242 | **/*.Server/GeneratedArtifacts 243 | **/*.Server/ModelManifest.xml 244 | _Pvt_Extensions 245 | 246 | # Paket dependency manager 247 | .paket/paket.exe 248 | paket-files/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | 254 | # CodeRush 255 | .cr/ 256 | 257 | # Python Tools for Visual Studio (PTVS) 258 | __pycache__/ 259 | *.pyc 260 | /tools 261 | /_output 262 | 263 | # Mac 264 | .DS_Store 265 | 266 | /build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2009 Richard S. Shepherd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FluentTransitions 2 | 3 | [![NuGet Status](https://img.shields.io/nuget/v/FluentTransitions.svg)](https://www.nuget.org/packages/FluentTransitions/) 4 | 5 | FluentTransitions lets you create animated transitions of any property of user-interface elements in .NET. It provides a simple API to perform UI animations in a similar way to Apple's Core Animation library for iOS, iPadOS and macOS. 6 | 7 | ![FluentDragDrop effects with FluentTransitions](./doc/Effects.gif) 8 | 9 | FluentTransitions powering [FluentDragDrop](https://github.com/awaescher/FluentDragDrop), a library to create stunning drag and drop effects with Windows Forms. 10 | 11 | ## What can it do for me? 12 | 13 | FluentTransitions allows smooth UI transitions with Windows Forms and GDI+. While the animation above shows several effects in combination, you will probably start with simpler things like the transition of a property of a Windows Forms control: 14 | 15 | ```csharp 16 | var maxTop = button1.Parent.Height - button1.Height; 17 | 18 | Transition 19 | .With(button1, nameof(Top), maxTop) // target, property, value 20 | .Bounce(TimeSpan.FromMilliseconds(500)); // method and duration 21 | ``` 22 | 23 | ![Button drop effect](./doc/button.gif) 24 | 25 | ### Multi targeting 26 | 27 | Transitions can manipulate multiple properties from one or multiple objects simultaneously just by chaining the `.With()` methods: 28 | 29 | ```csharp 30 | Transition 31 | .With(button1, nameof(Left), 300) 32 | .With(button2, nameof(Top), 200) 33 | .With(Form1, nameof(Opacity), 0.0) 34 | .EaseInEaseOut(TimeSpan.FromSeconds(2)); 35 | ``` 36 | 37 | This code animates the movement of two buttons while it fades out the whole form. All of this is running in parallel within two seconds. 38 | 39 | ### Chaining 40 | 41 | Some effects might require multiple transitions to be executed sequentially. FluentTransitions provides a concept called "Chaining". To use it, simply build your transitions and run them with `Transition.RunChain(...)`: 42 | 43 | ```csharp 44 | Transition.RunChain 45 | ( 46 | Transition 47 | .With(button1, nameof(Left), 300) 48 | .With(button2, nameof(Top), 200) 49 | .Build(new Linear(TimeSpan.FromSeconds(1))), 50 | Transition 51 | .With(Form1, nameof(Opacity), 0.0) 52 | .Build(new Linear(TimeSpan.FromSeconds(1))) 53 | ); 54 | ``` 55 | 56 | This code animates the movement of two buttons first. Once this is done, it fades out the whole form. Both transitions are completed within two seconds. 57 | 58 | ### Completion 59 | 60 | Each transition raises an event once it is completed. This can be useful to run code after the UI is done with animating. 61 | 62 | ```csharp 63 | var t1 = Transition 64 | .With(button1, nameof(Left), 300) 65 | .With(button2, nameof(Top), 200) 66 | .HookOnCompletionInUiThread(SynchronizationContext.Current, () => this.Close()) 67 | .Build(new EaseInEaseOut(TimeSpan.FromSeconds(1))); 68 | 69 | Transition.Run(t1); 70 | ``` 71 | 72 | > Prefer `Transition.RunChain()` to run animations sequentially. 73 | 74 | # More Samples 75 | 76 | Back in the year 2011, I used these transitions to spice up two login forms for a customer project. Be kind to me, I was young and just wanted to make something fancy. Nevertheless, I think it's pretty special to WinForms. 77 | 78 | ![Login form sample 1](./doc/itv1.gif) 79 | 80 | ![Login form sample 2](./doc/itv2.gif) 81 | 82 | But FluentTransitions is more that just smoothly moving and sizing controls, you can do pretty much everything if you're creative enough. 83 | 84 | ![Ripple effect sample](./doc/ripple.gif) 85 | 86 | ![Text transition sample](./doc/text.gif) 87 | 88 | ## Acknowledgements 89 | 90 | Idea and initial implementation by [Richard S. Shepherd on Google Code](https://code.google.com/p/dot-net-transitions/). 91 | 92 | Dec 2020 [Andreas Wäscher](https://github.com/awaescher) 93 | - Added support for [Easing Functions](https://easings.net/) 94 | - Added built-in easing implementations by [Mauro Sampietro](https://www.codeproject.com/Articles/827808/Control-Animation-in-Winforms) 95 | 96 | Oct 2020 [Andreas Wäscher](https://github.com/awaescher) 97 | - Migrated to .NET Core 3.1 and .NET Framework 4.8 98 | - Updated namespaces, class names and the code itself to meet modern code standards 99 | - Added fluent syntax to build and run transitions 100 | - Switched from "dot-net-transitions" to "FluentTransitions" 101 | 102 | Apr 2020 [zhenyuan0502](https://github.com/zhenyuan0502) 103 | - Migrated to .NET Core 3.0 104 | 105 | Jul 2015 [Uwe Keim](https://github.com/UweKeim) 106 | - Copied this repository from [Google Code](https://code.google.com/p/dot-net-transitions/) to save it from disappearing when Google Code shuts down 107 | 108 | © 2009 Richard S. Shepherd. 109 | -------------------------------------------------------------------------------- /doc/Effects.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/Effects.gif -------------------------------------------------------------------------------- /doc/FluentTransitions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/FluentTransitions.png -------------------------------------------------------------------------------- /doc/FluentTransitions.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/Source FluentTransitions.url: -------------------------------------------------------------------------------- 1 | [InternetShortcut] 2 | URL=https://www.flaticon.com/free-icon/website_1388665 3 | -------------------------------------------------------------------------------- /doc/button.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/button.gif -------------------------------------------------------------------------------- /doc/itv1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/itv1.gif -------------------------------------------------------------------------------- /doc/itv2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/itv2.gif -------------------------------------------------------------------------------- /doc/ripple.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/ripple.gif -------------------------------------------------------------------------------- /doc/text.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/doc/text.gif -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | Andreas Wäscher 4 | Andreas Wäscher 5 | FluentTransitions.png 6 | https://github.com/awaescher/FluentTransitions 7 | git 8 | https://github.com/awaescher/FluentTransitions 9 | MIT 10 | latest 11 | 12 | true 13 | 14 | true 15 | 16 | 17 | 18 | true 19 | 20 | 21 | 22 | true 23 | true 24 | 25 | 26 | 27 | true 28 | ..\FluentTransitions.snk 29 | false 30 | 31 | 32 | 33 | false 34 | false 35 | 36 | 37 | 38 | DEBUG;TRACE 39 | True 40 | true 41 | embedded 42 | false 43 | 44 | 45 | 46 | True 47 | embedded 48 | True 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/FluentTransitions.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentTransitions", "FluentTransitions\FluentTransitions.csproj", "{95CDE461-0720-4C3B-B5E1-D188391FB7C0}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestApp", "TestApp\TestApp.csproj", "{E5B3CEE3-973B-4400-BF81-62EEC02625F4}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {95CDE461-0720-4C3B-B5E1-D188391FB7C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {95CDE461-0720-4C3B-B5E1-D188391FB7C0}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {95CDE461-0720-4C3B-B5E1-D188391FB7C0}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {95CDE461-0720-4C3B-B5E1-D188391FB7C0}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {E5B3CEE3-973B-4400-BF81-62EEC02625F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {E5B3CEE3-973B-4400-BF81-62EEC02625F4}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {E5B3CEE3-973B-4400-BF81-62EEC02625F4}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {E5B3CEE3-973B-4400-BF81-62EEC02625F4}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {D7684A7E-357C-4C91-BE60-920B654D6FB3} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/FluentTransitions.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/src/FluentTransitions.snk -------------------------------------------------------------------------------- /src/FluentTransitions/FluentTransitions.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Library 4 | net48;net6.0-windows;net8.0-windows 5 | true 6 | true 7 | FluentTransitions lets you create animated transitions of any property of user-interface elements in .NET. 8 | It provides an easy way to perform UI animations in a similar way to Apple's Core Animation library for iOS, iPadOS and macOS. 9 | 10 | The app icon was made by Freepik from www.flaticon.com and is licensed by Creative Commons BY 3.0. 11 | Fluent;Transitions;Windows;Forms;WinForms;UI 12 | Andreas Wäscher, Richard S. Shepherd, Mauro Sampietro 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/FluentTransitions/InterpolationMethod.cs: -------------------------------------------------------------------------------- 1 | namespace FluentTransitions 2 | { 3 | /// 4 | /// Interpolation methods to 5 | /// 6 | public enum InterpolationMethod 7 | { 8 | /// 9 | /// Alters values in a linear way until they reach their destination values. 10 | /// 11 | Linear, 12 | 13 | /// 14 | /// Alters values to their destination values from a standing start with accelerating intervals. 15 | /// 16 | Accleration, 17 | 18 | /// 19 | /// Alters values starting from a high speed and decelerating to zero by the end of the transition. 20 | /// 21 | Deceleration, 22 | 23 | /// 24 | /// Alters values in an ease-in-ease-out transition. 25 | /// This accelerates during the first half of the transition, and then decelerates during the second half. 26 | /// 27 | EaseInEaseOut 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/FluentTransitions/ManagedTypes/IManagedType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.ManagedTypes 4 | { 5 | /// 6 | /// Interface for all types we can perform transitions on. 7 | /// Each type (e.g. int, double, Color) that we can perform a transition on 8 | /// needs to have its own class that implements this interface. These classes 9 | /// tell the transition system how to act on objects of that type. 10 | /// 11 | internal interface IManagedType 12 | { 13 | /// 14 | /// Returns the Type that the instance is managing. 15 | /// 16 | Type GetManagedType(); 17 | 18 | /// 19 | /// Returns a deep copy of the object passed in. (In particular this is 20 | /// needed for types that are objects.) 21 | /// 22 | object Copy(object o); 23 | 24 | /// 25 | /// Returns an object holding the value between the start and end corresponding 26 | /// to the percentage passed in. (Note: the percentage can be less than 0% or 27 | /// greater than 100%.) 28 | /// 29 | object GetIntermediateValue(object start, object end, double percentage); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/FluentTransitions/ManagedTypes/ManagedColor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | 4 | namespace FluentTransitions.ManagedTypes 5 | { 6 | /// 7 | /// Class that manages transitions for Color properties. For these we 8 | /// need to transition the R, G, B and A sub-properties independently. 9 | /// 10 | internal class ManagedColor : IManagedType 11 | { 12 | /// 13 | /// Returns the type we are managing. 14 | /// 15 | public Type GetManagedType() => typeof(Color); 16 | 17 | /// 18 | /// Returns a copy of the color object passed in. 19 | /// 20 | public object Copy(object o) => Color.FromArgb(((Color)o).ToArgb()); 21 | 22 | /// 23 | /// Creates an intermediate value for the colors depending on the percentage passed in. 24 | /// 25 | public object GetIntermediateValue(object start, object end, double percentage) 26 | { 27 | Color startColor = (Color)start; 28 | Color endColor = (Color)end; 29 | 30 | // We interpolate the R, G, B and A components separately... 31 | int start_R = startColor.R; 32 | int start_G = startColor.G; 33 | int start_B = startColor.B; 34 | int start_A = startColor.A; 35 | 36 | int end_R = endColor.R; 37 | int end_G = endColor.G; 38 | int end_B = endColor.B; 39 | int end_A = endColor.A; 40 | 41 | int new_R = Utility.Interpolate(start_R, end_R, percentage); 42 | int new_G = Utility.Interpolate(start_G, end_G, percentage); 43 | int new_B = Utility.Interpolate(start_B, end_B, percentage); 44 | int new_A = Utility.Interpolate(start_A, end_A, percentage); 45 | 46 | return Color.FromArgb(new_A, new_R, new_G, new_B); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/FluentTransitions/ManagedTypes/ManagedDouble.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.ManagedTypes 4 | { 5 | /// 6 | /// Manages transitions for double properties. 7 | /// 8 | internal class ManagedDouble : IManagedType 9 | { 10 | /// 11 | /// Returns the type managed by this class. 12 | /// 13 | public Type GetManagedType() => typeof(double); 14 | 15 | /// 16 | /// Returns a copy of the double passed in. 17 | /// 18 | public object Copy(object o) => (double)o; 19 | 20 | /// 21 | /// Returns the value between start and end for the percentage passed in. 22 | /// 23 | public object GetIntermediateValue(object start, object end, double percentage) 24 | { 25 | return Utility.Interpolate((double)start, (double)end, percentage); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/FluentTransitions/ManagedTypes/ManagedFloat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.ManagedTypes 4 | { 5 | internal class ManagedFloat : IManagedType 6 | { 7 | /// 8 | /// Returns the type we're managing. 9 | /// 10 | public Type GetManagedType() => typeof(float); 11 | 12 | /// 13 | /// Returns a copy of the float passed in. 14 | /// 15 | public object Copy(object o) => (float)o; 16 | 17 | /// 18 | /// Returns the interpolated value for the percentage passed in. 19 | /// 20 | public object GetIntermediateValue(object start, object end, double percentage) 21 | { 22 | return Utility.Interpolate((float)start, (float)end, percentage); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/FluentTransitions/ManagedTypes/ManagedInt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.ManagedTypes 4 | { 5 | /// 6 | /// Manages transitions for int properties. 7 | /// 8 | internal class ManagedInt : IManagedType 9 | { 10 | /// 11 | /// Returns the type we are managing. 12 | /// 13 | public Type GetManagedType() => typeof(int); 14 | 15 | /// 16 | /// Returns a copy of the int passed in. 17 | /// 18 | public object Copy(object o) => (int)o; 19 | 20 | /// 21 | /// Returns the value between the start and end for the percentage passed in. 22 | /// 23 | public object GetIntermediateValue(object start, object end, double percentage) 24 | { 25 | return Utility.Interpolate((int)start, (int)end, percentage); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/FluentTransitions/ManagedTypes/ManagedString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.ManagedTypes 4 | { 5 | /// 6 | /// Manages transitions for strings. This doesn't make as much sense as transitions 7 | /// on other types, but I like the way it looks! 8 | /// 9 | internal class ManagedString : IManagedType 10 | { 11 | /// 12 | /// Returns the type we're managing. 13 | /// 14 | public Type GetManagedType() => typeof(string); 15 | 16 | /// 17 | /// Returns a copy of the string passed in. 18 | /// 19 | public object Copy(object o) => new string(((string)o).ToCharArray()); 20 | 21 | /// 22 | /// Returns an "interpolated" string. 23 | /// 24 | public object GetIntermediateValue(object start, object end, double percentage) 25 | { 26 | string startString = (string)start; 27 | string endString = (string)end; 28 | 29 | // We find the length of the interpolated string... 30 | int startLength = startString.Length; 31 | int endLength = endString.Length; 32 | int length = Utility.Interpolate(startLength, endLength, percentage); 33 | char[] result = new char[length]; 34 | 35 | // Now we assign the letters of the results by interpolating the 36 | // letters from the start and end strings... 37 | for (int i = 0; i < length; ++i) 38 | { 39 | // We get the start and end chars at this position... 40 | char startChar = 'a'; 41 | if (i < startLength) 42 | startChar = startString[i]; 43 | 44 | char cEnd = 'a'; 45 | if (i < endLength) 46 | cEnd = endString[i]; 47 | 48 | // We interpolate them... 49 | char interpolatedChar; 50 | if (cEnd == ' ') 51 | { 52 | // If the end character is a space we just show a space 53 | // regardless of interpolation. It looks better this way... 54 | interpolatedChar = ' '; 55 | } 56 | else 57 | { 58 | // The end character is not a space, so we interpolate... 59 | int startCharValue = Convert.ToInt32(startChar); 60 | int endCharValue = Convert.ToInt32(cEnd); 61 | int interpolatedValue = Utility.Interpolate(startCharValue, endCharValue, percentage); 62 | interpolatedChar = Convert.ToChar(interpolatedValue); 63 | } 64 | 65 | result[i] = interpolatedChar; 66 | } 67 | 68 | return new string(result); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Acceleration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// Manages transitions under constant acceleration from a standing start. 7 | /// 8 | public class Acceleration : IMethod 9 | { 10 | private readonly double _duration = 0.0; 11 | 12 | /// 13 | /// Alters the property values to their destination values from a standing start with accelerating intervals. 14 | /// 15 | /// The duration until the properties should have reached their destination values 16 | public Acceleration(TimeSpan duration) : this((int)duration.TotalMilliseconds) 17 | { 18 | } 19 | 20 | /// 21 | /// Alters the property values to their destination values from a standing start with accelerating intervals. 22 | /// 23 | /// The duration in milliseconds until the properties should have reached their destination values 24 | public Acceleration(int duration) 25 | { 26 | if (duration <= 0) 27 | throw new ArgumentOutOfRangeException(nameof(duration), "Transition time must be greater than zero."); 28 | 29 | _duration = duration; 30 | } 31 | 32 | /// 33 | /// Works out the percentage completed given the time passed in. 34 | /// This uses the formula: 35 | /// s = ut + 1/2at^2 36 | /// The initial velocity is 0, and the acceleration to get to 1.0 37 | /// at t=1.0 is 2, so the formula just becomes: 38 | /// s = t^2 39 | /// 40 | public void OnTimer(int time, out double percentage, out bool completed) 41 | { 42 | // We find the percentage time elapsed... 43 | double elapsed = time / _duration; 44 | percentage = elapsed * elapsed; 45 | if (elapsed >= 1.0) 46 | { 47 | percentage = 1.0; 48 | completed = true; 49 | } 50 | else 51 | { 52 | completed = false; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Bounce.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentTransitions.Methods 5 | { 6 | /// 7 | /// This transition bounces the property to a destination value and back to the 8 | /// original value. It is accelerated to the destination and then decelerated back 9 | /// as if being dropped with gravity and bouncing back against gravity. 10 | /// 11 | public class Bounce : Sequence 12 | { 13 | /// 14 | /// Bounces the property values to their destination values and back to the original ones. 15 | /// They are accelerated to the destination and then decelerated back as if being dropped with gravity and bouncing back against gravity. 16 | /// 17 | /// The duration until the properties should have reached their destination values 18 | public Bounce(TimeSpan duration) : this((int)duration.TotalMilliseconds) 19 | { 20 | } 21 | 22 | /// 23 | /// Bounces the property values to their destination values and back to the original ones. 24 | /// They are accelerated to the destination and then decelerated back as if being dropped with gravity and bouncing back against gravity. 25 | /// 26 | /// The duration in milliseconds until the properties should have reached their destination values 27 | public Bounce(int duration) 28 | { 29 | // We create a custom "user-defined" transition to do the work... 30 | var elements = new List 31 | { 32 | new TransitionElement(50, 100, InterpolationMethod.Accleration), 33 | new TransitionElement(100, 0, InterpolationMethod.Deceleration) 34 | }; 35 | base.Setup(elements, duration); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/CriticalDamping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// This transition animates with an exponential decay. This has a damping effect 7 | /// similar to the motion of a needle on an electomagnetically controlled dial. 8 | /// 9 | public class CriticalDamping : IMethod 10 | { 11 | private readonly double _duration = 0.0; 12 | 13 | /// 14 | /// Alters the property values with an exponential decay. 15 | /// This has a damping effect similar to the motion of a needle on an electomagnetically controlled dial. 16 | /// 17 | /// The duration until the properties should have reached their destination values 18 | public CriticalDamping(TimeSpan duration) : this((int)duration.TotalMilliseconds) 19 | { 20 | } 21 | 22 | /// 23 | /// Alters the property values with an exponential decay. 24 | /// This has a damping effect similar to the motion of a needle on an electomagnetically controlled dial. 25 | /// 26 | /// The duration in milliseconds until the properties should have reached their destination values 27 | public CriticalDamping(int duration) 28 | { 29 | if (duration <= 0) 30 | throw new ArgumentOutOfRangeException(nameof(duration), "Transition time must be greater than zero."); 31 | 32 | _duration = duration; 33 | } 34 | 35 | /// 36 | /// Works out the percentage completed given the time passed in. 37 | /// 38 | public void OnTimer(int time, out double percentage, out bool completed) 39 | { 40 | // We find the percentage time elapsed... 41 | double elapsed = time / _duration; 42 | percentage = (1.0 - Math.Exp(-1.0 * elapsed * 5)) / 0.993262053; 43 | 44 | if (elapsed >= 1.0) 45 | { 46 | percentage = 1.0; 47 | completed = true; 48 | } 49 | else 50 | { 51 | completed = false; 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Deceleration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// Manages a transition starting from a high speed and decelerating to zero by 7 | /// the end of the transition. 8 | /// 9 | public class Deceleration : IMethod 10 | { 11 | private readonly double _duration = 0.0; 12 | 13 | /// 14 | /// Alters the property values starting from a high speed and decelerating to zero by the end of the transition. 15 | /// 16 | /// The duration until the properties should have reached their destination values 17 | public Deceleration(TimeSpan duration) : this((int)duration.TotalMilliseconds) 18 | { 19 | } 20 | 21 | /// 22 | /// Alters the property values starting from a high speed and decelerating to zero by the end of the transition. 23 | /// 24 | /// The duration in milliseconds until the properties should have reached their destination values 25 | public Deceleration(int duration) 26 | { 27 | if (duration <= 0) 28 | throw new ArgumentOutOfRangeException(nameof(duration), "Transition time must be greater than zero."); 29 | 30 | _duration = duration; 31 | } 32 | 33 | /// 34 | /// Works out the percentage completed given the time passed in. 35 | /// This uses the formula: 36 | /// s = ut + 1/2at^2 37 | /// The initial velocity is 2, and the acceleration to get to 1.0 38 | /// at t=1.0 is -2, so the formula becomes: 39 | /// s = t(2-t) 40 | /// 41 | public void OnTimer(int time, out double percentage, out bool completed) 42 | { 43 | // We find the percentage time elapsed... 44 | double elapsed = time / _duration; 45 | percentage = elapsed * (2.0 - elapsed); 46 | if (elapsed >= 1.0) 47 | { 48 | percentage = 1.0; 49 | completed = true; 50 | } 51 | else 52 | { 53 | completed = false; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/EaseInEaseOut.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// Manages an ease-in-ease-out transition. This accelerates during the first 7 | /// half of the transition, and then decelerates during the second half. 8 | /// 9 | public class EaseInEaseOut : IMethod 10 | { 11 | private readonly double _duration = 0.0; 12 | 13 | /// 14 | /// Alters the property values in an ease-in-ease-out transition. 15 | /// This accelerates during the first half of the transition, and then decelerates during the second half. 16 | /// 17 | /// The duration until the properties should have reached their destination values 18 | public EaseInEaseOut(TimeSpan duration) : this((int)duration.TotalMilliseconds) 19 | { 20 | } 21 | 22 | /// 23 | /// Alters the property values in an ease-in-ease-out transition. 24 | /// This accelerates during the first half of the transition, and then decelerates during the second half. 25 | /// 26 | /// The duration in milliseconds until the properties should have reached their destination values 27 | public EaseInEaseOut(int duration) 28 | { 29 | if (duration <= 0) 30 | throw new ArgumentOutOfRangeException(nameof(duration), "Transition time must be greater than zero."); 31 | 32 | _duration = duration; 33 | } 34 | 35 | /// 36 | /// Works out the percentage completed given the time passed in. 37 | /// This uses the formula: 38 | /// s = ut + 1/2at^2 39 | /// We accelerate as at the rate needed (a=4) to get to 0.5 at t=0.5, and 40 | /// then decelerate at the same rate to end up at 1.0 at t=1.0. 41 | /// 42 | public void OnTimer(int time, out double percentage, out bool completed) 43 | { 44 | // We find the percentage time elapsed... 45 | double elapsed = time / _duration; 46 | percentage = Utility.ConvertLinearToEaseInEaseOut(elapsed); 47 | 48 | if (elapsed >= 1.0) 49 | { 50 | percentage = 1.0; 51 | completed = true; 52 | } 53 | else 54 | { 55 | completed = false; 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/EaseWithFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// Allows natural transitions with custom or predefined easing functions. 7 | /// Easing functions are mathematical functions that are used to interpolate values between two endpoints 8 | /// usually with non-linear results. 9 | /// Thanks to Mauro Sampietro for providing the easing functions on https://www.codeproject.com/Articles/827808/Control-Animation-in-Winforms. 10 | /// 11 | public class EaseWithFunction : IMethod 12 | { 13 | private readonly double _duration = 0.0; 14 | 15 | /// 16 | /// Allows natural transitions with custom or predefined easing functions. 17 | /// Easing functions are mathematical functions that are used to interpolate values between two endpoints 18 | /// usually with non-linear results. 19 | /// 20 | /// 21 | /// The function to interpolate values with. See "" for prebuilt easing functions. 22 | /// Powererd by Mauro Sampietro (https://www.codeproject.com/Articles/827808/Control-Animation-in-Winforms). 23 | /// 24 | /// The duration until the properties should have reached their destination values 25 | public EaseWithFunction(EasingFunction easingFunction, TimeSpan duration) : this(easingFunction, (int)duration.TotalMilliseconds) 26 | { 27 | EasingFunction = easingFunction ?? throw new ArgumentNullException(nameof(easingFunction)); 28 | } 29 | 30 | /// 31 | /// Allows natural transitions with custom or predefined easing functions. 32 | /// Easing functions are mathematical functions that are used to interpolate values between two endpoints 33 | /// usually with non-linear results. 34 | /// 35 | /// 36 | /// The function to interpolate values with. See "" for prebuilt easing functions. 37 | /// Powererd by Mauro Sampietro (https://www.codeproject.com/Articles/827808/Control-Animation-in-Winforms). 38 | /// 39 | /// The duration in milliseconds until the properties should have reached their destination values 40 | public EaseWithFunction(EasingFunction easingFunction, int duration) 41 | { 42 | if (duration <= 0) 43 | throw new ArgumentOutOfRangeException(nameof(duration), "Transition time must be greater than zero."); 44 | 45 | EasingFunction = easingFunction ?? throw new ArgumentNullException(nameof(easingFunction)); 46 | 47 | _duration = duration; 48 | } 49 | 50 | /// 51 | /// Works out the percentage completed given the time passed in. 52 | /// 53 | public void OnTimer(int time, out double percentage, out bool completed) 54 | { 55 | percentage = EasingFunction(time, 0, 100, _duration) / 100; 56 | completed = time >= _duration; 57 | } 58 | 59 | /// 60 | /// Gets the easing function to interpolate values with 61 | /// 62 | public EasingFunction EasingFunction { get; } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Flash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentTransitions.Methods 5 | { 6 | /// 7 | /// This transition type 'flashes' the properties a specified number of times, ending 8 | /// up by reverting them to their initial values. You specify the number of bounces and 9 | /// the length of each bounce. 10 | /// 11 | public class Flash : Sequence 12 | { 13 | /// 14 | /// Flashes the property values a specified number of times, ending up by reverting them to their initial values. 15 | /// 16 | /// The number of flashes to animate 17 | /// The duration of each flash 18 | public Flash(int numberOfFlashes, TimeSpan durationOfEachFlash) : this(numberOfFlashes, (int)durationOfEachFlash.TotalMilliseconds) 19 | { 20 | } 21 | 22 | /// 23 | /// Flashes the property values a specified number of times, ending up by reverting them to their initial values. 24 | /// 25 | /// The number of flashes to animate 26 | /// The duration of each flash in milliseconds 27 | public Flash(int numberOfFlashes, int durationOfEachFlash) 28 | { 29 | // This class is derived from the user-defined transition type. 30 | // Here we set up a custom "user-defined" transition for the 31 | // number of flashes passed in... 32 | double flashInterval = 100.0 / numberOfFlashes; 33 | 34 | // We set up the elements of the user-defined transition... 35 | IList elements = new List(); 36 | for (int i = 0; i < numberOfFlashes; ++i) 37 | { 38 | // Each flash consists of two elements: one going to the destination value, 39 | // and another going back again... 40 | double flashStartTime = i * flashInterval; 41 | double flashEndTime = flashStartTime + flashInterval; 42 | double flashMidPoint = (flashStartTime + flashEndTime) / 2.0; 43 | elements.Add(new TransitionElement(flashMidPoint, 100, InterpolationMethod.EaseInEaseOut)); 44 | elements.Add(new TransitionElement(flashEndTime, 0, InterpolationMethod.EaseInEaseOut)); 45 | } 46 | 47 | base.Setup(elements, durationOfEachFlash * numberOfFlashes); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/IMethod.cs: -------------------------------------------------------------------------------- 1 | namespace FluentTransitions 2 | { 3 | /// 4 | /// Interface for all transition methods 5 | /// 6 | public interface IMethod 7 | { 8 | /// 9 | /// Called by the Transition framework when its timer ticks to pass in the 10 | /// time (in ms) since the transition started. 11 | /// 12 | /// You should return (in an out parameter) the percentage movement towards 13 | /// the destination value for the time passed in. Note: this does not need to 14 | /// be a smooth transition from 0% to 100%. You can overshoot with values 15 | /// greater than 100% or undershoot if you need to (for example, to have some 16 | /// form of "elasticity"). 17 | /// 18 | /// The percentage should be returned as (for example) 0.1 for 10%. 19 | /// 20 | /// You should return (in an out parameter) whether the transition has completed. 21 | /// (This may not be at the same time as the percentage has moved to 100%.) 22 | /// 23 | /// The time that has passed 24 | /// Returns the percentage how far the transition has come 25 | /// Returns whether the transition has completed or not 26 | void OnTimer(int time, out double percentage, out bool completed); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Linear.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// This class manages a linear transition. The percentage complete for the transition 7 | /// increases linearly with time. 8 | /// 9 | public class Linear : IMethod 10 | { 11 | private readonly double _duration = 0.0; 12 | 13 | /// 14 | /// Alters the property values in a linear way until they reach their destination values. 15 | /// 16 | /// The duration until the properties should have reached their destination values 17 | public Linear(TimeSpan duration) : this((int)duration.TotalMilliseconds) 18 | { 19 | } 20 | 21 | /// 22 | /// Alters the property values in a linear way until they reach their destination values. 23 | /// 24 | /// The duration in milliseconds until the properties should have reached their destination values 25 | public Linear(int duration) 26 | { 27 | if (duration <= 0) 28 | throw new ArgumentOutOfRangeException(nameof(duration), "Transition time must be greater than zero."); 29 | 30 | _duration = duration; 31 | } 32 | 33 | /// 34 | /// Works out the percentage completed given the time passed in. 35 | /// 36 | public void OnTimer(int time, out double percentage, out bool completed) 37 | { 38 | percentage = (time / _duration); 39 | if (percentage >= 1.0) 40 | { 41 | percentage = 1.0; 42 | completed = true; 43 | } 44 | else 45 | { 46 | completed = false; 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Rubberband.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// This transition interpolates values with EasingFunctions.BackEaseOut to mimic the behavior of a rubber band stopping a movement and pulling it back. 7 | /// 8 | public class Rubberband : EaseWithFunction 9 | { 10 | /// 11 | /// Interpolates values with EasingFunctions.BackEaseOut to mimic the behavior of a rubber band stopping a movement and pulling it back. 12 | /// 13 | /// The duration until the properties should have reached their destination values 14 | public Rubberband(TimeSpan duration) : this((int)duration.TotalMilliseconds) 15 | { 16 | } 17 | 18 | /// 19 | /// Interpolates values with EasingFunctions.BackEaseOut to mimic the behavior of a rubber band stopping a movement and pulling it back. 20 | /// 21 | /// The duration in milliseconds until the properties should have reached their destination values 22 | public Rubberband(int duration) : base(EasingFunctions.BackEaseOut, duration) 23 | { 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Sequence.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentTransitions.Methods 5 | { 6 | /// 7 | /// Allows the creation of user-defined transition methods, specified by a list of individual transition elements. 8 | /// 9 | /// Each of these defines: 10 | /// End time, End value, Interpolation method 11 | /// 12 | /// For example, say you want to make a bouncing effect with a decay: 13 | /// 14 | /// EndTime% EndValue% Interpolation 15 | /// -------- --------- ------------- 16 | /// 50 100 Acceleration 17 | /// 75 50 Deceleration 18 | /// 85 100 Acceleration 19 | /// 91 75 Deceleration 20 | /// 95 100 Acceleration 21 | /// 98 90 Deceleration 22 | /// 100 100 Acceleration 23 | /// 24 | /// The time values are expressed as a percentage of the overall transition time. This 25 | /// means that you can create a user-defined transition-type and then use it for transitions 26 | /// of different lengths. 27 | /// 28 | /// The values are percentages of the values between the start and end values of the properties 29 | /// being animated in the transitions. 0% is the start value and 100% is the end value. 30 | /// 31 | /// The interpolation is one of the values from the InterpolationMethod enum. 32 | /// 33 | /// So the example above accelerates to the destination (as if under gravity) by 34 | /// t=50%, then bounces back up to half the initial height by t=75%, slowing down 35 | /// (as if against gravity) before falling down again and bouncing to decreasing 36 | /// heights each time. 37 | /// 38 | /// 39 | public class Sequence : IMethod 40 | { 41 | // The collection of elements that make up the transition... 42 | private IList _elements = null; 43 | 44 | // The total transition time... 45 | private double _duration = 0.0; 46 | 47 | // The element that we are currently in (i.e. the current time within this element)... 48 | private int _currentElement = 0; 49 | 50 | /// 51 | /// Allows the creation of user-defined transition methods, specified by a list of individual transition elements. 52 | /// 53 | public Sequence() 54 | { 55 | } 56 | 57 | /// 58 | /// Allows the creation of user-defined transition methods, specified by a list of individual transition elements. 59 | /// 60 | /// The elements to process during the transition 61 | /// The duration until the properties should have reached their destination values 62 | public Sequence(IList elements, TimeSpan duration) : this(elements, (int)duration.TotalMilliseconds) 63 | { 64 | } 65 | 66 | /// 67 | /// Allows the creation of user-defined transition methods, specified by a list of individual transition elements. 68 | /// 69 | /// The elements to process during the transition 70 | /// The duration in milliseconds until the properties should have reached their destination values 71 | public Sequence(IList elements, int duration) 72 | { 73 | Setup(elements, duration); 74 | } 75 | 76 | /// 77 | /// Sets up the transitions. 78 | /// 79 | public void Setup(IList elements, int duration) 80 | { 81 | _elements = elements; 82 | _duration = duration; 83 | 84 | // We check that the elements list has some members... 85 | if (elements?.Count == 0) 86 | throw new ArgumentException($"The list of elements passed to the constructor of {nameof(Sequence)} had zero elements. It must have at least one element."); 87 | } 88 | 89 | /// 90 | /// Called to find the value for the movement of properties for the time passed in. 91 | /// 92 | public void OnTimer(int time, out double percentage, out bool completed) 93 | { 94 | double transitionTimeFraction = time / _duration; 95 | 96 | // We find the information for the element that we are currently processing... 97 | GetElementInfo(transitionTimeFraction, out double elementStartTime, out double elementEndTime, out double elementStartValue, out double elementEndValue, out InterpolationMethod interpolationMethod); 98 | 99 | // We find how far through this element we are as a fraction... 100 | double elementInterval = elementEndTime - elementStartTime; 101 | double elementElapsedTime = transitionTimeFraction - elementStartTime; 102 | double elementTimeFraction = elementElapsedTime / elementInterval; 103 | 104 | // We convert the time-fraction to an fraction of the movement within the 105 | // element using the interpolation method... 106 | double elementDistance; 107 | switch (interpolationMethod) 108 | { 109 | case InterpolationMethod.Linear: 110 | elementDistance = elementTimeFraction; 111 | break; 112 | 113 | case InterpolationMethod.Accleration: 114 | elementDistance = Utility.ConvertLinearToAcceleration(elementTimeFraction); 115 | break; 116 | 117 | case InterpolationMethod.Deceleration: 118 | elementDistance = Utility.ConvertLinearToDeceleration(elementTimeFraction); 119 | break; 120 | 121 | case InterpolationMethod.EaseInEaseOut: 122 | elementDistance = Utility.ConvertLinearToEaseInEaseOut(elementTimeFraction); 123 | break; 124 | 125 | default: 126 | throw new InvalidOperationException($"Interpolation method not handled: {interpolationMethod}"); 127 | } 128 | 129 | // We now know how far through the transition we have moved, so we can interpolate 130 | // the start and end values by this amount... 131 | percentage = Utility.Interpolate(elementStartValue, elementEndValue, elementDistance); 132 | 133 | // Has the transition completed? 134 | if (time >= _duration) 135 | { 136 | // The transition has completed, so we make sure that 137 | // it is at its final value... 138 | completed = true; 139 | percentage = elementEndValue; 140 | } 141 | else 142 | { 143 | completed = false; 144 | } 145 | } 146 | 147 | /// 148 | /// Returns the element info for the time-fraction passed in. 149 | /// 150 | private void GetElementInfo(double timeFraction, out double startTime, out double endTime, out double startValue, out double endValue, out InterpolationMethod interpolationMethod) 151 | { 152 | // We need to return the start and end values for the current element. So this 153 | // means finding the element for the time passed in as well as the previous element. 154 | 155 | // We hold the 'current' element as a hint. This was in fact the 156 | // element used the last time this function was called. In most cases 157 | // it will be the same one again, but it may have moved to a subsequent 158 | // on (maybe even skipping elements if enough time has passed)... 159 | int count = _elements.Count; 160 | for (; _currentElement < count; ++_currentElement) 161 | { 162 | TransitionElement element = _elements[_currentElement]; 163 | double elementEndTime = element.EndTime / 100.0; 164 | if (timeFraction < elementEndTime) 165 | { 166 | break; 167 | } 168 | } 169 | 170 | // If we have gone past the last element, we just use the last element... 171 | if (_currentElement == count) 172 | { 173 | _currentElement = count - 1; 174 | } 175 | 176 | // We find the start values. These come from the previous element, except in the 177 | // case where we are currently in the first element, in which case they are zeros... 178 | startTime = 0.0; 179 | startValue = 0.0; 180 | if (_currentElement > 0) 181 | { 182 | var previousElement = _elements[_currentElement - 1]; 183 | startTime = previousElement.EndTime / 100.0; 184 | startValue = previousElement.EndValue / 100.0; 185 | } 186 | 187 | // We get the end values from the current element... 188 | var currentElement = _elements[_currentElement]; 189 | endTime = currentElement.EndTime / 100.0; 190 | endValue = currentElement.EndValue / 100.0; 191 | interpolationMethod = currentElement.InterpolationMethod; 192 | } 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/Spring.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentTransitions.Methods 4 | { 5 | /// 6 | /// This transition interpolates values with EasingFunctions.ElasticEaseOut to mimic the behavior of a loaded spring. 7 | /// 8 | public class Spring : EaseWithFunction 9 | { 10 | /// 11 | /// Interpolates values with EasingFunctions.ElasticEaseOut to mimic the behavior of a loaded spring. 12 | /// 13 | /// The duration until the properties should have reached their destination values 14 | public Spring(TimeSpan duration) : this((int)duration.TotalMilliseconds) 15 | { 16 | } 17 | 18 | /// 19 | /// Interpolates values with EasingFunctions.ElasticEaseOut to mimic the behavior of a loaded spring. 20 | /// 21 | /// The duration in milliseconds until the properties should have reached their destination values 22 | public Spring(int duration) : base(EasingFunctions.ElasticEaseOut, duration) 23 | { 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/FluentTransitions/Methods/ThrowAndCatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentTransitions.Methods 5 | { 6 | /// 7 | /// This transition bounces the property to a destination value and back to the 8 | /// original value. It is decelerated to the destination and then acclerated back 9 | /// as if being thrown against gravity and then descending back with gravity. 10 | /// 11 | public class ThrowAndCatch : Sequence 12 | { 13 | /// 14 | /// Bounces the property values to their destination values and back to the original ones. 15 | /// They are decelerated to the destination and then acclerated back as if being thrown against gravity and then descending back with gravity. 16 | /// 17 | /// The duration until the properties should have reached their destination values 18 | public ThrowAndCatch(TimeSpan duration) : this((int)duration.TotalMilliseconds) 19 | { 20 | } 21 | 22 | /// 23 | /// Bounces the property values to their destination values and back to the original ones. 24 | /// They are decelerated to the destination and then acclerated back as if being thrown against gravity and then descending back with gravity. 25 | /// 26 | /// The duration in milliseconds until the properties should have reached their destination values 27 | public ThrowAndCatch(int duration) 28 | { 29 | // We create a custom "user-defined" transition to do the work... 30 | var elements = new List 31 | { 32 | new TransitionElement(50, 100, InterpolationMethod.Deceleration), 33 | new TransitionElement(100, 0, InterpolationMethod.Accleration) 34 | }; 35 | base.Setup(elements, duration); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/FluentTransitions/Transition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Diagnostics; 5 | using System.ComponentModel; 6 | using System.Windows.Forms; 7 | using FluentTransitions.ManagedTypes; 8 | 9 | namespace FluentTransitions 10 | { 11 | /// 12 | /// Lets you perform animated transitions of properties on arbitrary objects. These 13 | /// will often be transitions of UI properties, for example an animated fade-in of 14 | /// a UI object, or an animated move of a UI object from one position to another. 15 | /// 16 | /// Each transition can simulataneously change multiple properties, including properties 17 | /// across multiple objects. 18 | /// 19 | /// Example transition 20 | /// ------------------ 21 | /// a. var transition = new Transition(new Linear(500)); 22 | /// b. transition.add(form1, nameof(Width), 500); 23 | /// c. transition.add(form1, nameof(BackColor), Color.Red); 24 | /// d. transition.run(); 25 | /// 26 | /// Line a: Creates a new transition. You specify the transition method. 27 | /// 28 | /// Lines b. and c: Set the destination values of the properties you are animating. 29 | /// 30 | /// Line d: Starts the transition. 31 | /// 32 | /// Transition methods 33 | /// ------------------ 34 | /// TransitionMethod objects specify how the transition is made. Examples include 35 | /// linear transition, ease-in-ease-out and so on. Different transition methods may 36 | /// need different parameters. 37 | /// 38 | /// 39 | public class Transition 40 | { 41 | /// 42 | /// Event raised when the transition hass completed. 43 | /// 44 | public event EventHandler TransitionCompleted; 45 | 46 | // Helps us find the time interval from the time the transition starts to each timer tick... 47 | private readonly Stopwatch _stopwatch = new Stopwatch(); 48 | 49 | // A map of Type info to IManagedType objects. These are all the types that we 50 | // know how to perform transitions on... 51 | private static readonly IDictionary _mapManagedTypes = new Dictionary(); 52 | 53 | // The transition method used by this transition... 54 | private readonly IMethod _method = null; 55 | 56 | /// 57 | /// You should register all managed-types here. 58 | /// 59 | static Transition() 60 | { 61 | RegisterType(new ManagedInt()); 62 | RegisterType(new ManagedFloat()); 63 | RegisterType(new ManagedDouble()); 64 | RegisterType(new ManagedColor()); 65 | RegisterType(new ManagedString()); 66 | } 67 | 68 | /// 69 | /// Includes a target object and interpolates its property value to the destination value 70 | /// 71 | /// The target object to include to the transition 72 | /// The name of the property to animate 73 | /// The destination value which should be reached at the end of the transition 74 | /// 75 | public static TransitionDefinition With(object target, string propertyName, object destinationValue) 76 | { 77 | return new TransitionDefinition() 78 | .With(target, propertyName, destinationValue); 79 | } 80 | 81 | /// 82 | /// Constructor. You pass in the object that holds the properties 83 | /// that you are performing transitions on. 84 | /// 85 | public Transition(IMethod transitionMethod) 86 | { 87 | _method = transitionMethod; 88 | } 89 | 90 | /// 91 | /// Creates and immediately runs a transition on the property passed in. 92 | /// 93 | internal static void Run(object target, string propertyName, object destinationValue, IMethod transitionMethod) 94 | { 95 | var transition = new Transition(transitionMethod); 96 | transition.Add(target, propertyName, destinationValue); 97 | transition.Run(); 98 | } 99 | 100 | /// 101 | /// Sets the property passed in to the initial value passed in, then creates and 102 | /// immediately runs a transition on it. 103 | /// 104 | internal static void Run(object target, string propertyName, object initialValue, object destinationValue, IMethod transitionMethod) 105 | { 106 | Utility.SetValue(target, propertyName, initialValue); 107 | Run(target, propertyName, destinationValue, transitionMethod); 108 | } 109 | 110 | /// 111 | /// Creates a TransitionChain and runs it. 112 | /// 113 | public static void RunChain(params Transition[] transitions) 114 | { 115 | _ = new TransitionChain(transitions); 116 | } 117 | 118 | /// 119 | /// Adds a property that should be animated as part of this transition. 120 | /// 121 | public Transition Add(object target, string propertyName, object destinationValue) 122 | { 123 | // We get the property info... 124 | var targetType = target.GetType(); 125 | var propertyInfo = targetType.GetProperty(propertyName); 126 | if (propertyInfo == null) 127 | throw new InvalidOperationException($"Object: {target} does not have the property: {propertyName}"); 128 | 129 | // We check that we support the property type... 130 | var propertyType = propertyInfo.PropertyType; 131 | if (!_mapManagedTypes.ContainsKey(propertyType)) 132 | throw new InvalidOperationException($"Transition does not handle properties of type: {propertyType}"); 133 | 134 | // We can only transition properties that are both getable and setable... 135 | if (!propertyInfo.CanRead || !propertyInfo.CanWrite) 136 | throw new InvalidOperationException($"Property is not both getable and setable: {propertyName}"); 137 | 138 | var managedType = _mapManagedTypes[propertyType]; 139 | 140 | // We can manage this type, so we store the information for the 141 | // transition of this property... 142 | var info = new TransitionedPropertyInfo 143 | { 144 | EndValue = destinationValue, 145 | Target = target, 146 | PropertyInfo = propertyInfo, 147 | ManagedType = managedType 148 | }; 149 | 150 | lock (_lock) 151 | { 152 | TransitionedProperties.Add(info); 153 | } 154 | 155 | return this; 156 | } 157 | 158 | /// 159 | /// Starts the transition. 160 | /// 161 | public void Run() 162 | { 163 | // We find the current start values for the properties we 164 | // are animating... 165 | foreach (TransitionedPropertyInfo info in TransitionedProperties) 166 | { 167 | object value = info.PropertyInfo.GetValue(info.Target, null); 168 | info.StartValue = info.ManagedType.Copy(value); 169 | } 170 | 171 | // We start the stopwatch. We use this when the timer ticks to measure 172 | // how long the transition has been runnning for... 173 | _stopwatch.Reset(); 174 | _stopwatch.Start(); 175 | 176 | // We register this transition with the transition manager... 177 | TransitionManager.GetInstance().Register(this); 178 | } 179 | 180 | /// 181 | /// We remove the property with the info passed in from the transition. 182 | /// 183 | internal void RemoveProperty(TransitionedPropertyInfo info) 184 | { 185 | lock (_lock) 186 | { 187 | TransitionedProperties.Remove(info); 188 | } 189 | } 190 | 191 | /// 192 | /// Called when the transition timer ticks. 193 | /// 194 | internal void OnTimer() 195 | { 196 | // When the timer ticks we: 197 | // a. Find the elapsed time since the transition started. 198 | // b. Work out the percentage movement for the properties we're managing. 199 | // c. Find the actual values of each property, and set them. 200 | 201 | // a. 202 | var elapsedTime = (int)_stopwatch.ElapsedMilliseconds; 203 | 204 | // b. 205 | _method.OnTimer(elapsedTime, out double percentage, out bool completed); 206 | 207 | // We take a copy of the list of properties we are transitioning, as 208 | // they can be changed by another thread while this method is running... 209 | var listTransitionedProperties = new List(); 210 | lock (_lock) 211 | { 212 | foreach (TransitionedPropertyInfo info in TransitionedProperties) 213 | { 214 | listTransitionedProperties.Add(info.Copy()); 215 | } 216 | } 217 | 218 | // c. 219 | foreach (TransitionedPropertyInfo info in listTransitionedProperties) 220 | { 221 | // We get the current value for this property... 222 | object value = info.ManagedType.GetIntermediateValue(info.StartValue, info.EndValue, percentage); 223 | 224 | // We set it... 225 | var args = new PropertyUpdateEventArgs(info.Target, info.PropertyInfo, value); 226 | SetProperty(this, args); 227 | } 228 | 229 | // Has the transition completed? 230 | if (completed) 231 | { 232 | // We stop the stopwatch and the timer... 233 | _stopwatch.Stop(); 234 | 235 | // We raise an event to notify any observers that the transition has completed... 236 | Utility.RaiseEvent(TransitionCompleted, this, EventArgs.Empty); 237 | } 238 | } 239 | 240 | /// 241 | /// Sets a property on the object passed in to the value passed in. This method 242 | /// invokes itself on the GUI thread if the property is being invoked on a GUI 243 | /// object. 244 | /// 245 | private void SetProperty(object sender, PropertyUpdateEventArgs args) 246 | { 247 | try 248 | { 249 | // If the target is a control that has been disposed then we don't 250 | // try to update its properties. This can happen if the control is 251 | // on a form that has been closed while the transition is running... 252 | if (IsDisposed(args.Target)) 253 | return; 254 | 255 | if (args.Target is ISynchronizeInvoke invokeTarget && invokeTarget.InvokeRequired) 256 | { 257 | // There is some history behind the next two lines, which is worth 258 | // going through to understand why they are the way they are. 259 | 260 | // Initially we used BeginInvoke without the subsequent WaitOne for 261 | // the result. A transition could involve a large number of updates 262 | // to a property, and as this call was asynchronous it would send a 263 | // large number of updates to the UI thread. These would queue up at 264 | // the GUI thread and mean that the UI could be some way behind where 265 | // the transition was. 266 | 267 | // The line was then changed to the blocking Invoke call instead. This 268 | // meant that the transition only proceded at the pace that the GUI 269 | // could process it, and the UI was not overloaded with "old" updates. 270 | 271 | // However, in some circumstances Invoke could block and lock up the 272 | // Transitions background thread. In particular, this can happen if the 273 | // control that we are trying to update is in the process of being 274 | // disposed - for example, it is on a form that is being closed. See 275 | // here for details: 276 | // http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/7d2c941a-0016-431a-abba-67c5d5dac6a5 277 | 278 | // To solve this, we use a combination of the two earlier approaches. 279 | // We use BeginInvoke as this does not block and lock up, even if the 280 | // underlying object is being disposed. But we do want to wait to give 281 | // the UI a chance to process the update. So what we do is to do the 282 | // asynchronous BeginInvoke, but then wait (with a short timeout) for 283 | // it to complete. 284 | var asyncResult = invokeTarget.BeginInvoke(new EventHandler(SetProperty), new object[] { sender, args }); 285 | asyncResult.AsyncWaitHandle.WaitOne(50); 286 | } 287 | else 288 | { 289 | // We are on the correct thread, so we update the property... 290 | args.PropertyInfo.SetValue(args.Target, args.Value, null); 291 | } 292 | } 293 | catch 294 | { 295 | // We silently catch any exceptions. These could be things like 296 | // bounds exceptions when setting properties. 297 | } 298 | } 299 | 300 | /// 301 | /// Returns true if the object passed in is a Control and is disposed 302 | /// or in the process of disposing. (If this is the case, we don't want 303 | /// to make any changes to its properties.) 304 | /// 305 | private bool IsDisposed(object target) 306 | { 307 | // Is the object passed in a Control? 308 | if (!(target is Control controlTarget)) 309 | return false; 310 | 311 | // Is it disposed or disposing? 312 | return controlTarget.IsDisposed || controlTarget.Disposing; 313 | } 314 | 315 | /// 316 | /// Registers a transition-type. We hold them in a map. 317 | /// 318 | private static void RegisterType(IManagedType transitionType) 319 | { 320 | var type = transitionType.GetManagedType(); 321 | _mapManagedTypes[type] = transitionType; 322 | } 323 | 324 | /// 325 | /// Property that returns a list of information about each property managed 326 | /// by this transition. 327 | /// 328 | internal IList TransitionedProperties { get; } = new List(); 329 | 330 | // Holds information about one property on one taregt object that we are performing 331 | // a transition on... 332 | internal class TransitionedPropertyInfo 333 | { 334 | public object StartValue; 335 | public object EndValue; 336 | public object Target; 337 | public PropertyInfo PropertyInfo; 338 | public IManagedType ManagedType; 339 | 340 | public TransitionedPropertyInfo Copy() 341 | { 342 | return new TransitionedPropertyInfo 343 | { 344 | StartValue = StartValue, 345 | EndValue = EndValue, 346 | Target = Target, 347 | PropertyInfo = PropertyInfo, 348 | ManagedType = ManagedType 349 | }; 350 | } 351 | } 352 | 353 | // Event args used for the event we raise when updating a property... 354 | private class PropertyUpdateEventArgs : EventArgs 355 | { 356 | public PropertyUpdateEventArgs(object t, PropertyInfo pi, object v) 357 | { 358 | Target = t; 359 | PropertyInfo = pi; 360 | Value = v; 361 | } 362 | 363 | public object Target; 364 | 365 | public PropertyInfo PropertyInfo; 366 | 367 | public object Value; 368 | } 369 | 370 | // An object used to lock the list of transitioned properties, as it can be 371 | // accessed by multiple threads... 372 | private readonly object _lock = new object(); 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /src/FluentTransitions/TransitionChain.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace FluentTransitions 5 | { 6 | internal class TransitionChain 7 | { 8 | // The list of transitions in the chain... 9 | private readonly LinkedList _listTransitions = new LinkedList(); 10 | 11 | public TransitionChain(params Transition[] transitions) 12 | { 13 | // We store the list of transitions... 14 | foreach (Transition transition in transitions) 15 | _listTransitions.AddLast(transition); 16 | 17 | // We start running them... 18 | RunNextTransition(); 19 | } 20 | 21 | /// 22 | /// Runs the next transition in the list. 23 | /// 24 | private void RunNextTransition() 25 | { 26 | if (_listTransitions.Count == 0) 27 | return; 28 | 29 | // We find the next transition and run it. We also register 30 | // for its completed event, so that we can start the next transition 31 | // when this one completes... 32 | var nextTransition = _listTransitions.First.Value; 33 | nextTransition.TransitionCompleted += OnTransitionCompleted; 34 | nextTransition.Run(); 35 | } 36 | 37 | /// 38 | /// Called when the transition we have just run has completed. 39 | /// 40 | private void OnTransitionCompleted(object sender, EventArgs e) 41 | { 42 | // We unregister from the completed event... 43 | var transition = (Transition)sender; 44 | transition.TransitionCompleted -= OnTransitionCompleted; 45 | 46 | // We remove the completed transition from our collection, and 47 | // run the next one... 48 | _listTransitions.RemoveFirst(); 49 | RunNextTransition(); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/FluentTransitions/TransitionDefinition.cs: -------------------------------------------------------------------------------- 1 | using FluentTransitions.Methods; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Threading; 5 | using System.Windows.Forms; 6 | 7 | namespace FluentTransitions 8 | { 9 | /// 10 | /// The transition definition providing fluent syntax to build and run transitions 11 | /// 12 | public class TransitionDefinition 13 | { 14 | private readonly List _targetPropertyDestinations = new List(); 15 | private Action _completionHook; 16 | 17 | /// 18 | /// Includes a target object and interpolates its property value to the destination value 19 | /// 20 | /// The target object to include to the transition 21 | /// The name of the property to animate 22 | /// The destination value which should be reached at the end of the transition 23 | /// 24 | public TransitionDefinition With(object target, string propertyName, object destinationValue) 25 | { 26 | _targetPropertyDestinations.Add(new TargetPropertyDestination 27 | { 28 | Target = target, 29 | PropertyName = propertyName, 30 | DestinationValue = destinationValue 31 | }); 32 | 33 | return this; 34 | } 35 | 36 | 37 | /// 38 | /// Defines a hook that gets called as soon as the transition completes 39 | /// 40 | /// A delegate which gets called as soon as the transition did complete 41 | public TransitionDefinition HookOnCompletion(Action hookDelegate) 42 | { 43 | _completionHook = hookDelegate; 44 | return this; 45 | } 46 | 47 | /// 48 | /// Defines a hook that gets called as soon as the transition completes 49 | /// 50 | /// Any control to be used to invoke the hookDelegate to the UI thread 51 | /// A delegate which gets called as soon as the transition did complete 52 | public TransitionDefinition HookOnCompletionInUiThread(Control controlToInvoke, Action hookDelegate) 53 | { 54 | if (hookDelegate is object) 55 | _completionHook = () => controlToInvoke.BeginInvoke((Action)(() => hookDelegate.Invoke())); 56 | else 57 | _completionHook = null; 58 | 59 | return this; 60 | } 61 | 62 | /// 63 | /// Defines a hook that gets called as soon as the transition completes 64 | /// 65 | /// The synchronization context used to invoke the hookDelegate to the UI thread 66 | /// A delegate which gets called as soon as the transition did complete 67 | public TransitionDefinition HookOnCompletionInUiThread(SynchronizationContext synchronizationContext, Action hookDelegate) 68 | { 69 | if (hookDelegate is object) 70 | _completionHook = () => synchronizationContext.Post(_ => hookDelegate.Invoke(), null); 71 | else 72 | _completionHook = null; 73 | 74 | return this; 75 | } 76 | 77 | /// 78 | /// Alters the property values to their destination values from a standing start with accelerating intervals. 79 | /// 80 | /// The duration until the properties should have reached their destination values 81 | public void Accelerate(TimeSpan duration) 82 | { 83 | BuildAndRun(new Acceleration(duration)); 84 | } 85 | 86 | /// 87 | /// Bounces the property values to their destination values and back to the original ones. 88 | /// They are accelerated to the destination and then decelerated back as if being dropped with gravity and bouncing back against gravity. 89 | /// 90 | /// The duration until the properties should have reached their destination values 91 | public void Bounce(TimeSpan duration) 92 | { 93 | BuildAndRun(new Bounce(duration)); 94 | } 95 | 96 | /// 97 | /// Alters the property values with an exponential decay. 98 | /// This has a damping effect similar to the motion of a needle on an electomagnetically controlled dial. 99 | /// 100 | /// The duration until the properties should have reached their destination values 101 | public void CriticalDamp(TimeSpan duration) 102 | { 103 | BuildAndRun(new CriticalDamping(duration)); 104 | } 105 | 106 | /// 107 | /// Alters the property values starting from a high speed and decelerating to zero by the end of the transition. 108 | /// 109 | /// The duration until the properties should have reached their destination values 110 | public void Decelerate(TimeSpan duration) 111 | { 112 | BuildAndRun(new Deceleration(duration)); 113 | } 114 | 115 | /// 116 | /// Alters the property values in an ease-in-ease-out transition. 117 | /// This accelerates during the first half of the transition, and then decelerates during the second half. 118 | /// 119 | /// The duration until the properties should have reached their destination values 120 | public void EaseInEaseOut(TimeSpan duration) 121 | { 122 | BuildAndRun(new EaseInEaseOut(duration)); 123 | } 124 | 125 | /// 126 | /// Flashes the property values a specified number of times, ending up by reverting them to their initial values. 127 | /// 128 | /// The number of flashes to animate 129 | /// The duration of each flash 130 | public void Flash(int numberOfFlashes, TimeSpan durationOfEachFlash) 131 | { 132 | BuildAndRun(new Flash(numberOfFlashes, durationOfEachFlash)); 133 | } 134 | 135 | /// 136 | /// Alters the property values in a linear way until they reach their destination values. 137 | /// 138 | /// The duration until the properties should have reached their destination values 139 | public void Linear(TimeSpan duration) 140 | { 141 | BuildAndRun(new Linear(duration)); 142 | } 143 | 144 | /// 145 | /// Bounces the property values to their destination values and back to the original ones. 146 | /// They are decelerated to the destination and then acclerated back as if being thrown against gravity and then descending back with gravity. 147 | /// 148 | /// The duration until the properties should have reached their destination values 149 | public void ThrowAndCatch(TimeSpan duration) 150 | { 151 | BuildAndRun(new ThrowAndCatch(duration)); 152 | } 153 | 154 | /// 155 | /// Allows the creation of user-defined transition methods, specified by a list of individual transition elements. 156 | /// 157 | /// Each of these defines: 158 | /// End time, End value, Interpolation method 159 | /// 160 | /// For example, say you want to make a bouncing effect with a decay: 161 | /// 162 | /// EndTime% EndValue% Interpolation 163 | /// -------- --------- ------------- 164 | /// 50 100 Acceleration 165 | /// 75 50 Deceleration 166 | /// 85 100 Acceleration 167 | /// 91 75 Deceleration 168 | /// 95 100 Acceleration 169 | /// 98 90 Deceleration 170 | /// 100 100 Acceleration 171 | /// 172 | /// The time values are expressed as a percentage of the overall transition time. This 173 | /// means that you can create a user-defined transition-type and then use it for transitions 174 | /// of different lengths. 175 | /// 176 | /// The values are percentages of the values between the start and end values of the properties 177 | /// being animated in the transitions. 0% is the start value and 100% is the end value. 178 | /// 179 | /// The interpolation is one of the values from the InterpolationMethod enum. 180 | /// 181 | /// So the example above accelerates to the destination (as if under gravity) by 182 | /// t=50%, then bounces back up to half the initial height by t=75%, slowing down 183 | /// (as if against gravity) before falling down again and bouncing to decreasing 184 | /// heights each time. 185 | /// 186 | /// 187 | /// The elements to process during the transition 188 | /// The duration until the properties should have reached their destination values 189 | public void Sequence(IList elements, TimeSpan duration) 190 | { 191 | BuildAndRun(new Sequence(elements, duration)); 192 | } 193 | 194 | /// 195 | /// Interpolates values with EasingFunctions.BackEaseOut to mimic the behavior of a rubber band stopping a movement and pulling it back. 196 | /// 197 | /// The duration until the properties should have reached their destination values 198 | public void Rubberband(TimeSpan duration) 199 | { 200 | BuildAndRun(new Rubberband(duration)); 201 | } 202 | 203 | /// 204 | /// Interpolates values with EasingFunctions.ElasticEaseOut to mimic the behavior of a loaded spring. 205 | /// 206 | /// The duration until the properties should have reached their destination values 207 | public void Spring(TimeSpan duration) 208 | { 209 | BuildAndRun(new Spring(duration)); 210 | } 211 | 212 | /// 213 | /// Allows natural transitions with custom or predefined easing functions. 214 | /// Easing functions are mathematical functions that are used to interpolate values between two endpoints 215 | /// usually with non-linear results. 216 | /// 217 | /// 218 | /// The function to interpolate values with. See "" for prebuilt easing functions. 219 | /// Based on Mauro Sampietro's article on CodeProject: https://www.codeproject.com/Articles/827808/Control-Animation-in-Winforms 220 | /// 221 | /// The duration until the properties should have reached their destination values 222 | public void EaseWithFunction(EasingFunction easingFunction, TimeSpan duration) 223 | { 224 | BuildAndRun(new EaseWithFunction(easingFunction, duration)); 225 | } 226 | 227 | /// 228 | /// Builds a transition with a given method and runs it immediately 229 | /// 230 | /// The method the transition should be built with 231 | private void BuildAndRun(IMethod method) 232 | { 233 | Build(method).Run(); 234 | } 235 | 236 | /// 237 | /// Builds and returns a transition with a given method 238 | /// 239 | /// The method the transition should be built with 240 | public Transition Build(IMethod method) 241 | { 242 | var transition = new Transition(method); 243 | 244 | foreach (var item in _targetPropertyDestinations) 245 | transition.Add(item.Target, item.PropertyName, item.DestinationValue); 246 | 247 | if (_completionHook is object) 248 | { 249 | void SelfRemovingCompletionHandler(object s, EventArgs e) 250 | { 251 | transition.TransitionCompleted -= SelfRemovingCompletionHandler; 252 | _completionHook.Invoke(); 253 | } 254 | 255 | transition.TransitionCompleted += SelfRemovingCompletionHandler; 256 | } 257 | 258 | return transition; 259 | } 260 | 261 | internal class TargetPropertyDestination 262 | { 263 | public object Target { get; set; } 264 | 265 | public string PropertyName { get; set; } 266 | 267 | public object DestinationValue { get; set; } 268 | } 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /src/FluentTransitions/TransitionElement.cs: -------------------------------------------------------------------------------- 1 | namespace FluentTransitions 2 | { 3 | /// 4 | /// An individual part of a transition defining the method to interpolate towards a desitination value and how log this should take. 5 | /// 6 | public class TransitionElement 7 | { 8 | /// 9 | /// Creates a new transition element 10 | /// 11 | /// The percentage of elapsed time, expressed as (for example) 75 for 75%. 12 | /// The value of the animated properties at the EndTime. 13 | /// This is the percentage movement of the properties between their start and end values. 14 | /// This should be expressed as (for example) 75 for 75%. 15 | /// The interpolation method to use when moving between the previous value and the current one. 16 | public TransitionElement(double endTime, double endValue, InterpolationMethod interpolationMethod) 17 | { 18 | EndTime = endTime; 19 | EndValue = endValue; 20 | InterpolationMethod = interpolationMethod; 21 | } 22 | 23 | /// 24 | /// The percentage of elapsed time, expressed as (for example) 75 for 75%. 25 | /// 26 | public double EndTime { get; set; } 27 | 28 | /// 29 | /// The value of the animated properties at the EndTime. This is the percentage 30 | /// movement of the properties between their start and end values. This should 31 | /// be expressed as (for example) 75 for 75%. 32 | /// 33 | public double EndValue { get; set; } 34 | 35 | /// 36 | /// The interpolation method to use when moving between the previous value 37 | /// and the current one. 38 | /// 39 | public InterpolationMethod InterpolationMethod { get; set; } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/FluentTransitions/TransitionManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Timers; 4 | 5 | namespace FluentTransitions 6 | { 7 | /// 8 | /// This class is responsible for running transitions. It holds the timer that 9 | /// triggers transition animation. 10 | /// 11 | /// This class is a singleton. 12 | /// 13 | /// We manage the transition timer here so that we can have a single timer 14 | /// across all transitions. If each transition has its own timer, this creates 15 | /// one thread for each transition, and this can lead to too many threads in 16 | /// an application. 17 | /// 18 | /// This class essentially just manages the timer for the transitions. It calls 19 | /// back into the running transitions, which do the actual work of the transition. 20 | /// 21 | /// 22 | internal class TransitionManager 23 | { 24 | // The singleton instance... 25 | private static TransitionManager _instance = null; 26 | 27 | // The collection of transitions we're managing. (This should really be a set.) 28 | private readonly IDictionary _transitions = new Dictionary(); 29 | 30 | // The timer that controls the transition animation... 31 | private readonly Timer _timer = null; 32 | 33 | // An object to lock on. This class can be accessed by multiple threads: the 34 | // user thread can add new transitions; and the timerr thread can be animating 35 | // them. As they access the same collections, the methods need to be protected 36 | // by a lock... 37 | private readonly object _lock = new object(); 38 | 39 | /// 40 | /// Private constructor (for singleton). 41 | /// 42 | private TransitionManager() 43 | { 44 | _timer = new Timer(15); 45 | _timer.Elapsed += OnTimerElapsed; 46 | _timer.Enabled = true; 47 | } 48 | 49 | /// 50 | /// Singleton's getInstance method. 51 | /// 52 | public static TransitionManager GetInstance() 53 | { 54 | if (_instance == null) 55 | _instance = new TransitionManager(); 56 | 57 | return _instance; 58 | } 59 | 60 | /// 61 | /// You register a transition with the manager here. This will start to run 62 | /// the transition as the manager's timer ticks. 63 | /// 64 | public void Register(Transition transition) 65 | { 66 | lock (_lock) 67 | { 68 | // We check to see if the properties of this transition 69 | // are already being animated by any existing transitions... 70 | RemoveDuplicates(transition); 71 | 72 | // We add the transition to the collection we manage, and 73 | // observe it so that we know when it has completed... 74 | _transitions[transition] = true; 75 | transition.TransitionCompleted += OnTransitionCompleted; 76 | } 77 | } 78 | 79 | /// 80 | /// Checks if any existing transitions are acting on the same properties as the 81 | /// transition passed in. If so, we remove the duplicated properties from the 82 | /// older transitions. 83 | /// 84 | private void RemoveDuplicates(Transition transition) 85 | { 86 | // We look through the set of transitions we're currently managing... 87 | foreach (KeyValuePair pair in _transitions) 88 | RemoveDuplicates(transition, pair.Key); 89 | } 90 | 91 | /// 92 | /// Finds any properties in the old-transition that are also in the new one, 93 | /// and removes them from the old one. 94 | /// 95 | private void RemoveDuplicates(Transition newTransition, Transition oldTransition) 96 | { 97 | // Note: This checking might be a bit more efficient if it did the checking 98 | // with a set rather than looking through lists. That said, it is only done 99 | // when transitions are added (which isn't very often) rather than on the 100 | // timer, so I don't think this matters too much. 101 | 102 | // We get the list of properties for the old and new transitions... 103 | var newProperties = newTransition.TransitionedProperties; 104 | var oldProperties = oldTransition.TransitionedProperties; 105 | 106 | // We loop through the old properties backwards (as we may be removing 107 | // items from the list if we find a match)... 108 | for (int i = oldProperties.Count - 1; i >= 0; i--) 109 | { 110 | // We get one of the properties from the old transition... 111 | var oldProperty = oldProperties[i]; 112 | 113 | // Is this property part of the new transition? 114 | foreach (var newProperty in newProperties) 115 | { 116 | if (oldProperty.Target == newProperty.Target 117 | && oldProperty.PropertyInfo == newProperty.PropertyInfo) 118 | { 119 | // The old transition contains the same property as the new one, 120 | // so we remove it from the old transition... 121 | oldTransition.RemoveProperty(oldProperty); 122 | } 123 | } 124 | } 125 | } 126 | 127 | /// 128 | /// Called when the timer ticks. 129 | /// 130 | private void OnTimerElapsed(object sender, ElapsedEventArgs e) 131 | { 132 | // We turn the timer off while we process the tick, in case the 133 | // actions take longer than the tick itself... 134 | if (_timer == null) 135 | return; 136 | 137 | _timer.Enabled = false; 138 | 139 | IList listTransitions; 140 | lock (_lock) 141 | { 142 | // We take a copy of the collection of transitions as elements 143 | // might be removed as we iterate through it... 144 | listTransitions = new List(); 145 | foreach (KeyValuePair pair in _transitions) 146 | listTransitions.Add(pair.Key); 147 | } 148 | 149 | // We tick the timer for each transition we're managing... 150 | foreach (var transition in listTransitions) 151 | transition.OnTimer(); 152 | 153 | // We restart the timer... 154 | _timer.Enabled = true; 155 | } 156 | 157 | /// 158 | /// Called when a transition has completed. 159 | /// 160 | private void OnTransitionCompleted(object sender, EventArgs e) 161 | { 162 | // We stop observing the transition... 163 | var transition = (Transition)sender; 164 | transition.TransitionCompleted -= OnTransitionCompleted; 165 | 166 | // We remove the transition from the collection we're managing... 167 | lock (_lock) 168 | { 169 | _transitions.Remove(transition); 170 | } 171 | } 172 | } 173 | } 174 | 175 | 176 | -------------------------------------------------------------------------------- /src/FluentTransitions/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | 4 | namespace FluentTransitions 5 | { 6 | /// 7 | /// A class holding static utility functions. 8 | /// 9 | internal static class Utility 10 | { 11 | /// 12 | /// Returns the value of the property passed in. 13 | /// 14 | public static object GetValue(object target, string propertyName) 15 | { 16 | var targetType = target.GetType(); 17 | var propertyInfo = targetType.GetProperty(propertyName); 18 | if (propertyInfo == null) 19 | throw new InvalidOperationException($"Object: {target} does not have the property: {propertyName}"); 20 | 21 | return propertyInfo.GetValue(target, null); 22 | } 23 | 24 | /// 25 | /// Sets the value of the property passed in. 26 | /// 27 | public static void SetValue(object target, string propertyName, object value) 28 | { 29 | var targetType = target.GetType(); 30 | var propertyInfo = targetType.GetProperty(propertyName); 31 | if (propertyInfo == null) 32 | throw new InvalidOperationException($"Object: {target} does not have the property: {propertyName}"); 33 | 34 | propertyInfo.SetValue(target, value, null); 35 | } 36 | 37 | /// 38 | /// Returns a value between d1 and d2 for the percentage passed in. 39 | /// 40 | public static double Interpolate(double value1, double value2, double percentage) 41 | { 42 | double difference = value2 - value1; 43 | double distance = difference * percentage; 44 | double result = value1 + distance; 45 | 46 | return result; 47 | } 48 | 49 | /// 50 | /// Returns a value betweeen i1 and i2 for the percentage passed in. 51 | /// 52 | public static int Interpolate(int value1, int value2, double percentage) 53 | { 54 | return (int)Interpolate((double)value1, (double)value2, percentage); 55 | } 56 | 57 | /// 58 | /// Returns a value betweeen f1 and f2 for the percentage passed in. 59 | /// 60 | public static float Interpolate(float value1, float value2, double percentage) 61 | { 62 | return (float)Interpolate((double)value1, (double)value2, percentage); 63 | } 64 | 65 | /// 66 | /// Converts a fraction representing linear time to a fraction representing 67 | /// the distance traveled under an ease-in-ease-out transition. 68 | /// 69 | public static double ConvertLinearToEaseInEaseOut(double elapsed) 70 | { 71 | // The distance traveled is made up of two parts: the initial acceleration, 72 | // and then the subsequent deceleration... 73 | double firstHalfTime = (elapsed > 0.5) ? 0.5 : elapsed; 74 | double secondHalfTime = (elapsed > 0.5) ? elapsed - 0.5 : 0.0; 75 | double result = 2 * firstHalfTime * firstHalfTime + 2 * secondHalfTime * (1.0 - secondHalfTime); 76 | return result; 77 | } 78 | 79 | /// 80 | /// Converts a fraction representing linear time to a fraction representing 81 | /// the distance traveled under a constant acceleration transition. 82 | /// 83 | public static double ConvertLinearToAcceleration(double elapsed) 84 | { 85 | return elapsed * elapsed; 86 | } 87 | 88 | /// 89 | /// Converts a fraction representing linear time to a fraction representing 90 | /// the distance traveled under a constant deceleration transition. 91 | /// 92 | public static double ConvertLinearToDeceleration(double elapsed) 93 | { 94 | return elapsed * (2.0 - elapsed); 95 | } 96 | 97 | /// 98 | /// Fires the event passed in in a thread-safe way. 99 | /// 100 | /// This method loops through the targets of the event and invokes each in turn. If the 101 | /// target supports ISychronizeInvoke (such as forms or controls) and is set to run 102 | /// on a different thread, then we call BeginInvoke to marshal the event to the target 103 | /// thread. If the target does not support this interface (such as most non-form classes) 104 | /// or we are on the same thread as the target, then the event is fired on the same 105 | /// thread as this is called from. 106 | /// 107 | public static void RaiseEvent(EventHandler theEvent, object sender, T args) where T : System.EventArgs 108 | { 109 | if (theEvent == null) 110 | return; 111 | 112 | // We loop through each of the delegate handlers for this event. For each of 113 | // them we need to decide whether to invoke it on the current thread or to 114 | // make a cross-thread invocation... 115 | foreach (EventHandler handler in theEvent.GetInvocationList()) 116 | { 117 | try 118 | { 119 | if (!(handler.Target is ISynchronizeInvoke target) || !target.InvokeRequired) 120 | { 121 | // Either the target is not a form or control, or we are already 122 | // on the right thread for it. Either way we can just fire the 123 | // event as normal... 124 | handler(sender, args); 125 | } 126 | else 127 | { 128 | // The target is most likely a form or control that needs the 129 | // handler to be invoked on its own thread... 130 | target.BeginInvoke(handler, new object[] { sender, args }); 131 | } 132 | } 133 | catch 134 | { 135 | // The event handler may have been detached while processing the events. 136 | // We just ignore this and invoke the remaining handlers. 137 | } 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /src/TestApp/DemoForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp 2 | { 3 | partial class DemoForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DemoForm)); 32 | this.cmdRipple = new System.Windows.Forms.Button(); 33 | this.cmdThrowAndCatch = new System.Windows.Forms.Button(); 34 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 35 | this.lblTextTransition2 = new System.Windows.Forms.Label(); 36 | this.lblTextTransition1 = new System.Windows.Forms.Label(); 37 | this.cmdTextTransition = new System.Windows.Forms.Button(); 38 | this.gbThrowAndCatch = new System.Windows.Forms.GroupBox(); 39 | this.gbRipple = new System.Windows.Forms.GroupBox(); 40 | this.cmdMore = new System.Windows.Forms.Button(); 41 | this.groupBox2 = new System.Windows.Forms.GroupBox(); 42 | this.cmdSwapPictures = new System.Windows.Forms.Button(); 43 | this.groupBox3 = new System.Windows.Forms.GroupBox(); 44 | this.label1 = new System.Windows.Forms.Label(); 45 | this.txtPassword = new System.Windows.Forms.TextBox(); 46 | this.buttonDemo = new System.Windows.Forms.Button(); 47 | this.gpEasing = new System.Windows.Forms.GroupBox(); 48 | this.label9 = new System.Windows.Forms.Label(); 49 | this.label8 = new System.Windows.Forms.Label(); 50 | this.label6 = new System.Windows.Forms.Label(); 51 | this.buttonThrowAndCatch = new System.Windows.Forms.Button(); 52 | this.buttonLinear = new System.Windows.Forms.Button(); 53 | this.buttonFlash = new System.Windows.Forms.Button(); 54 | this.buttonEaseInEaseOut = new System.Windows.Forms.Button(); 55 | this.buttonDecelerate = new System.Windows.Forms.Button(); 56 | this.buttonCriticalDamp = new System.Windows.Forms.Button(); 57 | this.buttonBounce = new System.Windows.Forms.Button(); 58 | this.buttonAccelerate = new System.Windows.Forms.Button(); 59 | this.buttonSineEaseOut = new System.Windows.Forms.Button(); 60 | this.buttonQuintEaseOut = new System.Windows.Forms.Button(); 61 | this.buttonQuartEaseOut = new System.Windows.Forms.Button(); 62 | this.buttonQuadEaseOut = new System.Windows.Forms.Button(); 63 | this.buttonExpoEaseOut = new System.Windows.Forms.Button(); 64 | this.buttonElasticEaseOut = new System.Windows.Forms.Button(); 65 | this.buttonCubicEaseOut = new System.Windows.Forms.Button(); 66 | this.buttonCircEaseOut = new System.Windows.Forms.Button(); 67 | this.buttonBounceEaseOut = new System.Windows.Forms.Button(); 68 | this.buttonBackEaseOut = new System.Windows.Forms.Button(); 69 | this.label5 = new System.Windows.Forms.Label(); 70 | this.label4 = new System.Windows.Forms.Label(); 71 | this.label3 = new System.Windows.Forms.Label(); 72 | this.label2 = new System.Windows.Forms.Label(); 73 | this.label7 = new System.Windows.Forms.Label(); 74 | this.lblTargetLine = new System.Windows.Forms.Label(); 75 | this.buttonSprings = new System.Windows.Forms.Button(); 76 | this.ctrlPictures = new TestApp.KittenPuppyControl(); 77 | this.ctrlRipple = new TestApp.RippleControl(); 78 | this.groupBox1.SuspendLayout(); 79 | this.gbThrowAndCatch.SuspendLayout(); 80 | this.gbRipple.SuspendLayout(); 81 | this.groupBox2.SuspendLayout(); 82 | this.groupBox3.SuspendLayout(); 83 | this.gpEasing.SuspendLayout(); 84 | this.SuspendLayout(); 85 | // 86 | // cmdRipple 87 | // 88 | this.cmdRipple.Location = new System.Drawing.Point(9, 29); 89 | this.cmdRipple.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 90 | this.cmdRipple.Name = "cmdRipple"; 91 | this.cmdRipple.Size = new System.Drawing.Size(290, 57); 92 | this.cmdRipple.TabIndex = 9; 93 | this.cmdRipple.Text = "Ripple"; 94 | this.cmdRipple.UseVisualStyleBackColor = true; 95 | this.cmdRipple.Click += new System.EventHandler(this.cmdRipple_Click); 96 | // 97 | // cmdThrowAndCatch 98 | // 99 | this.cmdThrowAndCatch.Anchor = System.Windows.Forms.AnchorStyles.Bottom; 100 | this.cmdThrowAndCatch.Location = new System.Drawing.Point(9, 1226); 101 | this.cmdThrowAndCatch.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 102 | this.cmdThrowAndCatch.Name = "cmdThrowAndCatch"; 103 | this.cmdThrowAndCatch.Size = new System.Drawing.Size(298, 52); 104 | this.cmdThrowAndCatch.TabIndex = 11; 105 | this.cmdThrowAndCatch.Text = "Throw and Catch"; 106 | this.cmdThrowAndCatch.UseVisualStyleBackColor = true; 107 | this.cmdThrowAndCatch.Click += new System.EventHandler(this.cmdThrowAndCatch_Click); 108 | // 109 | // groupBox1 110 | // 111 | this.groupBox1.Controls.Add(this.lblTextTransition2); 112 | this.groupBox1.Controls.Add(this.lblTextTransition1); 113 | this.groupBox1.Controls.Add(this.cmdTextTransition); 114 | this.groupBox1.Location = new System.Drawing.Point(1020, 478); 115 | this.groupBox1.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 116 | this.groupBox1.Name = "groupBox1"; 117 | this.groupBox1.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); 118 | this.groupBox1.Size = new System.Drawing.Size(316, 155); 119 | this.groupBox1.TabIndex = 13; 120 | this.groupBox1.TabStop = false; 121 | this.groupBox1.Text = "Text transition"; 122 | // 123 | // lblTextTransition2 124 | // 125 | this.lblTextTransition2.AutoSize = true; 126 | this.lblTextTransition2.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 127 | this.lblTextTransition2.ForeColor = System.Drawing.Color.Crimson; 128 | this.lblTextTransition2.Location = new System.Drawing.Point(10, 115); 129 | this.lblTextTransition2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 130 | this.lblTextTransition2.Name = "lblTextTransition2"; 131 | this.lblTextTransition2.Size = new System.Drawing.Size(286, 22); 132 | this.lblTextTransition2.TabIndex = 15; 133 | this.lblTextTransition2.Text = "A longer piece of text."; 134 | // 135 | // lblTextTransition1 136 | // 137 | this.lblTextTransition1.AutoSize = true; 138 | this.lblTextTransition1.Font = new System.Drawing.Font("Courier New", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 139 | this.lblTextTransition1.ForeColor = System.Drawing.Color.Blue; 140 | this.lblTextTransition1.Location = new System.Drawing.Point(9, 91); 141 | this.lblTextTransition1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 142 | this.lblTextTransition1.Name = "lblTextTransition1"; 143 | this.lblTextTransition1.Size = new System.Drawing.Size(166, 22); 144 | this.lblTextTransition1.TabIndex = 14; 145 | this.lblTextTransition1.Text = "Hello, World!"; 146 | // 147 | // cmdTextTransition 148 | // 149 | this.cmdTextTransition.Location = new System.Drawing.Point(9, 29); 150 | this.cmdTextTransition.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 151 | this.cmdTextTransition.Name = "cmdTextTransition"; 152 | this.cmdTextTransition.Size = new System.Drawing.Size(290, 57); 153 | this.cmdTextTransition.TabIndex = 13; 154 | this.cmdTextTransition.Text = "Text Transition"; 155 | this.cmdTextTransition.UseVisualStyleBackColor = true; 156 | this.cmdTextTransition.Click += new System.EventHandler(this.cmdTextTransition_Click); 157 | // 158 | // gbThrowAndCatch 159 | // 160 | this.gbThrowAndCatch.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 161 | | System.Windows.Forms.AnchorStyles.Left))); 162 | this.gbThrowAndCatch.Controls.Add(this.cmdThrowAndCatch); 163 | this.gbThrowAndCatch.Location = new System.Drawing.Point(-316, 77); 164 | this.gbThrowAndCatch.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 165 | this.gbThrowAndCatch.Name = "gbThrowAndCatch"; 166 | this.gbThrowAndCatch.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); 167 | this.gbThrowAndCatch.Size = new System.Drawing.Size(316, 1287); 168 | this.gbThrowAndCatch.TabIndex = 15; 169 | this.gbThrowAndCatch.TabStop = false; 170 | this.gbThrowAndCatch.Text = "Throw and Catch"; 171 | // 172 | // gbRipple 173 | // 174 | this.gbRipple.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 175 | | System.Windows.Forms.AnchorStyles.Left))); 176 | this.gbRipple.Controls.Add(this.cmdRipple); 177 | this.gbRipple.Controls.Add(this.ctrlRipple); 178 | this.gbRipple.Location = new System.Drawing.Point(1020, 643); 179 | this.gbRipple.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 180 | this.gbRipple.Name = "gbRipple"; 181 | this.gbRipple.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); 182 | this.gbRipple.Size = new System.Drawing.Size(316, 713); 183 | this.gbRipple.TabIndex = 18; 184 | this.gbRipple.TabStop = false; 185 | this.gbRipple.Text = "Ripple"; 186 | // 187 | // cmdMore 188 | // 189 | this.cmdMore.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 190 | this.cmdMore.Location = new System.Drawing.Point(1242, 25); 191 | this.cmdMore.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 192 | this.cmdMore.Name = "cmdMore"; 193 | this.cmdMore.Size = new System.Drawing.Size(120, 41); 194 | this.cmdMore.TabIndex = 21; 195 | this.cmdMore.Text = "More »"; 196 | this.cmdMore.UseVisualStyleBackColor = true; 197 | this.cmdMore.Click += new System.EventHandler(this.cmdMore_Click); 198 | // 199 | // groupBox2 200 | // 201 | this.groupBox2.Controls.Add(this.cmdSwapPictures); 202 | this.groupBox2.Controls.Add(this.ctrlPictures); 203 | this.groupBox2.Location = new System.Drawing.Point(1020, 278); 204 | this.groupBox2.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 205 | this.groupBox2.Name = "groupBox2"; 206 | this.groupBox2.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); 207 | this.groupBox2.Size = new System.Drawing.Size(316, 191); 208 | this.groupBox2.TabIndex = 22; 209 | this.groupBox2.TabStop = false; 210 | this.groupBox2.Text = "Picture transition"; 211 | // 212 | // cmdSwapPictures 213 | // 214 | this.cmdSwapPictures.Location = new System.Drawing.Point(15, 29); 215 | this.cmdSwapPictures.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 216 | this.cmdSwapPictures.Name = "cmdSwapPictures"; 217 | this.cmdSwapPictures.Size = new System.Drawing.Size(110, 138); 218 | this.cmdSwapPictures.TabIndex = 1; 219 | this.cmdSwapPictures.Text = "Swap Pictures"; 220 | this.cmdSwapPictures.UseVisualStyleBackColor = true; 221 | this.cmdSwapPictures.Click += new System.EventHandler(this.cmdSwapPictures_Click); 222 | // 223 | // groupBox3 224 | // 225 | this.groupBox3.Controls.Add(this.label1); 226 | this.groupBox3.Controls.Add(this.txtPassword); 227 | this.groupBox3.Location = new System.Drawing.Point(1020, 86); 228 | this.groupBox3.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 229 | this.groupBox3.Name = "groupBox3"; 230 | this.groupBox3.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); 231 | this.groupBox3.Size = new System.Drawing.Size(316, 183); 232 | this.groupBox3.TabIndex = 23; 233 | this.groupBox3.TabStop = false; 234 | this.groupBox3.Text = "macOS like password box"; 235 | // 236 | // label1 237 | // 238 | this.label1.AutoSize = true; 239 | this.label1.Location = new System.Drawing.Point(36, 55); 240 | this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 241 | this.label1.Name = "label1"; 242 | this.label1.Size = new System.Drawing.Size(195, 40); 243 | this.label1.TabIndex = 1; 244 | this.label1.Text = "Enter the secret password\r\nand hit [Return]"; 245 | // 246 | // txtPassword 247 | // 248 | this.txtPassword.Location = new System.Drawing.Point(40, 112); 249 | this.txtPassword.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 250 | this.txtPassword.Name = "txtPassword"; 251 | this.txtPassword.PasswordChar = '●'; 252 | this.txtPassword.Size = new System.Drawing.Size(235, 26); 253 | this.txtPassword.TabIndex = 0; 254 | this.txtPassword.KeyDown += new System.Windows.Forms.KeyEventHandler(this.txtPassword_KeyDown); 255 | // 256 | // buttonDemo 257 | // 258 | this.buttonDemo.Location = new System.Drawing.Point(18, 25); 259 | this.buttonDemo.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 260 | this.buttonDemo.Name = "buttonDemo"; 261 | this.buttonDemo.Size = new System.Drawing.Size(195, 41); 262 | this.buttonDemo.TabIndex = 24; 263 | this.buttonDemo.Text = "Animate all && get back"; 264 | this.buttonDemo.UseVisualStyleBackColor = true; 265 | this.buttonDemo.Click += new System.EventHandler(this.buttonDemo_Click); 266 | // 267 | // gpEasing 268 | // 269 | this.gpEasing.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 270 | | System.Windows.Forms.AnchorStyles.Left))); 271 | this.gpEasing.Controls.Add(this.label9); 272 | this.gpEasing.Controls.Add(this.label8); 273 | this.gpEasing.Controls.Add(this.buttonThrowAndCatch); 274 | this.gpEasing.Controls.Add(this.buttonLinear); 275 | this.gpEasing.Controls.Add(this.buttonFlash); 276 | this.gpEasing.Controls.Add(this.buttonEaseInEaseOut); 277 | this.gpEasing.Controls.Add(this.buttonDecelerate); 278 | this.gpEasing.Controls.Add(this.buttonCriticalDamp); 279 | this.gpEasing.Controls.Add(this.buttonBounce); 280 | this.gpEasing.Controls.Add(this.buttonAccelerate); 281 | this.gpEasing.Controls.Add(this.buttonSineEaseOut); 282 | this.gpEasing.Controls.Add(this.buttonQuintEaseOut); 283 | this.gpEasing.Controls.Add(this.buttonQuartEaseOut); 284 | this.gpEasing.Controls.Add(this.buttonQuadEaseOut); 285 | this.gpEasing.Controls.Add(this.buttonExpoEaseOut); 286 | this.gpEasing.Controls.Add(this.buttonElasticEaseOut); 287 | this.gpEasing.Controls.Add(this.buttonCubicEaseOut); 288 | this.gpEasing.Controls.Add(this.buttonCircEaseOut); 289 | this.gpEasing.Controls.Add(this.buttonBounceEaseOut); 290 | this.gpEasing.Controls.Add(this.buttonBackEaseOut); 291 | this.gpEasing.Controls.Add(this.label5); 292 | this.gpEasing.Controls.Add(this.label3); 293 | this.gpEasing.Controls.Add(this.label2); 294 | this.gpEasing.Controls.Add(this.label7); 295 | this.gpEasing.Controls.Add(this.lblTargetLine); 296 | this.gpEasing.Controls.Add(this.label6); 297 | this.gpEasing.Controls.Add(this.label4); 298 | this.gpEasing.Location = new System.Drawing.Point(18, 86); 299 | this.gpEasing.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 300 | this.gpEasing.Name = "gpEasing"; 301 | this.gpEasing.Padding = new System.Windows.Forms.Padding(4, 5, 4, 5); 302 | this.gpEasing.Size = new System.Drawing.Size(994, 1270); 303 | this.gpEasing.TabIndex = 24; 304 | this.gpEasing.TabStop = false; 305 | this.gpEasing.Text = "Animation showcase"; 306 | // 307 | // label9 308 | // 309 | this.label9.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 310 | | System.Windows.Forms.AnchorStyles.Left))); 311 | this.label9.BackColor = System.Drawing.Color.NavajoWhite; 312 | this.label9.Location = new System.Drawing.Point(19, 484); 313 | this.label9.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 314 | this.label9.Name = "label9"; 315 | this.label9.Size = new System.Drawing.Size(8, 772); 316 | this.label9.TabIndex = 32; 317 | // 318 | // label8 319 | // 320 | this.label8.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 321 | | System.Windows.Forms.AnchorStyles.Left))); 322 | this.label8.BackColor = System.Drawing.Color.LightSteelBlue; 323 | this.label8.Location = new System.Drawing.Point(19, 46); 324 | this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 325 | this.label8.Name = "label8"; 326 | this.label8.Size = new System.Drawing.Size(8, 405); 327 | this.label8.TabIndex = 31; 328 | // 329 | // label6 330 | // 331 | this.label6.AutoSize = true; 332 | this.label6.Location = new System.Drawing.Point(224, 650); 333 | this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 334 | this.label6.Name = "label6"; 335 | this.label6.Size = new System.Drawing.Size(115, 20); 336 | this.label6.TabIndex = 30; 337 | this.label6.Text = "= Rubber band"; 338 | // 339 | // buttonThrowAndCatch 340 | // 341 | this.buttonThrowAndCatch.Location = new System.Drawing.Point(44, 416); 342 | this.buttonThrowAndCatch.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 343 | this.buttonThrowAndCatch.Name = "buttonThrowAndCatch"; 344 | this.buttonThrowAndCatch.Size = new System.Drawing.Size(172, 35); 345 | this.buttonThrowAndCatch.TabIndex = 25; 346 | this.buttonThrowAndCatch.Text = "ThrowAndCatch"; 347 | this.buttonThrowAndCatch.UseVisualStyleBackColor = true; 348 | this.buttonThrowAndCatch.Click += new System.EventHandler(this.buttonThrowAndCatch_Click); 349 | // 350 | // buttonLinear 351 | // 352 | this.buttonLinear.Location = new System.Drawing.Point(44, 371); 353 | this.buttonLinear.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 354 | this.buttonLinear.Name = "buttonLinear"; 355 | this.buttonLinear.Size = new System.Drawing.Size(172, 35); 356 | this.buttonLinear.TabIndex = 25; 357 | this.buttonLinear.Text = "Linear"; 358 | this.buttonLinear.UseVisualStyleBackColor = true; 359 | this.buttonLinear.Click += new System.EventHandler(this.buttonLinear_Click); 360 | // 361 | // buttonFlash 362 | // 363 | this.buttonFlash.Location = new System.Drawing.Point(44, 326); 364 | this.buttonFlash.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 365 | this.buttonFlash.Name = "buttonFlash"; 366 | this.buttonFlash.Size = new System.Drawing.Size(172, 35); 367 | this.buttonFlash.TabIndex = 25; 368 | this.buttonFlash.Text = "Flash"; 369 | this.buttonFlash.UseVisualStyleBackColor = true; 370 | this.buttonFlash.Click += new System.EventHandler(this.buttonFlash_Click); 371 | // 372 | // buttonEaseInEaseOut 373 | // 374 | this.buttonEaseInEaseOut.Location = new System.Drawing.Point(44, 282); 375 | this.buttonEaseInEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 376 | this.buttonEaseInEaseOut.Name = "buttonEaseInEaseOut"; 377 | this.buttonEaseInEaseOut.Size = new System.Drawing.Size(172, 35); 378 | this.buttonEaseInEaseOut.TabIndex = 25; 379 | this.buttonEaseInEaseOut.Text = "EaseInEaseOut"; 380 | this.buttonEaseInEaseOut.UseVisualStyleBackColor = true; 381 | this.buttonEaseInEaseOut.Click += new System.EventHandler(this.buttonEaseInEaseOut_Click); 382 | // 383 | // buttonDecelerate 384 | // 385 | this.buttonDecelerate.Location = new System.Drawing.Point(44, 237); 386 | this.buttonDecelerate.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 387 | this.buttonDecelerate.Name = "buttonDecelerate"; 388 | this.buttonDecelerate.Size = new System.Drawing.Size(172, 35); 389 | this.buttonDecelerate.TabIndex = 25; 390 | this.buttonDecelerate.Text = "Decelerate"; 391 | this.buttonDecelerate.UseVisualStyleBackColor = true; 392 | this.buttonDecelerate.Click += new System.EventHandler(this.buttonDecelerate_Click); 393 | // 394 | // buttonCriticalDamp 395 | // 396 | this.buttonCriticalDamp.Location = new System.Drawing.Point(44, 193); 397 | this.buttonCriticalDamp.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 398 | this.buttonCriticalDamp.Name = "buttonCriticalDamp"; 399 | this.buttonCriticalDamp.Size = new System.Drawing.Size(172, 35); 400 | this.buttonCriticalDamp.TabIndex = 25; 401 | this.buttonCriticalDamp.Text = "CriticalDamp"; 402 | this.buttonCriticalDamp.UseVisualStyleBackColor = true; 403 | this.buttonCriticalDamp.Click += new System.EventHandler(this.buttonCriticalDamp_Click); 404 | // 405 | // buttonBounce 406 | // 407 | this.buttonBounce.Location = new System.Drawing.Point(44, 148); 408 | this.buttonBounce.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 409 | this.buttonBounce.Name = "buttonBounce"; 410 | this.buttonBounce.Size = new System.Drawing.Size(172, 35); 411 | this.buttonBounce.TabIndex = 25; 412 | this.buttonBounce.Text = "Bounce"; 413 | this.buttonBounce.UseVisualStyleBackColor = true; 414 | this.buttonBounce.Click += new System.EventHandler(this.buttonBounce_Click); 415 | // 416 | // buttonAccelerate 417 | // 418 | this.buttonAccelerate.Location = new System.Drawing.Point(44, 103); 419 | this.buttonAccelerate.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 420 | this.buttonAccelerate.Name = "buttonAccelerate"; 421 | this.buttonAccelerate.Size = new System.Drawing.Size(172, 35); 422 | this.buttonAccelerate.TabIndex = 25; 423 | this.buttonAccelerate.Text = "Accelerate"; 424 | this.buttonAccelerate.UseVisualStyleBackColor = true; 425 | this.buttonAccelerate.Click += new System.EventHandler(this.buttonAccelerate_Click); 426 | // 427 | // buttonSineEaseOut 428 | // 429 | this.buttonSineEaseOut.Location = new System.Drawing.Point(44, 1044); 430 | this.buttonSineEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 431 | this.buttonSineEaseOut.Name = "buttonSineEaseOut"; 432 | this.buttonSineEaseOut.Size = new System.Drawing.Size(172, 35); 433 | this.buttonSineEaseOut.TabIndex = 25; 434 | this.buttonSineEaseOut.Text = "SineEaseOut"; 435 | this.buttonSineEaseOut.UseVisualStyleBackColor = true; 436 | this.buttonSineEaseOut.Click += new System.EventHandler(this.buttonSineEaseOut_Click); 437 | // 438 | // buttonQuintEaseOut 439 | // 440 | this.buttonQuintEaseOut.Location = new System.Drawing.Point(44, 1000); 441 | this.buttonQuintEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 442 | this.buttonQuintEaseOut.Name = "buttonQuintEaseOut"; 443 | this.buttonQuintEaseOut.Size = new System.Drawing.Size(172, 35); 444 | this.buttonQuintEaseOut.TabIndex = 25; 445 | this.buttonQuintEaseOut.Text = "QuintEaseOut"; 446 | this.buttonQuintEaseOut.UseVisualStyleBackColor = true; 447 | this.buttonQuintEaseOut.Click += new System.EventHandler(this.buttonQuintEaseOut_Click); 448 | // 449 | // buttonQuartEaseOut 450 | // 451 | this.buttonQuartEaseOut.Location = new System.Drawing.Point(44, 955); 452 | this.buttonQuartEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 453 | this.buttonQuartEaseOut.Name = "buttonQuartEaseOut"; 454 | this.buttonQuartEaseOut.Size = new System.Drawing.Size(172, 35); 455 | this.buttonQuartEaseOut.TabIndex = 25; 456 | this.buttonQuartEaseOut.Text = "QuartEaseOut"; 457 | this.buttonQuartEaseOut.UseVisualStyleBackColor = true; 458 | this.buttonQuartEaseOut.Click += new System.EventHandler(this.buttonQuartEaseOut_Click); 459 | // 460 | // buttonQuadEaseOut 461 | // 462 | this.buttonQuadEaseOut.Location = new System.Drawing.Point(44, 911); 463 | this.buttonQuadEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 464 | this.buttonQuadEaseOut.Name = "buttonQuadEaseOut"; 465 | this.buttonQuadEaseOut.Size = new System.Drawing.Size(172, 35); 466 | this.buttonQuadEaseOut.TabIndex = 25; 467 | this.buttonQuadEaseOut.Text = "QuadEaseOut"; 468 | this.buttonQuadEaseOut.UseVisualStyleBackColor = true; 469 | this.buttonQuadEaseOut.Click += new System.EventHandler(this.buttonQuadEaseOut_Click); 470 | // 471 | // buttonExpoEaseOut 472 | // 473 | this.buttonExpoEaseOut.Location = new System.Drawing.Point(44, 866); 474 | this.buttonExpoEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 475 | this.buttonExpoEaseOut.Name = "buttonExpoEaseOut"; 476 | this.buttonExpoEaseOut.Size = new System.Drawing.Size(172, 35); 477 | this.buttonExpoEaseOut.TabIndex = 25; 478 | this.buttonExpoEaseOut.Text = "ExpoEaseOut"; 479 | this.buttonExpoEaseOut.UseVisualStyleBackColor = true; 480 | this.buttonExpoEaseOut.Click += new System.EventHandler(this.buttonExpoEaseOut_Click); 481 | // 482 | // buttonElasticEaseOut 483 | // 484 | this.buttonElasticEaseOut.Location = new System.Drawing.Point(44, 821); 485 | this.buttonElasticEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 486 | this.buttonElasticEaseOut.Name = "buttonElasticEaseOut"; 487 | this.buttonElasticEaseOut.Size = new System.Drawing.Size(172, 35); 488 | this.buttonElasticEaseOut.TabIndex = 25; 489 | this.buttonElasticEaseOut.Text = "ElasticEaseOut"; 490 | this.buttonElasticEaseOut.UseVisualStyleBackColor = true; 491 | this.buttonElasticEaseOut.Click += new System.EventHandler(this.buttonElasticEaseOut_Click); 492 | // 493 | // buttonCubicEaseOut 494 | // 495 | this.buttonCubicEaseOut.Location = new System.Drawing.Point(44, 777); 496 | this.buttonCubicEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 497 | this.buttonCubicEaseOut.Name = "buttonCubicEaseOut"; 498 | this.buttonCubicEaseOut.Size = new System.Drawing.Size(172, 35); 499 | this.buttonCubicEaseOut.TabIndex = 25; 500 | this.buttonCubicEaseOut.Text = "CubicEaseOut"; 501 | this.buttonCubicEaseOut.UseVisualStyleBackColor = true; 502 | this.buttonCubicEaseOut.Click += new System.EventHandler(this.buttonCubicEaseOut_Click); 503 | // 504 | // buttonCircEaseOut 505 | // 506 | this.buttonCircEaseOut.Location = new System.Drawing.Point(44, 732); 507 | this.buttonCircEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 508 | this.buttonCircEaseOut.Name = "buttonCircEaseOut"; 509 | this.buttonCircEaseOut.Size = new System.Drawing.Size(172, 35); 510 | this.buttonCircEaseOut.TabIndex = 25; 511 | this.buttonCircEaseOut.Text = "CircEaseOut"; 512 | this.buttonCircEaseOut.UseVisualStyleBackColor = true; 513 | this.buttonCircEaseOut.Click += new System.EventHandler(this.buttonCircEaseOut_Click); 514 | // 515 | // buttonBounceEaseOut 516 | // 517 | this.buttonBounceEaseOut.Location = new System.Drawing.Point(44, 688); 518 | this.buttonBounceEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 519 | this.buttonBounceEaseOut.Name = "buttonBounceEaseOut"; 520 | this.buttonBounceEaseOut.Size = new System.Drawing.Size(172, 35); 521 | this.buttonBounceEaseOut.TabIndex = 25; 522 | this.buttonBounceEaseOut.Text = "BounceEaseOut"; 523 | this.buttonBounceEaseOut.UseVisualStyleBackColor = true; 524 | this.buttonBounceEaseOut.Click += new System.EventHandler(this.buttonBounceEaseOut_Click); 525 | // 526 | // buttonBackEaseOut 527 | // 528 | this.buttonBackEaseOut.Location = new System.Drawing.Point(44, 643); 529 | this.buttonBackEaseOut.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 530 | this.buttonBackEaseOut.Name = "buttonBackEaseOut"; 531 | this.buttonBackEaseOut.Size = new System.Drawing.Size(172, 35); 532 | this.buttonBackEaseOut.TabIndex = 25; 533 | this.buttonBackEaseOut.Text = "BackEaseOut"; 534 | this.buttonBackEaseOut.UseVisualStyleBackColor = true; 535 | this.buttonBackEaseOut.Click += new System.EventHandler(this.buttonBackEaseOut_Click); 536 | // 537 | // label5 538 | // 539 | this.label5.AutoSize = true; 540 | this.label5.Location = new System.Drawing.Point(40, 1096); 541 | this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 542 | this.label5.Name = "label5"; 543 | this.label5.Size = new System.Drawing.Size(654, 160); 544 | this.label5.TabIndex = 29; 545 | this.label5.Text = resources.GetString("label5.Text"); 546 | // 547 | // label4 548 | // 549 | this.label4.AutoSize = true; 550 | this.label4.Location = new System.Drawing.Point(224, 828); 551 | this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 552 | this.label4.Name = "label4"; 553 | this.label4.Size = new System.Drawing.Size(68, 20); 554 | this.label4.TabIndex = 28; 555 | this.label4.Text = "= Spring"; 556 | // 557 | // label3 558 | // 559 | this.label3.AutoSize = true; 560 | this.label3.Location = new System.Drawing.Point(40, 483); 561 | this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 562 | this.label3.Name = "label3"; 563 | this.label3.Size = new System.Drawing.Size(756, 140); 564 | this.label3.TabIndex = 27; 565 | this.label3.Text = resources.GetString("label3.Text"); 566 | // 567 | // label2 568 | // 569 | this.label2.AutoSize = true; 570 | this.label2.Location = new System.Drawing.Point(40, 46); 571 | this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 572 | this.label2.Name = "label2"; 573 | this.label2.Size = new System.Drawing.Size(936, 40); 574 | this.label2.TabIndex = 2; 575 | this.label2.Text = resources.GetString("label2.Text"); 576 | // 577 | // label7 578 | // 579 | this.label7.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 580 | | System.Windows.Forms.AnchorStyles.Left))); 581 | this.label7.BackColor = System.Drawing.Color.DarkGray; 582 | this.label7.Location = new System.Drawing.Point(619, 643); 583 | this.label7.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 584 | this.label7.Name = "label7"; 585 | this.label7.Size = new System.Drawing.Size(1, 436); 586 | this.label7.TabIndex = 26; 587 | // 588 | // lblTargetLine 589 | // 590 | this.lblTargetLine.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 591 | | System.Windows.Forms.AnchorStyles.Left))); 592 | this.lblTargetLine.BackColor = System.Drawing.Color.DarkGray; 593 | this.lblTargetLine.Location = new System.Drawing.Point(619, 103); 594 | this.lblTargetLine.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 595 | this.lblTargetLine.Name = "lblTargetLine"; 596 | this.lblTargetLine.Size = new System.Drawing.Size(1, 348); 597 | this.lblTargetLine.TabIndex = 26; 598 | // 599 | // buttonSprings 600 | // 601 | this.buttonSprings.Location = new System.Drawing.Point(0, 0); 602 | this.buttonSprings.Name = "buttonSprings"; 603 | this.buttonSprings.Size = new System.Drawing.Size(75, 23); 604 | this.buttonSprings.TabIndex = 0; 605 | // 606 | // ctrlPictures 607 | // 608 | this.ctrlPictures.Location = new System.Drawing.Point(142, 29); 609 | this.ctrlPictures.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); 610 | this.ctrlPictures.Name = "ctrlPictures"; 611 | this.ctrlPictures.Size = new System.Drawing.Size(154, 138); 612 | this.ctrlPictures.TabIndex = 0; 613 | // 614 | // ctrlRipple 615 | // 616 | this.ctrlRipple.Location = new System.Drawing.Point(9, 95); 617 | this.ctrlRipple.Margin = new System.Windows.Forms.Padding(6, 8, 6, 8); 618 | this.ctrlRipple.Name = "ctrlRipple"; 619 | this.ctrlRipple.Size = new System.Drawing.Size(290, 215); 620 | this.ctrlRipple.TabIndex = 8; 621 | // 622 | // DemoForm 623 | // 624 | this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); 625 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 626 | this.BackColor = System.Drawing.Color.White; 627 | this.ClientSize = new System.Drawing.Size(1377, 1373); 628 | this.Controls.Add(this.gpEasing); 629 | this.Controls.Add(this.groupBox3); 630 | this.Controls.Add(this.groupBox2); 631 | this.Controls.Add(this.cmdMore); 632 | this.Controls.Add(this.gbRipple); 633 | this.Controls.Add(this.gbThrowAndCatch); 634 | this.Controls.Add(this.groupBox1); 635 | this.Controls.Add(this.buttonDemo); 636 | this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); 637 | this.Name = "DemoForm"; 638 | this.Text = "FluentTransitions TestApp"; 639 | this.groupBox1.ResumeLayout(false); 640 | this.groupBox1.PerformLayout(); 641 | this.gbThrowAndCatch.ResumeLayout(false); 642 | this.gbRipple.ResumeLayout(false); 643 | this.groupBox2.ResumeLayout(false); 644 | this.groupBox3.ResumeLayout(false); 645 | this.groupBox3.PerformLayout(); 646 | this.gpEasing.ResumeLayout(false); 647 | this.gpEasing.PerformLayout(); 648 | this.ResumeLayout(false); 649 | 650 | } 651 | 652 | #endregion 653 | private RippleControl ctrlRipple; 654 | private System.Windows.Forms.Button cmdRipple; 655 | private System.Windows.Forms.Button cmdThrowAndCatch; 656 | private System.Windows.Forms.GroupBox groupBox1; 657 | private System.Windows.Forms.Label lblTextTransition2; 658 | private System.Windows.Forms.Label lblTextTransition1; 659 | private System.Windows.Forms.Button cmdTextTransition; 660 | private System.Windows.Forms.GroupBox gbThrowAndCatch; 661 | private System.Windows.Forms.GroupBox gbRipple; 662 | private System.Windows.Forms.Button cmdMore; 663 | private System.Windows.Forms.GroupBox groupBox2; 664 | private System.Windows.Forms.Button cmdSwapPictures; 665 | private KittenPuppyControl ctrlPictures; 666 | private System.Windows.Forms.GroupBox groupBox3; 667 | private System.Windows.Forms.Label label1; 668 | private System.Windows.Forms.TextBox txtPassword; 669 | private System.Windows.Forms.Button buttonDemo; 670 | private System.Windows.Forms.GroupBox gpEasing; 671 | private System.Windows.Forms.Button buttonBackEaseOut; 672 | private System.Windows.Forms.Label lblTargetLine; 673 | private System.Windows.Forms.Button buttonSprings; 674 | private System.Windows.Forms.Button buttonLinear; 675 | private System.Windows.Forms.Button buttonFlash; 676 | private System.Windows.Forms.Button buttonEaseInEaseOut; 677 | private System.Windows.Forms.Button buttonDecelerate; 678 | private System.Windows.Forms.Button buttonCriticalDamp; 679 | private System.Windows.Forms.Button buttonAccelerate; 680 | private System.Windows.Forms.Button buttonThrowAndCatch; 681 | private System.Windows.Forms.Button buttonBounce; 682 | private System.Windows.Forms.Button buttonExpoEaseOut; 683 | private System.Windows.Forms.Button buttonElasticEaseOut; 684 | private System.Windows.Forms.Button buttonCubicEaseOut; 685 | private System.Windows.Forms.Button buttonCircEaseOut; 686 | private System.Windows.Forms.Button buttonBounceEaseOut; 687 | private System.Windows.Forms.Button buttonQuintEaseOut; 688 | private System.Windows.Forms.Button buttonQuartEaseOut; 689 | private System.Windows.Forms.Button buttonQuadEaseOut; 690 | private System.Windows.Forms.Button buttonSineEaseOut; 691 | private System.Windows.Forms.Label label3; 692 | private System.Windows.Forms.Label label2; 693 | private System.Windows.Forms.Label label4; 694 | private System.Windows.Forms.Label label5; 695 | private System.Windows.Forms.Label label6; 696 | private System.Windows.Forms.Label label7; 697 | private System.Windows.Forms.Label label9; 698 | private System.Windows.Forms.Label label8; 699 | } 700 | } 701 | 702 | -------------------------------------------------------------------------------- /src/TestApp/DemoForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows.Forms; 4 | using FluentTransitions; 5 | using System.Drawing; 6 | using System.Threading.Tasks; 7 | using FluentTransitions.Methods; 8 | 9 | namespace TestApp 10 | { 11 | /// 12 | /// This form demonstrates a number of animated transitions using the Transitions 13 | /// library. Each event handler (form-load, button click) demonstrates a different 14 | /// transition. 15 | /// 16 | public partial class DemoForm : Form 17 | { 18 | // Colors used by the change-form-color transition... 19 | private readonly Color BACKCOLOR_GRAY = Color.FromArgb(80, 80, 80); 20 | private readonly Color BACKCOLOR_WHITE = Color.FromArgb(255, 255, 255); 21 | 22 | // The left point of the 'bounce' and 'throw-and-catch' group boxes... 23 | private const int GROUP_BOX_LEFT = 12; 24 | 25 | // Strings used for the text transition... 26 | private const string STRING_SHORT1 = "I've got two tickets"; 27 | private const string STRING_LONG1 = "to Iron Maiden, baby."; 28 | private const string STRING_SHORT2 = "Come with me friday"; 29 | private const string STRING_LONG2 = "don't say maybe!"; 30 | 31 | private bool _moving = false; 32 | private int _initialEasingButtonLeft; 33 | 34 | /// 35 | /// Constructor. 36 | /// 37 | public DemoForm() 38 | { 39 | InitializeComponent(); 40 | 41 | Width = CollapsedFormWidth; 42 | } 43 | 44 | protected override void OnShown(EventArgs e) 45 | { 46 | base.OnShown(e); 47 | 48 | _initialEasingButtonLeft = buttonAccelerate.Left; 49 | Center(); 50 | } 51 | 52 | protected override void OnResizeEnd(EventArgs e) 53 | { 54 | base.OnResizeEnd(e); 55 | 56 | Center(); 57 | } 58 | 59 | private void Center() 60 | { 61 | if (_moving) 62 | return; 63 | 64 | _moving = true; 65 | 66 | Task.Delay(10).ContinueWith(_ => 67 | { 68 | BeginInvoke(new MethodInvoker(() => 69 | { 70 | var area = Screen.FromControl(this).WorkingArea; 71 | var targetX = (area.Width / 2) - (Width / 2); 72 | var targetY = (area.Height / 2) - (Height / 2); 73 | 74 | Transition 75 | .With(this, nameof(Left), targetX) 76 | .With(this, nameof(Top), targetY) 77 | .HookOnCompletionInUiThread(this, () => 78 | { 79 | _moving = false; 80 | if (Width <= CollapsedFormWidth) 81 | cmdMore.Text = "More »"; 82 | else 83 | cmdMore.Text = "« Less"; 84 | }) 85 | .Spring(TimeSpan.FromSeconds(0.75)); 86 | })); 87 | }); 88 | } 89 | 90 | /// 91 | /// Called when the "Throw and Catch" button is pressed. 92 | /// 93 | private void cmdThrowAndCatch_Click(object sender, EventArgs e) 94 | { 95 | // The button is 'thrown' up to the top of the group-box it is in 96 | // and then falls back down again. 97 | 98 | // The throw-and-catch transition starts the animation at a high rate and 99 | // decelerates to zero (as if against gravity) at the destination value. It 100 | // then accelerates the value (as if with gravity) back to the original value. 101 | 102 | Transition 103 | .With(cmdThrowAndCatch, nameof(Top), 12) 104 | .ThrowAndCatch(TimeSpan.FromSeconds(1.5)); 105 | } 106 | 107 | /// 108 | /// Called when the "Ripple" button is pressed. 109 | /// 110 | private void cmdRipple_Click(object sender, EventArgs e) 111 | { 112 | // The ripple is handled by the RippleControl user-control. 113 | // This performs 100 simultaneous animations to create the 114 | // ripple effect... 115 | ctrlRipple.Ripple(); 116 | } 117 | 118 | /// 119 | /// Called when the "Swap Pictures" button is pressed. 120 | /// 121 | private void cmdSwapPictures_Click(object sender, EventArgs e) 122 | { 123 | // The transition is handled by the KittenPuppyControl... 124 | ctrlPictures.TransitionPictures(); 125 | } 126 | 127 | /// 128 | /// Called when the "Text Transition" button is pressed. 129 | /// 130 | private void cmdTextTransition_Click(object sender, EventArgs e) 131 | { 132 | // We transition four properties simulataneously here: 133 | // - The two labels' text is changed. 134 | // - The two labels' colors are changed. 135 | 136 | // We work out the new text and colors to transition to... 137 | string text1, text2; 138 | Color color1, color2; 139 | if (lblTextTransition1.Text == STRING_SHORT1) 140 | { 141 | text1 = STRING_SHORT2; 142 | color1 = Color.Red; 143 | text2 = STRING_LONG2; 144 | color2 = Color.Blue; 145 | } 146 | else 147 | { 148 | text1 = STRING_SHORT1; 149 | color1 = Color.Blue; 150 | text2 = STRING_LONG1; 151 | color2 = Color.Red; 152 | } 153 | 154 | // We create a transition to animate all four properties at the same time... 155 | Transition 156 | .With(lblTextTransition1, nameof(Text), text1) 157 | .With(lblTextTransition1, nameof(ForeColor), color1) 158 | .With(lblTextTransition2, nameof(Text), text2) 159 | .With(lblTextTransition2, nameof(ForeColor), color2) 160 | .EaseInEaseOut(TimeSpan.FromSeconds(1)); 161 | } 162 | 163 | /// 164 | /// Called when the "Change Form Color" button is pressed. 165 | /// 166 | private void ctrlChangeFormColor_Click(object sender, EventArgs e) 167 | { 168 | // We alternate the form's background color... 169 | var destination = (BackColor == BACKCOLOR_GRAY) ? BACKCOLOR_WHITE : BACKCOLOR_GRAY; 170 | Transition 171 | .With(this, nameof(BackColor), destination) 172 | .Linear(TimeSpan.FromSeconds(1)); 173 | } 174 | 175 | /// 176 | /// Called when the "More" or "Less" button is pressed. 177 | /// 178 | private void cmdMore_Click(object sender, EventArgs e) 179 | { 180 | // We either show more screen or less screen depending on the current state. 181 | // We find out whether we need to make the screen wider or narrower... 182 | int formWidth; 183 | if (Width > CollapsedFormWidth) 184 | formWidth = CollapsedFormWidth; 185 | else 186 | formWidth = ExpandedFormWidth; 187 | _moving = true; 188 | // We animate it with an ease-in-ease-out transition... 189 | Transition 190 | .With(this, nameof(Width), formWidth) 191 | .HookOnCompletion(() => 192 | { 193 | _moving = false; 194 | Center(); 195 | }) 196 | .Spring(TimeSpan.FromSeconds(1)); 197 | } 198 | 199 | private void txtPassword_KeyDown(object sender, KeyEventArgs e) 200 | { 201 | if (e.KeyCode == Keys.Return) 202 | { 203 | Transition 204 | .With(txtPassword, nameof(Left), txtPassword.Left - 15) 205 | .Flash(4, TimeSpan.FromMilliseconds(120)); 206 | } 207 | } 208 | 209 | private void buttonDemo_Click(object sender, EventArgs e) 210 | { 211 | var buttons = new[] { buttonAccelerate, buttonBounce, buttonCriticalDamp, buttonDecelerate, buttonEaseInEaseOut, buttonFlash, buttonLinear, 212 | buttonThrowAndCatch, buttonBackEaseOut, buttonBounceEaseOut, buttonCircEaseOut, buttonCubicEaseOut, buttonElasticEaseOut, buttonExpoEaseOut, 213 | buttonQuadEaseOut, buttonQuartEaseOut, buttonQuintEaseOut, buttonSineEaseOut }; 214 | 215 | if (buttons.Any(b => b.Left != _initialEasingButtonLeft)) 216 | EaseBack(); 217 | else 218 | Ease(); 219 | } 220 | 221 | private void Ease() 222 | { 223 | buttonAccelerate_Click(null, null); 224 | buttonBounce_Click(null, null); 225 | buttonCriticalDamp_Click(null, null); 226 | buttonDecelerate_Click(null, null); 227 | buttonEaseInEaseOut_Click(null, null); 228 | buttonFlash_Click(null, null); 229 | buttonLinear_Click(null, null); 230 | buttonThrowAndCatch_Click(null, null); 231 | 232 | // easing functions 233 | 234 | buttonBackEaseOut_Click(null, null); 235 | buttonBounceEaseOut_Click(null, null); 236 | buttonCircEaseOut_Click(null, null); 237 | buttonCubicEaseOut_Click(null, null); 238 | buttonElasticEaseOut_Click(null, null); 239 | buttonExpoEaseOut_Click(null, null); 240 | buttonQuadEaseOut_Click(null, null); 241 | buttonQuartEaseOut_Click(null, null); 242 | buttonQuintEaseOut_Click(null, null); 243 | buttonSineEaseOut_Click(null, null); 244 | } 245 | 246 | private void EaseBack() 247 | { 248 | Transition 249 | .With(buttonAccelerate, nameof(Left), _initialEasingButtonLeft) 250 | .With(buttonBounce, nameof(Left), _initialEasingButtonLeft) 251 | .With(buttonCriticalDamp, nameof(Left), _initialEasingButtonLeft) 252 | .With(buttonDecelerate, nameof(Left), _initialEasingButtonLeft) 253 | .With(buttonEaseInEaseOut, nameof(Left), _initialEasingButtonLeft) 254 | .With(buttonFlash, nameof(Left), _initialEasingButtonLeft) 255 | .With(buttonLinear, nameof(Left), _initialEasingButtonLeft) 256 | .With(buttonThrowAndCatch, nameof(Left), _initialEasingButtonLeft) 257 | .With(buttonBackEaseOut, nameof(Left), _initialEasingButtonLeft) 258 | .With(buttonBounceEaseOut, nameof(Left), _initialEasingButtonLeft) 259 | .With(buttonCircEaseOut, nameof(Left), _initialEasingButtonLeft) 260 | .With(buttonCubicEaseOut, nameof(Left), _initialEasingButtonLeft) 261 | .With(buttonElasticEaseOut, nameof(Left), _initialEasingButtonLeft) 262 | .With(buttonExpoEaseOut, nameof(Left), _initialEasingButtonLeft) 263 | .With(buttonQuadEaseOut, nameof(Left), _initialEasingButtonLeft) 264 | .With(buttonQuartEaseOut, nameof(Left), _initialEasingButtonLeft) 265 | .With(buttonQuintEaseOut, nameof(Left), _initialEasingButtonLeft) 266 | .With(buttonSineEaseOut, nameof(Left), _initialEasingButtonLeft) 267 | .EaseInEaseOut(EaseDuration); 268 | } 269 | 270 | private void buttonAccelerate_Click(object sender, EventArgs e) 271 | { 272 | Transition 273 | .With(buttonAccelerate, nameof(Left), lblTargetLine.Left) 274 | .Accelerate(EaseDuration); 275 | } 276 | 277 | private void buttonBounce_Click(object sender, EventArgs e) 278 | { 279 | Transition 280 | .With(buttonBounce, nameof(Left), lblTargetLine.Left) 281 | .Bounce(EaseDuration); 282 | } 283 | 284 | private void buttonCriticalDamp_Click(object sender, EventArgs e) 285 | { 286 | Transition 287 | .With(buttonCriticalDamp, nameof(Left), lblTargetLine.Left) 288 | .CriticalDamp(EaseDuration); 289 | } 290 | 291 | private void buttonDecelerate_Click(object sender, EventArgs e) 292 | { 293 | Transition 294 | .With(buttonDecelerate, nameof(Left), lblTargetLine.Left) 295 | .Decelerate(EaseDuration); 296 | } 297 | 298 | private void buttonEaseInEaseOut_Click(object sender, EventArgs e) 299 | { 300 | Transition 301 | .With(buttonEaseInEaseOut, nameof(Left), lblTargetLine.Left) 302 | .EaseInEaseOut(EaseDuration); 303 | } 304 | 305 | private void buttonFlash_Click(object sender, EventArgs e) 306 | { 307 | Transition 308 | .With(buttonFlash, nameof(Left), lblTargetLine.Left) 309 | .Flash(2, TimeSpan.FromMilliseconds(EaseDuration.TotalMilliseconds / 5)); 310 | } 311 | 312 | private void buttonLinear_Click(object sender, EventArgs e) 313 | { 314 | Transition 315 | .With(buttonLinear, nameof(Left), lblTargetLine.Left) 316 | .Linear(EaseDuration); 317 | } 318 | 319 | private void buttonThrowAndCatch_Click(object sender, EventArgs e) 320 | { 321 | Transition 322 | .With(buttonThrowAndCatch, nameof(Left), lblTargetLine.Left) 323 | .ThrowAndCatch(EaseDuration); 324 | } 325 | 326 | private void buttonBackEaseOut_Click(object sender, EventArgs e) 327 | { 328 | Transition 329 | .With(buttonBackEaseOut, nameof(Left), lblTargetLine.Left) 330 | .EaseWithFunction(EasingFunctions.BackEaseOut, EaseDuration); 331 | } 332 | 333 | private void buttonBounceEaseOut_Click(object sender, EventArgs e) 334 | { 335 | Transition 336 | .With(buttonBounceEaseOut, nameof(Left), lblTargetLine.Left) 337 | .EaseWithFunction(EasingFunctions.BounceEaseOut, EaseDuration); 338 | } 339 | 340 | private void buttonCircEaseOut_Click(object sender, EventArgs e) 341 | { 342 | Transition 343 | .With(buttonCircEaseOut, nameof(Left), lblTargetLine.Left) 344 | .EaseWithFunction(EasingFunctions.CircEaseOut, EaseDuration); 345 | } 346 | 347 | private void buttonCubicEaseOut_Click(object sender, EventArgs e) 348 | { 349 | Transition 350 | .With(buttonCubicEaseOut, nameof(Left), lblTargetLine.Left) 351 | .EaseWithFunction(EasingFunctions.CubicEaseOut, EaseDuration); 352 | } 353 | 354 | private void buttonElasticEaseOut_Click(object sender, EventArgs e) 355 | { 356 | Transition 357 | .With(buttonElasticEaseOut, nameof(Left), lblTargetLine.Left) 358 | .EaseWithFunction(EasingFunctions.ElasticEaseOut, EaseDuration); 359 | } 360 | 361 | private void buttonExpoEaseOut_Click(object sender, EventArgs e) 362 | { 363 | Transition 364 | .With(buttonExpoEaseOut, nameof(Left), lblTargetLine.Left) 365 | .EaseWithFunction(EasingFunctions.ExpoEaseOut, EaseDuration); 366 | } 367 | 368 | private void buttonQuadEaseOut_Click(object sender, EventArgs e) 369 | { 370 | Transition 371 | .With(buttonQuadEaseOut, nameof(Left), lblTargetLine.Left) 372 | .EaseWithFunction(EasingFunctions.QuadEaseOut, EaseDuration); 373 | } 374 | 375 | private void buttonQuartEaseOut_Click(object sender, EventArgs e) 376 | { 377 | Transition 378 | .With(buttonQuartEaseOut, nameof(Left), lblTargetLine.Left) 379 | .EaseWithFunction(EasingFunctions.QuartEaseOut, EaseDuration); 380 | } 381 | 382 | private void buttonQuintEaseOut_Click(object sender, EventArgs e) 383 | { 384 | Transition 385 | .With(buttonQuintEaseOut, nameof(Left), lblTargetLine.Left) 386 | .EaseWithFunction(EasingFunctions.QuintEaseOut, EaseDuration); 387 | } 388 | 389 | private void buttonSineEaseOut_Click(object sender, EventArgs e) 390 | { 391 | Transition 392 | .With(buttonSineEaseOut, nameof(Left), lblTargetLine.Left) 393 | .EaseWithFunction(EasingFunctions.SineEaseOut, EaseDuration); 394 | } 395 | 396 | private TimeSpan EaseDuration { get; } = TimeSpan.FromSeconds(1); 397 | 398 | private int CollapsedFormWidth => gpEasing.Right + gpEasing.Left + ((Width - ClientSize.Width) / 2); 399 | 400 | private int ExpandedFormWidth => groupBox1.Right + gpEasing.Left + ((Width - ClientSize.Width) / 2); 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /src/TestApp/DemoForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | For this demo, only the EaseOut functions are shown. 122 | However, each easing function is available as EaseIn, EaseOut, EaseInOut and EaseOutIn. 123 | 124 | To use Mauro's built-in easing functions, simpy use EaseWithFunction(): 125 | 126 | Transition 127 | .With(...) 128 | .EaseWithFunction(EasingFunctions.YOURCHOICE, duration); 129 | 130 | 131 | These are transitions implemented with easing functions. Easing functions are mathematical functions 132 | defining "how things change over time". 133 | 134 | Mauro Sampietro published an article on easing functions on CodeProject in 2014. 135 | 136 | FluentTransitions fully supports easing functions starting with version 2.0 and provides 137 | Mauro's easing implementations as built-in functions (see below). It is also possible to use custom easings. 138 | 139 | 140 | These are the animations of the original project Richard S. Shepherd uploaded to Google Code back in 2009. 141 | After using this project for a long time, I (Andreas Wäscher) decided to modernize the code and migrate it to modern .NET platforms. 142 | 143 | -------------------------------------------------------------------------------- /src/TestApp/KittenPuppyControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp 2 | { 3 | partial class KittenPuppyControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.ctrlPuppy = new System.Windows.Forms.PictureBox(); 32 | this.ctrlKitten = new System.Windows.Forms.PictureBox(); 33 | ((System.ComponentModel.ISupportInitialize)(this.ctrlPuppy)).BeginInit(); 34 | ((System.ComponentModel.ISupportInitialize)(this.ctrlKitten)).BeginInit(); 35 | this.SuspendLayout(); 36 | // 37 | // ctrlPuppy 38 | // 39 | this.ctrlPuppy.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 40 | | System.Windows.Forms.AnchorStyles.Left) 41 | | System.Windows.Forms.AnchorStyles.Right))); 42 | this.ctrlPuppy.Image = global::TestApp.Properties.Resources.puppy; 43 | this.ctrlPuppy.Location = new System.Drawing.Point(0, 0); 44 | this.ctrlPuppy.Name = "ctrlPuppy"; 45 | this.ctrlPuppy.Size = new System.Drawing.Size(150, 150); 46 | this.ctrlPuppy.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; 47 | this.ctrlPuppy.TabIndex = 1; 48 | this.ctrlPuppy.TabStop = false; 49 | // 50 | // ctrlKitten 51 | // 52 | this.ctrlKitten.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 53 | | System.Windows.Forms.AnchorStyles.Left) 54 | | System.Windows.Forms.AnchorStyles.Right))); 55 | this.ctrlKitten.Image = global::TestApp.Properties.Resources.kitten; 56 | this.ctrlKitten.Location = new System.Drawing.Point(0, 0); 57 | this.ctrlKitten.Name = "ctrlKitten"; 58 | this.ctrlKitten.Size = new System.Drawing.Size(150, 150); 59 | this.ctrlKitten.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom; 60 | this.ctrlKitten.TabIndex = 0; 61 | this.ctrlKitten.TabStop = false; 62 | // 63 | // KittenPuppyControl 64 | // 65 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 66 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 67 | this.Controls.Add(this.ctrlPuppy); 68 | this.Controls.Add(this.ctrlKitten); 69 | this.Name = "KittenPuppyControl"; 70 | ((System.ComponentModel.ISupportInitialize)(this.ctrlPuppy)).EndInit(); 71 | ((System.ComponentModel.ISupportInitialize)(this.ctrlKitten)).EndInit(); 72 | this.ResumeLayout(false); 73 | 74 | } 75 | 76 | #endregion 77 | 78 | private System.Windows.Forms.PictureBox ctrlKitten; 79 | private System.Windows.Forms.PictureBox ctrlPuppy; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/TestApp/KittenPuppyControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using FluentTransitions; 4 | 5 | namespace TestApp 6 | { 7 | /// 8 | /// This is a simple user-control that hosts two picture-boxes (one showing 9 | /// a kitten and the other showing a puppy). The transitionPictures method 10 | /// performs a random animated transition between the two pictures. 11 | /// 12 | public partial class KittenPuppyControl : UserControl 13 | { 14 | private PictureBox _activePicture = null; 15 | private PictureBox _inactivePicture = null; 16 | private readonly Random _random = new Random(); 17 | 18 | /// 19 | /// Constructor. 20 | /// 21 | public KittenPuppyControl() 22 | { 23 | InitializeComponent(); 24 | _activePicture = ctrlPuppy; 25 | _inactivePicture = ctrlKitten; 26 | } 27 | 28 | /// 29 | /// Performs a random tarnsition between the two pictures. 30 | /// 31 | public void TransitionPictures() 32 | { 33 | // We randomly choose where the current image is going to 34 | // slide off to (and where we are going to slide the inactive 35 | // image in from)... 36 | int destinationLeft = (_random.Next(2) == 0) ? Width : -Width; 37 | int destinationTop = (_random.Next(3) - 1) * Height; 38 | 39 | // We move the inactive image to this location... 40 | SuspendLayout(); 41 | _inactivePicture.Top = destinationTop; 42 | _inactivePicture.Left = destinationLeft; 43 | _inactivePicture.BringToFront(); 44 | ResumeLayout(); 45 | 46 | // We perform the transition which moves the active image off the 47 | // screen, and the inactive one onto the screen... 48 | Transition 49 | .With(_inactivePicture, nameof(Left), 0) 50 | .With(_inactivePicture, nameof(Top), 0) 51 | .With(_activePicture, nameof(Left), destinationLeft) 52 | .With(_activePicture, nameof(Top), destinationTop) 53 | .EaseInEaseOut(TimeSpan.FromSeconds(1)); 54 | 55 | // We swap over which image is active and inactive for next time 56 | // the function is called... 57 | var temp = _activePicture; 58 | _activePicture = _inactivePicture; 59 | _inactivePicture = temp; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/TestApp/KittenPuppyControl.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/TestApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | 4 | namespace TestApp 5 | { 6 | static class Program 7 | { 8 | /// 9 | /// The main entry point for the application. 10 | /// 11 | [STAThread] 12 | static void Main() 13 | { 14 | Application.EnableVisualStyles(); 15 | Application.SetCompatibleTextRenderingDefault(false); 16 | Application.Run(new DemoForm()); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/TestApp/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TestApp.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TestApp.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap kitten { 67 | get { 68 | object obj = ResourceManager.GetObject("kitten", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Bitmap. 75 | /// 76 | internal static System.Drawing.Bitmap puppy { 77 | get { 78 | object obj = ResourceManager.GetObject("puppy", resourceCulture); 79 | return ((System.Drawing.Bitmap)(obj)); 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/TestApp/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\puppy.jpg;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\Resources\kitten.jpg;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | -------------------------------------------------------------------------------- /src/TestApp/Resources/kitten.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/src/TestApp/Resources/kitten.jpg -------------------------------------------------------------------------------- /src/TestApp/Resources/puppy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awaescher/FluentTransitions/8d2a72cbee7c69e81522cb76b2b297ba7718ca79/src/TestApp/Resources/puppy.jpg -------------------------------------------------------------------------------- /src/TestApp/RippleControl.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp 2 | { 3 | partial class RippleControl 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.SuspendLayout(); 32 | // 33 | // RippleControl 34 | // 35 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 36 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 37 | this.Name = "RippleControl"; 38 | this.Load += new System.EventHandler(this.RippleControl_Load); 39 | this.ResumeLayout(false); 40 | 41 | } 42 | 43 | #endregion 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/TestApp/RippleControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Windows.Forms; 5 | using FluentTransitions; 6 | 7 | namespace TestApp 8 | { 9 | /// 10 | /// This control shows a color-rippling effect when you call its ripple() 11 | /// method. It holds a grid of 10x10 label controls with an initial white 12 | /// background color. The ripple method uses a separate transition on each 13 | /// label to move it to pink and back again. 14 | /// 15 | /// The code to set up the labels (in the Load method) is a bit complicated, 16 | /// but the ripple transition itself is very simple. 17 | /// 18 | public partial class RippleControl : UserControl 19 | { 20 | // A collection of cell-infos, i.e. info on each label on the control... 21 | private readonly IList _cellInfos = new List(); 22 | 23 | /// 24 | /// Constructor. 25 | /// 26 | public RippleControl() 27 | { 28 | InitializeComponent(); 29 | } 30 | 31 | /// 32 | /// Starts the ripple effect. 33 | /// 34 | public void Ripple() 35 | { 36 | // We run a transition on each of the labels shown on the control. 37 | // This means that we will be running 100 simulataneous transitions... 38 | foreach (var info in _cellInfos) 39 | { 40 | Transition 41 | .With(info.Control, nameof(BackColor), Color.Pink) 42 | .Flash(1, TimeSpan.FromMilliseconds(info.TransitionInterval)); 43 | } 44 | } 45 | 46 | /// 47 | /// Called when the control is first loaded. 48 | /// 49 | private void RippleControl_Load(object sender, EventArgs e) 50 | { 51 | double cellWidth = Width / 10.0; 52 | double cellHeight = Height / 10.0; 53 | 54 | // We set up a 10x10 grid of labels... 55 | double top = 0; 56 | for (int row = 0; row < 10; ++row) 57 | { 58 | double left = 0; 59 | double bottom = top + cellHeight; 60 | 61 | for (int col = 0; col < 10; ++col) 62 | { 63 | // We work out the size of this label... 64 | double right = left + cellWidth; 65 | int leftInt = (int)left; 66 | int topInt = (int)top; 67 | int rightInt = (int)right; 68 | int bottomInt = (int)bottom; 69 | int width = rightInt - leftInt; 70 | int height = bottomInt - topInt; 71 | 72 | // We create the label... 73 | var label = new Label 74 | { 75 | Left = leftInt, 76 | Top = topInt, 77 | Width = width, 78 | Height = height, 79 | BackColor = Color.White 80 | }; 81 | 82 | // And add it to the control... 83 | Controls.Add(label); 84 | 85 | // We work out a transition time for it, and store the information 86 | // to use when we do the ripple effect... 87 | int transitionInterval = row * 100 + col * 100; 88 | _cellInfos.Add(new CellInfo { Control = label, TransitionInterval = transitionInterval }); 89 | 90 | // The left for the next column is the right for this one... 91 | left = right; 92 | } 93 | 94 | // The top of the next row is the bottom of this one... 95 | top = bottom; 96 | } 97 | } 98 | 99 | // A small class that holds information about one of the labels on the control. 100 | private class CellInfo 101 | { 102 | public Control Control { get; set; } 103 | 104 | public int TransitionInterval { get; set; } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/TestApp/RippleControl.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /src/TestApp/TestApp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net48;net6.0-windows;net8.0-windows 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | true 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/TestApp/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | --------------------------------------------------------------------------------