├── .gitattributes ├── .gitignore ├── EasyCarousel.Control ├── EasyCarousel.Control.csproj ├── EasyCarousel.cs ├── Properties │ └── AssemblyInfo.cs └── project.json ├── EasyCarousel.Sample ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── Adventure-Time-Wallpaper-Backgrounds-A8Z.jpg │ ├── Gravity-Falls.png │ ├── LockScreenLogo.scale-200.png │ ├── MNXxLiq.jpg │ ├── S2e15_playing_interdimensional_chess.png │ ├── SplashScreen.scale-200.png │ ├── Square150x150Logo.scale-200.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── StoreLogo.png │ ├── Wide310x150Logo.scale-200.png │ ├── adventure_time_1920x1080_47767.jpg │ ├── c-1600x1200px.jpg │ ├── cn_cee_we_bare_bears__cn3__wallpaper_03_1600x900.jpg │ ├── hjyKMii94AsGAcrVTY0lUo3GCV9.jpg │ ├── honda-monkey-Kumamon-bike-picture-1000x502.jpg │ ├── maxresdefault.jpg │ ├── s9HNQHG.jpg │ ├── wallpaper1208-1-3.jpg │ └── we-bare-bears.jpg ├── EasyCarousel.Sample.csproj ├── MainPage.xaml ├── MainPage.xaml.cs ├── Package.appxmanifest ├── Properties │ ├── AssemblyInfo.cs │ └── Default.rd.xml └── project.json ├── EasyCarousel.sln ├── README.md ├── screencast_1.gif ├── screencast_2.gif └── screencast_3.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 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 | 24 | # Visual Studio 2015 cache/options directory 25 | .vs/ 26 | # Uncomment if you have tasks that create the project's static files in wwwroot 27 | #wwwroot/ 28 | 29 | # MSTest test Results 30 | [Tt]est[Rr]esult*/ 31 | [Bb]uild[Ll]og.* 32 | 33 | # NUNIT 34 | *.VisualState.xml 35 | TestResult.xml 36 | 37 | # Build Results of an ATL Project 38 | [Dd]ebugPS/ 39 | [Rr]eleasePS/ 40 | dlldata.c 41 | 42 | # DNX 43 | project.lock.json 44 | artifacts/ 45 | 46 | *_i.c 47 | *_p.c 48 | *_i.h 49 | *.ilk 50 | *.meta 51 | *.obj 52 | *.pch 53 | *.pdb 54 | *.pgc 55 | *.pgd 56 | *.rsp 57 | *.sbr 58 | *.tlb 59 | *.tli 60 | *.tlh 61 | *.tmp 62 | *.tmp_proj 63 | *.log 64 | *.vspscc 65 | *.vssscc 66 | .builds 67 | *.pidb 68 | *.svclog 69 | *.scc 70 | 71 | # Chutzpah Test files 72 | _Chutzpah* 73 | 74 | # Visual C++ cache files 75 | ipch/ 76 | *.aps 77 | *.ncb 78 | *.opendb 79 | *.opensdf 80 | *.sdf 81 | *.cachefile 82 | 83 | # Visual Studio profiler 84 | *.psess 85 | *.vsp 86 | *.vspx 87 | *.sap 88 | 89 | # TFS 2012 Local Workspace 90 | $tf/ 91 | 92 | # Guidance Automation Toolkit 93 | *.gpState 94 | 95 | # ReSharper is a .NET coding add-in 96 | _ReSharper*/ 97 | *.[Rr]e[Ss]harper 98 | *.DotSettings.user 99 | 100 | # JustCode is a .NET coding add-in 101 | .JustCode 102 | 103 | # TeamCity is a build add-in 104 | _TeamCity* 105 | 106 | # DotCover is a Code Coverage Tool 107 | *.dotCover 108 | 109 | # NCrunch 110 | _NCrunch_* 111 | .*crunch*.local.xml 112 | nCrunchTemp_* 113 | 114 | # MightyMoose 115 | *.mm.* 116 | AutoTest.Net/ 117 | 118 | # Web workbench (sass) 119 | .sass-cache/ 120 | 121 | # Installshield output folder 122 | [Ee]xpress/ 123 | 124 | # DocProject is a documentation generator add-in 125 | DocProject/buildhelp/ 126 | DocProject/Help/*.HxT 127 | DocProject/Help/*.HxC 128 | DocProject/Help/*.hhc 129 | DocProject/Help/*.hhk 130 | DocProject/Help/*.hhp 131 | DocProject/Help/Html2 132 | DocProject/Help/html 133 | 134 | # Click-Once directory 135 | publish/ 136 | 137 | # Publish Web Output 138 | *.[Pp]ublish.xml 139 | *.azurePubxml 140 | # TODO: Comment the next line if you want to checkin your web deploy settings 141 | # but database connection strings (with potential passwords) will be unencrypted 142 | *.pubxml 143 | *.publishproj 144 | 145 | # NuGet Packages 146 | *.nupkg 147 | # The packages folder can be ignored because of Package Restore 148 | **/packages/* 149 | # except build/, which is used as an MSBuild target. 150 | !**/packages/build/ 151 | # Uncomment if necessary however generally it will be regenerated when needed 152 | #!**/packages/repositories.config 153 | # NuGet v3's project.json files produces more ignoreable files 154 | *.nuget.props 155 | *.nuget.targets 156 | 157 | # Microsoft Azure Build Output 158 | csx/ 159 | *.build.csdef 160 | 161 | # Microsoft Azure Emulator 162 | ecf/ 163 | rcf/ 164 | 165 | # Microsoft Azure ApplicationInsights config file 166 | ApplicationInsights.config 167 | 168 | # Windows Store app package directory 169 | AppPackages/ 170 | BundleArtifacts/ 171 | 172 | # Visual Studio cache files 173 | # files ending in .cache can be ignored 174 | *.[Cc]ache 175 | # but keep track of directories ending in .cache 176 | !*.[Cc]ache/ 177 | 178 | # Others 179 | ClientBin/ 180 | ~$* 181 | *~ 182 | *.dbmdl 183 | *.dbproj.schemaview 184 | *.pfx 185 | *.publishsettings 186 | node_modules/ 187 | orleans.codegen.cs 188 | 189 | # RIA/Silverlight projects 190 | Generated_Code/ 191 | 192 | # Backup & report files from converting an old project file 193 | # to a newer Visual Studio version. Backup files are not needed, 194 | # because we have git ;-) 195 | _UpgradeReport_Files/ 196 | Backup*/ 197 | UpgradeLog*.XML 198 | UpgradeLog*.htm 199 | 200 | # SQL Server files 201 | *.mdf 202 | *.ldf 203 | 204 | # Business Intelligence projects 205 | *.rdl.data 206 | *.bim.layout 207 | *.bim_*.settings 208 | 209 | # Microsoft Fakes 210 | FakesAssemblies/ 211 | 212 | # GhostDoc plugin setting file 213 | *.GhostDoc.xml 214 | 215 | # Node.js Tools for Visual Studio 216 | .ntvs_analysis.dat 217 | 218 | # Visual Studio 6 build log 219 | *.plg 220 | 221 | # Visual Studio 6 workspace options file 222 | *.opt 223 | 224 | # Visual Studio LightSwitch build output 225 | **/*.HTMLClient/GeneratedArtifacts 226 | **/*.DesktopClient/GeneratedArtifacts 227 | **/*.DesktopClient/ModelManifest.xml 228 | **/*.Server/GeneratedArtifacts 229 | **/*.Server/ModelManifest.xml 230 | _Pvt_Extensions 231 | 232 | # Paket dependency manager 233 | .paket/paket.exe 234 | 235 | # FAKE - F# Make 236 | .fake/ 237 | -------------------------------------------------------------------------------- /EasyCarousel.Control/EasyCarousel.Control.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {178A2B38-A1D5-42CC-A2D4-07DA48E3970B} 8 | Library 9 | Properties 10 | EasyCarousel.Control 11 | EasyCarousel.Control 12 | en-US 13 | UAP 14 | 10.0.10586.0 15 | 10.0.10240.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | false 20 | 21 | 22 | AnyCPU 23 | true 24 | full 25 | false 26 | bin\Debug\ 27 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 28 | prompt 29 | 4 30 | 31 | 32 | AnyCPU 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE;NETFX_CORE;WINDOWS_UWP 37 | prompt 38 | 4 39 | 40 | 41 | x86 42 | true 43 | bin\x86\Debug\ 44 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 45 | ;2008 46 | full 47 | x86 48 | false 49 | prompt 50 | 51 | 52 | x86 53 | bin\x86\Release\ 54 | TRACE;NETFX_CORE;WINDOWS_UWP 55 | true 56 | ;2008 57 | pdbonly 58 | x86 59 | false 60 | prompt 61 | 62 | 63 | ARM 64 | true 65 | bin\ARM\Debug\ 66 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 67 | ;2008 68 | full 69 | ARM 70 | false 71 | prompt 72 | 73 | 74 | ARM 75 | bin\ARM\Release\ 76 | TRACE;NETFX_CORE;WINDOWS_UWP 77 | true 78 | ;2008 79 | pdbonly 80 | ARM 81 | false 82 | prompt 83 | 84 | 85 | x64 86 | true 87 | bin\x64\Debug\ 88 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 89 | ;2008 90 | full 91 | x64 92 | false 93 | prompt 94 | 95 | 96 | x64 97 | bin\x64\Release\ 98 | TRACE;NETFX_CORE;WINDOWS_UWP 99 | true 100 | ;2008 101 | pdbonly 102 | x64 103 | false 104 | prompt 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 14.0 116 | 117 | 118 | 119 | 120 | 121 | 128 | -------------------------------------------------------------------------------- /EasyCarousel.Control/EasyCarousel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.Linq; 6 | using System.Numerics; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Windows.ApplicationModel; 10 | using Windows.Foundation; 11 | using Windows.UI.Composition; 12 | using Windows.UI.Xaml; 13 | using Windows.UI.Xaml.Controls; 14 | using Windows.UI.Xaml.Hosting; 15 | using Windows.UI.Xaml.Input; 16 | using Windows.UI.Xaml.Media; 17 | 18 | namespace Marduk.Controls 19 | { 20 | public sealed class EasyCarousel : Panel 21 | { 22 | private DataTemplate _itemTemplate; 23 | 24 | private RectangleGeometry _viewportRect; 25 | 26 | private Compositor _carouselCompositor; 27 | 28 | private DispatcherTimer _timer; 29 | 30 | public event EventHandler ItemTapped; 31 | 32 | #region Properties 33 | 34 | public int SelectedIndex 35 | { 36 | get { return (int)GetValue(SelectedIndexProperty); } 37 | set { SetValue(SelectedIndexProperty, value); } 38 | } 39 | 40 | public static readonly DependencyProperty SelectedIndexProperty = 41 | DependencyProperty.Register("SelectedIndex", typeof(int), typeof(EasyCarousel), new PropertyMetadata(0, OnSelectedIndexChanged)); 42 | 43 | public object SelectedItem 44 | { 45 | get { return (int)GetValue(SelectedItemProperty); } 46 | private set { SetValue(SelectedItemProperty, value); } 47 | } 48 | 49 | public static readonly DependencyProperty SelectedItemProperty = 50 | DependencyProperty.Register("SelectedItem", typeof(object), typeof(EasyCarousel), new PropertyMetadata(null, null)); 51 | 52 | public double Duration 53 | { 54 | get { return (double)GetValue(DurationProperty); } 55 | set { SetValue(DurationProperty, value); } 56 | } 57 | 58 | public static readonly DependencyProperty DurationProperty = 59 | DependencyProperty.Register("Duration", typeof(double), typeof(EasyCarousel), new PropertyMetadata(300d, null)); 60 | 61 | public double ItemWidth 62 | { 63 | get { return (double)GetValue(ItemWidthProperty); } 64 | set { SetValue(ItemWidthProperty, value); } 65 | } 66 | 67 | public static readonly DependencyProperty ItemWidthProperty = 68 | DependencyProperty.Register("ItemWidth", typeof(double), typeof(EasyCarousel), new PropertyMetadata(310d, null)); 69 | 70 | public double ItemHeight 71 | { 72 | get { return (double)GetValue(ItemHeightProperty); } 73 | set { SetValue(ItemHeightProperty, value); } 74 | } 75 | 76 | public static readonly DependencyProperty ItemHeightProperty = 77 | DependencyProperty.Register("ItemHeight", typeof(double), typeof(EasyCarousel), new PropertyMetadata(150d, null)); 78 | 79 | public object ItemsSource 80 | { 81 | get { return GetValue(ItemsSourceProperty); } 82 | set { SetValue(ItemsSourceProperty, value); } 83 | } 84 | 85 | public static readonly DependencyProperty ItemsSourceProperty = 86 | DependencyProperty.Register("ItemsSource", typeof(object), typeof(EasyCarousel), new PropertyMetadata(null, OnItemsSourceChanged)); 87 | 88 | public double ItemSpacing 89 | { 90 | get { return Convert.ToDouble(GetValue(ItemSpacingProperty)); } 91 | set { SetValue(ItemSpacingProperty, value); } 92 | } 93 | 94 | public static readonly DependencyProperty ItemSpacingProperty = 95 | DependencyProperty.Register("ItemSpacing", typeof(double), typeof(EasyCarousel), new PropertyMetadata(0, OnItemSpacingChanged)); 96 | 97 | public DataTemplate ItemTemplate 98 | { 99 | get { return _itemTemplate; } 100 | set { _itemTemplate = value; } 101 | } 102 | 103 | public bool DetectPointerWheelChange 104 | { 105 | get { return (bool)(GetValue(DetectPointerWheelChangeProperty)); } 106 | set { SetValue(DetectPointerWheelChangeProperty, value); } 107 | } 108 | 109 | public static readonly DependencyProperty DetectPointerWheelChangeProperty = 110 | DependencyProperty.Register("DetectPointerWheelChange", typeof(bool), typeof(EasyCarousel), new PropertyMetadata(true, null)); 111 | 112 | public bool AutoShift 113 | { 114 | get { return (bool)(GetValue(AutoShiftProperty)); } 115 | set { SetValue(AutoShiftProperty, value); } 116 | } 117 | 118 | public static readonly DependencyProperty AutoShiftProperty = 119 | DependencyProperty.Register("AutoShift", typeof(bool), typeof(EasyCarousel), new PropertyMetadata(false, OnAutoShiftChanged)); 120 | 121 | public TimeSpan Interval 122 | { 123 | get { return (TimeSpan)(GetValue(IntervalProperty)); } 124 | set { SetValue(IntervalProperty, value); } 125 | } 126 | 127 | public static readonly DependencyProperty IntervalProperty = 128 | DependencyProperty.Register("Interval", typeof(TimeSpan), typeof(EasyCarousel), new PropertyMetadata(TimeSpan.FromSeconds(3), OnIntervalChanged)); 129 | 130 | public CarouselShiftingDirection ShiftingDirection 131 | { 132 | get { return (CarouselShiftingDirection)GetValue(ShiftingDirectionProperty); } 133 | set { SetValue(ShiftingDirectionProperty, value); } 134 | } 135 | 136 | public static readonly DependencyProperty ShiftingDirectionProperty = 137 | DependencyProperty.Register("ShiftingDirection", typeof(CarouselShiftingDirection), typeof(EasyCarousel), new PropertyMetadata(CarouselShiftingDirection.Forward, null)); 138 | 139 | #endregion 140 | 141 | /// 142 | /// Carousel Control 143 | /// 144 | public EasyCarousel() 145 | { 146 | if (DesignMode.DesignModeEnabled) 147 | return; 148 | 149 | Visual carouselVisual = ElementCompositionPreview.GetElementVisual(this); 150 | _carouselCompositor = carouselVisual.Compositor; 151 | _timer = new DispatcherTimer {Interval = this.Interval}; 152 | 153 | this.ManipulationMode = ManipulationModes.TranslateX; 154 | 155 | this.Tapped += OnTapped; 156 | this.ManipulationCompleted += OnManipulationCompleted; 157 | this.PointerWheelChanged += OnPointerWheelChanged; 158 | _timer.Tick += (sender, o) => 159 | { 160 | switch (this.ShiftingDirection) 161 | { 162 | case CarouselShiftingDirection.Forward: 163 | MoveForward(); 164 | break; 165 | case CarouselShiftingDirection.Backward: 166 | MoveBackward(); 167 | break; 168 | } 169 | }; 170 | } 171 | 172 | #region Event handlers 173 | 174 | private void OnTapped(object sender, TappedRoutedEventArgs args) 175 | { 176 | FrameworkElement fxElement = args.OriginalSource as FrameworkElement; 177 | ItemTapped?.Invoke(sender, fxElement); 178 | } 179 | 180 | private void OnManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) 181 | { 182 | if (e.Cumulative.Translation.X < -100) 183 | { 184 | MoveForward(); 185 | } 186 | 187 | if (e.Cumulative.Translation.X > 100) 188 | { 189 | MoveBackward(); 190 | } 191 | } 192 | 193 | private void OnPointerWheelChanged(object sender, PointerRoutedEventArgs e) 194 | { 195 | e.Handled = true; 196 | if (DetectPointerWheelChange) 197 | { 198 | if (e.GetCurrentPoint(this).Properties.MouseWheelDelta>0) 199 | { 200 | MoveForward(); 201 | } 202 | else 203 | { 204 | MoveBackward(); 205 | } 206 | } 207 | } 208 | 209 | private static void OnSelectedIndexChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 210 | { 211 | if (e.NewValue == null) 212 | return; 213 | 214 | if (e.NewValue == e.OldValue) 215 | return; 216 | 217 | EasyCarousel instance = d as EasyCarousel; 218 | 219 | if (instance == null) 220 | return; 221 | 222 | if (DesignMode.DesignModeEnabled) 223 | return; 224 | 225 | instance.ShiftElementsAnimatedly((int)e.NewValue); 226 | 227 | instance.SelectedItem = (instance.Children[(int)e.NewValue] as FrameworkElement)?.DataContext; 228 | } 229 | 230 | private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 231 | { 232 | if (e.NewValue == null) 233 | return; 234 | 235 | if (e.NewValue == e.OldValue) 236 | return; 237 | 238 | EasyCarousel instance = d as EasyCarousel; 239 | 240 | if (instance == null) 241 | return; 242 | 243 | instance.BindItems(); 244 | 245 | instance.SelectedItem = (instance.Children[instance.SelectedIndex] as FrameworkElement)?.DataContext; 246 | } 247 | 248 | private static void OnItemSpacingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 249 | { 250 | if (e.NewValue == null) 251 | return; 252 | 253 | if (e.NewValue == e.OldValue) 254 | return; 255 | 256 | EasyCarousel instance = d as EasyCarousel; 257 | 258 | if (instance == null) 259 | return; 260 | 261 | instance.InvalidateArrange(); 262 | } 263 | 264 | private static void OnAutoShiftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 265 | { 266 | if (e.NewValue == null) 267 | return; 268 | 269 | if (e.NewValue == e.OldValue) 270 | return; 271 | 272 | EasyCarousel instance = d as EasyCarousel; 273 | 274 | if (instance == null) 275 | return; 276 | 277 | if ((bool)e.NewValue) 278 | { 279 | instance._timer.Interval = instance.Interval; 280 | instance._timer.Start(); 281 | } 282 | else 283 | { 284 | instance._timer.Stop(); 285 | } 286 | } 287 | 288 | private static void OnIntervalChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 289 | { 290 | if (e.NewValue == null) 291 | return; 292 | 293 | if (e.NewValue == e.OldValue) 294 | return; 295 | 296 | EasyCarousel instance = d as EasyCarousel; 297 | 298 | if (instance == null) 299 | return; 300 | 301 | instance._timer.Interval = (TimeSpan)e.NewValue; 302 | } 303 | 304 | #endregion 305 | 306 | private void BindItems() 307 | { 308 | if (ItemsSource == null || (ItemsSource as IEnumerable) == null) 309 | return; 310 | 311 | this.Children.Clear(); 312 | 313 | foreach (object item in (IEnumerable)ItemsSource) 314 | this.CreateElement(item); 315 | } 316 | 317 | private void CreateElement(object item) 318 | { 319 | FrameworkElement element; 320 | 321 | if (this.ItemTemplate != null) 322 | { 323 | element = this.ItemTemplate.LoadContent() as FrameworkElement; 324 | } 325 | else 326 | { 327 | element = new TextBlock(); 328 | } 329 | 330 | if (element == null) 331 | return; 332 | 333 | element.DataContext = item; 334 | element.RenderTransformOrigin = new Point(0.5, 0.5); 335 | 336 | this.Children.Add(element); 337 | } 338 | 339 | /// 340 | /// Shift items with composition-powered animations. 341 | /// 342 | /// 343 | private void ShiftElementsAnimatedly(int targetIndex) 344 | { 345 | if (targetIndex < 0 || this.Children.Count <= 0) 346 | return; 347 | 348 | int sliceLength = (int)Math.Ceiling((double)this.Children.Count / 2); 349 | bool equallyDivided = (this.Children.Count % 2 == 0); 350 | 351 | int pointer = targetIndex; 352 | 353 | for (int i = 0; i < (!equallyDivided ? sliceLength : sliceLength + 1); i++) 354 | { 355 | pointer = (targetIndex + i) % this.Children.Count; 356 | 357 | UIElement element = this.Children[pointer]; 358 | 359 | double offsetX = i * (this.ItemWidth + this.ItemSpacing); 360 | 361 | //Do not animate elements outside of the viewport. 362 | ShiftElement(element, offsetX, Math.Abs(offsetX) < _viewportRect.Rect.Width); 363 | } 364 | 365 | for (int i = 1; i < sliceLength; i++) 366 | { 367 | pointer = (pointer + 1) % this.Children.Count; 368 | 369 | UIElement element = this.Children[pointer]; 370 | 371 | double offsetX = (i - sliceLength) * (this.ItemWidth + this.ItemSpacing); 372 | 373 | //Do not animate elements outside of the viewport. 374 | ShiftElement(element, offsetX, Math.Abs(offsetX) < _viewportRect.Rect.Width); 375 | } 376 | } 377 | 378 | /// 379 | /// Shift items with classic TranslateTransforms. 380 | /// 381 | /// 382 | private void ShiftElements(int targetIndex) 383 | { 384 | if (targetIndex < 0 || this.Children.Count <= 0) 385 | return; 386 | 387 | int sliceLength = (int)Math.Ceiling((double)this.Children.Count / 2); 388 | 389 | int pointer = targetIndex; 390 | 391 | for (int i = 0; i < sliceLength; i++) 392 | { 393 | pointer = (targetIndex + i) % this.Children.Count; 394 | 395 | UIElement element = this.Children[pointer]; 396 | 397 | double offsetX = i * this.ItemWidth; 398 | 399 | element.RenderTransform = new TranslateTransform { X = offsetX }; 400 | } 401 | 402 | for (int i = 1; i < sliceLength; i++) 403 | { 404 | pointer = (pointer + 1) % this.Children.Count; 405 | 406 | UIElement element = this.Children[pointer]; 407 | 408 | double offsetX = (i - sliceLength) * this.ItemWidth; 409 | 410 | element.RenderTransform = new TranslateTransform { X = offsetX }; 411 | } 412 | } 413 | 414 | private void ShiftElement(UIElement element, double offsetX, bool useAnimation) 415 | { 416 | Visual elementVisual = ElementCompositionPreview.GetElementVisual(element); 417 | 418 | if (useAnimation) 419 | { 420 | var scalarAnimation = _carouselCompositor.CreateScalarKeyFrameAnimation(); 421 | scalarAnimation.Duration = TimeSpan.FromMilliseconds(Duration); 422 | scalarAnimation.InsertKeyFrame(1f, (float)offsetX - (float)this.ItemSpacing); 423 | elementVisual.StartAnimation("Offset.X", scalarAnimation); 424 | } 425 | else 426 | { 427 | elementVisual.Offset = new Vector3((float)offsetX - (float)this.ItemSpacing, elementVisual.Offset.Y, elementVisual.Offset.Z); 428 | } 429 | } 430 | 431 | #region Public methods 432 | 433 | /// 434 | /// Move forward. 435 | /// 436 | public void MoveForward() 437 | { 438 | if (!this.Children.Any()) 439 | return; 440 | 441 | if (this.SelectedIndex == this.Children.Count - 1) 442 | { 443 | this.SelectedIndex = 0; 444 | } 445 | else 446 | { 447 | this.SelectedIndex += 1; 448 | } 449 | } 450 | 451 | /// 452 | /// Move backward. 453 | /// 454 | public void MoveBackward() 455 | { 456 | if (!this.Children.Any()) 457 | return; 458 | 459 | if (this.SelectedIndex == 0) 460 | { 461 | this.SelectedIndex = this.Children.Count - 1; 462 | } 463 | else 464 | { 465 | this.SelectedIndex -= 1; 466 | } 467 | } 468 | 469 | #endregion 470 | 471 | protected override Size MeasureOverride(Size availableSize) 472 | { 473 | _viewportRect = this.Clip = new RectangleGeometry { Rect = new Rect(0, 0, availableSize.Width, availableSize.Height) }; 474 | 475 | if (!this.Children.Any()) 476 | return (availableSize); 477 | 478 | foreach (var uiElement in this.Children) 479 | { 480 | var container = (FrameworkElement)uiElement; 481 | 482 | container.Measure(new Size(this.ItemWidth, this.ItemHeight)); 483 | } 484 | 485 | return (availableSize); 486 | } 487 | 488 | protected override Size ArrangeOverride(Size finalSize) 489 | { 490 | _viewportRect = this.Clip = new RectangleGeometry { Rect = new Rect(0, 0, finalSize.Width, finalSize.Height) }; 491 | 492 | if (!this.Children.Any()) 493 | return finalSize; 494 | 495 | var selectedElement = this.Children[SelectedIndex]; 496 | 497 | Double centerX = (finalSize.Width / 2) - (ItemWidth / 2); 498 | Double centerY = (finalSize.Height - selectedElement.DesiredSize.Height) / 2; 499 | 500 | for (int i = 0; i < this.Children.Count; i++) 501 | { 502 | UIElement element = this.Children[i]; 503 | 504 | if (double.IsNaN(element.DesiredSize.Width) || double.IsNaN(element.DesiredSize.Height)) 505 | continue; 506 | 507 | var rect = new Rect(centerX + this.ItemSpacing, centerY, element.DesiredSize.Width, element.DesiredSize.Height); 508 | 509 | element.Arrange(rect); 510 | } 511 | 512 | if (!DesignMode.DesignModeEnabled) 513 | ShiftElementsAnimatedly(SelectedIndex); 514 | 515 | return finalSize; 516 | } 517 | 518 | public enum CarouselShiftingDirection 519 | { 520 | Forward = 0, 521 | Backward = 1 522 | } 523 | } 524 | } 525 | -------------------------------------------------------------------------------- /EasyCarousel.Control/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("EasyCarousel.Control")] 9 | [assembly: AssemblyDescription("EasyCarousel built by Project Marduk")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Project Marduk")] 12 | [assembly: AssemblyProduct("EasyCarousel.Control")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Version information for an assembly consists of the following four values: 18 | // 19 | // Major Version 20 | // Minor Version 21 | // Build Number 22 | // Revision 23 | // 24 | // You can specify all the values or you can default the Build and Revision Numbers 25 | // by using the '*' as shown below: 26 | // [assembly: AssemblyVersion("1.0.*")] 27 | [assembly: AssemblyVersion("1.0.0.0")] 28 | [assembly: AssemblyFileVersion("1.0.0.0")] 29 | [assembly: ComVisible(false)] -------------------------------------------------------------------------------- /EasyCarousel.Control/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "Microsoft.NETCore.UniversalWindowsPlatform": "5.0.0" 4 | }, 5 | "frameworks": { 6 | "uap10.0": {} 7 | }, 8 | "runtimes": { 9 | "win10-arm": {}, 10 | "win10-arm-aot": {}, 11 | "win10-x86": {}, 12 | "win10-x86-aot": {}, 13 | "win10-x64": {}, 14 | "win10-x64-aot": {} 15 | } 16 | } -------------------------------------------------------------------------------- /EasyCarousel.Sample/App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | -------------------------------------------------------------------------------- /EasyCarousel.Sample/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.InteropServices.WindowsRuntime; 6 | using Windows.ApplicationModel; 7 | using Windows.ApplicationModel.Activation; 8 | using Windows.Foundation; 9 | using Windows.Foundation.Collections; 10 | using Windows.UI.Xaml; 11 | using Windows.UI.Xaml.Controls; 12 | using Windows.UI.Xaml.Controls.Primitives; 13 | using Windows.UI.Xaml.Data; 14 | using Windows.UI.Xaml.Input; 15 | using Windows.UI.Xaml.Media; 16 | using Windows.UI.Xaml.Navigation; 17 | 18 | namespace Marduk.Samples 19 | { 20 | /// 21 | /// Provides application-specific behavior to supplement the default Application class. 22 | /// 23 | sealed partial class App : Application 24 | { 25 | /// 26 | /// Initializes the singleton application object. This is the first line of authored code 27 | /// executed, and as such is the logical equivalent of main() or WinMain(). 28 | /// 29 | public App() 30 | { 31 | this.InitializeComponent(); 32 | this.Suspending += OnSuspending; 33 | } 34 | 35 | /// 36 | /// Invoked when the application is launched normally by the end user. Other entry points 37 | /// will be used such as when the application is launched to open a specific file. 38 | /// 39 | /// Details about the launch request and process. 40 | protected override void OnLaunched(LaunchActivatedEventArgs e) 41 | { 42 | #if DEBUG 43 | if (System.Diagnostics.Debugger.IsAttached) 44 | { 45 | this.DebugSettings.EnableFrameRateCounter = true; 46 | } 47 | #endif 48 | Frame rootFrame = Window.Current.Content as Frame; 49 | 50 | // Do not repeat app initialization when the Window already has content, 51 | // just ensure that the window is active 52 | if (rootFrame == null) 53 | { 54 | // Create a Frame to act as the navigation context and navigate to the first page 55 | rootFrame = new Frame(); 56 | 57 | rootFrame.NavigationFailed += OnNavigationFailed; 58 | 59 | if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) 60 | { 61 | //TODO: Load state from previously suspended application 62 | } 63 | 64 | // Place the frame in the current Window 65 | Window.Current.Content = rootFrame; 66 | } 67 | 68 | if (e.PrelaunchActivated == false) 69 | { 70 | if (rootFrame.Content == null) 71 | { 72 | // When the navigation stack isn't restored navigate to the first page, 73 | // configuring the new page by passing required information as a navigation 74 | // parameter 75 | rootFrame.Navigate(typeof(MainPage), e.Arguments); 76 | } 77 | // Ensure the current window is active 78 | Window.Current.Activate(); 79 | } 80 | } 81 | 82 | /// 83 | /// Invoked when Navigation to a certain page fails 84 | /// 85 | /// The Frame which failed navigation 86 | /// Details about the navigation failure 87 | void OnNavigationFailed(object sender, NavigationFailedEventArgs e) 88 | { 89 | throw new Exception("Failed to load Page " + e.SourcePageType.FullName); 90 | } 91 | 92 | /// 93 | /// Invoked when application execution is being suspended. Application state is saved 94 | /// without knowing whether the application will be terminated or resumed with the contents 95 | /// of memory still intact. 96 | /// 97 | /// The source of the suspend request. 98 | /// Details about the suspend request. 99 | private void OnSuspending(object sender, SuspendingEventArgs e) 100 | { 101 | var deferral = e.SuspendingOperation.GetDeferral(); 102 | //TODO: Save application state and stop any background activity 103 | deferral.Complete(); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/Adventure-Time-Wallpaper-Backgrounds-A8Z.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/Adventure-Time-Wallpaper-Backgrounds-A8Z.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/Gravity-Falls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/Gravity-Falls.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/LockScreenLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/LockScreenLogo.scale-200.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/MNXxLiq.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/MNXxLiq.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/S2e15_playing_interdimensional_chess.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/S2e15_playing_interdimensional_chess.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/Square44x44Logo.targetsize-24_altform-unplated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/Square44x44Logo.targetsize-24_altform-unplated.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/StoreLogo.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/adventure_time_1920x1080_47767.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/adventure_time_1920x1080_47767.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/c-1600x1200px.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/c-1600x1200px.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/cn_cee_we_bare_bears__cn3__wallpaper_03_1600x900.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/cn_cee_we_bare_bears__cn3__wallpaper_03_1600x900.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/hjyKMii94AsGAcrVTY0lUo3GCV9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/hjyKMii94AsGAcrVTY0lUo3GCV9.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/honda-monkey-Kumamon-bike-picture-1000x502.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/honda-monkey-Kumamon-bike-picture-1000x502.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/maxresdefault.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/maxresdefault.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/s9HNQHG.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/s9HNQHG.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/wallpaper1208-1-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/wallpaper1208-1-3.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/Assets/we-bare-bears.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/validvoid/UWP_EasyCarousel/61b5db535ee6be6c6a1966cb5ce84245e0b13ade/EasyCarousel.Sample/Assets/we-bare-bears.jpg -------------------------------------------------------------------------------- /EasyCarousel.Sample/EasyCarousel.Sample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x86 7 | {EF9F5A88-900E-44DD-92EC-3DB8BBE61AFD} 8 | AppContainerExe 9 | Properties 10 | EasyCarousel 11 | EasyCarousel 12 | en-US 13 | UAP 14 | 10.0.10586.0 15 | 10.0.10240.0 16 | 14 17 | 512 18 | {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | EasyCarousel_TemporaryKey.pfx 20 | 21 | 22 | true 23 | bin\x86\Debug\ 24 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 25 | ;2008 26 | full 27 | x86 28 | false 29 | prompt 30 | true 31 | 32 | 33 | bin\x86\Release\ 34 | TRACE;NETFX_CORE;WINDOWS_UWP 35 | true 36 | ;2008 37 | pdbonly 38 | x86 39 | false 40 | prompt 41 | true 42 | true 43 | 44 | 45 | true 46 | bin\ARM\Debug\ 47 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 48 | ;2008 49 | full 50 | ARM 51 | false 52 | prompt 53 | true 54 | 55 | 56 | bin\ARM\Release\ 57 | TRACE;NETFX_CORE;WINDOWS_UWP 58 | true 59 | ;2008 60 | pdbonly 61 | ARM 62 | false 63 | prompt 64 | true 65 | true 66 | 67 | 68 | true 69 | bin\x64\Debug\ 70 | DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP 71 | ;2008 72 | full 73 | x64 74 | false 75 | prompt 76 | true 77 | 78 | 79 | bin\x64\Release\ 80 | TRACE;NETFX_CORE;WINDOWS_UWP 81 | true 82 | ;2008 83 | pdbonly 84 | x64 85 | false 86 | prompt 87 | true 88 | true 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | App.xaml 97 | 98 | 99 | MainPage.xaml 100 | 101 | 102 | 103 | 104 | 105 | Designer 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | MSBuild:Compile 135 | Designer 136 | 137 | 138 | MSBuild:Compile 139 | Designer 140 | 141 | 142 | 143 | 144 | {178a2b38-a1d5-42cc-a2d4-07da48e3970b} 145 | EasyCarousel.Control 146 | 147 | 148 | 149 | 14.0 150 | 151 | 152 | 159 | -------------------------------------------------------------------------------- /EasyCarousel.Sample/MainPage.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |