├── sample.gif ├── icons8-hand-drag-50.png ├── Sample ├── App.xaml.cs ├── App.xaml ├── Sample.csproj ├── Resources.xaml ├── MainWindow.xaml └── MainWindow.xaml.cs ├── .github └── workflows │ └── dotnet.yml ├── WPF.JoshSmith.Controls.DragCanvas ├── WPF.JoshSmith.Controls.DragCanvas.csproj └── DragCanvas.cs ├── README.md ├── WPF.JoshSmith.Controls.DragCanvas.sln ├── .gitignore └── LICENSE.md /sample.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denxorz/WPF.JoshSmith.Controls.DragCanvas/HEAD/sample.gif -------------------------------------------------------------------------------- /icons8-hand-drag-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denxorz/WPF.JoshSmith.Controls.DragCanvas/HEAD/icons8-hand-drag-50.png -------------------------------------------------------------------------------- /Sample/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace Sample; 4 | 5 | public partial class App : Application 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /Sample/App.xaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample/Sample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0-windows 5 | true 6 | enable 7 | latest 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: windows-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 8.0.x 21 | 22 | - name: Restore dependencies 23 | run: dotnet restore 24 | 25 | - name: Build 26 | run: dotnet build --no-restore --configuration Release 27 | 28 | - name: Test 29 | run: dotnet test --no-build --configuration Release --verbosity normal 30 | 31 | - name: Artifact 32 | uses: actions/upload-artifact@v2 33 | with: 34 | name: Denxorz.WPF.JoshSmith.Controls.DragCanvas.nupkg 35 | path: WPF.JoshSmith.Controls.DragCanvas/bin/**/*.nupkg 36 | -------------------------------------------------------------------------------- /WPF.JoshSmith.Controls.DragCanvas/WPF.JoshSmith.Controls.DragCanvas.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net452;net472;netcoreapp3.1;net5.0-windows;net8.0-windows 4 | true 5 | enable 6 | latest 7 | 8 | true 9 | 10 | 2.0.3 11 | 12 | WPF.JoshSmith.Controls.DragCanvas 13 | true 14 | Denxorz 15 | Dennis Geldhof 16 | Copyright @ Dennis Geldhof 2024 17 | MIT 18 | A Canvas which manages dragging of the UIElements it contains. 19 | https://github.com/denxorz/WPF.JoshSmith.Controls.DragCanvas 20 | https://github.com/denxorz/WPF.JoshSmith.Controls.DragCanvas 21 | wpf drag canvas josh smith 22 | icons8-hand-drag-50.png 23 | WPF JoshSmith Controls DragCanvas 24 | README.md 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | True 35 | \ 36 | 37 | 38 | True 39 | \ 40 | 41 | 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WPF.JoshSmith.Controls.DragCanvas 2 | 3 | [![Build status](https://github.com/denxorz/WPF.JoshSmith.Controls.DragCanvas/workflows/.NET%20Core/badge.svg)](https://github.com/denxorz/WPF.JoshSmith.Controls.DragCanvas/actions) [![NuGet](https://buildstats.info/nuget/WPF.JoshSmith.Controls.DragCanvas)](https://www.nuget.org/packages/WPF.JoshSmith.Controls.DragCanvas/) [![License](https://img.shields.io/badge/license-CPOL--1.02-blue)](https://github.com/denxorz/WPF.JoshSmith.Controls.DragCanvas/blob/master/LICENSE.md) 4 | 5 | 6 | ## What does it do? 7 | A Canvas which manages dragging of the UIElements it contains. This Canvas is made by Josh Smith. 8 | 9 | This package is based on the following articles: 10 | 11 | * https://www.codeproject.com/Articles/15354/Dragging-Elements-in-a-Canvas 12 | 13 | 14 | ## Examples 15 | 16 | ```C# 17 | 18 | 19 | 20 | 21 | ``` 22 | 23 | ![DragCanvas sample gif](https://github.com/denxorz/WPF.JoshSmith.Controls.DragCanvas/raw/master/sample.gif "DragCanvas sample gif") 24 | 25 | 26 | ## Tools and Products Used 27 | 28 | * [Microsoft Visual Studio Community](https://www.visualstudio.com) 29 | * [Icons8](https://icons8.com/) 30 | * [NuGet](https://www.nuget.org/) 31 | * [GitHub](https://github.com/) 32 | 33 | 34 | ## Versions & Release Notes 35 | 36 | version 2.0.3: 37 | * Fix missing readme in nuget package 38 | 39 | version 2.0.2: 40 | * Add 'net8.0' 41 | 42 | version 2.0.1: 43 | * Add 'net5.0' 44 | 45 | version 2.0: 46 | * Replace 'preview mouse down' event by a normal one, so that buttons/comboboxes/scrollbars work when put on draggable controls. Downside, these controls can no longer be dragged on themselves. 47 | * Replace target `net462` and `net48` by `net472` 48 | 49 | version 1.1: 50 | * Expose `IsDragInProgress` 51 | 52 | version 1.0: 53 | * First version 54 | -------------------------------------------------------------------------------- /WPF.JoshSmith.Controls.DragCanvas.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29424.173 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WPF.JoshSmith.Controls.DragCanvas", "WPF.JoshSmith.Controls.DragCanvas\WPF.JoshSmith.Controls.DragCanvas.csproj", "{B902D82E-34FB-4E7A-AB5E-674F7EA97C89}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "Sample\Sample.csproj", "{6B44AE24-F828-4BC2-8696-346DDC7A4681}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{48F4E4E6-44CF-4C71-932E-4D31D708232F}" 11 | ProjectSection(SolutionItems) = preProject 12 | .gitignore = .gitignore 13 | LICENSE.md = LICENSE.md 14 | README.md = README.md 15 | EndProjectSection 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{B8E2EB0B-20FD-46F2-9864-CDF3A542D6A0}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{418BB687-FD9F-411B-807A-024A5C789A73}" 20 | ProjectSection(SolutionItems) = preProject 21 | .github\workflows\dotnet.yml = .github\workflows\dotnet.yml 22 | EndProjectSection 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {B902D82E-34FB-4E7A-AB5E-674F7EA97C89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {B902D82E-34FB-4E7A-AB5E-674F7EA97C89}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {B902D82E-34FB-4E7A-AB5E-674F7EA97C89}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {B902D82E-34FB-4E7A-AB5E-674F7EA97C89}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {6B44AE24-F828-4BC2-8696-346DDC7A4681}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {6B44AE24-F828-4BC2-8696-346DDC7A4681}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {6B44AE24-F828-4BC2-8696-346DDC7A4681}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {6B44AE24-F828-4BC2-8696-346DDC7A4681}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(SolutionProperties) = preSolution 40 | HideSolutionNode = FALSE 41 | EndGlobalSection 42 | GlobalSection(NestedProjects) = preSolution 43 | {418BB687-FD9F-411B-807A-024A5C789A73} = {B8E2EB0B-20FD-46F2-9864-CDF3A542D6A0} 44 | EndGlobalSection 45 | GlobalSection(ExtensibilityGlobals) = postSolution 46 | SolutionGuid = {D7B5BEDB-75FD-4ACB-A954-7A2E72495DEB} 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /Sample/Resources.xaml: -------------------------------------------------------------------------------- 1 | 4 | 43 | 44 | -------------------------------------------------------------------------------- /Sample/MainWindow.xaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | Don't worry, be happy. 79 | Hello, World! 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 100 | Hello! 101 | 102 | 103 | 104 | 105 | 106 | 107 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Sample/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Windows; 4 | using System.Windows.Controls; 5 | using System.Windows.Input; 6 | 7 | namespace Sample; 8 | 9 | /// 10 | /// Interaction logic for MainWindow.xaml 11 | /// 12 | public partial class MainWindow : Window 13 | { 14 | #region Data 15 | 16 | /// 17 | /// Stores a reference to the UIElement which the Canvas's context menu currently targets. 18 | /// 19 | private UIElement elementForContextMenu; 20 | 21 | #endregion // Data 22 | 23 | #region Constructor 24 | 25 | public MainWindow() 26 | { 27 | InitializeComponent(); 28 | 29 | this.PreviewMouseRightButtonDown += Window1_PreviewMouseRightButtonDown; 30 | this.btnOnlyShowOffsetIndicators.Checked += btnOnlyShowOffsetIndicators_Checked; 31 | this.btnOnlyShowOffsetIndicators.Unchecked += btnOnlyShowOffsetIndicators_Unchecked; 32 | 33 | // Add the blocks which display their positions within the Canvas. 34 | foreach (string key in new string[] { 35 | "labelTopLeft", 36 | "labelTopRight", 37 | "labelBottomRight", 38 | "labelBottomLeft", 39 | "labelAll", 40 | "labelNone" 41 | }) 42 | { 43 | Label button = this.FindResource(key) as Label; 44 | this.dragCanvas.Children.Add(button); 45 | } 46 | 47 | this.ResetZOrder(); 48 | } 49 | 50 | #endregion // Constructor 51 | 52 | #region btnOnlyShowOffsetIndicators_Checked 53 | 54 | void btnOnlyShowOffsetIndicators_Checked(object sender, RoutedEventArgs e) 55 | { 56 | foreach (UIElement child in this.dragCanvas.Children) 57 | { 58 | child.Visibility = 59 | child is Label && (child as Label).Content == null ? 60 | Visibility.Visible : 61 | Visibility.Collapsed; 62 | } 63 | 64 | this.ResetZOrder(); 65 | } 66 | 67 | #endregion // btnOnlyShowOffsetIndicators_Checked 68 | 69 | #region btnOnlyShowOffsetIndicators_Unchecked 70 | 71 | void btnOnlyShowOffsetIndicators_Unchecked(object sender, RoutedEventArgs e) 72 | { 73 | foreach (UIElement child in this.dragCanvas.Children) 74 | child.Visibility = Visibility.Visible; 75 | 76 | this.ResetZOrder(); 77 | } 78 | 79 | #endregion // btnOnlyShowOffsetIndicators_Unchecked 80 | 81 | #region OnButtonClick 82 | 83 | private void OnButtonClick(object sender, RoutedEventArgs e) 84 | { 85 | MessageBox.Show("Thank you for clicking today, your clicks are important to us."); 86 | } 87 | 88 | #endregion // OnButtonClick 89 | 90 | #region OnContextMenuOpened 91 | 92 | void OnContextMenuOpened(object sender, RoutedEventArgs e) 93 | { 94 | if (this.elementForContextMenu != null) 95 | this.menuItemCanBeDragged.IsChecked = WPF.JoshSmith.Controls.DragCanvas.GetCanBeDragged(this.elementForContextMenu); 96 | } 97 | 98 | #endregion // OnContextMenuOpened 99 | 100 | #region OnMenuItemClick 101 | 102 | /// 103 | /// Handles the Click event of both menu items in the context menu. 104 | /// 105 | void OnMenuItemClick(object sender, RoutedEventArgs e) 106 | { 107 | if (this.elementForContextMenu == null) 108 | return; 109 | 110 | if (e.Source == this.menuItemBringToFront || 111 | e.Source == this.menuItemSendToBack) 112 | { 113 | bool bringToFront = e.Source == this.menuItemBringToFront; 114 | 115 | if (bringToFront) 116 | this.dragCanvas.BringToFront(this.elementForContextMenu); 117 | else 118 | this.dragCanvas.SendToBack(this.elementForContextMenu); 119 | } 120 | else 121 | { 122 | bool canBeDragged = WPF.JoshSmith.Controls.DragCanvas.GetCanBeDragged(this.elementForContextMenu); 123 | WPF.JoshSmith.Controls.DragCanvas.SetCanBeDragged(this.elementForContextMenu, !canBeDragged); 124 | (e.Source as MenuItem).IsChecked = !canBeDragged; 125 | } 126 | } 127 | 128 | #endregion // OnMenuItemClick 129 | 130 | #region ResetZOrder 131 | 132 | private void ResetZOrder() 133 | { 134 | // Set the z-index of every visible child in the Canvas. 135 | int index = 0; 136 | for (int i = 0; i < this.dragCanvas.Children.Count; ++i) 137 | if (this.dragCanvas.Children[i].Visibility == Visibility.Visible) 138 | Canvas.SetZIndex(this.dragCanvas.Children[i], index++); 139 | } 140 | 141 | #endregion // ResetZOrder 142 | 143 | #region Window1_PreviewMouseRightButtonDown 144 | 145 | void Window1_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e) 146 | { 147 | // If the user right-clicks while dragging an element, assume that they want 148 | // to manipulate the z-index of the element being dragged (even if it is 149 | // behind another element at the time). 150 | if (this.dragCanvas.ElementBeingDragged != null) 151 | this.elementForContextMenu = this.dragCanvas.ElementBeingDragged; 152 | else 153 | this.elementForContextMenu = 154 | this.dragCanvas.FindCanvasChild(e.Source as DependencyObject); 155 | } 156 | 157 | #endregion // Window1_PreviewMouseRightButtonDown 158 | } 159 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 |

The Code Project Open License (CPOL) 1.02

2 |
3 | 4 |
5 |
6 | 7 |

Preamble

8 |

9 | This License governs Your use of the Work. This License is intended to allow developers 10 | to use the Source Code and Executable Files provided as part of the Work in any 11 | application in any form. 12 |

13 |

14 | The main points subject to the terms of the License are:

15 | 24 | 25 |

26 | This License is entered between You, the individual or other entity reading or otherwise 27 | making use of the Work licensed pursuant to this License and the individual or other 28 | entity which offers the Work under the terms of this License ("Author").

29 | 30 |

License

31 |

32 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CODE PROJECT OPEN 33 | LICENSE ("LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE 34 | LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT 35 | LAW IS PROHIBITED.

36 |

37 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HEREIN, YOU ACCEPT AND AGREE TO BE 38 | BOUND BY THE TERMS OF THIS LICENSE. THE AUTHOR GRANTS YOU THE RIGHTS CONTAINED HEREIN 39 | IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. IF YOU DO NOT 40 | AGREE TO ACCEPT AND BE BOUND BY THE TERMS OF THIS LICENSE, YOU CANNOT MAKE ANY 41 | USE OF THE WORK.

42 | 43 |
    44 |
  1. Definitions. 45 | 46 |
      47 |
    1. "Articles" means, collectively, all articles written by Author 48 | which describes how the Source Code and Executable Files for the Work may be used 49 | by a user.
    2. 50 |
    3. "Author" means the individual or entity that offers the Work under the terms 51 | of this License.
    4. 52 |
    5. "Derivative Work" means a work based upon the Work or upon the 53 | Work and other pre-existing works.
    6. 54 |
    7. "Executable Files" refer to the executables, binary files, configuration 55 | and any required data files included in the Work.
    8. 56 |
    9. "Publisher" means the provider of the website, magazine, CD-ROM, DVD or other 57 | medium from or by which the Work is obtained by You.
    10. 58 |
    11. "Source Code" refers to the collection of source code and configuration files 59 | used to create the Executable Files.
    12. 60 |
    13. "Standard Version" refers to such a Work if it has not been modified, or 61 | has been modified in accordance with the consent of the Author, such consent being 62 | in the full discretion of the Author.
    14. 63 |
    15. "Work" refers to the collection of files distributed by the Publisher, including 64 | the Source Code, Executable Files, binaries, data files, documentation, whitepapers 65 | and the Articles.
    16. 66 |
    17. "You" is you, an individual or entity wishing to use the Work and exercise 67 | your rights under this License. 68 |
    18. 69 |
    70 |
  2. 71 | 72 |
  3. Fair Use/Fair Use Rights. Nothing in this License is intended to 73 | reduce, limit, or restrict any rights arising from fair use, fair dealing, first 74 | sale or other limitations on the exclusive rights of the copyright owner under copyright 75 | law or other applicable laws. 76 |
  4. 77 | 78 |
  5. License Grant. Subject to the terms and conditions of this License, 79 | the Author hereby grants You a worldwide, royalty-free, non-exclusive, perpetual 80 | (for the duration of the applicable copyright) license to exercise the rights in 81 | the Work as stated below: 82 | 83 |
      84 |
    1. You may use the standard version of the Source Code or Executable Files in Your 85 | own applications.
    2. 86 |
    3. You may apply bug fixes, portability fixes and other modifications obtained from 87 | the Public Domain or from the Author. A Work modified in such a way shall still 88 | be considered the standard version and will be subject to this License.
    4. 89 |
    5. You may otherwise modify Your copy of this Work (excluding the Articles) in any 90 | way to create a Derivative Work, provided that You insert a prominent notice in 91 | each changed file stating how, when and where You changed that file.
    6. 92 |
    7. You may distribute the standard version of the Executable Files and Source Code 93 | or Derivative Work in aggregate with other (possibly commercial) programs as part 94 | of a larger (possibly commercial) software distribution.
    8. 95 |
    9. The Articles discussing the Work published in any form by the author may not be 96 | distributed or republished without the Author's consent. The author retains 97 | copyright to any such Articles. You may use the Executable Files and Source Code 98 | pursuant to this License but you may not repost or republish or otherwise distribute 99 | or make available the Articles, without the prior written consent of the Author.
    10. 100 |
    101 | 102 | Any subroutines or modules supplied by You and linked into the Source Code or Executable 103 | Files of this Work shall not be considered part of this Work and will not be subject 104 | to the terms of this License. 105 |
  6. 106 | 107 |
  7. Patent License. Subject to the terms and conditions of this License, 108 | each Author hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 109 | irrevocable (except as stated in this section) patent license to make, have made, use, import, 110 | and otherwise transfer the Work.
  8. 111 | 112 |
  9. Restrictions. The license granted in Section 3 above is expressly 113 | made subject to and limited by the following restrictions: 114 | 115 |
      116 |
    1. You agree not to remove any of the original copyright, patent, trademark, and 117 | attribution notices and associated disclaimers that may appear in the Source Code 118 | or Executable Files.
    2. 119 |
    3. You agree not to advertise or in any way imply that this Work is a product of Your 120 | own.
    4. 121 |
    5. The name of the Author may not be used to endorse or promote products derived from 122 | the Work without the prior written consent of the Author.
    6. 123 |
    7. You agree not to sell, lease, or rent any part of the Work. This does not restrict 124 | you from including the Work or any part of the Work inside a larger software 125 | distribution that itself is being sold. The Work by itself, though, cannot be sold, 126 | leased or rented.
    8. 127 |
    9. You may distribute the Executable Files and Source Code only under the terms of 128 | this License, and You must include a copy of, or the Uniform Resource Identifier 129 | for, this License with every copy of the Executable Files or Source Code You distribute 130 | and ensure that anyone receiving such Executable Files and Source Code agrees that 131 | the terms of this License apply to such Executable Files and/or Source Code. You 132 | may not offer or impose any terms on the Work that alter or restrict the terms of 133 | this License or the recipients' exercise of the rights granted hereunder. You 134 | may not sublicense the Work. You must keep intact all notices that refer to this 135 | License and to the disclaimer of warranties. You may not distribute the Executable 136 | Files or Source Code with any technological measures that control access or use 137 | of the Work in a manner inconsistent with the terms of this License.
    10. 138 |
    11. You agree not to use the Work for illegal, immoral or improper purposes, or on pages 139 | containing illegal, immoral or improper material. The Work is subject to applicable 140 | export laws. You agree to comply with all such laws and regulations that may apply 141 | to the Work after Your receipt of the Work. 142 |
    12. 143 |
    144 |
  10. 145 | 146 |
  11. Representations, Warranties and Disclaimer. THIS WORK IS PROVIDED 147 | "AS IS", "WHERE IS" AND "AS AVAILABLE", WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES 148 | OR CONDITIONS OR GUARANTEES. YOU, THE USER, ASSUME ALL RISK IN ITS USE, INCLUDING 149 | COPYRIGHT INFRINGEMENT, PATENT INFRINGEMENT, SUITABILITY, ETC. AUTHOR EXPRESSLY 150 | DISCLAIMS ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES OR CONDITIONS, INCLUDING 151 | WITHOUT LIMITATION, WARRANTIES OR CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY 152 | OR FITNESS FOR A PARTICULAR PURPOSE, OR ANY WARRANTY OF TITLE OR NON-INFRINGEMENT, 153 | OR THAT THE WORK (OR ANY PORTION THEREOF) IS CORRECT, USEFUL, BUG-FREE OR FREE OF 154 | VIRUSES. YOU MUST PASS THIS DISCLAIMER ON WHENEVER YOU DISTRIBUTE THE WORK OR DERIVATIVE 155 | WORKS. 156 |
  12. 157 | 158 |
  13. Indemnity. You agree to defend, indemnify and hold harmless the Author and 159 | the Publisher from and against any claims, suits, losses, damages, liabilities, 160 | costs, and expenses (including reasonable legal or attorneys fees) resulting from 161 | or relating to any use of the Work by You. 162 |
  14. 163 | 164 |
  15. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE 165 | LAW, IN NO EVENT WILL THE AUTHOR OR THE PUBLISHER BE LIABLE TO YOU ON ANY LEGAL 166 | THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES 167 | ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK OR OTHERWISE, EVEN IF THE AUTHOR 168 | OR THE PUBLISHER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 169 |
  16. 170 | 171 |
  17. Termination. 172 | 173 |
      174 |
    1. This License and the rights granted hereunder will terminate automatically upon 175 | any breach by You of any term of this License. Individuals or entities who have 176 | received Derivative Works from You under this License, however, will not have their 177 | licenses terminated provided such individuals or entities remain in full compliance 178 | with those licenses. Sections 1, 2, 6, 7, 8, 9, 10 and 11 will survive any termination 179 | of this License.
    2. 180 | 181 |
    3. If You bring a copyright, trademark, patent or any other infringement claim against 182 | any contributor over infringements You claim are made by the Work, your License 183 | from such contributor to the Work ends automatically.
    4. 184 | 185 |
    5. Subject to the above terms and conditions, this License is perpetual (for the duration 186 | of the applicable copyright in the Work). Notwithstanding the above, the Author 187 | reserves the right to release the Work under different license terms or to stop 188 | distributing the Work at any time; provided, however that any such election will 189 | not serve to withdraw this License (or any other license that has been, or is required 190 | to be, granted under the terms of this License), and this License will continue 191 | in full force and effect unless terminated as stated above. 192 |
    6. 193 |
    194 |
  18. 195 | 196 |
  19. Publisher. The parties hereby confirm that the Publisher shall 197 | not, under any circumstances, be responsible for and shall not have any liability 198 | in respect of the subject matter of this License. The Publisher makes no warranty 199 | whatsoever in connection with the Work and shall not be liable to You or any party 200 | on any legal theory for any damages whatsoever, including without limitation any 201 | general, special, incidental or consequential damages arising in connection to this 202 | license. The Publisher reserves the right to cease making the Work available to 203 | You at any time without notice
  20. 204 | 205 |
  21. Miscellaneous 206 | 207 |
      208 |
    1. This License shall be governed by the laws of the location of the head office of 209 | the Author or if the Author is an individual, the laws of location of the principal 210 | place of residence of the Author.
    2. 211 |
    3. If any provision of this License is invalid or unenforceable under applicable law, 212 | it shall not affect the validity or enforceability of the remainder of the terms 213 | of this License, and without further action by the parties to this License, such 214 | provision shall be reformed to the minimum extent necessary to make such provision 215 | valid and enforceable.
    4. 216 |
    5. No term or provision of this License shall be deemed waived and no breach consented 217 | to unless such waiver or consent shall be in writing and signed by the party to 218 | be charged with such waiver or consent.
    6. 219 |
    7. This License constitutes the entire agreement between the parties with respect to 220 | the Work licensed herein. There are no understandings, agreements or representations 221 | with respect to the Work not specified herein. The Author shall not be bound by 222 | any additional provisions that may appear in any communication from You. This License 223 | may not be modified without the mutual written agreement of the Author and You. 224 |
    8. 225 |
    226 | 227 |
  22. 228 |
229 | 230 |
231 |
232 | 233 | 234 | -------------------------------------------------------------------------------- /WPF.JoshSmith.Controls.DragCanvas/DragCanvas.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Windows.Controls; 4 | using System.Windows.Input; 5 | using System.Windows.Media; 6 | using System.Windows.Media.Media3D; 7 | 8 | namespace WPF.JoshSmith.Controls; 9 | 10 | /// 11 | /// A Canvas which manages dragging of the UIElements it contains. 12 | /// 13 | public class DragCanvas : Canvas 14 | { 15 | #region Data 16 | 17 | // Stores a reference to the UIElement currently being dragged by the user. 18 | private UIElement elementBeingDragged; 19 | 20 | // Keeps track of where the mouse cursor was when a drag operation began. 21 | private Point origCursorLocation; 22 | 23 | // The offsets from the DragCanvas' edges when the drag operation began. 24 | private double origHorizOffset, origVertOffset; 25 | 26 | // Keeps track of which horizontal and vertical offset should be modified for the drag element. 27 | private bool modifyLeftOffset, modifyTopOffset; 28 | 29 | /// 30 | /// True if a drag operation is underway, else false. 31 | /// 32 | public bool IsDragInProgress { get; private set; } 33 | 34 | #endregion // Data 35 | 36 | #region Attached Properties 37 | 38 | #region CanBeDragged 39 | 40 | public static readonly DependencyProperty CanBeDraggedProperty; 41 | 42 | public static bool GetCanBeDragged(UIElement uiElement) 43 | { 44 | if (uiElement == null) 45 | return false; 46 | 47 | return (bool)uiElement.GetValue(CanBeDraggedProperty); 48 | } 49 | 50 | public static void SetCanBeDragged(UIElement uiElement, bool value) 51 | { 52 | if (uiElement != null) 53 | uiElement.SetValue(CanBeDraggedProperty, value); 54 | } 55 | 56 | #endregion // CanBeDragged 57 | 58 | #endregion // Attached Properties 59 | 60 | #region Dependency Properties 61 | 62 | public static readonly DependencyProperty AllowDraggingProperty; 63 | public static readonly DependencyProperty AllowDragOutOfViewProperty; 64 | 65 | #endregion // Dependency Properties 66 | 67 | #region Static Constructor 68 | 69 | static DragCanvas() 70 | { 71 | AllowDraggingProperty = DependencyProperty.Register( 72 | "AllowDragging", 73 | typeof(bool), 74 | typeof(DragCanvas), 75 | new PropertyMetadata(true)); 76 | 77 | AllowDragOutOfViewProperty = DependencyProperty.Register( 78 | "AllowDragOutOfView", 79 | typeof(bool), 80 | typeof(DragCanvas), 81 | new UIPropertyMetadata(false)); 82 | 83 | CanBeDraggedProperty = DependencyProperty.RegisterAttached( 84 | "CanBeDragged", 85 | typeof(bool), 86 | typeof(DragCanvas), 87 | new UIPropertyMetadata(true)); 88 | } 89 | 90 | #endregion // Static Constructor 91 | 92 | #region Constructor 93 | 94 | /// 95 | /// Initializes a new instance of DragCanvas. UIElements in 96 | /// the DragCanvas will immediately be draggable by the user. 97 | /// 98 | public DragCanvas() 99 | { 100 | } 101 | 102 | #endregion // Constructor 103 | 104 | #region Interface 105 | 106 | #region AllowDragging 107 | 108 | /// 109 | /// Gets/sets whether elements in the DragCanvas should be draggable by the user. 110 | /// The default value is true. This is a dependency property. 111 | /// 112 | public bool AllowDragging 113 | { 114 | get { return (bool)base.GetValue(AllowDraggingProperty); } 115 | set { base.SetValue(AllowDraggingProperty, value); } 116 | } 117 | 118 | #endregion // AllowDragging 119 | 120 | #region AllowDragOutOfView 121 | 122 | /// 123 | /// Gets/sets whether the user should be able to drag elements in the DragCanvas out of 124 | /// the viewable area. The default value is false. This is a dependency property. 125 | /// 126 | public bool AllowDragOutOfView 127 | { 128 | get { return (bool)GetValue(AllowDragOutOfViewProperty); } 129 | set { SetValue(AllowDragOutOfViewProperty, value); } 130 | } 131 | 132 | #endregion // AllowDragOutOfView 133 | 134 | #region BringToFront / SendToBack 135 | 136 | /// 137 | /// Assigns the element a z-index which will ensure that 138 | /// it is in front of every other element in the Canvas. 139 | /// The z-index of every element whose z-index is between 140 | /// the element's old and new z-index will have its z-index 141 | /// decremented by one. 142 | /// 143 | /// 144 | /// The element to be sent to the front of the z-order. 145 | /// 146 | public void BringToFront(UIElement element) 147 | { 148 | this.UpdateZOrder(element, true); 149 | } 150 | 151 | /// 152 | /// Assigns the element a z-index which will ensure that 153 | /// it is behind every other element in the Canvas. 154 | /// The z-index of every element whose z-index is between 155 | /// the element's old and new z-index will have its z-index 156 | /// incremented by one. 157 | /// 158 | /// 159 | /// The element to be sent to the back of the z-order. 160 | /// 161 | public void SendToBack(UIElement element) 162 | { 163 | this.UpdateZOrder(element, false); 164 | } 165 | 166 | #endregion // BringToFront / SendToBack 167 | 168 | #region ElementBeingDragged 169 | 170 | /// 171 | /// Returns the UIElement currently being dragged, or null. 172 | /// 173 | /// 174 | /// Note to inheritors: This property exposes a protected 175 | /// setter which should be used to modify the drag element. 176 | /// 177 | public UIElement ElementBeingDragged 178 | { 179 | get 180 | { 181 | if (!this.AllowDragging) 182 | return null; 183 | else 184 | return this.elementBeingDragged; 185 | } 186 | protected set 187 | { 188 | if (this.elementBeingDragged != null) 189 | this.elementBeingDragged.ReleaseMouseCapture(); 190 | 191 | if (!this.AllowDragging) 192 | this.elementBeingDragged = null; 193 | else 194 | { 195 | if (DragCanvas.GetCanBeDragged(value)) 196 | { 197 | this.elementBeingDragged = value; 198 | this.elementBeingDragged.CaptureMouse(); 199 | } 200 | else 201 | this.elementBeingDragged = null; 202 | } 203 | } 204 | } 205 | 206 | #endregion // ElementBeingDragged 207 | 208 | #region FindCanvasChild 209 | 210 | /// 211 | /// Walks up the visual tree starting with the specified DependencyObject, 212 | /// looking for a UIElement which is a child of the Canvas. If a suitable 213 | /// element is not found, null is returned. If the 'depObj' object is a 214 | /// UIElement in the Canvas's Children collection, it will be returned. 215 | /// 216 | /// 217 | /// A DependencyObject from which the search begins. 218 | /// 219 | public UIElement FindCanvasChild(DependencyObject depObj) 220 | { 221 | while (depObj != null) 222 | { 223 | // If the current object is a UIElement which is a child of the 224 | // Canvas, exit the loop and return it. 225 | UIElement elem = depObj as UIElement; 226 | if (elem != null && base.Children.Contains(elem)) 227 | break; 228 | 229 | // VisualTreeHelper works with objects of type Visual or Visual3D. 230 | // If the current object is not derived from Visual or Visual3D, 231 | // then use the LogicalTreeHelper to find the parent element. 232 | if (depObj is Visual || depObj is Visual3D) 233 | depObj = VisualTreeHelper.GetParent(depObj); 234 | else 235 | depObj = LogicalTreeHelper.GetParent(depObj); 236 | } 237 | return depObj as UIElement; 238 | } 239 | 240 | #endregion // FindCanvasChild 241 | 242 | #endregion // Interface 243 | 244 | #region Overrides 245 | 246 | #region OnMouseLeftButtonDown 247 | 248 | protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) 249 | { 250 | base.OnMouseLeftButtonDown(e); 251 | 252 | this.IsDragInProgress = false; 253 | 254 | // Cache the mouse cursor location. 255 | this.origCursorLocation = e.GetPosition(this); 256 | 257 | // Walk up the visual tree from the element that was clicked, 258 | // looking for an element that is a direct child of the Canvas. 259 | this.ElementBeingDragged = this.FindCanvasChild(e.Source as DependencyObject); 260 | if (this.ElementBeingDragged == null) 261 | return; 262 | 263 | // Get the element's offsets from the four sides of the Canvas. 264 | double left = Canvas.GetLeft(this.ElementBeingDragged); 265 | double right = Canvas.GetRight(this.ElementBeingDragged); 266 | double top = Canvas.GetTop(this.ElementBeingDragged); 267 | double bottom = Canvas.GetBottom(this.ElementBeingDragged); 268 | 269 | // Calculate the offset deltas and determine for which sides 270 | // of the Canvas to adjust the offsets. 271 | this.origHorizOffset = ResolveOffset(left, right, out this.modifyLeftOffset); 272 | this.origVertOffset = ResolveOffset(top, bottom, out this.modifyTopOffset); 273 | 274 | // Set the Handled flag so that a control being dragged 275 | // does not react to the mouse input. 276 | e.Handled = true; 277 | 278 | this.IsDragInProgress = true; 279 | } 280 | 281 | #endregion // OnMouseLeftButtonDown 282 | 283 | #region OnPreviewMouseMove 284 | 285 | protected override void OnPreviewMouseMove(MouseEventArgs e) 286 | { 287 | base.OnPreviewMouseMove(e); 288 | 289 | // If no element is being dragged, there is nothing to do. 290 | if (this.ElementBeingDragged == null || !this.IsDragInProgress) 291 | return; 292 | 293 | // Get the position of the mouse cursor, relative to the Canvas. 294 | Point cursorLocation = e.GetPosition(this); 295 | 296 | // These values will store the new offsets of the drag element. 297 | double newHorizontalOffset, newVerticalOffset; 298 | 299 | #region Calculate Offsets 300 | 301 | // Determine the horizontal offset. 302 | if (this.modifyLeftOffset) 303 | newHorizontalOffset = this.origHorizOffset + (cursorLocation.X - this.origCursorLocation.X); 304 | else 305 | newHorizontalOffset = this.origHorizOffset - (cursorLocation.X - this.origCursorLocation.X); 306 | 307 | // Determine the vertical offset. 308 | if (this.modifyTopOffset) 309 | newVerticalOffset = this.origVertOffset + (cursorLocation.Y - this.origCursorLocation.Y); 310 | else 311 | newVerticalOffset = this.origVertOffset - (cursorLocation.Y - this.origCursorLocation.Y); 312 | 313 | #endregion // Calculate Offsets 314 | 315 | if (!this.AllowDragOutOfView) 316 | { 317 | #region Verify Drag Element Location 318 | 319 | // Get the bounding rect of the drag element. 320 | Rect elemRect = this.CalculateDragElementRect(newHorizontalOffset, newVerticalOffset); 321 | 322 | // 323 | // If the element is being dragged out of the viewable area, 324 | // determine the ideal rect location, so that the element is 325 | // within the edge(s) of the canvas. 326 | // 327 | bool leftAlign = elemRect.Left < 0; 328 | bool rightAlign = elemRect.Right > this.ActualWidth; 329 | 330 | if (leftAlign) 331 | newHorizontalOffset = modifyLeftOffset ? 0 : this.ActualWidth - elemRect.Width; 332 | else if (rightAlign) 333 | newHorizontalOffset = modifyLeftOffset ? this.ActualWidth - elemRect.Width : 0; 334 | 335 | bool topAlign = elemRect.Top < 0; 336 | bool bottomAlign = elemRect.Bottom > this.ActualHeight; 337 | 338 | if (topAlign) 339 | newVerticalOffset = modifyTopOffset ? 0 : this.ActualHeight - elemRect.Height; 340 | else if (bottomAlign) 341 | newVerticalOffset = modifyTopOffset ? this.ActualHeight - elemRect.Height : 0; 342 | 343 | #endregion // Verify Drag Element Location 344 | } 345 | 346 | #region Move Drag Element 347 | 348 | if (this.modifyLeftOffset) 349 | Canvas.SetLeft(this.ElementBeingDragged, newHorizontalOffset); 350 | else 351 | Canvas.SetRight(this.ElementBeingDragged, newHorizontalOffset); 352 | 353 | if (this.modifyTopOffset) 354 | Canvas.SetTop(this.ElementBeingDragged, newVerticalOffset); 355 | else 356 | Canvas.SetBottom(this.ElementBeingDragged, newVerticalOffset); 357 | 358 | #endregion // Move Drag Element 359 | } 360 | 361 | #endregion // OnPreviewMouseMove 362 | 363 | #region OnHostPreviewMouseUp 364 | 365 | protected override void OnPreviewMouseUp(MouseButtonEventArgs e) 366 | { 367 | base.OnPreviewMouseUp(e); 368 | 369 | // Reset the field whether the left or right mouse button was 370 | // released, in case a context menu was opened on the drag element. 371 | this.ElementBeingDragged = null; 372 | } 373 | 374 | #endregion // OnHostPreviewMouseUp 375 | 376 | #endregion // Host Event Handlers 377 | 378 | #region Private Helpers 379 | 380 | #region CalculateDragElementRect 381 | 382 | /// 383 | /// Returns a Rect which describes the bounds of the element being dragged. 384 | /// 385 | private Rect CalculateDragElementRect(double newHorizOffset, double newVertOffset) 386 | { 387 | if (this.ElementBeingDragged == null) 388 | throw new InvalidOperationException("ElementBeingDragged is null."); 389 | 390 | Size elemSize = this.ElementBeingDragged.RenderSize; 391 | 392 | double x, y; 393 | 394 | if (this.modifyLeftOffset) 395 | x = newHorizOffset; 396 | else 397 | x = this.ActualWidth - newHorizOffset - elemSize.Width; 398 | 399 | if (this.modifyTopOffset) 400 | y = newVertOffset; 401 | else 402 | y = this.ActualHeight - newVertOffset - elemSize.Height; 403 | 404 | Point elemLoc = new Point(x, y); 405 | 406 | return new Rect(elemLoc, elemSize); 407 | } 408 | 409 | #endregion // CalculateDragElementRect 410 | 411 | #region ResolveOffset 412 | 413 | /// 414 | /// Determines one component of a UIElement's location 415 | /// within a Canvas (either the horizontal or vertical offset). 416 | /// 417 | /// 418 | /// The value of an offset relative to a default side of the 419 | /// Canvas (i.e. top or left). 420 | /// 421 | /// 422 | /// The value of the offset relative to the other side of the 423 | /// Canvas (i.e. bottom or right). 424 | /// 425 | /// 426 | /// Will be set to true if the returned value should be used 427 | /// for the offset from the side represented by the 'side1' 428 | /// parameter. Otherwise, it will be set to false. 429 | /// 430 | private static double ResolveOffset(double side1, double side2, out bool useSide1) 431 | { 432 | // If the Canvas.Left and Canvas.Right attached properties 433 | // are specified for an element, the 'Left' value is honored. 434 | // The 'Top' value is honored if both Canvas.Top and 435 | // Canvas.Bottom are set on the same element. If one 436 | // of those attached properties is not set on an element, 437 | // the default value is Double.NaN. 438 | useSide1 = true; 439 | double result; 440 | if (Double.IsNaN(side1)) 441 | { 442 | if (Double.IsNaN(side2)) 443 | { 444 | // Both sides have no value, so set the 445 | // first side to a value of zero. 446 | result = 0; 447 | } 448 | else 449 | { 450 | result = side2; 451 | useSide1 = false; 452 | } 453 | } 454 | else 455 | { 456 | result = side1; 457 | } 458 | return result; 459 | } 460 | 461 | #endregion // ResolveOffset 462 | 463 | #region UpdateZOrder 464 | 465 | /// 466 | /// Helper method used by the BringToFront and SendToBack methods. 467 | /// 468 | /// 469 | /// The element to bring to the front or send to the back. 470 | /// 471 | /// 472 | /// Pass true if calling from BringToFront, else false. 473 | /// 474 | private void UpdateZOrder(UIElement element, bool bringToFront) 475 | { 476 | #region Safety Check 477 | 478 | if (element == null) 479 | throw new ArgumentNullException("element"); 480 | 481 | if (!base.Children.Contains(element)) 482 | throw new ArgumentException("Must be a child element of the Canvas.", "element"); 483 | 484 | #endregion // Safety Check 485 | 486 | #region Calculate Z-Indici And Offset 487 | 488 | // Determine the Z-Index for the target UIElement. 489 | int elementNewZIndex = -1; 490 | if (bringToFront) 491 | { 492 | foreach (UIElement elem in base.Children) 493 | if (elem.Visibility != Visibility.Collapsed) 494 | ++elementNewZIndex; 495 | } 496 | else 497 | { 498 | elementNewZIndex = 0; 499 | } 500 | 501 | // Determine if the other UIElements' Z-Index 502 | // should be raised or lowered by one. 503 | int offset = (elementNewZIndex == 0) ? +1 : -1; 504 | 505 | int elementCurrentZIndex = Canvas.GetZIndex(element); 506 | 507 | #endregion // Calculate Z-Indici And Offset 508 | 509 | #region Update Z-Indici 510 | 511 | // Update the Z-Index of every UIElement in the Canvas. 512 | foreach (UIElement childElement in base.Children) 513 | { 514 | if (childElement == element) 515 | Canvas.SetZIndex(element, elementNewZIndex); 516 | else 517 | { 518 | int zIndex = Canvas.GetZIndex(childElement); 519 | 520 | // Only modify the z-index of an element if it is 521 | // in between the target element's old and new z-index. 522 | if (bringToFront && elementCurrentZIndex < zIndex || 523 | !bringToFront && zIndex < elementCurrentZIndex) 524 | { 525 | Canvas.SetZIndex(childElement, zIndex + offset); 526 | } 527 | } 528 | } 529 | 530 | #endregion // Update Z-Indici 531 | } 532 | 533 | #endregion // UpdateZOrder 534 | 535 | #endregion // Private Helpers 536 | } --------------------------------------------------------------------------------