├── .gitattributes ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── LICENSE ├── Mechanism.AvaloniaUI.Controls ├── Controls │ ├── AlignableStackPanel.cs │ ├── CommandBar │ │ ├── CommandBar.cs │ │ └── CommandBarLayer.cs │ ├── NoSizeStackPanel.cs │ ├── OverflowFlyoutItemsControl.cs │ ├── OverflowFlyoutItemsPresenter │ │ └── OverflowFlyoutItemsPresenter.cs │ ├── SwipeSheet │ │ └── SwipeSheet.cs │ ├── ToolBar │ │ ├── ToolBar.cs │ │ ├── ToolBarTray.cs │ │ └── ToolBarTrayPanel.cs │ └── ToolStrip │ │ ├── AddToToolStripBehavior.cs │ │ ├── ButtonToolStripItem.cs │ │ ├── FlexibleSpaceToolStripItem.cs │ │ ├── IToolStripItem.cs │ │ ├── MoveOrRemoveFromToolStripBehavior.cs │ │ ├── SegmentedControlToolStripItem.cs │ │ ├── SegmentedControlToolStripItemSegment.cs │ │ ├── SegmentedControlToolStripItemSegmentPresenter.cs │ │ ├── ThumbDragGhostBehavior.cs │ │ ├── ToolStrip.cs │ │ ├── ToolStripFlexibleSpaceReference.cs │ │ ├── ToolStripItemPointerOverBehavior.cs │ │ ├── ToolStripItemReference.cs │ │ └── ToolStripItemsPanel.cs ├── Mechanism.AvaloniaUI.Controls.csproj ├── Properties │ └── AssemblyInfo.cs ├── Styles │ └── Fluent │ │ ├── Controls │ │ ├── CommandBar.xaml │ │ ├── OverflowFlyoutItemsPresenter.xaml │ │ ├── SwipeSheet.xaml │ │ ├── ToolBarTray.xaml │ │ └── ToolStrip.xaml │ │ └── FluentTheme.xaml └── nuget.config ├── Mechanism.AvaloniaUI.Core ├── Core │ ├── AttachedIcon.cs │ ├── NameScopeHelper.cs │ ├── PerPlatformUI.cs │ └── PerPlatformVisibilityBehavior.cs ├── Mechanism.AvaloniaUI.Core.csproj ├── Properties │ └── AssemblyInfo.cs └── nuget.config ├── Mechanism.AvaloniaUI.Extras ├── .gitignore ├── BlenderBar │ ├── BlenderBar.cs │ ├── BlenderBarItem.cs │ └── BlenderBarPanel.cs ├── Converters │ ├── BoolsToBoolConverter.cs │ └── ItemCountToHasItemsBooleanConverter.cs ├── ExpandToFillView │ ├── ExpandToFillPanel.cs │ ├── ExpandToFillView.cs │ ├── ExpandToFillViewItem.cs │ └── ExpandToFillViewItemContainerGenerator.cs ├── Mechanism.AvaloniaUI.Extras.csproj ├── Properties │ └── AssemblyInfo.cs ├── Styles │ └── Fluent │ │ ├── Controls │ │ ├── BlenderBar.xaml │ │ └── ExpandToFillView.xaml │ │ └── FluentTheme.xaml └── nuget.config ├── Mechanism.AvaloniaUI.Sample ├── .gitignore ├── App.xaml ├── App.xaml.cs ├── Assets │ ├── ico-image.ico │ ├── mechanism-for-avalonia-logo.ico │ ├── mechanism-for-avalonia-logo.png │ ├── mechanism-for-avalonia-logo.svg │ └── png-image.png ├── LayerDictionaryToLayerStringConverter.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Mechanism.AvaloniaUI.Sample.csproj ├── Program.cs ├── SampleIconButtonStyles.xaml ├── ViewLocator.cs ├── ViewModels │ ├── AttachedIconPageViewModel.cs │ ├── BlenderBarPageViewModel.cs │ ├── CommandBarPageViewModel.cs │ ├── DemoPageViewModel.cs │ ├── ExpandToFillViewPageViewModel.cs │ ├── HomePageViewModel.cs │ ├── MainViewModel.cs │ ├── SwipeSheetPageViewModel.cs │ ├── TestViewModel.cs │ ├── ToolBarTrayPageViewModel.cs │ ├── ToolStripPageViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── AttachedIconPageView.xaml │ ├── AttachedIconPageView.xaml.cs │ ├── BlenderBarPageView.xaml │ ├── BlenderBarPageView.xaml.cs │ ├── CommandBarPageView.xaml │ ├── CommandBarPageView.xaml.cs │ ├── ExpandToFillViewPageView.xaml │ ├── ExpandToFillViewPageView.xaml.cs │ ├── HomePageView.xaml │ ├── HomePageView.xaml.cs │ ├── MainView.xaml │ ├── MainView.xaml.cs │ ├── SwipeSheetPageView.xaml │ ├── SwipeSheetPageView.xaml.cs │ ├── TestView.xaml │ ├── TestView.xaml.cs │ ├── ToolBarTrayPageView.xaml │ ├── ToolBarTrayPageView.xaml.cs │ ├── ToolStripPageView.xaml │ └── ToolStripPageView.xaml.cs └── nuget.config ├── Mechanism.AvaloniaUI.sln ├── README.md ├── ReadmeImages ├── BlenderBar.gif ├── CommandBar.gif ├── ContentDialog.png ├── DecoratableWindow.png ├── ExpandToFillView.gif ├── FlyoutButton.png ├── ToolBar-ToolBarTray.gif ├── ToolStrip.gif └── ToolStripz.gif └── build ├── All.targets ├── Avalonia.props ├── Lib.targets ├── Output.props ├── Sample.targets └── cfg └── paperweight.lol /.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 | ## 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 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | 235 | # RIA/Silverlight projects 236 | Generated_Code/ 237 | 238 | # Backup & report files from converting an old project file 239 | # to a newer Visual Studio version. Backup files are not needed, 240 | # because we have git ;-) 241 | _UpgradeReport_Files/ 242 | Backup*/ 243 | UpgradeLog*.XML 244 | UpgradeLog*.htm 245 | ServiceFabricBackup/ 246 | *.rptproj.bak 247 | 248 | # SQL Server files 249 | *.mdf 250 | *.ldf 251 | *.ndf 252 | 253 | # Business Intelligence projects 254 | *.rdl.data 255 | *.bim.layout 256 | *.bim_*.settings 257 | *.rptproj.rsuser 258 | *- Backup*.rdl 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush personal settings 299 | .cr/personal 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | 336 | # Local History for Visual Studio 337 | .localhistory/ 338 | 339 | # BeatPulse healthcheck temp database 340 | healthchecksdb 341 | 342 | build/cfg/ -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "OS-COMMENT1": "Use IntelliSense to find out which attributes exist for C# debugging", 9 | "OS-COMMENT2": "Use hover for the description of the existing attributes", 10 | "OS-COMMENT3": "For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md", 11 | "name": ".NET Core Launch (console)", 12 | "type": "coreclr", 13 | "request": "launch", 14 | "preLaunchTask": "build", 15 | "OS-COMMENT4": "If you have changed target frameworks, make sure to update the program path.", 16 | "program": "${workspaceFolder}/Mechanism.AvaloniaUI.Sample/bin/Debug/net5.0/Mechanism.AvaloniaUI.Sample.dll", 17 | "args": [], 18 | "cwd": "${workspaceFolder}/Mechanism.AvaloniaUI.Sample", 19 | "OS-COMMENT5": "For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console", 20 | "console": "internalConsole", 21 | "stopAtEntry": false 22 | }, 23 | { 24 | "name": ".NET Core Attach", 25 | "type": "coreclr", 26 | "request": "attach", 27 | "processId": "${command:pickProcess}" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/Mechanism.AvaloniaUI.Sample/Mechanism.AvaloniaUI.Sample.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary", 13 | "--framework", 14 | "net5.0", 15 | ], 16 | "problemMatcher": "$msCompile" 17 | }, 18 | { 19 | "label": "publish", 20 | "command": "dotnet", 21 | "type": "process", 22 | "args": [ 23 | "publish", 24 | "${workspaceFolder}/Mechanism.AvaloniaUI.Sample/Mechanism.AvaloniaUI.Sample.csproj", 25 | "/property:GenerateFullPaths=true", 26 | "/consoleloggerparameters:NoSummary", 27 | "--framework", 28 | "net5.0", 29 | ], 30 | "problemMatcher": "$msCompile" 31 | }, 32 | { 33 | "label": "watch", 34 | "command": "dotnet", 35 | "type": "process", 36 | "args": [ 37 | "watch", 38 | "run", 39 | "${workspaceFolder}/Mechanism.AvaloniaUI.Sample/Mechanism.AvaloniaUI.Sample.csproj", 40 | "/property:GenerateFullPaths=true", 41 | "/consoleloggerparameters:NoSummary", 42 | "--framework", 43 | "net5.0", 44 | ], 45 | "problemMatcher": "$msCompile" 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Splitwirez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/AlignableStackPanel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Layout; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Mechanism.AvaloniaUI.Controls 9 | { 10 | public enum ChildrenHorizontalAlignment 11 | { 12 | Left, 13 | Right 14 | } 15 | 16 | public class AlignableStackPanel : StackPanel 17 | { 18 | public static readonly StyledProperty HorizontalChildrenAlignmentProperty = 19 | AvaloniaProperty.Register(nameof(HorizontalChildrenAlignment), defaultValue: ChildrenHorizontalAlignment.Left); 20 | 21 | public ChildrenHorizontalAlignment HorizontalChildrenAlignment 22 | { 23 | get => GetValue(HorizontalChildrenAlignmentProperty); 24 | set => SetValue(HorizontalChildrenAlignmentProperty, value); 25 | } 26 | 27 | static AlignableStackPanel() 28 | { 29 | AffectsMeasure(HorizontalChildrenAlignmentProperty); 30 | AffectsArrange(HorizontalChildrenAlignmentProperty); 31 | AffectsRender(HorizontalChildrenAlignmentProperty); 32 | } 33 | 34 | protected override Size ArrangeOverride(Size finalSize) 35 | { 36 | bool fHorizontal = (Orientation == Orientation.Horizontal); 37 | bool fRight = (HorizontalChildrenAlignment == ChildrenHorizontalAlignment.Right); 38 | if (fHorizontal) 39 | { 40 | if (fRight) 41 | { 42 | var children = Children.Reverse(); 43 | Rect rcChild = new Rect(finalSize); 44 | double previousChildSize = 0.0; 45 | var spacing = Spacing; 46 | 47 | for (int i = 0, count = children.Count(); i < count; ++i) 48 | { 49 | var child = children.ElementAt(i); 50 | 51 | if (child == null || !child.IsVisible) 52 | { continue; } 53 | 54 | 55 | previousChildSize += child.DesiredSize.Width; 56 | rcChild = rcChild.WithX(finalSize.Width - previousChildSize); 57 | rcChild = rcChild.WithWidth(child.DesiredSize.Width); 58 | rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height)); 59 | previousChildSize += spacing; 60 | 61 | ArrangeChild(child, rcChild); 62 | } 63 | } 64 | else 65 | { 66 | var children = Children; 67 | Rect rcChild = new Rect(finalSize); 68 | double previousChildSize = 0.0; 69 | var spacing = Spacing; 70 | 71 | for (int i = 0, count = children.Count; i < count; ++i) 72 | { 73 | var child = children[i]; 74 | 75 | if (child == null || !child.IsVisible) 76 | { continue; } 77 | 78 | 79 | rcChild = rcChild.WithX(rcChild.X + previousChildSize); 80 | previousChildSize = child.DesiredSize.Width; 81 | rcChild = rcChild.WithWidth(previousChildSize); 82 | rcChild = rcChild.WithHeight(Math.Max(finalSize.Height, child.DesiredSize.Height)); 83 | previousChildSize += spacing; 84 | 85 | ArrangeChild(child, rcChild); 86 | } 87 | } 88 | } 89 | else 90 | { 91 | var children = Children; 92 | Rect rcChild = new Rect(finalSize); 93 | double previousChildSize = 0.0; 94 | var spacing = Spacing; 95 | 96 | for (int i = 0, count = children.Count; i < count; ++i) 97 | { 98 | var child = children[i]; 99 | 100 | if (child == null || !child.IsVisible) 101 | { continue; } 102 | rcChild = rcChild.WithY(rcChild.Y + previousChildSize); 103 | previousChildSize = child.DesiredSize.Height; 104 | rcChild = rcChild.WithHeight(previousChildSize); 105 | rcChild = rcChild.WithWidth(Math.Max(finalSize.Width, child.DesiredSize.Width)); 106 | previousChildSize += spacing; 107 | 108 | ArrangeChild(child, rcChild); 109 | } 110 | } 111 | 112 | return finalSize; 113 | } 114 | 115 | void ArrangeChild(IControl child, Rect rect) 116 | { 117 | child.Arrange(rect); 118 | } 119 | 120 | /*protected override Size MeasureCore(Size availableSize) 121 | { 122 | var retVal = base.MeasureCore(availableSize); 123 | if (ForceZeroDesiredSize) 124 | return Size.Empty; 125 | else 126 | return retVal; 127 | } 128 | 129 | protected override Size MeasureOverride(Size availableSize) 130 | { 131 | var retVal = base.MeasureOverride(availableSize); 132 | if (ForceZeroDesiredSize) 133 | return Size.Empty; 134 | else 135 | return retVal; 136 | }*/ 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/CommandBar/CommandBarLayer.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Input; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | 8 | namespace Mechanism.AvaloniaUI.Controls 9 | { 10 | public class CommandBarLayer : Control 11 | { 12 | public static readonly StyledProperty IdentifierProperty = 13 | AvaloniaProperty.Register(nameof(Identifier), defaultValue: null); 14 | 15 | public string Identifier 16 | { 17 | get => GetValue(IdentifierProperty); 18 | set => SetValue(IdentifierProperty, value); 19 | } 20 | 21 | /*public static readonly StyledProperty IsVisibleProperty = 22 | AvaloniaProperty.Register(nameof(IsVisible), defaultValue: false); 23 | 24 | public bool IsVisible 25 | { 26 | get { return GetValue(IsVisibleProperty); } 27 | set { SetValue(IsVisibleProperty, value); } 28 | }*/ 29 | 30 | static CommandBarLayer() 31 | { 32 | IsVisibleProperty.Changed.AddClassHandler(new Action((sneder, args) => IsLayerVisibleChanged?.Invoke(sneder, new EventArgs()))); 33 | } 34 | 35 | public static event EventHandler IsLayerVisibleChanged; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/NoSizeStackPanel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | 7 | namespace Mechanism.AvaloniaUI.Controls 8 | { 9 | public class NoSizeStackPanel : StackPanel 10 | { 11 | protected override Size MeasureOverride(Size availableSize) 12 | { 13 | /*if (double.IsInfinity(availableSize.Width)) 14 | return new Size(0, base.MeasureOverride(availableSize).Height); 15 | else 16 | { 17 | Size retSize = base.MeasureOverride(availableSize); 18 | return new Size(Math.Min(availableSize.Width, retSize.Width), retSize.Height); 19 | }*/ 20 | if (double.IsInfinity(availableSize.Width)) 21 | return new Size(0, base.MeasureOverride(availableSize).Height); 22 | else 23 | { 24 | Size retSize = base.MeasureOverride(availableSize); 25 | return new Size(Math.Min(availableSize.Width, retSize.Width), retSize.Height); 26 | } 27 | /*Size retSize = base.MeasureOverride(availableSize); 28 | if (availableSize.Width < retSize.Width) 29 | return new Size(0, retSize.Height); 30 | else 31 | return retSize;*/ 32 | } 33 | 34 | protected override Size MeasureCore(Size availableSize) 35 | { 36 | /*if (double.IsInfinity(availableSize.Width)) 37 | return new Size(0, base.MeasureCore(availableSize).Height); 38 | else 39 | { 40 | Size retSize = base.MeasureCore(availableSize); 41 | return new Size(Math.Min(availableSize.Width, retSize.Width), retSize.Height); 42 | }*/ 43 | if (double.IsInfinity(availableSize.Width)) 44 | return new Size(0, base.MeasureCore(availableSize).Height); 45 | else 46 | { 47 | Size retSize = base.MeasureCore(availableSize); 48 | return new Size(Math.Min(availableSize.Width, retSize.Width), retSize.Height); 49 | } 50 | /*Size retSize = base.MeasureCore(availableSize); 51 | if (availableSize.Width < retSize.Width) 52 | return new Size(0, retSize.Height); 53 | else 54 | return retSize;*/ 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/SwipeSheet/SwipeSheet.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Collections.ObjectModel; 6 | using System.Collections.Specialized; 7 | using System.Linq; 8 | using System.Text; 9 | using Avalonia.Layout; 10 | using Avalonia.Controls.Templates; 11 | using System.Collections; 12 | using Avalonia.Collections; 13 | using Avalonia.Controls.Primitives; 14 | using Avalonia.Controls.Presenters; 15 | using System.Diagnostics; 16 | using Avalonia.Media; 17 | using Avalonia.Input; 18 | 19 | namespace Mechanism.AvaloniaUI.Controls 20 | { 21 | public class SwipeSheet : HeaderedContentControl 22 | { 23 | public static readonly StyledProperty SwipeFromEdgeProperty = 24 | AvaloniaProperty.Register(nameof(SwipeFromEdge), defaultValue: Dock.Bottom); 25 | 26 | public Dock SwipeFromEdge 27 | { 28 | get => GetValue(SwipeFromEdgeProperty); 29 | set => SetValue(SwipeFromEdgeProperty, value); 30 | } 31 | 32 | 33 | public static readonly StyledProperty SwipeToOpenThresholdProperty = 34 | AvaloniaProperty.Register(nameof(SwipeToOpenThreshold), defaultValue: new RelativePoint(0.5, 0.5, RelativeUnit.Relative)); 35 | 36 | public RelativePoint SwipeToOpenThreshold 37 | { 38 | get => GetValue(SwipeToOpenThresholdProperty); 39 | set => SetValue(SwipeToOpenThresholdProperty, value); 40 | } 41 | 42 | 43 | private bool _isOpen; 44 | /// 45 | /// Defines the property. 46 | /// 47 | public static readonly DirectProperty IsOpenProperty = Popup.IsOpenProperty.AddOwner( 48 | o => o.IsOpen, 49 | (o, v) => o.IsOpen = v); 50 | 51 | 52 | /// 53 | /// Gets or sets a value indicating whether the SwipeSheet is currently open. 54 | /// 55 | public bool IsOpen 56 | { 57 | get => _isOpen; 58 | set => SetAndRaise(IsOpenProperty, ref _isOpen, value); 59 | } 60 | 61 | 62 | Thumb _swipeThumb = null; 63 | Thumb _swipePanelThumb = null; 64 | ContentControl _contentArea = null; 65 | 66 | //TranslateTransform _transform = new TranslateTransform(0, -10); 67 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e) 68 | { 69 | base.OnApplyTemplate(e); 70 | 71 | _contentArea = e.NameScope.Find("PART_ContentArea"); 72 | _contentArea.IsVisible = false; 73 | Debug.WriteLine("Margin: " + _contentArea.Margin); 74 | //_contentArea.LayoutTransform = _transform; 75 | 76 | _swipeThumb = e.NameScope.Find("PART_SwipeEdge"); 77 | _swipeThumb.DragStarted += Thumb_DragStarted; 78 | _swipeThumb.DragDelta += Thumb_DragDelta; 79 | _swipeThumb.DragCompleted += Thumb_DragCompleted; 80 | 81 | _swipePanelThumb = e.NameScope.Find("PART_SwipePanelThumb"); 82 | _swipePanelThumb.DragStarted += Thumb_DragStarted; 83 | _swipePanelThumb.DragDelta += Thumb_DragDelta; 84 | _swipePanelThumb.DragCompleted += Thumb_DragCompleted; 85 | } 86 | 87 | void Thumb_DragStarted(object sender, VectorEventArgs e) 88 | { 89 | _contentArea.IsVisible = true; 90 | _contentArea.Margin = GetMarginForOffset(GetSwipeOffset(Vector.Zero, SwipeFromEdge), SwipeFromEdge); 91 | } 92 | 93 | void Thumb_DragDelta(object sender, VectorEventArgs e) 94 | { 95 | Debug.WriteLine("DragDelta: \n " + e.Vector + "\n " + _contentArea.Margin); //: " + (_contentArea.LayoutTransform as TranslateTransform).X + ", " + (_contentArea.LayoutTransform as TranslateTransform).Y); 96 | 97 | _contentArea.Margin = GetMarginForOffset(GetSwipeOffset(e.Vector, SwipeFromEdge), SwipeFromEdge); 98 | } 99 | 100 | void Thumb_DragCompleted(object sender, VectorEventArgs e) 101 | { 102 | if ( 103 | ((SwipeToOpenThreshold.Unit == RelativeUnit.Absolute) && (Math.Abs(_contentArea.Margin.Bottom) < SwipeToOpenThreshold.Point.Y)) || 104 | ((SwipeToOpenThreshold.Unit == RelativeUnit.Relative) && (Math.Abs(_contentArea.Margin.Bottom) < (SwipeToOpenThreshold.Point.Y * Bounds.Height))) 105 | ) 106 | { 107 | _contentArea.Margin = new Thickness(0); 108 | } 109 | else 110 | { 111 | _contentArea.IsVisible = false; 112 | } 113 | /*double threshold = 0; 114 | double movement = 0; 115 | if (IsHorizontal) 116 | { 117 | movement = _transform.X; 118 | 119 | if (SwipeToOpenThreshold.Unit == RelativeUnit.Absolute) 120 | threshold = SwipeToOpenThreshold.Point.X; 121 | else 122 | threshold = Bounds.Width * SwipeToOpenThreshold.Point.X; 123 | 124 | if (SwipeFromEdge == Dock.Right) 125 | { 126 | threshold = Bounds.Width - threshold; 127 | movement = Bounds.Width - _transform.X; 128 | } 129 | } 130 | else 131 | { 132 | if (SwipeToOpenThreshold.Unit == RelativeUnit.Absolute) 133 | threshold = SwipeToOpenThreshold.Point.Y; 134 | else 135 | threshold = Bounds.Height * SwipeToOpenThreshold.Point.Y; 136 | 137 | if (SwipeFromEdge == Dock.Bottom) 138 | { 139 | threshold = Bounds.Height - threshold; 140 | movement = Bounds.Height - _transform.Y; 141 | } 142 | } 143 | 144 | if (movement < threshold) 145 | { 146 | if (SwipeFromEdge == Dock.Left) 147 | { 148 | _transform.X = -Bounds.Width; 149 | } 150 | else if (SwipeFromEdge == Dock.Top) 151 | { 152 | _transform.Y = -Bounds.Height; 153 | } 154 | else if (SwipeFromEdge == Dock.Right) 155 | { 156 | _transform.X = 0; 157 | } 158 | else 159 | { 160 | 161 | } 162 | } 163 | else 164 | { 165 | if (SwipeFromEdge == Dock.Left) 166 | { 167 | 168 | } 169 | else if (SwipeFromEdge == Dock.Top) 170 | { 171 | 172 | } 173 | else if (SwipeFromEdge == Dock.Right) 174 | { 175 | 176 | } 177 | else 178 | { 179 | 180 | } 181 | }*/ 182 | } 183 | 184 | bool IsHorizontal => (SwipeFromEdge == Dock.Left) || (SwipeFromEdge == Dock.Right); 185 | 186 | protected double GetSwipeOffset(Vector vector, Dock edge) 187 | { 188 | if ((edge == Dock.Left) || (edge == Dock.Right)) 189 | return GetSwipeOffset(vector.X, edge); 190 | else 191 | return GetSwipeOffset(vector.Y, edge); 192 | } 193 | 194 | protected double GetSwipeOffset(double distance, Dock edge) 195 | { 196 | double offset = 0; 197 | if (edge == Dock.Left) 198 | offset = Math.Max(0, distance) + Bounds.Width; 199 | else if (edge == Dock.Top) 200 | offset = Math.Max(0, distance) + Bounds.Height; 201 | else if (edge == Dock.Right) 202 | offset = Math.Max(0, distance) + Bounds.Width; 203 | else //Bottom 204 | offset = Math.Min(0, distance) + Bounds.Height; 205 | 206 | return offset; 207 | } 208 | 209 | protected double GetSwipeOffset(Thumb thumb, Vector vector, Dock edge) 210 | { 211 | if ((edge == Dock.Left) || (edge == Dock.Right)) 212 | return GetSwipeOffset(thumb, vector.X, edge); 213 | else 214 | return GetSwipeOffset(thumb, vector.Y, edge); 215 | } 216 | 217 | protected double GetSwipeOffset(Thumb thumb, double distance, Dock edge) 218 | { 219 | double offset = 0; 220 | if (edge == Dock.Left) 221 | offset = Math.Max(0, distance) + (Bounds.Right - thumb.Bounds.Right); 222 | else if (edge == Dock.Top) 223 | offset = Math.Max(0, distance) + (Bounds.Bottom - thumb.Bounds.Bottom); 224 | else if (edge == Dock.Right) 225 | offset = Math.Max(0, distance) + (Bounds.Right - thumb.Bounds.Right); 226 | else //Bottom 227 | offset = Math.Min(0, distance) + (Bounds.Bottom - thumb.Bounds.Bottom); 228 | 229 | return offset; 230 | } 231 | 232 | protected Thickness GetMarginForOffset(double offset, Dock edge) 233 | { 234 | if (edge == Dock.Bottom) 235 | return new Thickness(0, offset, 0, -offset); 236 | else 237 | return new Thickness(offset, 0, -offset, 0); 238 | } 239 | } 240 | 241 | 242 | public class RelativeDouble 243 | { 244 | } 245 | } -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolBar/ToolBarTray.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Generators; 4 | using System; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Collections.Specialized; 8 | using System.Diagnostics; 9 | using System.Linq; 10 | using System.Text; 11 | 12 | namespace Mechanism.AvaloniaUI.Controls 13 | { 14 | public class ToolBarTray : ItemsControl 15 | { 16 | 17 | public static readonly StyledProperty IsLockedProperty = 18 | AvaloniaProperty.Register(nameof(IsLocked), defaultValue: false); 19 | 20 | public bool IsLocked 21 | { 22 | get => GetValue(IsLockedProperty); 23 | set => SetValue(IsLockedProperty, value); 24 | } 25 | 26 | 27 | static ToolBarTray() 28 | { 29 | AffectsArrange(IsLockedProperty); 30 | AffectsMeasure(IsLockedProperty); 31 | AffectsRender(IsLockedProperty); 32 | } 33 | 34 | protected override IItemContainerGenerator CreateItemContainerGenerator() 35 | { 36 | return new ItemContainerGenerator(this, ToolBar.ContentProperty, ToolBar.ContentTemplateProperty); 37 | } 38 | 39 | /*protected int GetBandCount() 40 | { 41 | if (Items.OfType().Count() > 0) 42 | return Items.OfType().Max(x => /*GetBand(x.GetParentTrayItem()*x.Band); 43 | else 44 | return 0; 45 | }*/ 46 | 47 | protected override void ItemsChanged(AvaloniaPropertyChangedEventArgs e) 48 | { 49 | /*IEnumerable newBars = (e.NewValue as IEnumerable).OfType(); 50 | int lastBandIndex = 0; 51 | if (Items.OfType().Count() > 0) 52 | lastBandIndex = Items.OfType().Where(x => x.Band == GetBandCount()).Max(x => x.BandIndex); 53 | for (int i = 0; i < newBars.Count(); i++) 54 | { 55 | newBars.ElementAt(i).Band = GetBandCount(); 56 | //newBars.ElementAt(i).BandIndex = i + lastBandIndex; 57 | //Debug.WriteLine("Band info set!"); 58 | } 59 | base.ItemsChanged(e);*/ 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolBar/ToolBarTrayPanel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Linq; 7 | using System.Text; 8 | 9 | namespace Mechanism.AvaloniaUI.Controls 10 | { 11 | public class ToolBarTrayPanel : Panel 12 | { 13 | protected override Size ArrangeOverride(Size finalSize) 14 | { 15 | 16 | var retVal = base.ArrangeOverride(finalSize); 17 | var children = Children.OfType(); 18 | //LayoutToolBars(retVal); 19 | foreach (ToolBar bar in children.Where(x => double.IsNaN(x.BandLength))) 20 | { 21 | bar.Measure(Size.Infinity); 22 | bar.BandLength = bar.DesiredSize.Width; 23 | //Debug.WriteLine("LENGTH SET IN PANEL: " + bar.BandLength); 24 | } 25 | 26 | 27 | int bandCount = children.Max(x => x.Band) + 1; 28 | 29 | double topOffset = 0; 30 | for (int band = 0; band < bandCount; band++) 31 | { 32 | var bandBars = children.Where(x => x.Band == band); 33 | if (bandBars.Count() > 0) 34 | { 35 | double bandHeight = bandBars.Max(x => x.DesiredSize.Height); 36 | 37 | /*int nIndex = Math.Max(-1, bandBars.Max(x => x.BandIndex)); 38 | foreach (ToolBar bar in bandBars.Where(x => x.BandIndex < 0)) 39 | { 40 | nIndex++; 41 | bar.BandIndex = nIndex; 42 | }*/ 43 | 44 | bandBars = bandBars.OrderBy(x => x.BandIndex); 45 | double leftOffset = 0; 46 | var lastBar = bandBars.Last(); 47 | 48 | if (bandBars.Count() > 1) 49 | { 50 | bandBars = bandBars.Take(bandBars.Count() - 1); 51 | foreach (ToolBar bar in bandBars) 52 | { 53 | double width = bar.BandLength; 54 | if ((leftOffset + width) > (finalSize.Width - lastBar.MinWidth)) 55 | { 56 | width -= lastBar.MinWidth; 57 | } 58 | bar.Arrange(new Rect(leftOffset, topOffset, Math.Max(0, width), bandHeight)); 59 | leftOffset += width; 60 | } 61 | } 62 | 63 | lastBar.Arrange(new Rect(leftOffset, topOffset, Math.Max(0, retVal.Width - leftOffset), bandHeight)); 64 | 65 | topOffset += bandHeight; 66 | } 67 | } 68 | 69 | return retVal; 70 | } 71 | 72 | public void LayoutToolBars(Size finalSize) 73 | { 74 | //double ctrlWidth = 0; 75 | //double ctrlHeight = 0; 76 | ////Debug.WriteLine("ArrangeOverride"); 77 | IEnumerable unsortedItems = Children.OfType(); //.Where(x => x is ToolBar).Cast(); 78 | 79 | int bandCount = unsortedItems.Max(x => x.Band) + 1; 80 | double top = 0; 81 | //int prevBandsCount = 0; 82 | for (int i = 0; i <= bandCount; i++) 83 | { 84 | IEnumerable iBandItems = unsortedItems.Where(x => x.Band == i); 85 | 86 | if (iBandItems.Count() > 0) 87 | { 88 | int nIndex = iBandItems.Max(x => x.BandIndex); 89 | foreach (ToolBar bar in iBandItems.Where(x => x.BandIndex < 0)) 90 | { 91 | nIndex++; 92 | bar.BandIndex = nIndex; 93 | } 94 | } 95 | 96 | 97 | List bandItems = iBandItems.OrderBy(x => x.BandIndex).ToList(); 98 | double bandThickness = 0; 99 | if (unsortedItems.Where(x => x.Band == i).Count() > 0) 100 | { 101 | bandThickness = bandItems/*.Where(x => x.Band == i)*/.Max(x => x.DesiredSize.Height); 102 | double left = 0; 103 | int index = 0; 104 | foreach (ToolBar item in bandItems) 105 | { 106 | item.BandIndex = bandItems.IndexOf(item)/* - prevBandsCount*/; 107 | double width = Math.Max(item.MinWidth, Math.Min(item.Width, item.MaxWidth)); //item.Width; 108 | /*if ((width <= 0) && (item.DesiredSize.Width > 0)) 109 | width = item.DesiredSize.Width;*/ 110 | /*if (width <= 0) 111 | width = 100;*/ 112 | /*if (left + width > Bounds.Width) 113 | width -= (left + width) - Bounds.Width;*/ 114 | if (width < 0) 115 | width = 0; 116 | if (bandItems.Last() == item) 117 | { 118 | double targetWidth = Math.Max(Bounds.Width - left, 0); 119 | item.Width = targetWidth; 120 | item.Arrange(new Rect(left, top, targetWidth, bandThickness)); 121 | ////Debug.WriteLine("Last item"); 122 | } 123 | else 124 | item.Arrange(new Rect(left, top, width, bandThickness)); 125 | ////Debug.WriteLine("arranged ToolBar " + index + " at X = " + left); 126 | left += width; 127 | index++; 128 | } 129 | top += bandThickness; 130 | } 131 | //prevBandsCount += bandItems.Count(); 132 | } 133 | } 134 | 135 | protected override Size MeasureOverride(Size availableSize) 136 | { 137 | Size retVal = base.MeasureOverride(availableSize); 138 | 139 | IEnumerable unsortedItems = Children.OfType(); //.Where(x => x is ToolBar).Cast(); 140 | double bandThickness = 0; 141 | if (unsortedItems.Count() > 0) 142 | { 143 | int bandCount = unsortedItems.Max(x => x.Band) + 1; 144 | while (unsortedItems.Where(x => x.Band == 0).Count() == 0) 145 | { 146 | foreach (ToolBar t in unsortedItems) 147 | t.Band--; 148 | } 149 | for (int i = 0; i <= bandCount; i++) 150 | { 151 | IEnumerable bandItems = unsortedItems.Where(x => x.Band == i)/*.OrderBy(x => x.BandIndex)*/; 152 | if (bandItems/*.Where(x => x.Band == i)*/.Count() > 0) 153 | { 154 | bandThickness += bandItems.Max(x => x.DesiredSize.Height); 155 | } 156 | } 157 | } 158 | 159 | return new Size(retVal.Width, bandThickness); 160 | } 161 | 162 | static ToolBarTrayPanel() 163 | { 164 | //BoundsProperty.Changed.AddClassHandler(new Action((sneder, args) => sneder.LayoutToolBars(sneder.Bounds.Size))); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/AddToToolStripBehavior.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.Primitives; 3 | using Avalonia.Xaml.Interactivity; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using Avalonia.Layout; 8 | using Avalonia.Input; 9 | using Avalonia.Controls.Shapes; 10 | using Avalonia.Media; 11 | using Avalonia.Controls; 12 | using Avalonia.Controls.Presenters; 13 | using Avalonia.Media.Imaging; 14 | using System.IO; 15 | 16 | namespace Mechanism.AvaloniaUI.Controls 17 | { 18 | public class AddToToolStripBehavior : Behavior 19 | { 20 | public static readonly StyledProperty OwnerProperty = 21 | AvaloniaProperty.Register(nameof(Owner)); 22 | 23 | public ToolStrip Owner 24 | { 25 | get => GetValue(OwnerProperty); 26 | set => SetValue(OwnerProperty, value); 27 | } 28 | 29 | public static readonly StyledProperty TargetItemProperty = 30 | AvaloniaProperty.Register(nameof(TargetItem)); 31 | 32 | public IToolStripItem TargetItem 33 | { 34 | get => GetValue(TargetItemProperty); 35 | set => SetValue(TargetItemProperty, value); 36 | } 37 | 38 | protected override void OnAttached() 39 | { 40 | base.OnAttached(); 41 | AssociatedObject.DragStarted += AssociatedObject_DragStarted; 42 | AssociatedObject.DragDelta += AssociatedObject_DragDelta; 43 | AssociatedObject.DragCompleted += AssociatedObject_DragCompleted; 44 | } 45 | 46 | 47 | Rectangle _popupDragMovePreview = null; 48 | Rectangle _windowDragMovePreview = null; 49 | double _dragMoveStartLeft = 0; 50 | double _dragMoveStartTop = 0; 51 | private void AssociatedObject_DragStarted(object sender, VectorEventArgs e) 52 | { 53 | var visRoot = Avalonia.VisualTree.VisualExtensions.GetVisualRoot(AssociatedObject); 54 | var pnt = AssociatedObject.TranslatePoint(new Point(0, 0), visRoot).GetValueOrDefault(); 55 | _dragMoveStartLeft = pnt.X; 56 | _dragMoveStartTop = pnt.Y; 57 | 58 | 59 | var tmplParent = AssociatedObject.Parent as Visual; 60 | var pxSize = new PixelSize((int)(tmplParent.Bounds.Width * visRoot.RenderScaling), (int)(tmplParent.Bounds.Height * visRoot.RenderScaling)); 61 | 62 | RenderTargetBitmap bmp = new RenderTargetBitmap(pxSize); 63 | bmp.Render(tmplParent); 64 | ImageBrush brush = null; 65 | using (MemoryStream stream = new MemoryStream()) 66 | { 67 | bmp.Save(stream); 68 | stream.Position = 0; 69 | brush = new ImageBrush(new Bitmap(stream)); 70 | } 71 | 72 | _popupDragMovePreview = new Rectangle() 73 | { 74 | Width = AssociatedObject.Bounds.Width, 75 | Height = AssociatedObject.Bounds.Height, 76 | HorizontalAlignment = HorizontalAlignment.Left, 77 | VerticalAlignment = VerticalAlignment.Top, 78 | Fill = brush, 79 | Margin = new Thickness(_dragMoveStartLeft, _dragMoveStartTop, -_dragMoveStartLeft, -_dragMoveStartTop) 80 | }; 81 | _windowDragMovePreview = new Rectangle() 82 | { 83 | Width = AssociatedObject.Bounds.Width, 84 | Height = AssociatedObject.Bounds.Height, 85 | HorizontalAlignment = HorizontalAlignment.Left, 86 | VerticalAlignment = VerticalAlignment.Top, 87 | Fill = brush, 88 | Margin = PopupMarginToWindowMargin(_popupDragMovePreview.Margin) 89 | }; 90 | 91 | AdornerLayer.GetAdornerLayer(AssociatedObject).Children.Add(_popupDragMovePreview); 92 | AdornerLayer.GetAdornerLayer(Owner).Children.Add(_windowDragMovePreview); 93 | } 94 | 95 | private void AssociatedObject_DragDelta(object sender, VectorEventArgs e) 96 | { 97 | double x = _dragMoveStartLeft + e.Vector.X; 98 | double y = _dragMoveStartTop + e.Vector.Y; 99 | _popupDragMovePreview.Margin = new Thickness(x, y, -x, -y); 100 | _windowDragMovePreview.Margin = PopupMarginToWindowMargin(_popupDragMovePreview.Margin); 101 | } 102 | 103 | private void AssociatedObject_DragCompleted(object sender, VectorEventArgs e) 104 | { 105 | var popupLayer = AdornerLayer.GetAdornerLayer(AssociatedObject); 106 | 107 | if (popupLayer.Children.Contains(_popupDragMovePreview)) 108 | popupLayer.Children.Remove(_popupDragMovePreview); 109 | 110 | var windowLayer = AdornerLayer.GetAdornerLayer(Owner); 111 | 112 | if (windowLayer.Children.Contains(_windowDragMovePreview)) 113 | windowLayer.Children.Remove(_windowDragMovePreview); 114 | 115 | _popupDragMovePreview = null; 116 | 117 | Owner.ValidateAddToToolStrip(TargetItem, sender as Visual, e.Vector); 118 | } 119 | 120 | Thickness PopupMarginToWindowMargin(Thickness popupMargin) 121 | { 122 | var pnt = Avalonia.VisualTree.VisualExtensions.GetVisualRoot(Owner).PointToClient(Avalonia.VisualTree.VisualExtensions.GetVisualRoot(AssociatedObject).PointToScreen(new Point(popupMargin.Left, popupMargin.Top))); 123 | return new Thickness(pnt.X, pnt.Y, -pnt.X, -pnt.Y); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/ButtonToolStripItem.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Primitives; 4 | using Avalonia.Controls.Templates; 5 | using Avalonia.Data; 6 | using Avalonia.Interactivity; 7 | using Avalonia.Markup.Xaml.MarkupExtensions; 8 | using Avalonia.Styling; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Diagnostics; 12 | using System.Text; 13 | using System.Windows.Input; 14 | 15 | namespace Mechanism.AvaloniaUI.Controls 16 | { 17 | public class ButtonToolStripItem : Control, IStyleable, IToolStripItem 18 | { 19 | /*private static readonly IControlTemplate DefaultTemplate = new FuncControlTemplate((tctrl, namescope) => new Button() 20 | { 21 | Classes = new Classes("ToolStripButton"), 22 | DataContext = new Binding("."), 23 | Content = new TemplatedControl() 24 | { 25 | [!TemplatedControl.TemplateProperty] = new Binding() 26 | { 27 | Path = "Icon", 28 | RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor) 29 | { 30 | AncestorType = typeof(ButtonToolStripItem) 31 | } 32 | } 33 | } 34 | }); 35 | 36 | private static IControlTemplate DefaultTemplate2 => (IControlTemplate)Application.Current.FindResource("ButtonToolStripItemTemplate");*/ 37 | 38 | public static readonly StyledProperty TemplateProperty = 39 | AvaloniaProperty.Register(nameof(Template)); 40 | 41 | public IControlTemplate Template 42 | { 43 | get => GetValue(TemplateProperty); 44 | set => SetValue(TemplateProperty, value); 45 | } 46 | public static readonly StyledProperty CommandProperty = 47 | AvaloniaProperty.Register(nameof(Command), null); 48 | 49 | public ICommand Command 50 | { 51 | get => GetValue(CommandProperty); 52 | set => SetValue(CommandProperty, value); 53 | } 54 | 55 | public static readonly StyledProperty CommandParameterProperty = 56 | AvaloniaProperty.Register(nameof(CommandParameter)); 57 | 58 | public object CommandParameter 59 | { 60 | get => GetValue(CommandParameterProperty); 61 | set => SetValue(CommandParameterProperty, value); 62 | } 63 | 64 | public static readonly StyledProperty DisplayNameProperty = 65 | AvaloniaProperty.Register(nameof(DisplayName), string.Empty); 66 | 67 | public string DisplayName 68 | { 69 | get => GetValue(DisplayNameProperty); 70 | set => SetValue(DisplayNameProperty, value); 71 | } 72 | 73 | public static readonly StyledProperty AllowDuplicatesProperty = 74 | AvaloniaProperty.Register(nameof(AllowDuplicates), false); 75 | 76 | public bool AllowDuplicates 77 | { 78 | get => GetValue(AllowDuplicatesProperty); 79 | set => SetValue(AllowDuplicatesProperty, value); 80 | } 81 | 82 | public static readonly StyledProperty OwnerProperty = 83 | AvaloniaProperty.Register(nameof(Owner)); 84 | 85 | public ToolStrip Owner 86 | { 87 | get => GetValue(OwnerProperty); 88 | set => SetValue(OwnerProperty, value); 89 | } 90 | 91 | public static readonly RoutedEvent ClickEvent = RoutedEvent.Register(nameof(Click), RoutingStrategies.Bubble); 92 | 93 | public event EventHandler Click 94 | { 95 | add { AddHandler(ClickEvent, value); } 96 | remove { RemoveHandler(ClickEvent, value); } 97 | } 98 | 99 | public void OnClick() 100 | { 101 | Debug.WriteLine("OnClick"); 102 | var e = new RoutedEventArgs(ClickEvent); 103 | RaiseEvent(e); 104 | 105 | if (!e.Handled && Command?.CanExecute(CommandParameter) == true) 106 | { 107 | Command.Execute(CommandParameter); 108 | e.Handled = true; 109 | Debug.WriteLine("Command executed!"); 110 | } 111 | else if (Command == null) 112 | { 113 | Debug.WriteLine("Command == null"); 114 | } 115 | } 116 | 117 | Type IStyleable.StyleKey => typeof(ButtonToolStripItem); 118 | 119 | /*public ButtonToolStripItem() 120 | { 121 | this.Bind(TemplateProperty, new DynamicResourceExtension("ButtonToolStripItemTemplate")); 122 | }*/ 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/FlexibleSpaceToolStripItem.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Text; 7 | using System.Windows.Input; 8 | 9 | namespace Mechanism.AvaloniaUI.Controls 10 | { 11 | public class FlexibleSpaceToolStripItem : Control, IToolStripItem 12 | { 13 | public static readonly StyledProperty TemplateProperty = 14 | AvaloniaProperty.Register(nameof(Template)); 15 | 16 | public IControlTemplate Template 17 | { 18 | get => GetValue(TemplateProperty); 19 | set => SetValue(TemplateProperty, value); 20 | } 21 | 22 | public string DisplayName 23 | { 24 | get => "Flexible space"; 25 | set { } 26 | } 27 | 28 | public bool AllowDuplicates 29 | { 30 | get => true; 31 | set { } 32 | } 33 | 34 | public static readonly StyledProperty OwnerProperty = 35 | AvaloniaProperty.Register(nameof(Owner)); 36 | 37 | public ToolStrip Owner 38 | { 39 | get => GetValue(OwnerProperty); 40 | set => SetValue(OwnerProperty, value); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/IToolStripItem.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Controls.Templates; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Text; 6 | using System.Windows.Input; 7 | 8 | namespace Mechanism.AvaloniaUI.Controls 9 | { 10 | public interface IToolStripItem 11 | { 12 | IControlTemplate Template { get; set; } 13 | 14 | string DisplayName { get; set; } 15 | 16 | bool AllowDuplicates { get; set; } 17 | 18 | ToolStrip Owner { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/MoveOrRemoveFromToolStripBehavior.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.Primitives; 3 | using Avalonia.Controls.Shapes; 4 | using Avalonia.Xaml.Interactivity; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Diagnostics; 8 | using Avalonia.Input; 9 | using Avalonia.Media.Imaging; 10 | using Avalonia.Media; 11 | using System.IO; 12 | using Avalonia.Layout; 13 | using Avalonia.Controls; 14 | 15 | namespace Mechanism.AvaloniaUI.Controls 16 | { 17 | public class MoveOrRemoveFromToolStripBehavior : Behavior 18 | { 19 | public static readonly StyledProperty OwnerProperty = 20 | AvaloniaProperty.Register(nameof(Owner)); 21 | 22 | public ToolStrip Owner 23 | { 24 | get => GetValue(OwnerProperty); 25 | set => SetValue(OwnerProperty, value); 26 | } 27 | 28 | public static readonly StyledProperty TargetProperty = 29 | AvaloniaProperty.Register(nameof(Target)); 30 | 31 | public ToolStripItemReference Target 32 | { 33 | get => GetValue(TargetProperty); 34 | set => SetValue(TargetProperty, value); 35 | } 36 | 37 | protected override void OnAttached() 38 | { 39 | base.OnAttached(); 40 | AssociatedObject.DragStarted += AssociatedObject_DragStarted; 41 | AssociatedObject.DragDelta += AssociatedObject_DragDelta; 42 | AssociatedObject.DragCompleted += AssociatedObject_DragCompleted; 43 | } 44 | 45 | Rectangle _popupDragMovePreview = null; 46 | Rectangle _windowDragMovePreview = null; 47 | double _dragMoveStartLeft = 0; 48 | double _dragMoveStartTop = 0; 49 | private void AssociatedObject_DragStarted(object sender, VectorEventArgs e) 50 | { 51 | var visRoot = Avalonia.VisualTree.VisualExtensions.GetVisualRoot(AssociatedObject); 52 | var pnt = AssociatedObject.TranslatePoint(new Point(0, 0), visRoot).GetValueOrDefault(); 53 | _dragMoveStartLeft = pnt.X; 54 | _dragMoveStartTop = pnt.Y; 55 | 56 | 57 | var tmplParent = AssociatedObject.Parent as Visual; 58 | var pxSize = new PixelSize((int)(tmplParent.Bounds.Width * visRoot.RenderScaling), (int)(tmplParent.Bounds.Height * visRoot.RenderScaling)); 59 | 60 | RenderTargetBitmap bmp = new RenderTargetBitmap(pxSize); 61 | bmp.Render(tmplParent); 62 | ImageBrush brush = null; 63 | using (MemoryStream stream = new MemoryStream()) 64 | { 65 | bmp.Save(stream); 66 | stream.Position = 0; 67 | brush = new ImageBrush(new Bitmap(stream)); 68 | } 69 | 70 | 71 | _windowDragMovePreview = new Rectangle() 72 | { 73 | Width = AssociatedObject.Bounds.Width, 74 | Height = AssociatedObject.Bounds.Height, 75 | HorizontalAlignment = HorizontalAlignment.Left, 76 | VerticalAlignment = VerticalAlignment.Top, 77 | Fill = brush, 78 | Margin = new Thickness(_dragMoveStartLeft, _dragMoveStartTop, -_dragMoveStartLeft, -_dragMoveStartTop) 79 | }; 80 | _popupDragMovePreview = new Rectangle() 81 | { 82 | Width = AssociatedObject.Bounds.Width, 83 | Height = AssociatedObject.Bounds.Height, 84 | HorizontalAlignment = HorizontalAlignment.Left, 85 | VerticalAlignment = VerticalAlignment.Top, 86 | Fill = brush, 87 | Margin = WindowMarginToPopupMargin(_windowDragMovePreview.Margin) 88 | }; 89 | 90 | AdornerLayer.GetAdornerLayer(Owner).Children.Add(_windowDragMovePreview); 91 | AdornerLayer.GetAdornerLayer(Owner._customizePopup).Children.Add(_popupDragMovePreview); 92 | } 93 | 94 | private void AssociatedObject_DragDelta(object sender, VectorEventArgs e) 95 | { 96 | double x = _dragMoveStartLeft + e.Vector.X; 97 | double y = _dragMoveStartTop + e.Vector.Y; 98 | _windowDragMovePreview.Margin = new Thickness(x, y, -x, -y); 99 | _popupDragMovePreview.Margin = WindowMarginToPopupMargin(_windowDragMovePreview.Margin); 100 | } 101 | 102 | private void AssociatedObject_DragCompleted(object sender, VectorEventArgs e) 103 | { 104 | var popupLayer = AdornerLayer.GetAdornerLayer(Owner._customizePopup); 105 | 106 | if (popupLayer.Children.Contains(_popupDragMovePreview)) 107 | popupLayer.Children.Remove(_popupDragMovePreview); 108 | 109 | var windowLayer = AdornerLayer.GetAdornerLayer(Owner); 110 | 111 | if (windowLayer.Children.Contains(_windowDragMovePreview)) 112 | windowLayer.Children.Remove(_windowDragMovePreview); 113 | 114 | Owner.ValidateMoveOrRemoveFromToolStrip(Target, sender as Visual, e.Vector); 115 | } 116 | 117 | Thickness WindowMarginToPopupMargin(Thickness popupMargin) 118 | { 119 | var win = Avalonia.VisualTree.VisualExtensions.GetVisualRoot(Owner); 120 | var pnt = win.PointToScreen(Avalonia.VisualTree.VisualExtensions.GetVisualRoot(Owner._customizePopup).PointToClient(new PixelPoint((int)(popupMargin.Left * win.RenderScaling), (int)(popupMargin.Top * win.RenderScaling)))); 121 | return new Thickness(pnt.X, pnt.Y, -pnt.X, -pnt.Y); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/SegmentedControlToolStripItem.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Primitives; 4 | using Avalonia.Controls.Templates; 5 | using Avalonia.Markup.Xaml.MarkupExtensions; 6 | using Avalonia.Styling; 7 | using System; 8 | using Avalonia.Xaml.Interactivity; 9 | using Avalonia.Layout; 10 | using Avalonia.Controls.Generators; 11 | using System.Collections; 12 | using System.Collections.ObjectModel; 13 | using Avalonia.Metadata; 14 | using Avalonia.Interactivity; 15 | using System.Collections.Generic; 16 | using System.Linq; 17 | 18 | namespace Mechanism.AvaloniaUI.Controls 19 | { 20 | public class SegmentedControlToolStripItem : Control, IStyleable, IToolStripItem 21 | { 22 | private static readonly FuncTemplate DefaultPanel = 23 | new FuncTemplate(() => 24 | { 25 | var stacc = new StackPanel() 26 | { 27 | Orientation = Orientation.Horizontal 28 | }; 29 | return stacc; 30 | }); 31 | 32 | public static readonly StyledProperty> ItemsPanelProperty = 33 | ItemsControl.ItemsPanelProperty.AddOwner(); 34 | 35 | public ITemplate ItemsPanel 36 | { 37 | get => GetValue(ItemsPanelProperty); 38 | set => SetValue(ItemsPanelProperty, value); 39 | } 40 | 41 | 42 | public static readonly StyledProperty ItemTemplateProperty = 43 | ItemsControl.ItemTemplateProperty.AddOwner(); 44 | 45 | public IDataTemplate ItemTemplate 46 | { 47 | get => GetValue(ItemTemplateProperty); 48 | set => SetValue(ItemTemplateProperty, value); 49 | } 50 | 51 | public static readonly StyledProperty SelectedItemProperty = 52 | AvaloniaProperty.Register(nameof(SelectedItem), null); 53 | 54 | public object SelectedItem 55 | { 56 | get => GetValue(SelectedItemProperty); 57 | set => SetValue(SelectedItemProperty, value); 58 | } 59 | 60 | 61 | public static readonly StyledProperty TemplateProperty = 62 | AvaloniaProperty.Register(nameof(Template)); 63 | 64 | public IControlTemplate Template 65 | { 66 | get => GetValue(TemplateProperty); 67 | set => SetValue(TemplateProperty, value); 68 | } 69 | 70 | private IEnumerable _items = new ObservableCollection(); 71 | public static readonly DirectProperty ItemsProperty = 72 | AvaloniaProperty.RegisterDirect(nameof(Items), o => o.Items, (o, v) => o.Items = v); 73 | 74 | [Content] 75 | public IEnumerable Items 76 | { 77 | get => _items; 78 | set => SetAndRaise(ItemsProperty, ref _items, value); 79 | } 80 | 81 | public static readonly StyledProperty DisplayNameProperty = 82 | AvaloniaProperty.Register(nameof(DisplayName), string.Empty); 83 | 84 | public string DisplayName 85 | { 86 | get => GetValue(DisplayNameProperty); 87 | set => SetValue(DisplayNameProperty, value); 88 | } 89 | 90 | 91 | static string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; 92 | static Random _groupRandom = new Random(); 93 | static string GetRandomGroupName() 94 | { 95 | /*var stringChars = new char[8]; 96 | 97 | for (int i = 0; i < stringChars.Length; i++) 98 | { 99 | stringChars[i] = _chars[_groupRandom.Next(_chars.Length)]; 100 | } 101 | 102 | return new String(stringChars);*/ 103 | return System.IO.Path.GetRandomFileName(); 104 | } 105 | 106 | public static readonly StyledProperty GroupNameProperty = 107 | AvaloniaProperty.Register(nameof(GroupName), string.Empty); 108 | 109 | public string GroupName 110 | { 111 | get => GetValue(GroupNameProperty); 112 | protected set => SetValue(GroupNameProperty, value); 113 | } 114 | 115 | public static readonly StyledProperty AllowDuplicatesProperty = 116 | AvaloniaProperty.Register(nameof(AllowDuplicates), false); 117 | 118 | public bool AllowDuplicates 119 | { 120 | get => GetValue(AllowDuplicatesProperty); 121 | set => SetValue(AllowDuplicatesProperty, value); 122 | } 123 | 124 | public static readonly StyledProperty OwnerProperty = 125 | AvaloniaProperty.Register(nameof(Owner)); 126 | 127 | public ToolStrip Owner 128 | { 129 | get => GetValue(OwnerProperty); 130 | set => SetValue(OwnerProperty, value); 131 | } 132 | 133 | public static readonly RoutedEvent SelectionChangedEvent = 134 | RoutedEvent.Register("SelectionChanged", RoutingStrategies.Bubble); 135 | 136 | public event EventHandler SelectionChanged 137 | { 138 | add => AddHandler(SelectionChangedEvent, value); 139 | remove => RemoveHandler(SelectionChangedEvent, value); 140 | } 141 | 142 | static SegmentedControlToolStripItem() 143 | { 144 | ItemsPanelProperty.OverrideDefaultValue(DefaultPanel); 145 | SelectedItemProperty.Changed.AddClassHandler((sender, e) => 146 | { 147 | List oldSelection = null; 148 | if (e.OldValue != null) 149 | oldSelection = new List() 150 | { 151 | e.OldValue 152 | }; 153 | 154 | List newSelection = null; 155 | if (e.NewValue != null) 156 | newSelection = new List() 157 | { 158 | e.NewValue 159 | }; 160 | 161 | sender.RaiseEvent(new SelectionChangedEventArgs(SelectionChangedEvent, oldSelection, newSelection)); 162 | }); 163 | } 164 | 165 | internal bool _settingSelectedItem = false; 166 | internal void SetSelectedItem(object item) 167 | { 168 | _settingSelectedItem = true; 169 | SelectedItem = item; 170 | _settingSelectedItem = false; 171 | } 172 | 173 | public SegmentedControlToolStripItem() 174 | { 175 | SegmentedControlToolStripItemSegmentPresenter.AnyClicked += SegmentedControlToolStripItemSegmentPresenter_AnyClicked; 176 | //GroupName = GetRandomGroupName(); 177 | } 178 | 179 | public void SegmentedControlToolStripItemSegmentPresenter_AnyClicked(object sender, EventArgs e) 180 | { 181 | var dataContext = (sender as Visual).DataContext; 182 | var relevantItems = _items.OfType(); 183 | if (relevantItems.Contains(dataContext)) 184 | { 185 | SelectedItem = dataContext; 186 | (sender as SegmentedControlToolStripItemSegmentPresenter).IsSelected = true; 187 | AnySelectionChanged?.Invoke(new object[] 188 | { 189 | this, 190 | relevantItems 191 | }, new EventArgs()); 192 | } 193 | } 194 | 195 | internal static event EventHandler AnySelectionChanged; 196 | 197 | Type IStyleable.StyleKey => typeof(SegmentedControlToolStripItem); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/SegmentedControlToolStripItemSegment.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Primitives; 4 | using Avalonia.Controls.Templates; 5 | using Avalonia.Markup.Xaml.MarkupExtensions; 6 | using Avalonia.Styling; 7 | using System; 8 | using Avalonia.Xaml.Interactivity; 9 | using Avalonia.Layout; 10 | using Avalonia.Controls.Generators; 11 | using System.ComponentModel; 12 | using System.Runtime.CompilerServices; 13 | using System.Windows.Input; 14 | 15 | namespace Mechanism.AvaloniaUI.Controls 16 | { 17 | public class SegmentedControlToolStripItemSegment : AvaloniaObject, IToolStripItem//, INotifyPropertyChanged 18 | { 19 | //Type IStyleable.StyleKey => typeof(ListBoxItem); 20 | 21 | /*ICommand _command = null; 22 | public ICommand Command 23 | { 24 | get => _command; 25 | set 26 | { 27 | _command = value; 28 | NotifyPropertyChanged(); 29 | } 30 | } 31 | 32 | object _commandParameter = null; 33 | public object CommandParameter 34 | { 35 | get => _commandParameter; 36 | set 37 | { 38 | _commandParameter = value; 39 | NotifyPropertyChanged(); 40 | } 41 | }*/ 42 | 43 | /*string _displayName = string.Empty; 44 | public string DisplayName 45 | { 46 | get => _displayName; 47 | set 48 | { 49 | _displayName = value; 50 | NotifyPropertyChanged(); 51 | } 52 | } 53 | 54 | IControlTemplate _icon = null; 55 | public IControlTemplate Icon 56 | { 57 | get => _icon; 58 | set 59 | { 60 | _icon = value; 61 | NotifyPropertyChanged(); 62 | } 63 | }*/ 64 | 65 | public static readonly StyledProperty CommandProperty = 66 | AvaloniaProperty.Register(nameof(Command)); 67 | 68 | public ICommand Command 69 | { 70 | get => GetValue(CommandProperty); 71 | set => SetValue(CommandProperty, value); 72 | } 73 | 74 | public static readonly StyledProperty CommandParameterProperty = 75 | AvaloniaProperty.Register(nameof(CommandParameter)); 76 | 77 | public object CommandParameter 78 | { 79 | get => GetValue(CommandParameterProperty); 80 | set => SetValue(CommandParameterProperty, value); 81 | } 82 | 83 | public static readonly StyledProperty DisplayNameProperty = 84 | AvaloniaProperty.Register(nameof(DisplayName), string.Empty); 85 | 86 | public string DisplayName 87 | { 88 | get => GetValue(DisplayNameProperty); 89 | set => SetValue(DisplayNameProperty, value); 90 | } 91 | 92 | public static readonly StyledProperty TemplateProperty = 93 | AvaloniaProperty.Register(nameof(Template)); 94 | 95 | public IControlTemplate Template 96 | { 97 | get => GetValue(TemplateProperty); 98 | set => SetValue(TemplateProperty, value); 99 | } 100 | 101 | public bool AllowDuplicates 102 | { 103 | get => false; 104 | set { } 105 | } 106 | public static readonly StyledProperty OwnerProperty = 107 | AvaloniaProperty.Register(nameof(Owner)); 108 | 109 | public ToolStrip Owner 110 | { 111 | get => GetValue(OwnerProperty); 112 | set => SetValue(OwnerProperty, value); 113 | } 114 | 115 | /*void NotifyPropertyChanged([CallerMemberName]string propertyName = "") => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 116 | 117 | public event PropertyChangedEventHandler PropertyChanged;*/ 118 | 119 | public static readonly StyledProperty IsSelectedProperty = 120 | ListBoxItem.IsSelectedProperty.AddOwner(); 121 | 122 | public bool IsSelected 123 | { 124 | get => GetValue(IsSelectedProperty); 125 | set => SetValue(IsSelectedProperty, value); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Mechanism.AvaloniaUI.Controls/Controls/ToolStrip/SegmentedControlToolStripItemSegmentPresenter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Primitives; 4 | using Avalonia.Controls.Templates; 5 | using Avalonia.Markup.Xaml.MarkupExtensions; 6 | using Avalonia.Styling; 7 | using System; 8 | using Avalonia.Xaml.Interactivity; 9 | using Avalonia.Layout; 10 | using Avalonia.Controls.Generators; 11 | using System.ComponentModel; 12 | using System.Runtime.CompilerServices; 13 | using Avalonia.LogicalTree; 14 | using System.Collections.Generic; 15 | using System.Linq; 16 | 17 | namespace Mechanism.AvaloniaUI.Controls 18 | { 19 | public class SegmentedControlToolStripItemSegmentPresenter : TemplatedControl, IStyleable 20 | { 21 | /*public static readonly StyledProperty IconProperty = 22 | AvaloniaProperty.Register(nameof(Icon)); 23 | 24 | public IControlTemplate Icon 25 | { 26 | get => GetValue(IconProperty); 27 | set => SetValue(IconProperty, value); 28 | }*/ 29 | 30 | public static readonly StyledProperty DisplayNameProperty = 31 | AvaloniaProperty.Register(nameof(DisplayName)); 32 | 33 | public string DisplayName 34 | { 35 | get => GetValue(DisplayNameProperty); 36 | set => SetValue(DisplayNameProperty, value); 37 | } 38 | 39 | public static readonly StyledProperty IsSelectedProperty = 40 | ListBoxItem.IsSelectedProperty.AddOwner(); 41 | 42 | public bool IsSelected 43 | { 44 | get => GetValue(IsSelectedProperty); 45 | set => SetValue(IsSelectedProperty, value); 46 | } 47 | 48 | static SegmentedControlToolStripItemSegmentPresenter() 49 | { 50 | IsSelectedProperty.Changed.AddClassHandler((sender, e) => 51 | { 52 | 53 | }); 54 | } 55 | 56 | SegmentedControlToolStripItem _groupOwner = null; 57 | public SegmentedControlToolStripItemSegmentPresenter() 58 | { 59 | /*PointerReleased += (sender, e) => 60 | { 61 | 62 | };*/ 63 | 64 | PropertyChanged += SegmentedControlToolStripItemSegmentPresenter_PropertyChanged; 65 | 66 | SegmentedControlToolStripItem.AnySelectionChanged += (sender, args) => 67 | { 68 | var senders = (sender as object[]); 69 | if ((senders[1] is IEnumerable relevantItems) && (senders[0] is SegmentedControlToolStripItem item) && (relevantItems.Contains(DataContext) && (item.SelectedItem != this.DataContext))) 70 | { 71 | IsSelected = false; 72 | } 73 | }; 74 | } 75 | 76 | 77 | internal static event EventHandler AnyClicked; 78 | 79 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e) 80 | { 81 | base.OnApplyTemplate(e); 82 | e.NameScope.Find