diff --git a/Program.cs b/Program.cs deleted file mode 100644 index a3388e0..0000000 --- a/Program.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace dotnetcore -{ - class Program - { - static void Main(string[] args) - { - Console.WriteLine("Hello World!"); - } - } -} diff --git a/discovery.sln b/discovery.sln new file mode 100644 index 0000000..faec255 --- /dev/null +++ b/discovery.sln @@ -0,0 +1,60 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36518.9 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discovery.Core", "src\Discovery.Core\Discovery.Core.csproj", "{69E5A763-1B0B-A8C2-76B9-B5AB1024AA61}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discovery.Avalonia", "src\Discovery.Avalonia\Discovery.Avalonia.csproj", "{09326749-BBC2-21DE-BA52-6E3B4BB48BB6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discovery.Desktop", "src\Discovery.Desktop\Discovery.Desktop.csproj", "{E1BC3B5E-9798-0849-B8D1-EEDB326CDA8B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E3E8567F-52C2-4CE2-894D-13FD700D02AA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discovery.Loader.Avalonia", "test\Discovery.Loader.Avalonia\Discovery.Loader.Avalonia.csproj", "{C2CE7D56-2AEB-33AB-B0EA-6854925C8A22}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discovery.Loader.Desktop", "test\Discovery.Loader\Discovery.Loader.Desktop.csproj", "{D7CBCD4D-F381-763E-9D57-6200BEC92F8C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {69E5A763-1B0B-A8C2-76B9-B5AB1024AA61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69E5A763-1B0B-A8C2-76B9-B5AB1024AA61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69E5A763-1B0B-A8C2-76B9-B5AB1024AA61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69E5A763-1B0B-A8C2-76B9-B5AB1024AA61}.Release|Any CPU.Build.0 = Release|Any CPU + {09326749-BBC2-21DE-BA52-6E3B4BB48BB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {09326749-BBC2-21DE-BA52-6E3B4BB48BB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {09326749-BBC2-21DE-BA52-6E3B4BB48BB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {09326749-BBC2-21DE-BA52-6E3B4BB48BB6}.Release|Any CPU.Build.0 = Release|Any CPU + {E1BC3B5E-9798-0849-B8D1-EEDB326CDA8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1BC3B5E-9798-0849-B8D1-EEDB326CDA8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1BC3B5E-9798-0849-B8D1-EEDB326CDA8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1BC3B5E-9798-0849-B8D1-EEDB326CDA8B}.Release|Any CPU.Build.0 = Release|Any CPU + {C2CE7D56-2AEB-33AB-B0EA-6854925C8A22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2CE7D56-2AEB-33AB-B0EA-6854925C8A22}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2CE7D56-2AEB-33AB-B0EA-6854925C8A22}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2CE7D56-2AEB-33AB-B0EA-6854925C8A22}.Release|Any CPU.Build.0 = Release|Any CPU + {D7CBCD4D-F381-763E-9D57-6200BEC92F8C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7CBCD4D-F381-763E-9D57-6200BEC92F8C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7CBCD4D-F381-763E-9D57-6200BEC92F8C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7CBCD4D-F381-763E-9D57-6200BEC92F8C}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {69E5A763-1B0B-A8C2-76B9-B5AB1024AA61} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {09326749-BBC2-21DE-BA52-6E3B4BB48BB6} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {E1BC3B5E-9798-0849-B8D1-EEDB326CDA8B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} + {C2CE7D56-2AEB-33AB-B0EA-6854925C8A22} = {E3E8567F-52C2-4CE2-894D-13FD700D02AA} + {D7CBCD4D-F381-763E-9D57-6200BEC92F8C} = {E3E8567F-52C2-4CE2-894D-13FD700D02AA} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C88FC77E-459A-4DFD-9202-FB1070834AAB} + EndGlobalSection +EndGlobal diff --git a/dotnetcore.csproj b/dotnetcore.csproj deleted file mode 100644 index 120e38c..0000000 --- a/dotnetcore.csproj +++ /dev/null @@ -1,8 +0,0 @@ - - - - Exe - net7.0 - - - diff --git a/src/Discovery.Avalonia/App.axaml b/src/Discovery.Avalonia/App.axaml new file mode 100644 index 0000000..2e65aa8 --- /dev/null +++ b/src/Discovery.Avalonia/App.axaml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/src/Discovery.Avalonia/App.axaml.cs b/src/Discovery.Avalonia/App.axaml.cs new file mode 100644 index 0000000..6de3fc0 --- /dev/null +++ b/src/Discovery.Avalonia/App.axaml.cs @@ -0,0 +1,30 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Discovery.Avalonia.ViewModels; +using Discovery.Avalonia.Windows; + +namespace Discovery.Avalonia; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() + { + //BindingPlugins.DataValidators.RemoveAt(0); + + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow + { + DataContext = new MainViewModel() + }; + } + + base.OnFrameworkInitializationCompleted(); + } +} diff --git a/src/Discovery.Avalonia/Assets/icon.ico b/src/Discovery.Avalonia/Assets/icon.ico new file mode 100644 index 0000000..6492fda Binary files /dev/null and b/src/Discovery.Avalonia/Assets/icon.ico differ diff --git a/src/Discovery.Avalonia/Converters/MoveConverters.cs b/src/Discovery.Avalonia/Converters/MoveConverters.cs new file mode 100644 index 0000000..95aeda2 --- /dev/null +++ b/src/Discovery.Avalonia/Converters/MoveConverters.cs @@ -0,0 +1,47 @@ +using Avalonia.Data.Converters; +using Discovery.Avalonia.ViewModels; +using System.Globalization; + +namespace Discovery.Avalonia.Converters; + +public class CanMoveUpConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return true; + + if (parameter is IEnumerable items && value is object item) + { + var list = items.Cast().ToList(); + var idx = list.IndexOf(item); + return idx > 0; + } + return false; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} + +public class CanMoveDownConverter : IValueConverter +{ + public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) + { + return true; + + if (parameter is IEnumerable items && value is object item) + { + var list = items.Cast().ToList(); + var idx = list.IndexOf(item); + return idx >= 0 && idx < list.Count - 1; + } + return false; + } + + public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Discovery.Avalonia/Discovery.Avalonia.csproj b/src/Discovery.Avalonia/Discovery.Avalonia.csproj new file mode 100644 index 0000000..6637fde --- /dev/null +++ b/src/Discovery.Avalonia/Discovery.Avalonia.csproj @@ -0,0 +1,53 @@ + + + net9.0 + latest + enable + enable + Discovery.Avalonia + Discovery + Kevin Kai Berthold + Webmatic GmbH + 2025.3.14.0 + 2025.3.14.0 + 2025.3.14.0 + true + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MainView.axaml + + + diff --git a/src/Discovery.Avalonia/Models/TargetViewModel.cs b/src/Discovery.Avalonia/Models/TargetViewModel.cs new file mode 100644 index 0000000..5d48b30 --- /dev/null +++ b/src/Discovery.Avalonia/Models/TargetViewModel.cs @@ -0,0 +1,42 @@ +using Discovery.Avalonia.Windows; +using Discovery.Models; +using System.Diagnostics; +using System.Net; + +namespace Discovery.Avalonia.Models; + +public class TargetViewModel(int index, IPAddress ipAddress) : Target(index, ipAddress) +{ + public void CopyAddress() => SetClipboard(Address); + public void CopyHostname() => SetClipboard(Hostname); + public void CopyMac() => SetClipboard(Mac); + public void CopyVendor() => SetClipboard(Vendor); + + public void HttpAddress() => OpenHref(Address, false); + public void HttpsAddress() => OpenHref(Address, true); + public void HttpHostname() => OpenHref(Hostname, false); + public void HttpsHostname() => OpenHref(Hostname, true); + + private static void OpenHref(string? uri, bool secure) + { + if (uri == null) return; + + uri = secure ? "https://" + uri : "http://" + uri; + + try + { + Process.Start(new ProcessStartInfo { FileName = uri, UseShellExecute = true }); + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + + public static void SetClipboard(string? text) + { + if (text == null) return; + if (MainWindow.Instance?.Clipboard == null) return; + _ = MainWindow.Instance.Clipboard.SetTextAsync(text); + } +} \ No newline at end of file diff --git a/src/Discovery.Avalonia/ViewModels/MainViewModel.cs b/src/Discovery.Avalonia/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..b278071 --- /dev/null +++ b/src/Discovery.Avalonia/ViewModels/MainViewModel.cs @@ -0,0 +1,354 @@ +using Avalonia.Controls; +using Avalonia.Platform.Storage; +using Avalonia.Threading; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging; +using Discovery.Avalonia.Models; +using Discovery.Avalonia.Windows; +using Discovery.Helpers; +using Discovery.Models; +using Discovery.Services; +using System.Net; + +namespace Discovery.Avalonia.ViewModels; + +public partial class MainViewModel : ViewModelBase +{ + public string? HostInput + { + get + { + return _hostInput; + } + set + { + if (value != _hostInput) + { + _hostInput = value; + OnPropertyChanged(nameof(HostInput)); + } + } + } + + public int OnlineFilter + { + get { return _onlineInput; } + set + { + if (value != _onlineInput) + { + _onlineInput = value; + OnFilterChanged(); + OnPropertyChanged(nameof(OnlineFilter)); + } + } + } + + public string? RegexFilter + { + get + { + return _regexInput; + } + set + { + if (value != _regexInput) + { + _regexInput = value; + OnFilterChanged(); + OnPropertyChanged(nameof(RegexFilter)); + } + } + } + + public string? ScanText + { + get + { + return _scanText; + } + set + { + if (value != _scanText) + { + _scanText = value; + OnPropertyChanged(nameof(ScanText)); + } + } + } + + public string? StatusMessage + { + get + { + return _status; + } + set + { + if (value != _status) + { + _status = value; + OnPropertyChanged(nameof(StatusMessage)); + } + } + } + + public string? StatusMessage2 + { + get + { + return _status2; + } + set + { + if (value != _status2) + { + _status2 = value; + OnPropertyChanged(nameof(StatusMessage2)); + } + } + } + + public double Progress + { + get + { + return _progress; + } + set + { + if (value != _progress) + { + _progress = value; + OnPropertyChanged(nameof(Progress)); + } + } + } + + public string? Version + { + get + { + return _version; + } + set + { + if (value != _version) + { + _version = value; + OnPropertyChanged(nameof(Version)); + } + } + } + + public ScannerOptions Options { get; set; } = new(); + public SynchronizedCollection Targets { get; } + public RelayCommand ScanCommand { get; } + public RelayCommand ExitCommand { get; } + public RelayCommand ExportCsvCommand { get; } + public RelayCommand ExportHtmlCommand { get; } + public RelayCommand TestCommand { get; } + + private string? _version = "2025.8.19.0"; + private string? _hostInput; + private string? _regexInput; + private int _onlineInput = 1; + private string? _scanText = "Scan"; + private string? _status; + private string? _status2; + private double _progress; + + private readonly SynchronizationContext _uiContext; + private readonly NetworkInfo? _networkInfo; + private readonly Scanner _scanner = new(); + + public MainViewModel() + { + _uiContext = SynchronizationContext.Current ?? throw new Exception("SynchronizationContext"); + _networkInfo = _scanner.GetPrimaryNetwork(); + + if (_networkInfo != null) + { + HostInput = $"{_networkInfo.Network}/{_networkInfo.Cidr}"; + Options.Nameservers = _networkInfo.Nameservers; + } + + Targets = new(p => p.Index, _uiContext); + + ScanCommand = new(async () => { await OnScanAsync(); }); + ExitCommand = new(Shutdown); + ExportCsvCommand = new(async () => { await ExportCsvAsync(); }); + ExportHtmlCommand = new(async () => { await ExportHtmlAsync(); }); + TestCommand = new(async () => { await OpenOptionDialog(); }); + + // init filter, needed + OnFilterChanged(); + } + + public async Task OnScanAsync() + { + if (_scanner.State.Operation == ScanOperation.Scanning) + { + await _scanner.CancelAsync(); + return; + } + + if (string.IsNullOrWhiteSpace(_hostInput) || + !IpHelper.TryParseHostNotation(_hostInput, out var address, out var cidr)) + { + StatusMessage = "Invalid Address"; + return; + } + + if (IPNetwork2.TryParse($"{address}/{cidr}", out var network) is false) + { + StatusMessage = "Invalid Network"; + return; + } + + var addresses = network.ListIPAddress(filter: Filter.Usable); + + Targets.Clear(); + + for (int i = 0; i < addresses.Count; i++) + { + Targets.Add(new TargetViewModel(i, addresses[i])); + } + + var events = new ScannerEvents() + { + OnStateUpdate = (state) => Task.Run(() => + { + Dispatcher.UIThread.Invoke(() => + { + Progress = state.Progress; + }, DispatcherPriority.Default, default); + }, default) + }; + + var scanTask = _scanner.ScanAsync(Targets, Options, events, default).ConfigureAwait(false); + + ScanText = "Abort"; + StatusMessage = $"Scanning..."; + StatusMessage2 = ""; + + var result = await scanTask; + if (result.Operation == ScanOperation.Idle) + { + ScanText = "Scan"; + StatusMessage = $"Scanned {addresses.Count} Hosts in: {result.Elapsed}"; + StatusMessage2 = $"{Targets.Where(p => p.Online).Count()} Online"; + } + else + { + ScanText = "Scan"; + StatusMessage = "Scan aborted!"; + StatusMessage2 = $"{Targets.Where(p => p.Online).Count()} Online"; + } + } + + public static void Shutdown() + { + Dispatcher.UIThread.InvokeShutdown(); + } + + private void OnFilterChanged() + { + bool online(Target p) => _onlineInput switch + { + 0 => true, + 1 => p.Online == true, + 2 => p.Online == false, + _ => true, + }; + + bool regex(Target p) + { + if (string.IsNullOrWhiteSpace(_regexInput)) return true; + if (p.Address != null && p.Address.Contains(_regexInput, StringComparison.InvariantCultureIgnoreCase)) return true; + if (p.Hostname != null && p.Hostname.ToString().Contains(_regexInput, StringComparison.InvariantCultureIgnoreCase)) return true; + if (p.Mac != null && p.Mac.ToString().Contains(_regexInput, StringComparison.InvariantCultureIgnoreCase)) return true; + if (p.Vendor != null && p.Vendor.ToString().Contains(_regexInput, StringComparison.InvariantCultureIgnoreCase)) return true; + return false; + } + + Targets.Filter = (x) => online(x) && regex(x); + + // force re-render (avalonia render bug) + if (MainWindow.Instance != null) + { + Dispatcher.UIThread.Invoke(async () => + { + await Task.Delay(50); + MainWindow.Instance.Width = MainWindow.Instance.Width + 2; + }, DispatcherPriority.Send); + + Dispatcher.UIThread.Invoke(async () => + { + await Task.Delay(50); + MainWindow.Instance.Width = MainWindow.Instance.Width - 2; + }, DispatcherPriority.Send); + } + } + + private async Task ExportCsvAsync() + { + if (Targets is null) return; + + var pre = $"discovery_{DateTime.Now:MM-dd-yyyy_hh-mm-ss}"; + + var topLevel = TopLevel.GetTopLevel(MainWindow.Instance); + if (topLevel is null) return; + + var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + { + SuggestedFileName = pre, + DefaultExtension = "csv", + ShowOverwritePrompt = true, + Title = "Save as CSV" + }); + + if (file is null || file.Path is null) + return; + + await Exporter.ExportAsCsvAsync(Targets, file.Path.LocalPath); + + await Dispatcher.UIThread.InvokeAsync(() => + { + StatusMessage = $"Exported as CSV to: {file.Path.LocalPath}"; + }, DispatcherPriority.Default, default); + } + + private async Task ExportHtmlAsync() + { + if (Targets is null) return; + + var pre = $"discovery_{DateTime.Now:MM-dd-yyyy_hh-mm-ss}"; + + var topLevel = TopLevel.GetTopLevel(MainWindow.Instance); + if (topLevel is null) return; + + var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions + { + SuggestedFileName = pre, + DefaultExtension = "html", + ShowOverwritePrompt = true, + Title = "Save as HTML" + }); + + if (file is null || file.Path is null) + return; + + await Exporter.ExportAsHtmlAsync(Targets, file.Path.LocalPath); + + await Dispatcher.UIThread.InvokeAsync(() => + { + StatusMessage = $"Exported as HTML to: {file.Path.LocalPath}"; + }, DispatcherPriority.Default, default); + } + + private async Task OpenOptionDialog() + { + var option = await WeakReferenceMessenger.Default.Send(new OpenOptionsMessage()); + } +} \ No newline at end of file diff --git a/src/Discovery.Avalonia/ViewModels/OptionViewModel.cs b/src/Discovery.Avalonia/ViewModels/OptionViewModel.cs new file mode 100644 index 0000000..4d0120d --- /dev/null +++ b/src/Discovery.Avalonia/ViewModels/OptionViewModel.cs @@ -0,0 +1,92 @@ +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Mvvm.Messaging.Messages; +using System.Collections.ObjectModel; +using System.Windows.Input; + +namespace Discovery.Avalonia.ViewModels; + +public class OpenOptionsMessage : AsyncRequestMessage; + +public class OptionViewModel : ViewModelBase +{ + public ObservableCollection Nameservers { get; } = []; + public int? SelectedNameserver { get; set; } + public ICommand AddCommand { get; } + public ICommand RemoveCommand { get; } + public ICommand MoveUpCommand { get; } + public ICommand MoveDownCommand { get; } + + public OptionViewModel() + { + AddCommand = new RelayCommand(Add); + RemoveCommand = new RelayCommand(Remove); + MoveUpCommand = new RelayCommand(MoveUp); + MoveDownCommand = new RelayCommand(MoveDown); + + //if (Design.IsDesignMode) + //{ + // Nameservers.Add(new Nameserver { Host = "1.1.1.1"} ); + // Nameservers.Add(new Nameserver { Host = "8.8.8.8" }); + //} + } + + private void Add() + { + Nameservers.Add(new Nameserver { Host = "127.0.0.1" }); + OnPropertyChanged(nameof(Nameservers)); + } + + private void Remove() + { + if (SelectedNameserver == null) + return; + + Nameservers.RemoveAt(SelectedNameserver.Value); + OnPropertyChanged(nameof(Nameservers)); + } + + private void MoveUp(Nameserver? item) + { + Console.WriteLine("MoveUp"); + + if (item == null) + { + Console.WriteLine("no item"); + return; + } + int index = Nameservers.IndexOf(item); + + if (index > 0) + { + Nameservers.RemoveAt(index); + Nameservers.Insert(index - 1, item); + } + + OnPropertyChanged(nameof(Nameservers)); + } + + private void MoveDown(Nameserver? item) + { + Console.WriteLine("MoveDown"); + + if (item == null) + { + Console.WriteLine("no item"); + return; + } + int index = Nameservers.IndexOf(item); + + if (index >= 0 && index < Nameservers.Count - 1) + { + Nameservers.RemoveAt(index); + Nameservers.Insert(index + 1, item); + } + + OnPropertyChanged(nameof(Nameservers)); + } +} + +public class Nameserver +{ + public string Host { get; set; } = null!; +} \ No newline at end of file diff --git a/src/Discovery.Avalonia/ViewModels/ViewModelBase.cs b/src/Discovery.Avalonia/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..019d25a --- /dev/null +++ b/src/Discovery.Avalonia/ViewModels/ViewModelBase.cs @@ -0,0 +1,7 @@ +using CommunityToolkit.Mvvm.ComponentModel; + +namespace Discovery.Avalonia.ViewModels; + +public class ViewModelBase : ObservableObject +{ +} diff --git a/src/Discovery.Avalonia/Views/MainView.axaml b/src/Discovery.Avalonia/Views/MainView.axaml new file mode 100644 index 0000000..026f686 --- /dev/null +++ b/src/Discovery.Avalonia/Views/MainView.axaml @@ -0,0 +1,280 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Alle + Online + Offline + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Discovery.Avalonia/Views/MainView.axaml.cs b/src/Discovery.Avalonia/Views/MainView.axaml.cs new file mode 100644 index 0000000..605fc24 --- /dev/null +++ b/src/Discovery.Avalonia/Views/MainView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.Controls; + +namespace Discovery.Avalonia.Views; + +public partial class MainView : UserControl +{ + public MainView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Discovery.Avalonia/Windows/MainWindow.axaml b/src/Discovery.Avalonia/Windows/MainWindow.axaml new file mode 100644 index 0000000..4ad7e82 --- /dev/null +++ b/src/Discovery.Avalonia/Windows/MainWindow.axaml @@ -0,0 +1,15 @@ + + + diff --git a/src/Discovery.Avalonia/Windows/MainWindow.axaml.cs b/src/Discovery.Avalonia/Windows/MainWindow.axaml.cs new file mode 100644 index 0000000..d56269c --- /dev/null +++ b/src/Discovery.Avalonia/Windows/MainWindow.axaml.cs @@ -0,0 +1,32 @@ +using Avalonia.Controls; +using CommunityToolkit.Mvvm.Messaging; +using Discovery.Avalonia.ViewModels; + +namespace Discovery.Avalonia.Windows; + +public partial class MainWindow : Window +{ + public static MainWindow? Instance { get; set; } + + public MainWindow() + { + Instance = this; + InitializeComponent(); + + if (Design.IsDesignMode) + return; + + // Whenever 'Send(new PurchaseAlbumMessage())' is called, invoke this callback on the MainWindow instance: + WeakReferenceMessenger.Default.Register(this, static (w, m) => + { + // Create an instance of MusicStoreWindow and set MusicStoreViewModel as its DataContext. + var dialog = new OptionWindow + { + DataContext = new OptionViewModel() + }; + + // Show dialog window and reply with returned AlbumViewModel or null when the dialog is closed. + m.Reply(dialog.ShowDialog(w)); + }); + } +} \ No newline at end of file diff --git a/src/Discovery.Avalonia/Windows/OptionWindow.axaml b/src/Discovery.Avalonia/Windows/OptionWindow.axaml new file mode 100644 index 0000000..0d0ec79 --- /dev/null +++ b/src/Discovery.Avalonia/Windows/OptionWindow.axaml @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Concurrency + + + + + + + + + + + + + + + Timeout (ms) + + + + + + + + + + + + + + + + + + + + + Timeout (ms) + + + + TTL + + + + Dont Fragment + + + + + + + + + + + + + + + +