├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yaml │ └── feature_request.yaml ├── .gitignore ├── BoTech.DesignerForAvalonia.Android ├── BoTech.DesignerForAvalonia.Android.csproj ├── Icon.png ├── MainActivity.cs ├── Properties │ └── AndroidManifest.xml └── Resources │ ├── drawable │ └── splash_screen.xml │ ├── values-night │ └── colors.xml │ └── values │ ├── colors.xml │ └── styles.xml ├── BoTech.DesignerForAvalonia.Browser ├── BoTech.DesignerForAvalonia.Browser.csproj ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ └── launchSettings.json ├── runtimeconfig.template.json └── wwwroot │ ├── app.css │ ├── favicon.ico │ ├── index.html │ └── main.js ├── BoTech.DesignerForAvalonia.Desktop ├── BoTech.DesignerForAvalonia.Desktop.csproj ├── Program.cs └── app.manifest ├── BoTech.DesignerForAvalonia.Tests ├── BoTech.DesignerForAvalonia.Tests.csproj └── Services │ └── Binding │ └── BindingManagerTest.cs ├── BoTech.DesignerForAvalonia.iOS ├── AppDelegate.cs ├── BoTech.DesignerForAvalonia.iOS.csproj ├── Entitlements.plist ├── Info.plist ├── Main.cs └── Resources │ └── LaunchScreen.xib ├── BoTech.DesignerForAvalonia.sln ├── BoTech.DesignerForAvalonia ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── BoTechLogoNew.png │ ├── BoTech_DesignerForAvalonia_Mini.ico │ ├── BoTech_DesignerForAvalonia_Mini.png │ ├── BoTech_DesignerForAvalonia_Small.png │ ├── BoTech_DesignerForAvalonia_WithText.png │ └── BoTech_DesignerForAvalonia_WithText_NoBG.png ├── BoTech.DesignerForAvalonia.csproj ├── Controller │ └── Editor │ │ ├── EditorController.cs │ │ └── ProjectController.cs ├── Models │ ├── Binding │ │ ├── ExtractedRelativeSource.cs │ │ └── ValueType.cs │ ├── Editor │ │ ├── EDragAndDropOperation.cs │ │ └── TreeViewNodeBase.cs │ ├── Project │ │ ├── Binding.cs │ │ ├── CSharp │ │ │ ├── ExtractedBindingInfo.cs │ │ │ ├── ExtractedClassInfo.cs │ │ │ ├── ExtractedMethodInfo.cs │ │ │ ├── ExtractedPropertyInfo.cs │ │ │ └── Modifier.cs │ │ ├── DisplayableProjectInfo.cs │ │ ├── OpenableProject.cs │ │ ├── Project.cs │ │ ├── ProjectView.cs │ │ └── ProjectViewModel.cs │ └── XML │ │ └── XmlControl.cs ├── Services │ ├── Avalonia │ │ ├── CloneService.cs │ │ └── TypeCastingService.cs │ ├── Binding │ │ └── BindingManager.cs │ ├── CSharp │ │ └── CSharpParser.cs │ ├── PropertiesView │ │ ├── ControlsCreator.cs │ │ ├── ControlsCreatorAvalonia.cs │ │ ├── ControlsCreatorObject.cs │ │ └── EditBoxOptions.cs │ └── XML │ │ ├── Deserializer.cs │ │ └── Serializer.cs ├── Templates │ └── Editor │ │ └── PropertiesView │ │ ├── AppearanceViewTemplate.cs │ │ ├── ContentViewTemplate.cs │ │ ├── IViewTemplate.cs │ │ ├── InputViewTemplate.cs │ │ ├── LayoutViewTemplate.cs │ │ └── StandardViewTemplate.cs ├── ViewLocator.cs ├── ViewModels │ ├── AboutViewModel.cs │ ├── Abstraction │ │ └── CloseablePageViewModel.cs │ ├── Editor │ │ ├── Dialogs │ │ │ ├── GenericDialogViewModel.cs │ │ │ └── ProjectColorDialogViewModel.cs │ │ ├── ItemsExplorerViewModel.cs │ │ ├── PreviewViewModel.cs │ │ ├── PropertiesViewModel.cs │ │ ├── SolutionExplorerViewModel.cs │ │ ├── TopNavigationViewModel.cs │ │ └── ViewHierarchyViewModel.cs │ ├── LoadingViewModel.cs │ ├── MainViewModel.cs │ ├── ProjectStartViewModel.cs │ └── ViewModelBase.cs └── Views │ ├── AboutView.axaml │ ├── AboutView.axaml.cs │ ├── Abstraction │ └── CloseablePageCodeBehind.cs │ ├── Editor │ ├── Dialogs │ │ ├── GenericDialogView.axaml │ │ ├── GenericDialogView.axaml.cs │ │ ├── ProjectColorDialogView.axaml │ │ ├── ProjectColorDialogView.axaml.cs │ │ ├── PropertiesDialogView.axaml │ │ └── PropertiesDialogView.axaml.cs │ ├── ItemsExplorerView.axaml │ ├── ItemsExplorerView.axaml.cs │ ├── PreviewView.axaml │ ├── PreviewView.axaml.cs │ ├── PropertiesView.axaml │ ├── PropertiesView.axaml.cs │ ├── SolutionExplorerView.axaml │ ├── SolutionExplorerView.axaml.cs │ ├── StatusConsoleView.axaml │ ├── StatusConsoleView.axaml.cs │ ├── TopNavigationView.axaml │ ├── TopNavigationView.axaml.cs │ ├── ViewHierarchyView.axaml │ └── ViewHierarchyView.axaml.cs │ ├── LoadingView.axaml │ ├── LoadingView.axaml.cs │ ├── MainView.axaml │ ├── MainView.axaml.cs │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ ├── ProjectStartView.axaml │ └── ProjectStartView.axaml.cs ├── CODE_OF_CONDUCT.md ├── Contributing.md ├── Directory.Build.props ├── Docs └── Code │ ├── .$BoTech.AvaloniaDesigner.drawio.bkp │ ├── BoTech.AvaloniaDesigner.drawio │ ├── MainCodeStructure.md │ └── README.md ├── LICENSE ├── README.md └── global.json /.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 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report an bug in the BoTech.DesignerForAvalonia 3 | labels: ["bug"] 4 | body: 5 | - type: textarea 6 | id: what-happened 7 | attributes: 8 | label: Describe the bug 9 | description: A clear and concise description of what the bug is. 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: steps 14 | attributes: 15 | label: To Reproduce 16 | description: Steps to reproduce the behavior. 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: what-expected 21 | attributes: 22 | label: Expected behavior 23 | description: A clear and concise description of what you expected to happen. 24 | - type: input 25 | id: version 26 | attributes: 27 | label: BoTech.DesignerForAvalonia Version 28 | placeholder: e.g. V1.0.19, V1.1.0 or V1.1.1 29 | validations: 30 | required: true 31 | - type: dropdown 32 | id: OS 33 | attributes: 34 | label: Operating System 35 | multiple: true 36 | options: 37 | - Windows 38 | - macOS 39 | - Linux 40 | - Web 41 | - Android 42 | - iOS 43 | - type: textarea 44 | id: additional-info 45 | attributes: 46 | label: Additional context 47 | description: | 48 | Add any other context about the problem here. 49 | If applicable, add screenshots to help explain your problem. 50 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yaml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions, Discussions, Ideas 4 | url: https://github.com/BoTech-Development/BoTech.DesignerForAvalonia/discussions/new 5 | about: Please ask and answer questions here 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: Suggest an idea for this project 3 | labels: ["feature"] 4 | body: 5 | - type: textarea 6 | id: problem 7 | attributes: 8 | label: Is your feature request related to ani issue? Please describe and paste a link of the issue. 9 | description: A clear and concise description of what the problem is. 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: solution 14 | attributes: 15 | label: Describe the solution you'd like 16 | description: A clear and concise description of what you want to happen. Which feature should be implemented. 17 | validations: 18 | required: true 19 | - type: textarea 20 | id: implementation 21 | attributes: 22 | label: Do you have an idea how to implement your feature? 23 | description: A clear and precise description of how we can implement the feature. Please use the repository as reference 24 | validations: 25 | required: false 26 | - type: textarea 27 | id: alternatives 28 | attributes: 29 | label: Describe alternatives you've considered 30 | description: A clear and concise description of any alternative solutions or features you've considered. 31 | - type: textarea 32 | id: additional-context 33 | attributes: 34 | label: Additional context 35 | description: Add any other context or screenshots about the feature request here. 36 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/BoTech.DesignerForAvalonia.Android.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net9.0-android 5 | 21 6 | enable 7 | com.CompanyName.AvaloniaTest 8 | 1 9 | 1.0 10 | apk 11 | False 12 | 13 | 14 | 15 | 16 | Resources\drawable\Icon.png 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia.Android/Icon.png -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/MainActivity.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | 4 | using Avalonia; 5 | using Avalonia.Android; 6 | using Avalonia.ReactiveUI; 7 | 8 | namespace BoTech.DesignerForAvalonia.Android; 9 | 10 | [Activity( 11 | Label = "BoTech.DesignerForAvalonia.Android", 12 | Theme = "@style/MyTheme.NoActionBar", 13 | Icon = "@drawable/icon", 14 | MainLauncher = true, 15 | ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)] 16 | public class MainActivity : AvaloniaMainActivity 17 | { 18 | protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) 19 | { 20 | return base.CustomizeAppBuilder(builder) 21 | .WithInterFont() 22 | .UseReactiveUI(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/Properties/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/Resources/drawable/splash_screen.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/Resources/values-night/colors.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | #212121 4 | 5 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/Resources/values/colors.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | #FFFFFF 4 | 5 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Android/Resources/values/styles.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/BoTech.DesignerForAvalonia.Browser.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0-browser 4 | Exe 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Versioning; 2 | using System.Threading.Tasks; 3 | 4 | using Avalonia; 5 | using Avalonia.Browser; 6 | using Avalonia.ReactiveUI; 7 | 8 | using BoTech.DesignerForAvalonia; 9 | 10 | internal sealed partial class Program 11 | { 12 | private static Task Main(string[] args) => BuildAvaloniaApp() 13 | .WithInterFont() 14 | .UseReactiveUI() 15 | .StartBrowserAppAsync("out"); 16 | 17 | public static AppBuilder BuildAvaloniaApp() 18 | => AppBuilder.Configure(); 19 | } 20 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly:System.Runtime.Versioning.SupportedOSPlatform("browser")] 2 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "BoTech.AvaloniaDesigner.Browser": { 4 | "commandName": "Project", 5 | "launchBrowser": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development" 8 | }, 9 | "applicationUrl": "https://localhost:7169;http://localhost:5235", 10 | "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/runtimeconfig.template.json: -------------------------------------------------------------------------------- 1 | { 2 | "wasmHostProperties": { 3 | "perHostConfig": [ 4 | { 5 | "name": "browser", 6 | "host": "browser" 7 | } 8 | ] 9 | } 10 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/wwwroot/app.css: -------------------------------------------------------------------------------- 1 | /* HTML styles for the splash screen */ 2 | .avalonia-splash { 3 | position: absolute; 4 | height: 100%; 5 | width: 100%; 6 | background: white; 7 | font-family: 'Outfit', sans-serif; 8 | justify-content: center; 9 | align-items: center; 10 | display: flex; 11 | pointer-events: none; 12 | } 13 | 14 | /* Light theme styles */ 15 | @media (prefers-color-scheme: light) { 16 | .avalonia-splash { 17 | background: white; 18 | } 19 | 20 | .avalonia-splash h2 { 21 | color: #1b2a4e; 22 | } 23 | 24 | .avalonia-splash a { 25 | color: #0D6EFD; 26 | } 27 | } 28 | 29 | @media (prefers-color-scheme: dark) { 30 | .avalonia-splash { 31 | background: #1b2a4e; 32 | } 33 | 34 | .avalonia-splash h2 { 35 | color: white; 36 | } 37 | 38 | .avalonia-splash a { 39 | color: white; 40 | } 41 | } 42 | 43 | .avalonia-splash h2 { 44 | font-weight: 400; 45 | font-size: 1.5rem; 46 | } 47 | 48 | .avalonia-splash a { 49 | text-decoration: none; 50 | font-size: 2.5rem; 51 | display: block; 52 | } 53 | 54 | .avalonia-splash.splash-close { 55 | transition: opacity 200ms, display 200ms; 56 | display: none; 57 | opacity: 0; 58 | } 59 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia.Browser/wwwroot/favicon.ico -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Browser/wwwroot/main.js: -------------------------------------------------------------------------------- 1 | import { dotnet } from './_framework/dotnet.js' 2 | 3 | const is_browser = typeof window != "undefined"; 4 | if (!is_browser) throw new Error(`Expected to be running in a browser`); 5 | 6 | const dotnetRuntime = await dotnet 7 | .withDiagnosticTracing(false) 8 | .withApplicationArgumentsFromQuery() 9 | .create(); 10 | 11 | const config = dotnetRuntime.getConfig(); 12 | 13 | await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]); 14 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Desktop/BoTech.DesignerForAvalonia.Desktop.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | 6 | net9.0 7 | enable 8 | true 9 | app.manifest 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Desktop/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia; 4 | using Avalonia.ReactiveUI; 5 | 6 | namespace BoTech.DesignerForAvalonia.Desktop; 7 | 8 | class Program 9 | { 10 | // Initialization code. Don't use any Avalonia, third-party APIs or any 11 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 12 | // yet and stuff might break. 13 | [STAThread] 14 | public static void Main(string[] args) => BuildAvaloniaApp() 15 | .StartWithClassicDesktopLifetime(args); 16 | 17 | // Avalonia configuration, don't remove; also used by visual designer. 18 | public static AppBuilder BuildAvaloniaApp() 19 | => AppBuilder.Configure() 20 | .UsePlatformDetect() 21 | .WithInterFont() 22 | .LogToTrace() 23 | .UseReactiveUI(); 24 | } 25 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Desktop/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Tests/BoTech.DesignerForAvalonia.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.Tests/Services/Binding/BindingManagerTest.cs: -------------------------------------------------------------------------------- 1 | using BoTech.DesignerForAvalonia.Services.Binding; 2 | using JetBrains.Annotations; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace BoTech.DesignerForAvalonia.Tests.Services.Binding; 6 | 7 | [TestClass] 8 | [TestSubject(typeof(BindingManager))] 9 | public class BindingManagerTest 10 | { 11 | 12 | [TestMethod] 13 | public void TestNodeTree() 14 | { 15 | BindingManager.ParseBindingsFromSource( 16 | "Binding Title, RelativeSource={RelativeSource Tree=Logical, Mode=FindAncestor, AncestorType=Window}, TargetNullValue={Binding MyInt}"); 17 | } 18 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.iOS/AppDelegate.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.iOS; 3 | using Avalonia.ReactiveUI; 4 | 5 | using Foundation; 6 | 7 | namespace BoTech.DesignerForAvalonia.iOS; 8 | 9 | // The UIApplicationDelegate for the application. This class is responsible for launching the 10 | // User Interface of the application, as well as listening (and optionally responding) to 11 | // application events from iOS. 12 | [Register("AppDelegate")] 13 | public partial class AppDelegate : AvaloniaAppDelegate 14 | { 15 | protected override AppBuilder CustomizeAppBuilder(AppBuilder builder) 16 | { 17 | return base.CustomizeAppBuilder(builder) 18 | .WithInterFont() 19 | .UseReactiveUI(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.iOS/BoTech.DesignerForAvalonia.iOS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Exe 4 | net9.0-ios 5 | 13.0 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.iOS/Entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | AvaloniaTest 7 | CFBundleIdentifier 8 | companyName.AvaloniaTest 9 | CFBundleShortVersionString 10 | 1.0 11 | CFBundleVersion 12 | 1.0 13 | LSRequiresIPhoneOS 14 | 15 | MinimumOSVersion 16 | 10.0 17 | UIDeviceFamily 18 | 19 | 1 20 | 2 21 | 22 | UILaunchStoryboardName 23 | LaunchScreen 24 | UIRequiredDeviceCapabilities 25 | 26 | armv7 27 | 28 | UISupportedInterfaceOrientations 29 | 30 | UIInterfaceOrientationPortrait 31 | UIInterfaceOrientationPortraitUpsideDown 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.iOS/Main.cs: -------------------------------------------------------------------------------- 1 | using UIKit; 2 | 3 | namespace BoTech.DesignerForAvalonia.iOS; 4 | 5 | public class Application 6 | { 7 | // This is the main entry point of the application. 8 | static void Main(string[] args) 9 | { 10 | // if you want to use a different Application Delegate class from "AppDelegate" 11 | // you can specify it here. 12 | UIApplication.Main(args, null, typeof(AppDelegate)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.iOS/Resources/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 21 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.12.35521.163 d17.12 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoTech.DesignerForAvalonia", "BoTech.DesignerForAvalonia\BoTech.DesignerForAvalonia.csproj", "{5CCA3558-0F1B-4429-A435-32DF9D7EC5EA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoTech.DesignerForAvalonia.Android", "BoTech.DesignerForAvalonia.Android\BoTech.DesignerForAvalonia.Android.csproj", "{A86AFF4E-103A-4018-B137-8D27FED26BAD}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoTech.DesignerForAvalonia.Browser", "BoTech.DesignerForAvalonia.Browser\BoTech.DesignerForAvalonia.Browser.csproj", "{E5B4F8E5-C1CB-4B46-A9FB-62F85DB5D5A1}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoTech.DesignerForAvalonia.Desktop", "BoTech.DesignerForAvalonia.Desktop\BoTech.DesignerForAvalonia.Desktop.csproj", "{46094B01-28F9-4438-8E6B-0358102F66C9}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoTech.DesignerForAvalonia.iOS", "BoTech.DesignerForAvalonia.iOS\BoTech.DesignerForAvalonia.iOS.csproj", "{797C477A-EBED-4DD4-BEA6-376DAEF9B00B}" 15 | EndProject 16 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BoTech.DesignerForAvalonia.Tests", "BoTech.DesignerForAvalonia.Tests\BoTech.DesignerForAvalonia.Tests.csproj", "{FBDFCCA0-6709-495D-B784-24BD0F7D3232}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {5CCA3558-0F1B-4429-A435-32DF9D7EC5EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {5CCA3558-0F1B-4429-A435-32DF9D7EC5EA}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {5CCA3558-0F1B-4429-A435-32DF9D7EC5EA}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {5CCA3558-0F1B-4429-A435-32DF9D7EC5EA}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {A86AFF4E-103A-4018-B137-8D27FED26BAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {A86AFF4E-103A-4018-B137-8D27FED26BAD}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {A86AFF4E-103A-4018-B137-8D27FED26BAD}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 31 | {A86AFF4E-103A-4018-B137-8D27FED26BAD}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {A86AFF4E-103A-4018-B137-8D27FED26BAD}.Release|Any CPU.Build.0 = Release|Any CPU 33 | {A86AFF4E-103A-4018-B137-8D27FED26BAD}.Release|Any CPU.Deploy.0 = Release|Any CPU 34 | {E5B4F8E5-C1CB-4B46-A9FB-62F85DB5D5A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {E5B4F8E5-C1CB-4B46-A9FB-62F85DB5D5A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {E5B4F8E5-C1CB-4B46-A9FB-62F85DB5D5A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {E5B4F8E5-C1CB-4B46-A9FB-62F85DB5D5A1}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {46094B01-28F9-4438-8E6B-0358102F66C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {46094B01-28F9-4438-8E6B-0358102F66C9}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {46094B01-28F9-4438-8E6B-0358102F66C9}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {46094B01-28F9-4438-8E6B-0358102F66C9}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {797C477A-EBED-4DD4-BEA6-376DAEF9B00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {797C477A-EBED-4DD4-BEA6-376DAEF9B00B}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {797C477A-EBED-4DD4-BEA6-376DAEF9B00B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 45 | {797C477A-EBED-4DD4-BEA6-376DAEF9B00B}.Release|Any CPU.ActiveCfg = Release|Any CPU 46 | {797C477A-EBED-4DD4-BEA6-376DAEF9B00B}.Release|Any CPU.Build.0 = Release|Any CPU 47 | {797C477A-EBED-4DD4-BEA6-376DAEF9B00B}.Release|Any CPU.Deploy.0 = Release|Any CPU 48 | {FBDFCCA0-6709-495D-B784-24BD0F7D3232}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {FBDFCCA0-6709-495D-B784-24BD0F7D3232}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {FBDFCCA0-6709-495D-B784-24BD0F7D3232}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {FBDFCCA0-6709-495D-B784-24BD0F7D3232}.Release|Any CPU.Build.0 = Release|Any CPU 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | EndGlobal 57 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/App.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using BoTech.DesignerForAvalonia.ViewModels; 5 | using BoTech.DesignerForAvalonia.Views; 6 | 7 | namespace BoTech.DesignerForAvalonia; 8 | 9 | public partial class App : Application 10 | { 11 | public override void Initialize() 12 | { 13 | AvaloniaXamlLoader.Load(this); 14 | } 15 | 16 | public override void OnFrameworkInitializationCompleted() 17 | { 18 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 19 | { 20 | desktop.MainWindow = new MainWindow 21 | { 22 | DataContext = new MainViewModel() 23 | }; 24 | } 25 | else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) 26 | { 27 | singleViewPlatform.MainView = new MainView 28 | { 29 | DataContext = new MainViewModel() 30 | }; 31 | } 32 | 33 | base.OnFrameworkInitializationCompleted(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Assets/BoTechLogoNew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia/Assets/BoTechLogoNew.png -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_Mini.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_Mini.ico -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_Mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_Mini.png -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_Small.png -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_WithText.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_WithText.png -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_WithText_NoBG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BoTech-Development/BoTech.DesignerForAvalonia/f5617e067f6949a588bf501d666abc3adc08daa7/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_WithText_NoBG.png -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/BoTech.DesignerForAvalonia.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net9.0 4 | enable 5 | latest 6 | true 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Binding/ExtractedRelativeSource.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Data; 3 | 4 | namespace BoTech.DesignerForAvalonia.Models.Binding; 5 | 6 | public class ExtractedRelativeSource 7 | { 8 | 9 | private int _ancestorLevel = 1; 10 | /// 11 | /// Type of the RelativeSource. 12 | /// 13 | public ValueType AncestorLevelValueType { get; private set; } 14 | /// 15 | /// Gets the level of ancestor to look for when in mode. 16 | /// 17 | /// 18 | /// Use the default value of 1 to look for the first ancestor of the specified type. 19 | /// 20 | public object? AncestorLevel 21 | { 22 | get { return _ancestorLevel; } 23 | set 24 | { 25 | if (value is int ancestorLevel) 26 | { 27 | if (ancestorLevel <= 0) 28 | throw new ArgumentOutOfRangeException(nameof(value), 29 | "AncestorLevel may not be set to less than 1."); 30 | _ancestorLevel = ancestorLevel; 31 | AncestorLevelValueType = ValueType.Value; 32 | } 33 | else 34 | { 35 | AncestorLevel = value; 36 | AncestorLevelValueType = ValueType.Binding; 37 | } 38 | } 39 | } 40 | /// 41 | /// Type of the RelativeSource. 42 | /// 43 | public ValueType AncestorTypeValueType { get; private set; } 44 | 45 | private object? _ancestorType; 46 | 47 | /// 48 | /// Gets the type of ancestor to look for when in mode. 49 | /// 50 | public object? AncestorType 51 | { 52 | get => _ancestorType; 53 | set 54 | { 55 | if (value is string) 56 | AncestorTypeValueType = ValueType.Value; 57 | else 58 | AncestorTypeValueType = ValueType.Binding; 59 | _ancestorType = value; 60 | } 61 | } 62 | /// 63 | /// Type of the RelativeSource. 64 | /// 65 | public ValueType ModeValueType { get; private set; } 66 | 67 | private object? _mode; 68 | /// 69 | /// Gets or sets a value that describes the type of relative source lookup. 70 | /// 71 | public object? Mode 72 | { 73 | get => _mode; 74 | set 75 | { 76 | if (value is RelativeSourceMode) 77 | ModeValueType = ValueType.Value; 78 | else 79 | ModeValueType = ValueType.Binding; 80 | _mode = value; 81 | } 82 | } //RelativeSourceMode 83 | /// 84 | /// Type of the RelativeSource. 85 | /// 86 | public ValueType TreeValueType { get; private set; } 87 | 88 | private object? _tree; 89 | /// 90 | /// On which tree type the binding should be applied. 91 | /// 92 | public object? Tree 93 | { 94 | get => _tree; 95 | set 96 | { 97 | if (value is TreeType tree) 98 | TreeValueType = ValueType.Value; 99 | else 100 | TreeValueType = ValueType.Binding; 101 | _tree = value; 102 | } 103 | } // TreeType 104 | 105 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Binding/ValueType.cs: -------------------------------------------------------------------------------- 1 | namespace BoTech.DesignerForAvalonia.Models.Binding; 2 | 3 | public enum ValueType 4 | { 5 | /// 6 | /// When no FallbackValue is defined. 7 | /// 8 | None, 9 | /// 10 | /// When the fallbackvalue is defined through another Binding. 11 | /// 12 | Binding, 13 | /// 14 | /// An direct value assigment => {Binding Text, FallbackValue=Hello} 15 | /// 16 | Value 17 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Editor/EDragAndDropOperation.cs: -------------------------------------------------------------------------------- 1 | namespace BoTech.DesignerForAvalonia.Models.Editor; 2 | 3 | public enum EDragAndDropOperation 4 | { 5 | None, 6 | DropObjectToPreview, 7 | Paused 8 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Editor/TreeViewNodeBase.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 3 | 4 | namespace BoTech.DesignerForAvalonia.Models.Editor; 5 | /// 6 | /// Implements some basic functionality for all TreeViewNodes (SolutionExplorer, ViewHierarchy and ItemsExplorer) 7 | /// 8 | public abstract class TreeViewNodeBase 9 | { 10 | /// 11 | /// Will be used by the Search Functionality. 12 | /// 13 | public string Text { get; set; } 14 | public ObservableCollection Children { get; set; } = new ObservableCollection(); 15 | 16 | /// 17 | /// This Method search for an Item in the current instance, where the Text Property Contains the given Text. 18 | /// 19 | /// 20 | /// 21 | /// True when any Item was found, else false 22 | public bool Search(string text, TreeViewNodeBase newNode, bool firstIteration = true) 23 | { 24 | if (firstIteration) 25 | { 26 | bool foundInChild = false; 27 | foreach (TreeViewNodeBase child in Children) 28 | if (child.Search(text, newNode, false)) 29 | foundInChild = true; 30 | return foundInChild; 31 | } 32 | else 33 | { 34 | TreeViewNodeBase? copy = Copy(this); 35 | if (copy != null) 36 | { 37 | bool found = Text.Contains(text); 38 | bool foundInChild = false; 39 | foreach (TreeViewNodeBase child in Children) 40 | if (child.Search(text, copy, false)) 41 | foundInChild = true; 42 | if (found || foundInChild) 43 | { 44 | newNode.Children.Add(copy); 45 | return true; 46 | } 47 | } 48 | } 49 | 50 | return false; 51 | } 52 | 53 | /// 54 | /// Method should Copy the given Node and return the Copy. 55 | /// 56 | /// 57 | /// 58 | protected abstract TreeViewNodeBase? Copy(TreeViewNodeBase nodeToCopy); 59 | 60 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/Binding.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Text.Json.Serialization; 3 | using Avalonia.Controls; 4 | using Avalonia.Data; 5 | 6 | namespace BoTech.DesignerForAvalonia.Models.Project; 7 | 8 | public class Binding 9 | { 10 | /// 11 | /// The string for the Property in the Xml file => "{Binding MyProperty}" 12 | /// 13 | public required string XmlDefinition { get; set; } 14 | /// 15 | /// The Property in the View Model 16 | /// 17 | [JsonIgnore] 18 | public PropertyInfo ViewModelProperty { get; set; } 19 | /// 20 | /// The Property which is defined in the selected control. 21 | /// 22 | [JsonIgnore] 23 | public PropertyInfo ControlPropertyInfo { get; set; } 24 | /// 25 | /// The Binding mode defines the "direction" of a Binding. 26 | /// 27 | public required BindingMode BindingMode { get; set; } 28 | [JsonIgnore] 29 | public Control ControlInstance { get; set; } 30 | [JsonIgnore] 31 | public object LastSourceValue { get; set; } = null; 32 | [JsonIgnore] 33 | public object LastReceiverValue { get; set; } = null; 34 | 35 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/CSharp/ExtractedBindingInfo.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Data; 2 | using BoTech.DesignerForAvalonia.Models.Binding; 3 | 4 | namespace BoTech.DesignerForAvalonia.Models.Project.CSharp; 5 | 6 | public class ExtractedBindingInfo 7 | { 8 | /// 9 | /// The name of the source property to be bound. 10 | /// 11 | public string Path { get; set; } 12 | /// 13 | /// Type of the Mode. 14 | /// 15 | public ValueType ModeValueType { get; private set; } = ValueType.None; 16 | 17 | private object? _mode; 18 | /// 19 | /// The synchronization direction of the binding. 20 | /// 21 | public object? Mode 22 | { 23 | get => _mode; 24 | set 25 | { 26 | _mode = value; 27 | if(value is BindingMode) ModeValueType = ValueType.Value; 28 | else ModeValueType = ValueType.Binding; 29 | } 30 | } 31 | 32 | /// 33 | /// Priority of the property setter. 34 | /// 35 | public BindingPriority Priority { get; set; } 36 | /// 37 | /// Type of the RelativeSource. 38 | /// 39 | public ValueType RelativeSourceValueType { get; private set; } 40 | /// 41 | /// Describes where the property to which the binding refers can be found. 42 | /// 43 | public ExtractedRelativeSource RelativeSource { get; set; } = new ExtractedRelativeSource(); 44 | /// 45 | /// Type of the ElementName. 46 | /// 47 | public ValueType ElementNameValueType { get; private set; } = ValueType.None; 48 | 49 | private object? _elementName; 50 | /// 51 | /// Can be set to the name of another control when the binding refers to a property of this control. 52 | /// For instance: #MyControl.Text and {Binding Text, ElementName=MyControl} are the same. 53 | /// 54 | public object? ElementName 55 | { 56 | get => _elementName; 57 | set 58 | { 59 | _elementName = value; 60 | if(value is string) ElementNameValueType = ValueType.Value; 61 | else ElementNameValueType = ValueType.Binding; 62 | } 63 | } 64 | /// 65 | /// Type of the FallbackValue. 66 | /// 67 | public ValueType FallBackValueType { get; private set; } = ValueType.None; 68 | 69 | private object? _fallbackValue; 70 | /// 71 | /// The Value which will be injected into the property when the referenced property is not reachable or null. 72 | /// 73 | public object? FallbackValue 74 | { 75 | get => _fallbackValue; 76 | set 77 | { 78 | _fallbackValue = value; 79 | if(value is string) FallBackValueType = ValueType.Value; 80 | else FallBackValueType = ValueType.Binding; 81 | } 82 | } 83 | /// 84 | /// Type of the TargetNullValue. 85 | /// 86 | public ValueType TargetNullValueType { get; private set; } = ValueType.None; 87 | 88 | private object? _targetNullValue; 89 | /// 90 | /// Will be applied when the refenced property of this binding is null. 91 | /// 92 | public object? TargetNullValue 93 | { 94 | get => _targetNullValue; 95 | set 96 | { 97 | _targetNullValue = value; 98 | if(value is string) TargetNullValueType = ValueType.Value; 99 | else TargetNullValueType = ValueType.Binding; 100 | } 101 | } 102 | /// 103 | /// Type of the UpdateSourceTrigger. 104 | /// 105 | public ValueType UpdateSourceTriggerValueType { get; private set; } = ValueType.None; 106 | 107 | private object? _updateSourceTrigger; 108 | /// 109 | /// Set when the binding should be updated. 110 | /// NOTE: Some parts may not have supported in this Version. For Instance Explicit. 111 | /// 112 | public object UpdateSourceTrigger 113 | { 114 | get => _updateSourceTrigger; 115 | set 116 | { 117 | _updateSourceTrigger = value; 118 | if(value is string) UpdateSourceTriggerValueType = ValueType.Value; 119 | else UpdateSourceTriggerValueType = ValueType.Binding; 120 | } 121 | } 122 | 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/CSharp/ExtractedClassInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace BoTech.DesignerForAvalonia.Models.Project.CSharp; 5 | 6 | public class ExtractedClassInfo 7 | { 8 | /// 9 | /// The Classname of this Class 10 | /// 11 | public string ClassName { get; set; } 12 | /// 13 | /// The xml Documentation 14 | /// 15 | public string Documentation { get; set; } = string.Empty; 16 | /// 17 | /// The full namespace of this Class. 18 | /// 19 | public string Namespace { get; set; } 20 | /// 21 | /// All declared Methods in this class 22 | /// 23 | public List Methods { get; set; } = new List(); 24 | /// 25 | /// All declared Properties in this Class. 26 | /// 27 | public List Properties { get; set; } = new List(); 28 | /// 29 | /// All Classes that are defined within this Class. 30 | /// 31 | public List SubClasses { get; set; } = new List(); 32 | /// 33 | /// If an exception is thrown during the parsing process, the error text can be stored here. 34 | /// 35 | [JsonIgnore] 36 | public string ParseError { get; set; } = string.Empty; 37 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/CSharp/ExtractedMethodInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text.Json.Serialization; 3 | 4 | namespace BoTech.DesignerForAvalonia.Models.Project.CSharp; 5 | 6 | public class ExtractedMethodInfo 7 | { 8 | /// 9 | /// The Name of the Method 10 | /// 11 | public required string Name { get; set; } 12 | /// 13 | /// Return Type name with namespace if necessary 14 | /// 15 | public string ReturnType { get; set; } = "void"; 16 | /// 17 | /// The xml Documentation 18 | /// 19 | public string Documentation { get; set; } = string.Empty; 20 | /// 21 | /// All Parameters of the Method 22 | /// 23 | public List Parameters { get; set; } = new List(); 24 | /// 25 | /// If an exception is thrown during the parsing process, the error text can be stored here. 26 | /// 27 | [JsonIgnore] 28 | public string ParseError { get; set; } = string.Empty; 29 | } 30 | 31 | public class ExtractedParamInfo 32 | { 33 | /// 34 | /// The name of the Parameter 35 | /// 36 | public required string Name { get; set; } 37 | /// 38 | /// Type name with namespace if necessary 39 | /// 40 | public required string Type { get; set; } 41 | /// 42 | /// Type name with namespace if necessary 43 | /// 44 | public required string DefaultValue { get; set; } 45 | /// 46 | /// extracted Documentation of the Method documentation 47 | /// 48 | public required string Documentation { get; set; } 49 | /// 50 | /// If an exception is thrown during the parsing process, the error text can be stored here. 51 | /// 52 | [JsonIgnore] 53 | public string ParseError { get; set; } = string.Empty; 54 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/CSharp/ExtractedPropertyInfo.cs: -------------------------------------------------------------------------------- 1 |  2 | using System.Text.Json.Serialization; 3 | 4 | namespace BoTech.DesignerForAvalonia.Models.Project.CSharp; 5 | 6 | public class ExtractedPropertyInfo 7 | { 8 | /// 9 | /// The Name of the Property 10 | /// 11 | public string Name { get; set; } 12 | /// 13 | /// The XML Documentation 14 | /// 15 | public string Documentation { get; set; } = string.Empty; 16 | /// 17 | /// The Type with Namespace if necessary 18 | /// 19 | public string Type { get; set; } 20 | /// 21 | /// The AccessModifier of the Property 22 | /// 23 | public Modifier AccessModifier { get; set; } = Modifier.Public; 24 | /// 25 | /// When the Property is defined with the keyword static 26 | /// 27 | public bool IsStatic { get; set; } = false; 28 | /// 29 | /// When the property is defined with the keyword constant 30 | /// 31 | public bool IsConstant { get; set; } = false; 32 | /// 33 | /// When the Property is defined with the keyword readonly 34 | /// 35 | public bool IsReadOnly { get; set; } = false; 36 | /// 37 | /// The definition of the default value. For Example "new User("Florian");" 38 | /// 39 | public string DefaultValue { get; set; } = string.Empty; 40 | /// 41 | /// When a Getter is defined in the brackets => {get;} 42 | /// 43 | public bool HasGetter { get; set; } = false; 44 | /// 45 | /// When a Setter is defined in the brackets => {set;} 46 | /// 47 | public bool HasSetter { get; set; } = false; 48 | /// 49 | /// If an exception is thrown during the parsing process, the error text can be stored here. 50 | /// 51 | [JsonIgnore] 52 | public string ParseError { get; set; } = string.Empty; 53 | 54 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/CSharp/Modifier.cs: -------------------------------------------------------------------------------- 1 | namespace BoTech.DesignerForAvalonia.Models.Project.CSharp; 2 | 3 | public enum Modifier 4 | { 5 | Public, 6 | Protected, 7 | Internal, 8 | Private, 9 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/DisplayableProjectInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive; 2 | using BoTech.DesignerForAvalonia.ViewModels; 3 | using ReactiveUI; 4 | 5 | namespace BoTech.DesignerForAvalonia.Models.Project; 6 | /// 7 | /// This class can be used to show the Project label on the first view. 8 | /// 9 | public class DisplayableProjectInfo 10 | { 11 | 12 | public bool Red { get; set; } = true; 13 | public bool Pink { get; set; } = false; 14 | public bool Purple { get; set; } = false; 15 | public bool Violet { get; set; } = false; 16 | public bool Indigo { get; set; } = false; 17 | public bool Blue { get; set; } = false; 18 | public bool LightBlue { get; set; } = false; 19 | public bool Cyan { get; set; } = false; 20 | public bool Teal { get; set; } = false; 21 | public bool Green { get; set; } = false; 22 | public bool LightGreen { get; set; } = false; 23 | public bool Lime { get; set; } = false; 24 | public bool Yellow { get; set; } = false; 25 | public bool Amber { get; set; } = false; 26 | public bool Orange { get; set; } = false; 27 | public bool Grey { get; set; } = false; 28 | public bool White { get; set; } = false; 29 | 30 | public void SetColorByName(string colorName) 31 | { 32 | SetAllToLow(); 33 | switch (colorName) 34 | { 35 | case "Red": 36 | Red = true; 37 | break; 38 | case "Pink": 39 | Pink = true; 40 | break; 41 | case "Purple": 42 | Purple = true; 43 | break; 44 | case "Violet": 45 | Violet = true; 46 | break; 47 | case "Indigo": 48 | Indigo = true; 49 | break; 50 | case "Blue": 51 | Blue = true; 52 | break; 53 | case "Cyan": 54 | Cyan = true; 55 | break; 56 | case "Teal": 57 | Teal = true; 58 | break; 59 | case "Green": 60 | Green = true; 61 | break; 62 | case "Lime": 63 | Lime = true; 64 | break; 65 | case "Yellow": 66 | Yellow = true; 67 | break; 68 | case "Amber": 69 | Amber = true; 70 | break; 71 | case "Orange": 72 | Orange = true; 73 | break; 74 | case "Grey": 75 | Grey = true; 76 | break; 77 | case "White": 78 | White = true; 79 | break; 80 | } 81 | } 82 | 83 | private void SetAllToLow() 84 | { 85 | Red = false; 86 | Pink = false; 87 | Purple = false; 88 | Violet = false; 89 | Indigo = false; 90 | Blue = false; 91 | LightBlue = false; 92 | Cyan = false; 93 | Teal = false; 94 | Green = false; 95 | LightGreen = false; 96 | Lime = false; 97 | Yellow = false; 98 | Grey = false; 99 | White = false; 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/OpenableProject.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive; 2 | using BoTech.DesignerForAvalonia.ViewModels; 3 | using ReactiveUI; 4 | 5 | namespace BoTech.DesignerForAvalonia.Models.Project; 6 | /// 7 | /// This class implementes the LoadRecentProjectCommand 8 | /// 9 | public class OpenableProject : Project 10 | { 11 | /// 12 | /// Command for the ItemsControl at the Project start Page 13 | /// 14 | public ReactiveCommand LoadRecentProjectCommand { get; set; } 15 | 16 | public OpenableProject(ProjectStartViewModel viewModel, Project project, bool disable = false) 17 | { 18 | // Copy all Properties of base class 19 | this.SolutionFilePath = project.SolutionFilePath; 20 | this.OutputPath = project.OutputPath; 21 | this.LastUsed = project.LastUsed; 22 | this.Name = project.Name; 23 | this.Views = project.Views; 24 | this.ViewModelPath = project.ViewModelPath; 25 | this.SolutionFile = project.SolutionFile; 26 | this.ViewPath = project.ViewPath; 27 | this.ShortName = project.ShortName; 28 | this.DisplayableProjectInfo = project.DisplayableProjectInfo; 29 | if (!disable) 30 | { 31 | LoadRecentProjectCommand = ReactiveCommand.Create(() => { viewModel.LoadOpenableProject(this); }); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/Project.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text.Json.Serialization; 5 | using Microsoft.Build.Construction; 6 | 7 | namespace BoTech.DesignerForAvalonia.Models.Project; 8 | 9 | public class Project 10 | { 11 | /// 12 | /// The Path to the .sln File. 13 | /// 14 | public string SolutionFilePath { get; set; } = string.Empty; 15 | /// 16 | /// The Model which contains Information about the Solution File. For example, it contains all defined Projects in the Solution. 17 | /// 18 | [JsonIgnore] 19 | public SolutionFile SolutionFile { get; set; } 20 | /// 21 | /// The Name of the Solution 22 | /// 23 | public string Name { get; set; } = string.Empty; 24 | /// 25 | /// A Short name which will be displayed in a Rounded Colored rectangle. 26 | /// 27 | public string ShortName { get; set; } = string.Empty; 28 | /// 29 | /// The Date and Time when the project was opened. 30 | /// 31 | public DateTime LastUsed { get; set; } 32 | /// 33 | /// The complete Path to the ViewModel folder. 34 | /// 35 | public string ViewModelPath { get; set; } = string.Empty; 36 | /// 37 | /// The complete Path to the Views folder. 38 | /// 39 | public string ViewPath { get; set; } = string.Empty; 40 | /// 41 | /// Saves the Path to the prebuilt assemblies, which contains all Classes and Views of the Project, so that they can be instantiated. 42 | /// 43 | public string OutputPath { get; set; } = string.Empty; 44 | 45 | /// 46 | /// The Library which contains all compiled classes of the solution. 47 | /// 48 | [JsonIgnore] 49 | public Assembly Assembly { get; set; } = null; 50 | /// 51 | /// All Views that the Project contains. By default, this will be the MainView and the MainWindow. 52 | /// 53 | public List Views { get; set; } = new List(); 54 | /// 55 | /// All Views that the Project contains. By default, this will be the MainViewModel. 56 | /// 57 | public List ViewModels { get; set; } = new List(); 58 | /// 59 | /// A simple Model which provides Information about the Label Color 60 | /// 61 | public DisplayableProjectInfo DisplayableProjectInfo { get; set; } = new DisplayableProjectInfo(); 62 | 63 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/ProjectView.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text.Json.Serialization; 4 | using BoTech.DesignerForAvalonia.Models.Project.CSharp; 5 | 6 | namespace BoTech.DesignerForAvalonia.Models.Project; 7 | 8 | public class ProjectView 9 | { 10 | /// 11 | /// Name of the View 12 | /// 13 | public string Name { get; set; } 14 | /// 15 | /// Complete Path to the .axaml File 16 | /// 17 | public string PathToView { get; set; } 18 | /// 19 | /// Complete Path to the .axaml.cs File 20 | /// 21 | public string PathToCodeBehind { get; set; } 22 | /// 23 | /// The ViewModel which is selected in the .axaml file. 24 | /// 25 | public ProjectViewModel ViewModel { get; set; } = null; 26 | /// 27 | /// All Bindings defined in this View. 28 | /// 29 | public List Bindings { get; set; } = new List(); 30 | /// 31 | /// The parsed information from the .cs source File. 32 | /// 33 | [JsonIgnore] 34 | public ExtractedClassInfo? ClassInfoFromFile { get; set; } 35 | /// 36 | /// Is true when all members, functions that are located in the Source File are also defined in the Assembly. 37 | /// 38 | [JsonIgnore] 39 | public bool IsAssemblyEqualsToSource { get; set; } 40 | 41 | 42 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/Project/ProjectViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Reflection; 3 | using System.Text.Json.Serialization; 4 | using BoTech.DesignerForAvalonia.Models.Project.CSharp; 5 | 6 | 7 | namespace BoTech.DesignerForAvalonia.Models.Project; 8 | /// 9 | /// Stores all important information for an ViewModel. 10 | /// 11 | public class ProjectViewModel 12 | { 13 | /// 14 | /// The absolute Path to the ViewModel 15 | /// 16 | public required string Path { get; set; } 17 | /// 18 | /// The Name of the class 19 | /// 20 | public required string Name { get; set; } 21 | /// 22 | /// The full namespace of the class with the same Name as the File. The classname is included. 23 | /// 24 | public string Namespace { get; set; } = string.Empty; 25 | /// 26 | /// The parsed information from the .cs source File. 27 | /// 28 | [JsonIgnore] 29 | public ExtractedClassInfo? ClassInfoFromFile { get; set; } 30 | /// 31 | /// Is true when all members, functions that are located in the Source File are also defined in the Assembly. 32 | /// 33 | [JsonIgnore] 34 | public bool IsAssemblyEqualsToSource { get; set; } 35 | 36 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Models/XML/XmlControl.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Xml; 6 | using Avalonia.Controls; 7 | using BoTech.DesignerForAvalonia.Services.Avalonia; 8 | 9 | namespace BoTech.DesignerForAvalonia.Models.XML; 10 | /// 11 | /// This Model connects an XmlNode with a Control. 12 | /// This is necessary for the serialization Process where old XmlNodes may be edited. 13 | /// 14 | public class XmlControl : ICloneable 15 | { 16 | public XmlControl? Parent { get; set; } 17 | public List Children { get; set; } = new List(); 18 | public Control Control { get; set; } 19 | public XmlNode Node { get; set; } 20 | /// 21 | /// Searches in every Child for the given Control and returns itself. 22 | /// 23 | /// 24 | /// Returns null when nothing found otherwise itself 25 | public XmlControl? Find(Control control) 26 | { 27 | if (control == Control) 28 | { 29 | return this; 30 | } 31 | XmlControl? xmlControl = null; 32 | foreach (XmlControl child in Children) 33 | { 34 | if ((xmlControl = child.Find(control)) != null) 35 | { 36 | return xmlControl; 37 | } 38 | } 39 | return null; 40 | } 41 | /// 42 | /// Searches in every Child for the given node and returns itself. 43 | /// 44 | /// 45 | /// Returns null when nothing found otherwise itself. 46 | public XmlControl Find(XmlNode node) 47 | { 48 | if (node == Node) 49 | { 50 | return this; 51 | } 52 | XmlControl? xmlControl = null; 53 | foreach (XmlControl child in Children) 54 | { 55 | if ((xmlControl = child.Find(node)) != null) 56 | { 57 | return xmlControl; 58 | } 59 | } 60 | return null; 61 | } 62 | /// 63 | /// Gets the most Parent Control. 64 | /// 65 | /// 66 | public XmlControl GetMostParent() 67 | { 68 | if(Parent != null) return Parent.GetMostParent(); 69 | return this; 70 | } 71 | /// 72 | /// Clone the object: Method clones all Properties of the Control and inject it into a new Control. 73 | /// It also only copies all Attributes of the XmlNode into a new XmlNode. 74 | /// 75 | /// 76 | public object Clone() 77 | { 78 | XmlControl xmlControlCopy = new XmlControl(); 79 | 80 | // Create a "deep Copy" of the Control by using the XmlNode. 81 | 82 | Control? copiedControl = Activator.CreateInstance(this.Control.GetType()) as Control; 83 | if (copiedControl != null) 84 | { 85 | // We want to iterate through the Attribute List to only copy the Properties that have really changed. 86 | if (this.Node.Attributes != null) 87 | { 88 | foreach (XmlAttribute attribute in this.Node.Attributes) 89 | { 90 | PropertyInfo? propertyInfo = copiedControl.GetType().GetProperty(attribute.Name); 91 | if (propertyInfo != null) 92 | { 93 | // Get the Property of the Current Control and Clone it 94 | 95 | // propertyInfo.SetValue(copiedControl, propertyInfo.GetValue(this.Node.Attributes[attribute.Name], null)); 96 | if (propertyInfo.PropertyType.IsEnum) 97 | { 98 | propertyInfo.SetValue(copiedControl, 99 | Enum.Parse(propertyInfo.PropertyType, attribute.Value)); 100 | } 101 | else 102 | { 103 | propertyInfo.SetValue(copiedControl, CloneService.CloneProperty(propertyInfo, copiedControl, Control)); 104 | 105 | } 106 | } 107 | } 108 | } 109 | xmlControlCopy.Control = copiedControl; 110 | } 111 | 112 | // Creating a Copy of the Node 113 | 114 | xmlControlCopy.Node = Node.CloneNode(true); 115 | 116 | return xmlControlCopy; 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Services/Avalonia/CloneService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | 6 | namespace BoTech.DesignerForAvalonia.Services.Avalonia; 7 | 8 | public class CloneService 9 | { 10 | public static object CloneProperty(PropertyInfo propertyInfo, object copy, object original) 11 | { 12 | // Cloning the Property 13 | if (HasTypeConstructorWithNoParams(propertyInfo.PropertyType)) 14 | { 15 | // Cloning all Properties: 16 | PropertyInfo[] properties = copy.GetType().GetProperties(); 17 | List originalProperties = original.GetType().GetProperties().ToList(); 18 | foreach (PropertyInfo property in properties) 19 | { 20 | // When it is a primitive Type we can use the Parse Method of the primitive type to copy it 21 | if (property.PropertyType.IsPrimitive) 22 | { 23 | PropertyInfo? originalProperty = originalProperties.Find(p => p.Name == property.Name); 24 | if (originalProperty != null && originalProperty.GetValue(original) != null) 25 | { 26 | string originalString = originalProperty.GetValue(original).ToString(); 27 | switch (propertyInfo.PropertyType.Name) 28 | { 29 | case "Boolean": 30 | property.SetValue(copy, Boolean.Parse(originalString)); 31 | break; 32 | case "Byte": 33 | property.SetValue(copy, Byte.Parse(originalString)); 34 | break; 35 | case "SByte": 36 | property.SetValue(copy, SByte.Parse(originalString)); 37 | break; 38 | case "Char": 39 | property.SetValue(copy, Char.Parse(originalString)); 40 | break; 41 | case "Decimal": 42 | property.SetValue(copy, Decimal.Parse(originalString)); 43 | break; 44 | case "Double": 45 | property.SetValue(copy, Double.Parse(originalString)); 46 | break; 47 | case "Single": 48 | property.SetValue(copy, Char.Parse(originalString)); 49 | break; 50 | case "Int32": 51 | property.SetValue(copy, Int32.Parse(originalString)); 52 | break; 53 | case "UInt32": 54 | property.SetValue(copy, UInt32.Parse(originalString)); 55 | break; 56 | case "IntPtr": 57 | property.SetValue(copy, IntPtr.Parse(originalString)); 58 | break; 59 | case "UIntPtr": 60 | property.SetValue(copy, UIntPtr.Parse(originalString)); 61 | break; 62 | case "Int64": 63 | property.SetValue(copy, Int64.Parse(originalString)); 64 | break; 65 | case "UInt64": 66 | property.SetValue(copy, UInt64.Parse(originalString)); 67 | break; 68 | case "Int16": 69 | property.SetValue(copy, Int16.Parse(originalString)); 70 | break; 71 | case "UInt16": 72 | property.SetValue(copy, UInt16.Parse(originalString)); 73 | break; 74 | } 75 | } 76 | } 77 | else 78 | { 79 | // Go step deeper until the Property is a primitive. 80 | property.SetValue(copy, CloneProperty(property, copy, original)); 81 | } 82 | } 83 | return copy; 84 | } 85 | 86 | // TODO: Copy all Types which have params in the constructors 87 | //ExecuteCloneThroughTheCtor(propertyInfo, copy, original); 88 | // For now just return null to short up the development cycle 89 | return null; 90 | } 91 | /// 92 | /// Checks if the given Type has a constructor which does not contain any params in it definition. 93 | /// 94 | /// 95 | /// 96 | private static bool HasTypeConstructorWithNoParams(Type type) 97 | { 98 | foreach (ConstructorInfo ctor in type.GetConstructors()) 99 | { 100 | if(ctor.GetParameters().Length == 0) return true; 101 | } 102 | return false; 103 | } 104 | /// 105 | /// Make a deep copy by searching for the Properties which are needed to invoke the Constructor. 106 | /// For Instance when the given Type is Thickness. The Thickness class has the Properties Bottom, Left, Right and Top and a constructor (Thickness(double left, double top, double right, double bottom). 107 | /// 108 | /// 109 | /// 110 | /// 111 | private static void ExecuteCloneThroughTheCtor(PropertyInfo propertyInfo, object copy, object original) 112 | { 113 | // Search for the Constructor where all params exists as Properties in the Type 114 | foreach (ConstructorInfo constructor in propertyInfo.PropertyType.GetConstructors()) 115 | { 116 | List propertyInfos = propertyInfo.PropertyType.GetProperties().ToList(); 117 | List paramValues = new List(); 118 | int parameterCount = 0; 119 | foreach (ParameterInfo parameter in constructor.GetParameters()) 120 | { 121 | PropertyInfo property = propertyInfos.Find(p => p.Name == parameter.Name); 122 | if (property != null) 123 | { 124 | parameterCount++; 125 | paramValues.Add(property.GetValue(original, null)); 126 | } 127 | } 128 | // All necessary params for the Constructor was found 129 | if (parameterCount == constructor.GetParameters().Length) 130 | { 131 | try 132 | { 133 | object copyObj = constructor.Invoke(paramValues.ToArray()); 134 | // TODO: All Properties has to be updated 135 | // Update the copy Value 136 | propertyInfo.SetValue(copy, copyObj); 137 | } 138 | catch (Exception e) 139 | { 140 | // Do nothing 141 | continue; 142 | } 143 | } 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Services/Avalonia/TypeCastingService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Reflection; 4 | using Avalonia.Controls; 5 | 6 | namespace BoTech.DesignerForAvalonia.Services.Avalonia; 7 | 8 | public static class TypeCastingService 9 | { 10 | /// 11 | /// Checks if the given Control is a Layout Control ("Border", "Canvas", "DockPanel", "Expander", "Grid", "GridSplitter", "Panel", "RelativePanel", "ScrollViewer", "SplitView", "StackPanel", "TabControl", "UniformGrid", "WrapPanel") and has the Childs Property. 12 | /// 13 | /// 14 | /// 15 | public static bool IsLayoutControl(Control control) 16 | { 17 | // Possible Layout Types 18 | string[] layoutControlNames = ["Border", "Canvas", "DockPanel", "Expander", "Grid", "GridSplitter", "Panel", "RelativePanel", "ScrollViewer", "SplitView", "StackPanel", "TabControl", "UniformGrid", "WrapPanel"]; 19 | if (layoutControlNames.Contains(control.GetType().Name)) return true; //control.GetType().GetField("Childs") != null; 20 | return false; 21 | } 22 | /// 23 | /// Checks if the given ControlTypeName is a Layout Control ("Border", "Canvas", "DockPanel", "Expander", "Grid", "GridSplitter", "Panel", "RelativePanel", "ScrollViewer", "SplitView", "StackPanel", "TabControl", "UniformGrid", "WrapPanel") and has the Childs Property. 24 | /// 25 | /// 26 | /// 27 | public static bool IsLayoutControl(string controlTypeName) 28 | { 29 | // Possible Layout Types 30 | string[] layoutControlNames = ["Border", "Canvas", "DockPanel", "Expander", "Grid", "GridSplitter", "Panel", "RelativePanel", "ScrollViewer", "SplitView", "StackPanel", "TabControl", "UniformGrid", "WrapPanel"]; 31 | if (layoutControlNames.Contains(controlTypeName)) return true; //control.GetType().GetField("Childs") != null; 32 | return false; 33 | } 34 | 35 | /// 36 | /// This Method tries to get the Child, Children or Content property of the given Control and return it. 37 | /// 38 | /// 39 | /// Can return null when the given Control is no LayoutControl. 40 | public static Controls? GetChildControlsOfLayoutControl(Control control) 41 | { 42 | if (TypeCastingService.IsLayoutControl(control)) 43 | { 44 | // Try Different Names for the Same Attribute: 45 | PropertyInfo[] propertyInfos = control.GetType().GetProperties(); 46 | PropertyInfo? info = propertyInfos.Where(p => p.Name == "Children").FirstOrDefault(); 47 | 48 | if (info == null) info = propertyInfos.Where(p => p.Name == "Child").FirstOrDefault(); 49 | if (info == null) info = propertyInfos.Where(p => p.Name == "Content").FirstOrDefault(); 50 | 51 | if (info != null) 52 | { 53 | object? obj = info.GetValue(control); 54 | if (obj != null) 55 | { 56 | return (Controls)obj; 57 | } 58 | } 59 | } 60 | return null; 61 | } 62 | 63 | public static List GetAllControlBasedAvaloniaTypes() 64 | { 65 | // Gets all Types which are nested under the Avalonia.? Namespace 66 | List allTypes = Assembly.Load(new AssemblyName("Avalonia.Controls")).DefinedTypes.ToList(); 67 | // Therefore it is needed to get all Types which are directly nested under Avalonia.Controls 68 | List sortedTypes = allTypes.Where(type => type.Namespace == "Avalonia.Controls").ToList(); 69 | 70 | // Because of in the new Sorted List are a lot of Types which we do not use for example Interfaces or Classes like Control, it is necessary to filter all Type which inherit from Control. 71 | return sortedTypes.Where(type => type.AsType().IsSubclassOf(typeof(Control))).ToList(); 72 | } 73 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Services/PropertiesView/EditBoxOptions.cs: -------------------------------------------------------------------------------- 1 | namespace BoTech.DesignerForAvalonia.Services.PropertiesView; 2 | /// 3 | /// Auto => the Method decides which type of Input Control is necessary. For e.g. a CheckBox 4 | /// EmbedBindingsView => A Flyout Menu to Manage Bindings. 5 | /// 6 | public enum EditBoxOptions 7 | { 8 | 9 | Auto, 10 | EmbedBindingsView, 11 | 12 | 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Services/XML/Serializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Reflection; 6 | using System.Xml; 7 | using System.Xml.Serialization; 8 | using Avalonia.Controls; 9 | using Avalonia.Layout; 10 | using BoTech.DesignerForAvalonia.Views; 11 | using BoTech.DesignerForAvalonia.Models.XML; 12 | using BoTech.DesignerForAvalonia.Services.Avalonia; 13 | using BoTech.DesignerForAvalonia.ViewModels; 14 | using DialogHostAvalonia; 15 | 16 | namespace BoTech.DesignerForAvalonia.Services.XML; 17 | 18 | public class Serializer 19 | { 20 | private List _allAvaloniaControlTypes; 21 | /// 22 | /// The Loading View for the Message Box 23 | /// 24 | private LoadingViewModel _loadingViewModel; 25 | public Serializer(LoadingViewModel loadingViewModel) 26 | { 27 | _allAvaloniaControlTypes = TypeCastingService.GetAllControlBasedAvaloniaTypes(); 28 | _loadingViewModel = loadingViewModel; 29 | } 30 | 31 | public string Serialize(XmlControl xmlControl) 32 | { 33 | _loadingViewModel.Maximum = CountAllChildren(xmlControl); 34 | _loadingViewModel.IsIndeterminate = false; 35 | UpdateXmlNodesForControl(xmlControl); 36 | string result = SerializeXmlNode(xmlControl.Node); 37 | return result; 38 | } 39 | public string SerializeXmlNode(XmlNode node) 40 | { 41 | XmlSerializer serializer = new XmlSerializer(typeof(XmlNode));//, new XmlRootAttribute(node.Name)); 42 | // TextWriter writer = new Stream 43 | 44 | using (StringWriter stringWriter = new StringWriter()) 45 | { 46 | serializer.Serialize(stringWriter, node); 47 | string result = stringWriter.ToString(); 48 | return result.Replace("\r\n", ""); 49 | } 50 | } 51 | private void UpdateXmlNodesForControl(XmlControl current) 52 | { 53 | _loadingViewModel.Current += 1; 54 | _loadingViewModel.SubStatusText = "Updating Xml Node: " + current.Node.Name; 55 | 56 | //AddPropertiesThatChanged(current.Node, current.Control); 57 | // When the current XmlControl has more than one Children => it must be a Control which has the Children Property. 58 | if (current.Children.Count > 1) 59 | { 60 | foreach (XmlControl child in current.Children) 61 | { 62 | UpdateXmlNodesForControl(child); 63 | } 64 | } 65 | else 66 | { 67 | PropertyInfo? propertyInfo = null; 68 | if ((propertyInfo = current.Control.GetType().GetProperty("Child")) != null) 69 | { 70 | TranslateChildOrContentProperty(propertyInfo, current, current.Control); 71 | } 72 | 73 | if ((propertyInfo = current.Control.GetType().GetProperty("Content")) != null) 74 | { 75 | TranslateChildOrContentProperty(propertyInfo, current, current.Control); 76 | } 77 | 78 | if ((propertyInfo = current.Control.GetType().GetProperty("Text")) != null) 79 | { 80 | string? text = propertyInfo.GetValue(current.Control)?.ToString(); 81 | if (text != null) current.Node.InnerText = text; 82 | } 83 | } 84 | } 85 | /// 86 | /// Checks which Property of a Control has not the default Value and creates new Attribute for this Node. 87 | /// 88 | /// 89 | /// 90 | [Obsolete("All Properties are added during the drop of a new Control.")] 91 | private void AddPropertiesThatChanged(XmlNode current, Control control) 92 | { 93 | object? convertedControl = TryToChangeTypeToAvaloniaControl(control); 94 | 95 | if (convertedControl != null) 96 | { 97 | object? defaultControl = Activator.CreateInstance(convertedControl.GetType()); 98 | if (defaultControl != null) 99 | { 100 | List defaultProperties = defaultControl.GetType().GetProperties().ToList(); 101 | List properties = convertedControl.GetType().GetProperties().ToList(); 102 | foreach (PropertyInfo property in properties) 103 | { 104 | // Check if the Property is not the Default Value 105 | if (property.CanRead) 106 | { 107 | // Find the equivalent property to the "property" in the defaultProperty List 108 | PropertyInfo? defaultProperty = defaultProperties.Find(p => p.Name == property.Name); 109 | if (defaultProperty != null) 110 | { 111 | if (defaultProperty.CanRead) 112 | { 113 | try 114 | { 115 | if (defaultProperty.GetValue(defaultControl) != property.GetValue(control)) 116 | { 117 | XmlAttribute attribute = current.OwnerDocument.CreateAttribute(property.Name); 118 | attribute.Value = property.GetValue(control).ToString(); 119 | current.Attributes.Append(attribute); 120 | } 121 | } 122 | catch (Exception e) 123 | { 124 | // GetValue can throw an TargetParameterCountException 125 | Console.WriteLine(e); 126 | } 127 | } 128 | } 129 | } 130 | } 131 | } 132 | } 133 | } 134 | /// 135 | /// This Method is needed to get the correct Type. For example when the given Control is a TextBlock the Method will return the TextBlock. 136 | /// 137 | /// 138 | /// 139 | private object? TryToChangeTypeToAvaloniaControl(Control control) 140 | { 141 | foreach (TypeInfo typeInfo in _allAvaloniaControlTypes) 142 | { 143 | try 144 | { 145 | object? changedType = Convert.ChangeType(control, typeInfo); 146 | if(changedType != null) return changedType; 147 | } 148 | catch (Exception e) 149 | { 150 | Console.WriteLine(e); 151 | } 152 | } 153 | return null; 154 | } 155 | /// 156 | /// This Method can be used to set the InnerText Property of an XmlNode. 157 | /// 158 | /// 159 | /// 160 | /// 161 | private void TranslateChildOrContentProperty(PropertyInfo propertyInfo, XmlControl current, Control control) 162 | { 163 | object? content = propertyInfo.GetValue(control, null); 164 | if (content != null) 165 | { 166 | // Data can be set without another xml Node between. 167 | if (content is TextBlock textBlock) 168 | { 169 | if (textBlock.Text != null) current.Node.InnerText = textBlock.Text; 170 | } 171 | // The Data Between is another Control so it is necessary to create another xml node. 172 | else if (content is Control childControl) 173 | { 174 | UpdateXmlNodesForControl(current.Children[0]); 175 | //current.Children.Add(UpdateXmlNodesForControl(current)); 176 | } 177 | } 178 | } 179 | /// 180 | /// This Method counts all Children of the xmlControl 181 | /// 182 | /// 183 | /// 184 | private int CountAllChildren(XmlControl xmlControl) 185 | { 186 | int count = xmlControl.Children.Count; 187 | foreach (XmlControl child in xmlControl.Children) 188 | { 189 | count += CountAllChildren(child); 190 | } 191 | return count; 192 | } 193 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Templates/Editor/PropertiesView/AppearanceViewTemplate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using BoTech.DesignerForAvalonia.Models.XML; 5 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 6 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 7 | 8 | namespace BoTech.DesignerForAvalonia.Templates.Editor.PropertiesView; 9 | 10 | public class AppearanceViewTemplate : IViewTemplate 11 | { 12 | public string Name { get; } = "Appearance"; 13 | public List StandardViewTemplates { get; set; } 14 | public Control GetViewTemplateForControl(XmlControl xmlControl, PropertiesViewModel.TabContent tabContent) 15 | { 16 | // Main StackPanel 17 | StackPanel stackPanel = new StackPanel(); 18 | 19 | StandardViewTemplates = new List(); 20 | StandardViewTemplate stdViewTemplate = new StandardViewTemplate() 21 | { 22 | ReferencedProperties = 23 | { 24 | new StandardViewTemplate.ReferencedProperty("IsEnabled", xmlControl.Control, EditBoxOptions.Auto), 25 | new StandardViewTemplate.ReferencedProperty("IsVisible", xmlControl.Control, EditBoxOptions.Auto), 26 | new StandardViewTemplate.ReferencedProperty("Classes", xmlControl.Control, EditBoxOptions.Auto), 27 | new StandardViewTemplate.ReferencedProperty("Styles", xmlControl.Control, EditBoxOptions.Auto), 28 | new StandardViewTemplate.ReferencedProperty("FontFamily", xmlControl.Control, EditBoxOptions.Auto), 29 | new StandardViewTemplate.ReferencedProperty("FontSize", xmlControl.Control, EditBoxOptions.Auto), 30 | new StandardViewTemplate.ReferencedProperty("FontStyle", xmlControl.Control, EditBoxOptions.Auto), 31 | new StandardViewTemplate.ReferencedProperty("FontWeight", xmlControl.Control, EditBoxOptions.Auto), 32 | new StandardViewTemplate.ReferencedProperty("Foreground", xmlControl.Control, EditBoxOptions.Auto), 33 | new StandardViewTemplate.ReferencedProperty("Background", xmlControl.Control, EditBoxOptions.Auto), 34 | new StandardViewTemplate.ReferencedProperty("BorderBrush", xmlControl.Control, EditBoxOptions.Auto), 35 | } 36 | }; 37 | 38 | 39 | 40 | // Saving the Standard View Template for the event handling 41 | StandardViewTemplates.Add(stdViewTemplate); 42 | stackPanel.Children.Add(stdViewTemplate.GetViewTemplateForControl(xmlControl, tabContent, Name)); 43 | return stackPanel; 44 | } 45 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Templates/Editor/PropertiesView/ContentViewTemplate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Avalonia.Controls; 3 | using BoTech.DesignerForAvalonia.Models.XML; 4 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 5 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 6 | 7 | namespace BoTech.DesignerForAvalonia.Templates.Editor.PropertiesView; 8 | 9 | public class ContentViewTemplate : IViewTemplate 10 | { 11 | public string Name { get; } = "Content"; 12 | public List StandardViewTemplates { get; set; } 13 | public Control GetViewTemplateForControl(XmlControl xmlControl, PropertiesViewModel.TabContent tabContent) 14 | { 15 | // Main StackPanel 16 | StackPanel stackPanel = new StackPanel(); 17 | 18 | StandardViewTemplates = new List(); 19 | StandardViewTemplate stdViewTemplate = new StandardViewTemplate() 20 | { 21 | ReferencedProperties = 22 | { 23 | new StandardViewTemplate.ReferencedProperty("Text", xmlControl.Control, EditBoxOptions.Auto) 24 | } 25 | }; 26 | 27 | stackPanel.Children.Add(new TextBlock() 28 | { 29 | Text = "Note: All Content Properties will be available in Version V1.2.x of this Product. \n For more details visit our Roadmap: https://aka.botech.dev/go/DesignerForAvalonia/Roadmap" 30 | }); 31 | 32 | 33 | 34 | // Saving the Standard View Template for the event handling 35 | StandardViewTemplates.Add(stdViewTemplate); 36 | stackPanel.Children.Add(stdViewTemplate.GetViewTemplateForControl(xmlControl, tabContent, Name)); 37 | return stackPanel; 38 | } 39 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Templates/Editor/PropertiesView/IViewTemplate.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Collections.Generic; 3 | using Avalonia.Controls; 4 | using BoTech.DesignerForAvalonia.Models.XML; 5 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 6 | 7 | namespace BoTech.DesignerForAvalonia.Templates.Editor.PropertiesView; 8 | 9 | public interface IViewTemplate 10 | { 11 | /// 12 | /// Will be displayed on the Expander Header. 13 | /// The Name has to be unique. 14 | /// 15 | public string Name { get; } 16 | /// 17 | /// In this each view Template has to store all its StandardViewTemplates. 18 | /// This is necessary to Handle the Rerender event of an ControlsCreatorObject Class. 19 | /// The PropertiesViewModel will select the correct ViewTemplate which has called the event and execute the Rerender Method (GetViewTemplateForControl()). 20 | /// 21 | public List StandardViewTemplates { get; set; } 22 | 23 | /// 24 | /// This Method Creates all static Editable Controls which are needed to edit the Propertiesd of the current Control. 25 | /// For Instance, it creates the Margin and Padding Box in the LayoutViewTemplate 26 | /// 27 | /// 28 | /// 29 | /// 30 | public Control GetViewTemplateForControl(XmlControl xmlControl, PropertiesViewModel.TabContent tabContent); 31 | 32 | /// 33 | /// When the ViewTemplate uses StandardViewTemplates, a repaint (Rerender) might be requested by the ControlsCreatorObject. 34 | /// This Method processes these Repaints. 35 | /// 36 | /// The Selected Control 37 | /// The Current TabContent Model where this View Template is located in. 38 | /// The Editable Controls 39 | public Control GetRerenderedViewTemplateForControl(XmlControl xmlControl, 40 | PropertiesViewModel.TabContent tabContent) 41 | { 42 | StackPanel stackPanel = new StackPanel(); 43 | foreach (StandardViewTemplate standardViewTemplate in StandardViewTemplates) 44 | { 45 | stackPanel.Children.Add(standardViewTemplate.GetViewTemplateForControl(xmlControl, tabContent, Name)); 46 | } 47 | return stackPanel; 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Templates/Editor/PropertiesView/InputViewTemplate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Avalonia.Controls; 3 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 4 | using BoTech.DesignerForAvalonia.Models.XML; 5 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 6 | 7 | namespace BoTech.DesignerForAvalonia.Templates.Editor.PropertiesView; 8 | 9 | public class InputViewTemplate : IViewTemplate 10 | { 11 | public string Name { get; } = "Inputs"; 12 | public List StandardViewTemplates { get; set; } 13 | public Control GetViewTemplateForControl(XmlControl xmlControl, PropertiesViewModel.TabContent tabContent) 14 | { 15 | // Main StackPanel 16 | StackPanel stackPanel = new StackPanel(); 17 | 18 | StandardViewTemplates = new List(); 19 | StandardViewTemplate stdViewTemplate = new StandardViewTemplate() 20 | { 21 | ReferencedProperties = 22 | { 23 | //new StandardViewTemplate.ReferencedProperty("HotKey", xmlControl.Control, EditBoxOptions.Auto), 24 | } 25 | }; 26 | stackPanel.Children.Add(new TextBlock() 27 | { 28 | Text = "Note: All events will be available in Version V1.2.x of this Product.\n For more details visit our Roadmap: https://aka.botech.dev/go/DesignerForAvalonia/Roadmap" 29 | }); 30 | 31 | 32 | 33 | // Saving the Standard View Template for the event handling 34 | StandardViewTemplates.Add(stdViewTemplate); 35 | stackPanel.Children.Add(stdViewTemplate.GetViewTemplateForControl(xmlControl, tabContent, Name)); 36 | return stackPanel; 37 | } 38 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Templates/Editor/PropertiesView/StandardViewTemplate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Avalonia; 6 | using Avalonia.Controls; 7 | using Avalonia.Media; 8 | using BoTech.DesignerForAvalonia.Models.XML; 9 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 10 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 11 | 12 | namespace BoTech.DesignerForAvalonia.Templates.Editor.PropertiesView; 13 | 14 | /// 15 | /// A wrapper Class for the Controls Creator. This class can be used when you want to create an editable Box for one of the Avalonia Properties which are defined in the ControlsCreator Class. 16 | /// 17 | 18 | public class StandardViewTemplate 19 | { 20 | public string Name { get; } = "Standard View"; 21 | /// 22 | /// List of all Properties which are located under the Expander. 23 | /// 24 | public List ReferencedProperties { get; set; } = new List(); 25 | // Is not necessary in this Class. 26 | public List StandardViewTemplates { get; set; } = new List(); 27 | 28 | private bool _firstRender = true; 29 | // Note this Method rerenders the Referenced Property when this Method called more than one time. 30 | public Control GetViewTemplateForControl(XmlControl xmlControl, PropertiesViewModel.TabContent tabContent, string viewTemplateName) 31 | { 32 | if (_firstRender) 33 | { 34 | // If it is necessary new Instances of the ControlsCreatorObject Class will be created and stored. 35 | StackPanel stackPanel = new StackPanel(); 36 | foreach (ReferencedProperty referencedProperty in ReferencedProperties) 37 | { 38 | if (referencedProperty.PropertyInfo == null) 39 | { 40 | // The Property is not available in the selected Control: 41 | stackPanel.Children.Add(new TextBlock() 42 | { 43 | Text = "The Property " + referencedProperty.PropertyName + " does not exist in the selected Control.", 44 | Margin = new Thickness(0,5,0,0), 45 | Foreground = Brushes.Orange, 46 | }); 47 | } 48 | else 49 | { 50 | if (ControlsCreator.SupportedPrimitiveTypes.Contains(referencedProperty.PropertyInfo.PropertyType 51 | .Name) || 52 | ControlsCreator.SupportedAvaloniaTypes.Contains(referencedProperty.PropertyInfo.PropertyType 53 | .Name)) 54 | { 55 | stackPanel.Children.Add(ControlsCreator.CreateEditBox(xmlControl, referencedProperty.PropertyName, 56 | referencedProperty.Options)); 57 | } 58 | else 59 | { 60 | // Create a new Editable Control with the ControlsCreatorObject class: 61 | referencedProperty.ControlCreator = new ControlsCreatorObject(referencedProperty.PropertyInfo, 62 | xmlControl, tabContent, viewTemplateName); 63 | stackPanel.Children.Add(referencedProperty.ControlCreator.VisualControl); 64 | } 65 | } 66 | } 67 | _firstRender = false; 68 | return stackPanel; 69 | } 70 | else 71 | { 72 | // If one of the ControlsCreatorObjects call a rerender event. This is the case when the user changed a Value or the Selected Constructor. 73 | StackPanel stackPanel = new StackPanel(); 74 | foreach (ReferencedProperty referencedProperty in ReferencedProperties) 75 | { 76 | if (referencedProperty.PropertyInfo == null) 77 | { 78 | // The Property is not available in the selected Control: 79 | stackPanel.Children.Add(new TextBlock() 80 | { 81 | Text = "The Property " + referencedProperty.PropertyName + " does not exist in the selected Control.", 82 | Margin = new Thickness(0,5,0,0), 83 | Foreground = Brushes.Orange, 84 | }); 85 | } 86 | else 87 | { 88 | if (ControlsCreator.SupportedPrimitiveTypes.Contains(referencedProperty.PropertyInfo.PropertyType.Name) || 89 | ControlsCreator.SupportedAvaloniaTypes.Contains(referencedProperty.PropertyInfo.PropertyType.Name)) 90 | { 91 | stackPanel.Children.Add(ControlsCreator.CreateEditBox(xmlControl, referencedProperty.PropertyName, referencedProperty.Options)); 92 | } 93 | else 94 | { 95 | // One of the Created instances have called a Rerender event. 96 | if (referencedProperty.ControlCreator != null) 97 | { 98 | // Check if Something has changed. 99 | //if (referencedProperty.ControlCreator.Rerender()) 100 | //{ 101 | referencedProperty.ControlCreator.Rerender(); 102 | stackPanel.Children.Add(referencedProperty.ControlCreator.VisualControl); 103 | //} 104 | } 105 | } 106 | } 107 | } 108 | return stackPanel; 109 | } 110 | } 111 | /// 112 | /// The class ReferencedProperty is a Model for each Property which is nested under the Expander. 113 | /// 114 | /// The PropertyName is the Name of the Property which is stored in the referenced Control. 115 | /// Display Options 116 | public class ReferencedProperty 117 | { 118 | public string PropertyName { get; set; } 119 | public EditBoxOptions Options { get; set; } 120 | /// 121 | /// The Property Info stores the Information about the property in the selected Control. 122 | /// It is null when the Selected Control hasn't this Property. 123 | /// For Instance a Button does not have a Text Property. Therefore, the PropertyInfo will be null. 124 | /// 125 | public PropertyInfo? PropertyInfo { get; set; } 126 | /// 127 | /// Will store an Instance of the ControlsCreatorClass when the given Property could not be handled by the ControlsCreator or the ControlsCreatorAvalonia Class.
128 | /// Can be null when it is not necessary when it works to create an editable Control with one of the named Classes. 129 | ///
130 | public ControlsCreatorObject? ControlCreator { get; set; } 131 | 132 | public ReferencedProperty(string propertyName, Control control, EditBoxOptions options) 133 | { 134 | PropertyName = propertyName; 135 | Options = options; 136 | Type controlType = control.GetType(); 137 | // if (controlType.BaseType != typeof(Control)) 138 | //{ 139 | PropertyInfo[] propertyInfos = controlType.GetProperties(); 140 | PropertyInfo? propertyInfo; 141 | if ((propertyInfo = propertyInfos.Where(p => p.Name == propertyName).FirstOrDefault()) != null) 142 | { 143 | PropertyInfo = propertyInfo; 144 | } 145 | } 146 | } 147 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using BoTech.DesignerForAvalonia.ViewModels; 5 | 6 | namespace BoTech.DesignerForAvalonia; 7 | 8 | public class ViewLocator : IDataTemplate 9 | { 10 | public bool SupportsRecycling => false; 11 | 12 | public Control Build(object data) 13 | { 14 | var name = data.GetType().FullName.Replace("ViewModel", "View"); 15 | var type = Type.GetType(name); 16 | 17 | if (type != null) 18 | { 19 | return (Control)Activator.CreateInstance(type); 20 | } 21 | else 22 | { 23 | return new TextBlock { Text = "Not Found: " + name }; 24 | } 25 | } 26 | 27 | public bool Match(object data) 28 | { 29 | return data is ViewModelBase; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/AboutViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reactive; 3 | using DialogHostAvalonia; 4 | using ReactiveUI; 5 | 6 | namespace BoTech.DesignerForAvalonia.ViewModels; 7 | 8 | public class AboutViewModel : ViewModelBase 9 | { 10 | public ReactiveCommand OpenWebsiteCommand { get; set; } 11 | public ReactiveCommand OpenSupportWebsiteCommand { get; set; } 12 | public ReactiveCommand CloseCommand { get; set; } 13 | 14 | public AboutViewModel() 15 | { 16 | OpenWebsiteCommand = ReactiveCommand.Create(OpenWebsite); 17 | OpenSupportWebsiteCommand = ReactiveCommand.Create(OpenSupportWebsite); 18 | CloseCommand = ReactiveCommand.Create(Close); 19 | } 20 | 21 | private void OpenSupportWebsite() 22 | { 23 | Process.Start(new ProcessStartInfo 24 | { 25 | FileName = "https://aka.botech.dev/go/Support/DesignerForAvalonia/", 26 | UseShellExecute = true 27 | }); 28 | } 29 | 30 | private void OpenWebsite() 31 | { 32 | Process.Start(new ProcessStartInfo 33 | { 34 | FileName = "https://www.botech.dev", 35 | UseShellExecute = true 36 | }); 37 | } 38 | 39 | private void Close() 40 | { 41 | DialogHost.Close("MainDialogHost"); 42 | } 43 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/Abstraction/CloseablePageViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive; 2 | using BoTech.DesignerForAvalonia.Views; 3 | using BoTech.DesignerForAvalonia.Views.Abstraction; 4 | using ReactiveUI; 5 | 6 | namespace BoTech.DesignerForAvalonia.ViewModels.Abstraction; 7 | 8 | public class CloseablePageViewModel : ViewModelBase where T : CloseablePageCodeBehind 9 | { 10 | public ReactiveCommand CloseCommand { get; set; } 11 | 12 | public CloseablePageViewModel(T codeBehind) 13 | { 14 | if(codeBehind is null) throw new System.ArgumentNullException(nameof(codeBehind)); 15 | CloseCommand = ReactiveCommand.Create(() => 16 | { 17 | codeBehind.Close(); 18 | }); 19 | } 20 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/Editor/Dialogs/GenericDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive; 2 | using Avalonia.Controls; 3 | using Avalonia.Media; 4 | using DialogHostAvalonia; 5 | using Material.Icons; 6 | using ReactiveUI; 7 | 8 | namespace BoTech.DesignerForAvalonia.ViewModels.Editor.Dialogs; 9 | /// 10 | /// A Dialog View with basic functionality: Close and Save Button. Content can be set Dynamically. 11 | /// 12 | public class GenericDialogViewModel : ViewModelBase 13 | { 14 | private Control _content; 15 | 16 | public Control Content 17 | { 18 | get => _content; 19 | set => this.RaiseAndSetIfChanged(ref _content, value); 20 | } 21 | public MaterialIconKind Icon { get; set; } 22 | public Brush IconColor { get; set; } 23 | public ReactiveCommand CloseCommand { get; set; } 24 | public ReactiveCommand SaveCommand { get; set; } 25 | public ReactiveCommand HelpCommand { get; set; } 26 | 27 | public GenericDialogViewModel() 28 | { 29 | CloseCommand = ReactiveCommand.Create(() => 30 | { 31 | DialogHost.Close("MainDialogHost"); 32 | }); 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/Editor/Dialogs/ProjectColorDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Reactive; 2 | using BoTech.DesignerForAvalonia.Models.Project; 3 | using ReactiveUI; 4 | 5 | namespace BoTech.DesignerForAvalonia.ViewModels.Editor.Dialogs; 6 | /// 7 | /// This View can be used to let the user decide which label-color he wants to use for th Project 8 | /// 9 | public class ProjectColorDialogViewModel : ViewModelBase 10 | { 11 | public string ShortName { get; set; } = "Test"; 12 | public ReactiveCommand ChangeColorCommand { get; set; } 13 | public Project Project { get; set; } 14 | public ProjectColorDialogViewModel(Project project) 15 | { 16 | Project = project; 17 | ShortName = project.ShortName; 18 | ChangeColorCommand = ReactiveCommand.Create(colorName => Project.DisplayableProjectInfo.SetColorByName(colorName)); 19 | } 20 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/Editor/ItemsExplorerViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Linq; 5 | using System.Reactive; 6 | using System.Reactive.Linq; 7 | using System.Reflection; 8 | using Avalonia; 9 | using Avalonia.Controls; 10 | using Avalonia.Media; 11 | using BoTech.DesignerForAvalonia.Controller.Editor; 12 | using BoTech.DesignerForAvalonia.Models.Editor; 13 | using BoTech.DesignerForAvalonia.Services.Avalonia; 14 | using BoTech.DesignerForAvalonia.ViewModels.Abstraction; 15 | using BoTech.DesignerForAvalonia.Views.Editor; 16 | using ReactiveUI; 17 | 18 | namespace BoTech.DesignerForAvalonia.ViewModels.Editor; 19 | 20 | public class ItemsExplorerViewModel : CloseablePageViewModel 21 | { 22 | private ObservableCollection _treeViewNodes = new(); 23 | 24 | /// 25 | /// Displayed Controls 26 | /// 27 | public ObservableCollection TreeViewNodes 28 | { 29 | get => _treeViewNodes; 30 | set => this.RaiseAndSetIfChanged(ref _treeViewNodes, value); 31 | } 32 | /// 33 | /// Selected Control 34 | /// 35 | public TreeViewNode? SelectedItem { get; set; } 36 | /// 37 | /// starts the search function 38 | /// 39 | public ReactiveCommand SearchCommand { get; set; } 40 | /// 41 | /// Replaces the Search results to the original list of TreeViewNodes. 42 | /// 43 | public ReactiveCommand DeleteSearchResultsCommand { get; set; } 44 | /// 45 | /// The search string that the User can enter. 46 | /// 47 | public string CurrentSearchText { get; set; } 48 | 49 | /// 50 | /// All Controls without the Search function Applied. 51 | /// 52 | private TreeViewNode _allControls; 53 | 54 | // will be injected 55 | private EditorController _editorController; 56 | 57 | public ItemsExplorerViewModel(EditorController editorController, ItemsExplorerView codeBehind) : base(codeBehind) 58 | { 59 | _editorController = editorController; 60 | SearchCommand = ReactiveCommand.Create(Search); 61 | DeleteSearchResultsCommand = ReactiveCommand.Create(DeleteSearchResults); 62 | CreateTreeView(); 63 | TreeViewNodes = new ObservableCollection() { _allControls }; 64 | } 65 | 66 | public void Search() 67 | { 68 | TreeViewNode searchNode = new TreeViewNode() 69 | { 70 | Text = "Avalonia Controls", 71 | ControlType = null, 72 | }; 73 | if (_allControls.Search(CurrentSearchText, searchNode)) 74 | { 75 | TreeViewNodes = new ObservableCollection() { searchNode }; 76 | } 77 | else 78 | { 79 | TreeViewNodes = new ObservableCollection() 80 | { 81 | new TreeViewNode() 82 | { 83 | Text = "Nothing Found.", 84 | ControlType = null, 85 | } 86 | }; 87 | } 88 | } 89 | private void DeleteSearchResults() => TreeViewNodes = new ObservableCollection() { _allControls }; 90 | private void CreateTreeView() 91 | { 92 | List controlBasedTypes = TypeCastingService.GetAllControlBasedAvaloniaTypes(); 93 | ObservableCollection nodes = new ObservableCollection(); 94 | foreach (TypeInfo controlBasedType in controlBasedTypes) 95 | { 96 | nodes.Add(new TreeViewNode() 97 | { 98 | Text = controlBasedType.Name, 99 | ControlType = controlBasedType 100 | }); 101 | } 102 | 103 | _allControls = new TreeViewNode() 104 | { 105 | Text = "Avalonia Controls", 106 | ControlType = null, 107 | Children = nodes 108 | }; 109 | 110 | } 111 | /// 112 | /// Returns an instance of the given Control Type with a default Text or Content. 113 | /// Additionally, the Name of the Control will bes et with a count Var. 114 | /// 115 | /// 116 | /// 117 | /// 118 | private Control GetDefaultInstanceForControl(Type controlType, int count) 119 | { 120 | try 121 | { 122 | Control? control = Activator.CreateInstance(controlType) as Control; 123 | if (control != null) 124 | { 125 | if (controlType.GetProperty("Text") != null) 126 | { 127 | // Control has the Text Property 128 | control.GetType().GetProperty("Text").SetValue(control, "Your Text goes here..."); 129 | } 130 | 131 | if (controlType.GetProperty("Content") != null) 132 | { 133 | control.GetType().GetProperty("Content").SetValue(control, "Your Content goes here..."); 134 | } 135 | 136 | // Create the unique Name: 137 | if (controlType.GetProperty("Name") != null) 138 | { 139 | control.GetType().GetProperty("Name").SetValue(control, controlType.Name + "_" + count.ToString()); 140 | } 141 | return control; 142 | } 143 | return new TextBlock() 144 | { 145 | Text = "Error by creating an Instance of Type: " + controlType.FullName + " dynamically created Type was null.", 146 | Foreground = Brushes.Orange, 147 | }; 148 | } 149 | catch (Exception e) 150 | { 151 | return new TextBlock() 152 | { 153 | Text = "Error by creating an Instance of Type: " + controlType.FullName + "\n Error Message: " + e.Message, 154 | Foreground = Brushes.Orange, 155 | }; 156 | } 157 | } 158 | 159 | 160 | /// 161 | /// This Method will be called from the Code behind when the User selects an Item of the Tree View. 162 | /// 163 | public void OnTreeViewSelectionChanged() 164 | { 165 | if (SelectedItem != null) //&& SelectedItem != _oldSelectedItem) 166 | { 167 | _editorController.StartDrag(GetDefaultInstanceForControl(SelectedItem.ControlType, SelectedItem.Count)); 168 | SelectedItem.Count++; 169 | } 170 | } 171 | /// 172 | /// The TreeViewNode class is a model for each TreeView Node.
173 | /// It can be used to store the Text to display and an Instance for the referenced Control. 174 | ///
175 | public class TreeViewNode : TreeViewNodeBase 176 | { 177 | 178 | protected override TreeViewNodeBase? Copy(TreeViewNodeBase nodeToCopy) 179 | { 180 | TreeViewNode copy; 181 | if (nodeToCopy is TreeViewNode treeViewNode) 182 | { 183 | copy = new TreeViewNode() 184 | { 185 | Text = treeViewNode.Text, 186 | ControlType = treeViewNode.ControlType, 187 | Count = treeViewNode.Count, 188 | }; 189 | return copy; 190 | } 191 | return null; 192 | } 193 | 194 | 195 | /// 196 | /// Referenced Control 197 | /// 198 | public required Type ControlType { get; set; } 199 | /// 200 | /// Counts how often the Control was added to the Preview to create a unique name for the Control. 201 | /// 202 | public int Count { get; set; } 203 | // Icon 204 | } 205 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/Editor/PropertiesViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reactive.Subjects; 4 | using Avalonia; 5 | using Avalonia.Controls; 6 | using Avalonia.Controls.Primitives; 7 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 8 | using BoTech.DesignerForAvalonia.Views; 9 | using BoTech.DesignerForAvalonia.Controller.Editor; 10 | using BoTech.DesignerForAvalonia.Models.XML; 11 | using BoTech.DesignerForAvalonia.Templates.Editor.PropertiesView; 12 | using BoTech.DesignerForAvalonia.ViewModels.Abstraction; 13 | using BoTech.DesignerForAvalonia.Views.Editor; 14 | using ReactiveUI; 15 | 16 | namespace BoTech.DesignerForAvalonia.ViewModels.Editor; 17 | 18 | public class PropertiesViewModel : CloseablePageViewModel 19 | { 20 | private TabControl _tabs = new TabControl(); 21 | /// 22 | /// Tabs for the View 23 | /// 24 | public TabControl Tabs 25 | { 26 | get => _tabs; 27 | set => this.RaiseAndSetIfChanged(ref _tabs, value); 28 | } 29 | 30 | private List TabContents { get; } = new List(); 31 | 32 | private EditorController _editorController; 33 | 34 | 35 | public PropertiesViewModel(EditorController editorController, PropertiesView codeBehind) : base(codeBehind) 36 | { 37 | //Creating all Templates 38 | 39 | // Layout 40 | List templates = new List(); 41 | templates.Add(new LayoutViewTemplate()); 42 | TabContent layout = new TabContent("Layout", templates); 43 | layout.vm = this; 44 | TabContents.Add(layout); 45 | // Appearance 46 | templates = new List(); 47 | templates.Add(new AppearanceViewTemplate()); 48 | TabContent appearance = new TabContent("Appearance", templates); 49 | appearance.vm = this; 50 | TabContents.Add(appearance); 51 | // Content 52 | templates = new List(); 53 | templates.Add(new ContentViewTemplate()); 54 | TabContent content = new TabContent("Content", templates); 55 | content.vm = this; 56 | TabContents.Add(content); 57 | // Input 58 | templates = new List(); 59 | templates.Add(new InputViewTemplate()); 60 | TabContent input = new TabContent("Input", templates); 61 | input.vm = this; 62 | TabContents.Add(input); 63 | 64 | 65 | 66 | _editorController = editorController; 67 | _editorController.PropertiesViewModel = this; 68 | } 69 | /// 70 | /// This Method is called by the Code-Behind. 71 | /// The Code Behind has to set some Properties like the CornerBorder. 72 | /// Only when these properties are set the propertiesView can render. 73 | /// 74 | public void Init() 75 | { 76 | UpdateView(); 77 | } 78 | /// 79 | /// Render the IViewTemplates with the correct data from the Control injected. 80 | /// 81 | /// 82 | public void RenderForControl(XmlControl xmlControl) 83 | { 84 | // Prerendering 85 | foreach (TabContent content in TabContents) 86 | { 87 | content.Render(xmlControl); 88 | } 89 | UpdateView(); 90 | } 91 | 92 | /// 93 | /// Applies the Prerendered Content of each TabContent Object to the View. 94 | /// 95 | private void UpdateView() 96 | { 97 | // Removing all Tabs 98 | Tabs.Items.Clear(); 99 | // Converting 100 | foreach (TabContent content in TabContents) 101 | { 102 | TabItem tab = new TabItem(); 103 | tab.Header = content.Name; 104 | tab.Content = new ScrollViewer() 105 | { 106 | Content = content.PreRenderedContent, 107 | //VerticalScrollBarVisibility = ScrollBarVisibility.Visible, 108 | //HorizontalScrollBarVisibility = ScrollBarVisibility.Visible, 109 | //Height = 800, 110 | }; 111 | Tabs.Items.Add(tab); 112 | } 113 | } 114 | /// 115 | /// This Class represents the Content of a Tab. Each Tab has one Tab Content. 116 | /// The Tab content consist of a List of templates, which can be rednered by the Method. 117 | /// 118 | public class TabContent(string name, List templates) 119 | { 120 | public string Name {get; set;} = name; 121 | public List Templates { get; set; } = templates; 122 | 123 | public Control PreRenderedContent { get; set; } = new TextBlock() 124 | { 125 | Text = "Please wait...", 126 | }; 127 | /// 128 | /// The refernce to the vm is needed to update the Tabs Property when for instance a repaint was called 129 | /// 130 | public PropertiesViewModel? vm { get; set; } 131 | private List _preRenderedControls = new List(); 132 | 133 | public void Render(XmlControl xmlControl) 134 | { 135 | StackPanel stackPanel = new StackPanel(); 136 | _preRenderedControls.Clear(); 137 | foreach (IViewTemplate viewTemplate in Templates) 138 | { 139 | Expander expander = new Expander() 140 | { 141 | Header = viewTemplate.Name, 142 | Content = viewTemplate.GetViewTemplateForControl(xmlControl, this), 143 | }; 144 | 145 | stackPanel.Children.Add(expander); 146 | // Saving the new Expander to the PreRenderedControls List. 147 | // It is needed to cache the Created Controls to make it easier to Render when only one ViewTemplate of the Collection of ViewTemplates has changed. 148 | _preRenderedControls.Add(expander); 149 | } 150 | PreRenderedContent = stackPanel; 151 | } 152 | /// 153 | /// This Method Handle the OnSelectedConstructorChanged Event from a ConstructorModel. 154 | /// This Method is needed because the ConstructorModel can not rerender itself because it can not access the view to set or add the new Controls in the PropertiesView. 155 | /// When calling the Method the selected ViewTemplate will be rerendered and all StandardViewTemplates. 156 | /// 157 | /// 158 | /// The Name of the View Template where the Instance of the ControlsCreatorObject class is located in. 159 | /// The Control which was Selected by the User. 160 | 161 | public void RenderOnlySelectedTemplate(string viewTemplateName, XmlControl currentXmlControl) 162 | { 163 | IViewTemplate? template = null; 164 | Control newControl = new(); 165 | if ((template = Templates.Find(t => t.Name == viewTemplateName)) != null) 166 | { 167 | newControl = template.GetRerenderedViewTemplateForControl(currentXmlControl, this); 168 | } 169 | // Find the Correct Index in the PreRenderedControls List 170 | int index = Templates.FindIndex(t => t.Name == viewTemplateName); 171 | _preRenderedControls[index] = newControl; 172 | // Update the PreRenderedControl var: 173 | StackPanel stackPanel = new StackPanel(); 174 | foreach (Control control in _preRenderedControls) 175 | { 176 | stackPanel.Children.Add(control); 177 | } 178 | PreRenderedContent = stackPanel; 179 | 180 | // Apply the new Controls to the View 181 | 182 | vm?.UpdateView(); 183 | } 184 | } 185 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/Editor/TopNavigationViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reactive; 3 | using System.Runtime.Serialization; 4 | using Avalonia.Controls; 5 | using Avalonia.Media; 6 | using BoTech.DesignerForAvalonia.Controller.Editor; 7 | using BoTech.DesignerForAvalonia.Models.Project; 8 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 9 | using BoTech.DesignerForAvalonia.ViewModels.Editor.Dialogs; 10 | using BoTech.DesignerForAvalonia.Views; 11 | using BoTech.DesignerForAvalonia.Views.Editor; 12 | using BoTech.DesignerForAvalonia.Views.Editor.Dialogs; 13 | using DialogHostAvalonia; 14 | using Material.Icons; 15 | using ReactiveUI; 16 | 17 | namespace BoTech.DesignerForAvalonia.ViewModels.Editor; 18 | 19 | public class TopNavigationViewModel : ViewModelBase 20 | { 21 | /// 22 | /// Shows the Dialog which can edit the Label Color 23 | /// 24 | public ReactiveCommand ChangeProjectLabelColorCommand { get; set; } 25 | 26 | /// 27 | /// This command Opens the view with the given name. 28 | /// 29 | public ReactiveCommand OpenViewCommand { get; set; } 30 | /// 31 | /// Loads a new Porject 32 | /// 33 | public ReactiveCommand LoadProjectCommand { get; set; } 34 | /// 35 | /// Shows the About View Dialog. 36 | /// 37 | public ReactiveCommand AboutViewCommand { get; set; } 38 | 39 | private bool _isProjectLoaded = false; 40 | /// 41 | /// Is true when a project is loaded so that the View can show the name and can display some more Controls in the TopNavBar 42 | /// 43 | public bool IsProjectLoaded 44 | { 45 | get => _isProjectLoaded; 46 | set => this.RaiseAndSetIfChanged(ref _isProjectLoaded, value); 47 | } 48 | 49 | private Project _loadedProject = new(); 50 | 51 | /// 52 | /// The Project which was Loaded. Must be initialized for the View, which access this Variable. 53 | /// 54 | public Project LoadedProject 55 | { 56 | get => _loadedProject; 57 | set => this.RaiseAndSetIfChanged(ref _loadedProject, value); 58 | } 59 | 60 | /// 61 | /// It is needed to have the MainViewModel reference to set the Content Object => Can change when the user wants to work either in the Editor or in the Style Editor. 62 | /// 63 | private MainViewModel _mainViewModel { get; set; } 64 | private ProjectController _projectController { get; set; } 65 | 66 | public TopNavigationViewModel(MainViewModel mainViewModel) 67 | { 68 | LoadProjectCommand = ReactiveCommand.Create(LoadProject); 69 | AboutViewCommand = ReactiveCommand.Create(ShowAboutView); 70 | OpenViewCommand = ReactiveCommand.Create(OpenView); 71 | ChangeProjectLabelColorCommand = ReactiveCommand.Create(() => 72 | { 73 | IsProjectLoaded = false; 74 | ChangeColorOfProject(LoadedProject); 75 | IsProjectLoaded = true; 76 | }); 77 | _mainViewModel = mainViewModel; 78 | 79 | } 80 | 81 | public static void ChangeColorOfProject(Project project) 82 | { 83 | 84 | DialogHost.Show(new GenericDialogView() 85 | { 86 | DataContext = new GenericDialogViewModel() 87 | { 88 | Icon = MaterialIconKind.PaletteOutline, 89 | Content = new ProjectColorDialogView() 90 | { 91 | DataContext = new ProjectColorDialogViewModel(project) 92 | } 93 | } 94 | }); 95 | } 96 | /// 97 | /// Initialise the View => Shows for example the Current Opened project in the top nav bar. 98 | /// 99 | public void OnProjectLoaded(ProjectController projectController) 100 | { 101 | _projectController = projectController; 102 | LoadedProject = projectController.LoadedProject; 103 | IsProjectLoaded = true; 104 | } 105 | private void OpenView(string viewName) 106 | { 107 | switch (viewName) 108 | { 109 | case "Solution-Explorer": 110 | _projectController.EditorController.SolutionExplorerView.Open(); 111 | break; 112 | case "Items-Explorer": 113 | _projectController.EditorController.ItemsView.Open(); 114 | break; 115 | case "Preview-View": 116 | _projectController.EditorController.PreviewView.Open(); 117 | break; 118 | case "Hierarchy-View": 119 | _projectController.EditorController.ViewHierarchyView.Open(); 120 | break; 121 | case "Properties-View": 122 | _projectController.EditorController.PropertiesView.Open(); 123 | break; 124 | } 125 | } 126 | private void LoadProject() 127 | { 128 | new ProjectStartViewModel(_mainViewModel).OpenProject(); 129 | } 130 | 131 | private void ShowAboutView() 132 | { 133 | DialogHost.Show(new AboutView() 134 | { 135 | DataContext = new AboutViewModel(), 136 | }, "MainDialogHost"); 137 | } 138 | 139 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/LoadingViewModel.cs: -------------------------------------------------------------------------------- 1 | using BoTech.DesignerForAvalonia.Views; 2 | using ReactiveUI; 3 | 4 | namespace BoTech.DesignerForAvalonia.ViewModels; 5 | 6 | public class LoadingViewModel : ViewModelBase 7 | { 8 | private string _statusText = "Init..."; 9 | 10 | public string StatusText 11 | { 12 | get => _statusText; 13 | set => this.RaiseAndSetIfChanged(ref _statusText, value); 14 | } 15 | 16 | private string _subStatusText = ""; 17 | 18 | public string SubStatusText 19 | { 20 | get => _subStatusText; 21 | set => this.RaiseAndSetIfChanged(ref _subStatusText, value); 22 | } 23 | 24 | private bool _isIndeterminate = true; 25 | 26 | public bool IsIndeterminate 27 | { 28 | get => _isIndeterminate; 29 | set => this.RaiseAndSetIfChanged(ref _isIndeterminate, value); 30 | } 31 | public int _current = 0; 32 | public int Current 33 | { 34 | get => _current; 35 | set => this.RaiseAndSetIfChanged(ref _current, value); 36 | } 37 | 38 | public int _maximum = 100; 39 | public int Maximum 40 | { 41 | get => _maximum; 42 | set => this.RaiseAndSetIfChanged(ref _maximum, value); 43 | } 44 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Media; 4 | using BoTech.DesignerForAvalonia.Controller.Editor; 5 | using BoTech.DesignerForAvalonia.Services.PropertiesView; 6 | using BoTech.DesignerForAvalonia.Services.XML; 7 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 8 | using BoTech.DesignerForAvalonia.Views; 9 | using BoTech.DesignerForAvalonia.Views.Editor; 10 | using ReactiveUI; 11 | 12 | namespace BoTech.DesignerForAvalonia.ViewModels; 13 | 14 | public class MainViewModel : ViewModelBase 15 | { 16 | public TopNavigationView TopNavigationView { get; set; } 17 | 18 | private Control _content = new TextBlock() 19 | { 20 | Text = "Please Open a Directory", 21 | FontWeight = FontWeight.Bold, 22 | Foreground = Brushes.Orange, 23 | }; 24 | 25 | /// 26 | /// Here all Views will be injected with a Grid. 27 | /// 28 | public Control Content 29 | { 30 | get => _content; 31 | set => this.RaiseAndSetIfChanged(ref _content, value); 32 | } 33 | public StatusConsoleView StatusConsoleView { get; set; } 34 | 35 | public MainViewModel() 36 | { 37 | 38 | 39 | TopNavigationView = new TopNavigationView() 40 | { 41 | DataContext = new TopNavigationViewModel(this) 42 | }; 43 | 44 | 45 | _content = new ProjectStartView() 46 | { 47 | DataContext = new ProjectStartViewModel(this) 48 | }; 49 | 50 | StatusConsoleView = new StatusConsoleView() 51 | { 52 | 53 | }; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/ProjectStartViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.IO; 4 | using System.Reactive; 5 | using System.Threading.Tasks; 6 | using Avalonia; 7 | using Avalonia.Controls.ApplicationLifetimes; 8 | using Avalonia.Platform.Storage; 9 | using BoTech.DesignerForAvalonia.Controller.Editor; 10 | using BoTech.DesignerForAvalonia.Models.Project; 11 | 12 | 13 | using ReactiveUI; 14 | 15 | namespace BoTech.DesignerForAvalonia.ViewModels; 16 | 17 | public class ProjectStartViewModel : ViewModelBase 18 | { 19 | private ObservableCollection _displayedProjects = new ObservableCollection(); 20 | 21 | public ObservableCollection DisplayedProjects 22 | { 23 | get => _displayedProjects; 24 | set => this.RaiseAndSetIfChanged(ref _displayedProjects, value); 25 | } 26 | /// 27 | /// Opens a File Picker 28 | /// 29 | public ReactiveCommand OpenProjectCommand { get; set; } 30 | /// 31 | /// Loads the Project and opens the editor View. 32 | /// 33 | public ReactiveCommand LoadProjectCommand { get; set; } 34 | private ProjectController _projectController; 35 | 36 | 37 | 38 | private bool _openFilePickerSuccess = false; 39 | private IStorageFile? currentSolutionFile = null; 40 | 41 | 42 | public ProjectStartViewModel(MainViewModel mainViewModel) 43 | { 44 | _projectController = new ProjectController(mainViewModel); 45 | _projectController.Initialize(); 46 | 47 | LoadProjectCommand = ReactiveCommand.Create(() => 48 | { 49 | if (_openFilePickerSuccess && currentSolutionFile != null) 50 | _projectController.LoadProject(currentSolutionFile.Path.LocalPath, 51 | currentSolutionFile.Path.AbsolutePath, currentSolutionFile.Name); 52 | }); 53 | OpenProjectCommand = ReactiveCommand.CreateRunInBackground(OpenProject); 54 | 55 | // Save the Loaded List of recent Porjects in the DisplayedProjects Collection, so that they appear on the Screen 56 | if (_projectController.RecentProjects.Count > 0) 57 | { 58 | foreach (Project recentProject in _projectController.RecentProjects) 59 | { 60 | DisplayedProjects.Add(new OpenableProject(this, recentProject)); 61 | } 62 | } 63 | else 64 | { 65 | DisplayedProjects.Add(new OpenableProject(this, new Project(){Name = "Welcome to the BoTech.DesignerForAvalonia.", SolutionFilePath = " Please select a project."}, true)); 66 | } 67 | } 68 | 69 | /// 70 | /// Loads a Project from the Recent Project List. 71 | /// 72 | /// 73 | public void LoadOpenableProject(OpenableProject openableProject) => _projectController.LoadProject((Project)openableProject); 74 | /// 75 | /// This Method will be invoked by a Button on the UI, to show the OpenFilePicker, where the User can select an .sln File. 76 | /// It also will Load the Project and show the Working Screen after that. 77 | /// 78 | public async void OpenProject() 79 | { 80 | _openFilePickerSuccess = false; 81 | currentSolutionFile = await DoOpenFilePickerAsync(); 82 | if (currentSolutionFile != null) 83 | { 84 | if (File.Exists(currentSolutionFile.Path.LocalPath)) 85 | { 86 | _openFilePickerSuccess = true; 87 | } 88 | } 89 | } 90 | /// 91 | /// Opens a File Picker Dialog (https://github.com/AvaloniaUI/AvaloniaUI.QuickGuides/tree/main/FileOps) 92 | /// Only allows the user to choose .sln Files. 93 | /// 94 | /// The chosen File. 95 | /// Missing StorageProvider instance. 96 | private async Task DoOpenFilePickerAsync() 97 | { 98 | 99 | // See IoCFileOps project for an example of how to accomplish this. 100 | if (Application.Current?.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || 101 | desktop.MainWindow?.StorageProvider is not { } provider) 102 | throw new NullReferenceException("Missing StorageProvider instance."); 103 | 104 | var files = await provider.OpenFilePickerAsync(new FilePickerOpenOptions() 105 | { 106 | Title = "Open Solution File", 107 | AllowMultiple = false, 108 | FileTypeFilter = new [] 109 | { 110 | new FilePickerFileType("Solution File") 111 | { 112 | Patterns = new [] 113 | { 114 | "*.sln" 115 | } 116 | } 117 | } 118 | }); 119 | 120 | return files?.Count >= 1 ? files[0] : null; 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | namespace BoTech.DesignerForAvalonia.ViewModels; 3 | public class ViewModelBase : ReactiveObject 4 | { 5 | } 6 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/AboutView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 15 | 18 | 19 | 20 | 21 | Powered by: 22 | 23 | Version: V1.1.6 (24.04.2025) 24 | 25 | 26 | For Support click here. 27 | 28 | 29 | 30 | www.botech.dev 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/AboutView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views; 6 | 7 | public partial class AboutView : UserControl 8 | { 9 | public AboutView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Abstraction/CloseablePageCodeBehind.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | namespace BoTech.DesignerForAvalonia.Views.Abstraction; 4 | 5 | public class CloseablePageCodeBehind : UserControl 6 | { 7 | public void Close() 8 | { 9 | this.IsVisible = false; 10 | } 11 | public void Open() 12 | { 13 | this.IsVisible = true; 14 | } 15 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/Dialogs/GenericDialogView.axaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 19 | 22 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/Dialogs/GenericDialogView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views.Editor.Dialogs; 6 | /// 7 | /// A Dialog View with basic functionality: Close and Save Button. Content can be set Dynamically. 8 | /// 9 | public partial class GenericDialogView : UserControl 10 | { 11 | public GenericDialogView() 12 | { 13 | InitializeComponent(); 14 | } 15 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/Dialogs/ProjectColorDialogView.axaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 13 | 14 | 15 | 18 | 19 | 20 | 23 | 24 | 25 | 28 | 29 | 30 | 33 | 34 | 35 | 38 | 39 | 40 | 43 | 44 | 45 | 48 | 49 | 50 | 53 | 54 | 55 | 58 | 59 | 60 | 63 | 64 | 65 | 68 | 69 | 70 | 73 | 74 | 75 | 78 | 79 | 80 | 83 | 84 | 85 | 88 | 89 | 90 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/Dialogs/ProjectColorDialogView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views.Editor.Dialogs; 6 | 7 | public partial class ProjectColorDialogView : UserControl 8 | { 9 | public ProjectColorDialogView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/Dialogs/PropertiesDialogView.axaml: -------------------------------------------------------------------------------- 1 | 7 | Welcome to Avalonia! 8 | 9 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/Dialogs/PropertiesDialogView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views.Editor.Dialogs; 6 | 7 | public partial class PropertiesDialogView : UserControl 8 | { 9 | public PropertiesDialogView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/ItemsExplorerView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 23 | Items Explorer 26 | 30 | 31 | 32 | 37 | 38 | 39 | 42 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/ItemsExplorerView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 5 | using BoTech.DesignerForAvalonia.Views.Abstraction; 6 | 7 | namespace BoTech.DesignerForAvalonia.Views.Editor; 8 | 9 | public partial class ItemsExplorerView : CloseablePageCodeBehind 10 | { 11 | public ItemsExplorerView() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | private void TreeView_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) 17 | { 18 | if (DataContext is ItemsExplorerViewModel vm) 19 | { 20 | vm.OnTreeViewSelectionChanged(); 21 | } 22 | } 23 | 24 | private void AutoCompleteBox_OnTextChanged(object? sender, TextChangedEventArgs e) 25 | { 26 | if (DataContext is ItemsExplorerViewModel vm) 27 | { 28 | vm.Search(); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/PreviewView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 15 | 16 | 17 | 22 | Preview 25 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/PreviewView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Input; 4 | using Avalonia.Markup.Xaml; 5 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 6 | using BoTech.DesignerForAvalonia.Views.Abstraction; 7 | 8 | namespace BoTech.DesignerForAvalonia.Views.Editor; 9 | 10 | public partial class PreviewView : CloseablePageCodeBehind 11 | { 12 | public PreviewView() 13 | { 14 | InitializeComponent(); 15 | } 16 | 17 | private void Preview_OnPointerMoved(object? sender, PointerEventArgs e) 18 | { 19 | if (DataContext is PreviewViewModel vm) 20 | { 21 | vm.OnPointerMoved(e); 22 | } 23 | } 24 | 25 | private void Preview_OnPointerExited(object? sender, PointerEventArgs e) 26 | { 27 | if (DataContext is PreviewViewModel vm) 28 | { 29 | vm.OnPointerExited(e); 30 | } 31 | } 32 | 33 | private void Preview_OnPointerPressed(object? sender, PointerPressedEventArgs e) 34 | { 35 | if (DataContext is PreviewViewModel vm) 36 | { 37 | vm.OnPointerPressed(e); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/PropertiesView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 13 | 14 | 15 | 21 | Properties 25 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/PropertiesView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 5 | using BoTech.DesignerForAvalonia.Views.Abstraction; 6 | 7 | namespace BoTech.DesignerForAvalonia.Views.Editor; 8 | public partial class PropertiesView : CloseablePageCodeBehind 9 | { 10 | public PropertiesView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/SolutionExplorerView.axaml: -------------------------------------------------------------------------------- 1 | 10 | 14 | 15 | 16 | 21 | Solution Explorer 24 | 28 | 29 | 30 | 35 | 36 | 37 | 40 | 43 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | 52 | Solution 53 | 54 | 55 | 56 | 57 | 58 | File System 59 | 60 | 61 | 62 | 63 | 66 | 67 | 68 | 69 | 72 | 73 | 74 | 75 | 81 | 83 | 84 | 85 | 86 | 87 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/SolutionExplorerView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 5 | using BoTech.DesignerForAvalonia.Views.Abstraction; 6 | 7 | namespace BoTech.DesignerForAvalonia.Views.Editor; 8 | 9 | public partial class SolutionExplorerView : CloseablePageCodeBehind 10 | { 11 | public SolutionExplorerView() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | private void TreeView_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) 17 | { 18 | if (DataContext is SolutionExplorerViewModel vm) 19 | { 20 | vm.OnTreeViewNodeSelectedChanged(); 21 | } 22 | } 23 | 24 | private void SelectingItemsControl_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) 25 | { 26 | if (DataContext is SolutionExplorerViewModel vm) 27 | { 28 | vm.OnExplorerTypeChanged(); 29 | } 30 | } 31 | 32 | private void AutoCompleteBox_OnTextChanged(object? sender, TextChangedEventArgs e) 33 | { 34 | if (DataContext is SolutionExplorerViewModel vm) 35 | { 36 | vm.SearchForAFileOrFolder(vm.CurrentSearchText); 37 | } 38 | //throw new System.NotImplementedException(); 39 | } 40 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/StatusConsoleView.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | Console: 8 | 9 | 10 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/StatusConsoleView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views.Editor; 6 | 7 | public partial class StatusConsoleView : UserControl 8 | { 9 | public StatusConsoleView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/TopNavigationView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 31 | 32 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 63 | 66 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/TopNavigationView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views.Editor; 6 | 7 | public partial class TopNavigationView : UserControl 8 | { 9 | public TopNavigationView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/ViewHierarchyView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 13 | 14 | 15 | 20 | View-Hierarchy 23 | 27 | 28 | 29 | 34 | 35 | 36 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | 62 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 77 | 78 | 79 | 80 | 81 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/Editor/ViewHierarchyView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Input; 4 | using Avalonia.Markup.Xaml; 5 | using BoTech.DesignerForAvalonia.ViewModels.Editor; 6 | using BoTech.DesignerForAvalonia.Views.Abstraction; 7 | 8 | namespace BoTech.DesignerForAvalonia.Views.Editor; 9 | 10 | public partial class ViewHierarchyView : CloseablePageCodeBehind 11 | { 12 | public ViewHierarchyView() 13 | { 14 | InitializeComponent(); 15 | } 16 | 17 | private void TreeView_OnSelectionChanged(object? sender, SelectionChangedEventArgs e) 18 | { 19 | if (DataContext is ViewHierarchyViewModel vm) 20 | { 21 | vm.OnTreeViewSelectionChanged(); 22 | } 23 | } 24 | 25 | private void AutoCompleteBox_OnTextChanged(object? sender, TextChangedEventArgs e) 26 | { 27 | if (DataContext is ViewHierarchyViewModel vm) 28 | { 29 | vm.Search(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/LoadingView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 30 | 31 | 32 | 33 | Please wait while Loading... 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/LoadingView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views; 6 | 7 | public partial class LoadingView : UserControl 8 | { 9 | public LoadingView() 10 | { 11 | InitializeComponent(); 12 | } 13 | } -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/MainView.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/MainView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | 4 | namespace BoTech.DesignerForAvalonia.Views; 5 | 6 | public partial class MainView : UserControl 7 | { 8 | public MainView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 11 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using BoTech.DesignerForAvalonia.ViewModels; 3 | 4 | namespace BoTech.DesignerForAvalonia.Views; 5 | 6 | public partial class MainWindow : Window 7 | { 8 | public MainWindow() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/ProjectStartView.axaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 46 | 47 | 48 | 49 | 50 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /BoTech.DesignerForAvalonia/Views/ProjectStartView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | 5 | namespace BoTech.DesignerForAvalonia.Views; 6 | 7 | public partial class ProjectStartView : UserControl 8 | { 9 | 10 | public ProjectStartView() 11 | { 12 | InitializeComponent(); 13 | 14 | } 15 | 16 | 17 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | support@botech.dev. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to 2 | 3 | ![Logo](https://github.com/BoTech-Development/BoTech.DesignerForAvalonia/blob/master/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_WithText_NoBG.png) 4 | 5 | You are welcome to contribute to this project.
6 | First of all it is necessary to know which ways we provide to contribute to this project. 7 | 8 | **In this Article** 9 | 1. Issues
10 | 1.1 Creating Issues
11 | 1.1.1 Naming conventions for the title
12 | 1.1.2 Feature-Request
13 | 1.1.3 Bug-Report
14 | 1.2 How Issues should be managed. 15 | 2. Contribution Policy
16 | 2.1 Become a Contributor
17 | 2.2 Commit Policy
18 | 2.3 Code Style 19 | 20 | ## Issues 21 | ### 1. Create an issue. 22 | If you want to create an issue you can use the Bug-Report Template or the Feature-Request Template. When you want to report an issue it is necessary that you report any important information about the issue. 23 | 24 | #### Naming conventions for the title 25 | Each Title of an **official** must follow the following sheme: `{Short Sentence which decribe the conentent} | {Which part of the code should be modified (Views or Backend Classes)} | {Milestone} | {Unique Version (for E.g. V1.0.28)}` 26 | 27 | All **unoffcial** community Issues must not follow this sheme. Unofficail means that the Issue was not created by one of the official contributors or of the owner of this porject. Unofficail contributors should use the issues. 28 | 29 | **The title will be created by the contributors or the owner of this repository.** 30 | 31 | #### Feature-Request 32 | When you want to submitt a feature report you have to first search for an issue on which your feature-request is or could base on. Please use the [Release Project](https://github.com/users/BoTech-Development/projects/1/views/2) to search for the Issue. Write something about the feature. For example you could wirte: 33 | > "There should be support for bindings in the ViewModel. The user should be able to delete, modify or add them" 34 | 35 | Then Think about how we can implement this feature (UI and Backend). For example you could write: 36 | 37 | > You could contain the Information about the Bindings in the XML-Nodes and update them if necessary. Furthermore you should create the Bindings to create an realistic preview. 38 | 39 | If you have any additional Information about the feature, feel free to add them. 40 | 41 | #### Bug-Report 42 | 1. Please check the [release project](https://github.com/users/BoTech-Development/projects/1/views/2) to identify duplicate issues if you want to report a new bug. Please check all major and minor versions in the project. For example, you might be working with an outdated version, but the bug has already been fixed in a newer version or is on the [roadmap](https://aka.botech.dev/BoTech.DesignerForAvalonia/Roadmap/Current). 43 | 2. Now create an Issue with the Bug-Report template. Please fill out all text fields to the best of your knowledge and belief. 44 | ### How Issues should be managed 45 | 1. When a new issue is opened, somebody on the team adds it to the release project. This also determines when (in which version) and whether the issue will be addressed. 46 | 2. The Issue will be categorized in "Backlog🟠", "In review🟣", "Ready to work on🔵", "In progress🟡", "Done🟢" 47 | + "Backlog🟠" => Issue was created and will be fixed or implemented in this Minor/Mayor Version. 48 | + "In review🟣" => This means that someone from the team looks at the issue and makes adjustments if errors are present. In this step, the unique version that each issue receives is also determined. 49 | + "Ready to work on🔵" => The issue has been fully processed and can be implemented. 50 | + "In progress🟡" => Sombody of the team currently works on this Issue. See who is assigned to this Issue. 51 | + "Done🟢" => This only applies if the issue has been fully implemented without any build errors and a commit has been made that adds the new code. **After each commit, all parts of the code must be buildable.** 52 | 53 | 54 | ## Contribution Policy 55 | ### Become a Contributor 56 | What you need to do at least to become a contributor: 57 | 1. Check out what you can do => Work on a Issue or implement a part of the [roadmap](https://aka.botech.dev/BoTech.DesignerForAvalonia/Roadmap/Current). Please let us know what you would like to work on. 58 | 2. Fork this repo to create your changes. 59 | 3. Make a Pull request => Describe how your Code works and on which Issue the code is based on. 60 | 4. We will check to PR out and accepted if the code is ok. 61 | 5. After that you probably become a contribute. 62 | ### Commit Policy 63 | If you want to make a commit, it's important that you base the commit on an issue. Therefore, the commit message must be formatted as follows: Feature and/or enhancement and/or bug #{Issue Number} is now fully implemented. 64 | 65 | **❗You are only allowed to commit which is buildable and runnable!** 66 | By setting this rule, we ensure that the **repository is buildable and executable at all time**. This way, others can use the repository at any time to further develop the code or use the program. 67 | ### Code Style 68 | + All members (Functions, Properties, Constants, Enums) must have a xml documentation (summary blocks), if they are not easy to understand. 69 | + All Classes must have a xml documentation too. 70 | + Methods names must begin with an Capital letter. 71 | + Params of the Method must begin with an small letter. 72 | + Member names must begin with a capital letter. 73 | + Except private members which must begin with an underscore (_) and a small letter. 74 | + Differnt words must be seperated by using capital letters. 75 | 76 | This article was last updated on 09.04.2025 by BoTech-Development.
77 | ❗These rules may be changed without notice 78 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | enable 4 | 11.1.0 5 | 6 | 7 | -------------------------------------------------------------------------------- /Docs/Code/.$BoTech.AvaloniaDesigner.drawio.bkp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Docs/Code/MainCodeStructure.md: -------------------------------------------------------------------------------- 1 | # Main Code Structure-Policy 2 | ## Editor 3 | + For the Editor we are using the following Folders: 4 | 1. /ViewModels/Editor/ 5 | 2. /Views/Editor/ 6 | 3. /Controller/Editor/ 7 | 8 | **Description:** 9 | 1. All the ViewModels for each editor view are stored in this folder. 10 | Each ViewModel should only provide functionality which is only referred to the View. 11 | Functionality that is connected to multiple Views has to be programmed in the PreviewController. 12 | 2. All the Views for e.g. the Properties View the PreviewView are stored in this folder. 13 | 3. All the Controllers which provide Properties(Information) or functionality for all or for some of the Views are stored here. 14 | 15 | -------------------------------------------------------------------------------- /Docs/Code/README.md: -------------------------------------------------------------------------------- 1 | # This is the Code Documentation for this Repository 2 | ## Table of Contents: 3 | + [Main Code Structure-Policy](https://github.com/BoTech-Development/BoTech.AvaloniaDesigner/tree/master/BoTech.AvaloniaDesigner/Docs/Code/MainCodeStructure.md) 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 www.botech.dev 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Logo](https://github.com/BoTech-Development/BoTech.DesignerForAvalonia/blob/master/BoTech.DesignerForAvalonia/Assets/BoTech_DesignerForAvalonia_WithText_NoBG.png) 2 | 3 | ### Design Preview and Style your Avalonia App in the BoTech.DesignerForAvalonia. 4 | 5 | 6 | ![GitHub Release](https://img.shields.io/github/v/release/BoTech-Development/BoTech.AvaloniaDesigner) 7 | ![GitHub Created At](https://img.shields.io/github/created-at/BoTech-Development/BoTech.AvaloniaDesigner) 8 | ![GitHub License](https://img.shields.io/github/license/BoTech-Development/BoTech.DesignerForAvalonia) 9 | 10 | ![GitHub stars](https://img.shields.io/github/stars/BoTech-Development/BoTech.AvaloniaDesigner.svg) 11 | ![GitHub watchers](https://img.shields.io/github/watchers/BoTech-Development/BoTech.AvaloniaDesigner.svg) 12 | [![GitHub followers](https://img.shields.io/github/followers/BoTech-Development.svg?style=social&label=Follow&maxAge=2592000)](https://github.com/BoTech-Development?tab=followers) 13 | ![GitHub forks](https://img.shields.io/github/forks/BoTech-Development/BoTech.DesignerForAvalonia) 14 | 15 | ### Current Version: [V1.1.3](https://github.com/users/BoTech-Development/projects/1) 16 | **This project is still in development. [You are welcome to help us further develop the project.](https://github.com/BoTech-Development/BoTech.DesignerForAvalonia/blob/master/Contributing.md) 17 | ## Current Status: 18 | + I have published the first Release Version 🎉**V1.1.0**🎉 19 | + We are currently working on V1.2.0 20 | + See the Second Release Roadmap [V1.2.0](https://docs.botech.dev/DesignerForAvalonia/1.1/roadmap-v1-1.html) for more info. 21 | ## Contributing 22 | + [You are welcome to this Project.!](https://github.com/BoTech-Development/BoTech.DesignerForAvalonia/blob/master/Contributing.md) 23 | ## ℹ️📖Documentation 24 | + [Online Doku](https://docs.botech.dev/) 25 | ## How to build? 26 | ### ⚙️Preparation of the development environment 27 | 1. Install the dotnet8.0 sdk: [click_here](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) 28 | 2. Install [Jetbrains Rider®](https://www.jetbrains.com/rider/download/) or [VisualStudio® 2022](https://visualstudio.microsoft.com/de/downloads/) 29 | 3. Install the [Avalonia™ Templates](https://docs.avaloniaui.net/docs/get-started/install) 30 | ### 🪲⚒️Build 31 | + Clone the Repo, open and run the Desktop Build Configuration. 32 | + 😊 Happy developing and create cool Designs. 33 | ## ©️License 34 | This Repository is licensed under the MIT License: [Go To the MIT License](https://github.com/BoTech-Development/BoTech.DesignerForAvalonia/blob/master/LICENSE) 35 | 36 | We reserve the right to change the license without notice and without justification. If this happens, the MIT license may be restricted or the license may be changed entirely. All previous versions are licensed under the license defined here, regardless of which license they were previously licensed under. The current version is also licensed under the license defined here. 37 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.0", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": true 6 | } 7 | } --------------------------------------------------------------------------------