2
0
mirror of https://github.com/9ParsonsB/Pulsar.git synced 2025-07-01 08:23:42 -04:00

Add project files.

This commit is contained in:
Xjph
2021-06-03 22:25:32 -02:30
parent 7099cf23c6
commit a5154996ee
32 changed files with 2287 additions and 0 deletions

View File

@ -0,0 +1,13 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Observatory.UI"
x:Class="Observatory.UI.MainApplication">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme Mode="Dark"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
<StyleInclude Source="resm:Avalonia.Themes.Default.Accents.BaseDark.xaml?assembly=Avalonia.Themes.Default"/>
</Application.Styles>
</Application>

View File

@ -0,0 +1,29 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Observatory.UI.ViewModels;
namespace Observatory.UI
{
public class MainApplication : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var pluginManager = PluginManagement.PluginManager.GetInstance;
desktop.MainWindow = new Views.MainWindow()
{
DataContext = new MainWindowViewModel(pluginManager)
};
}
base.OnFrameworkInitializationCompleted();
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observatory.UI.Models
{
public class BasicUIModel
{
public string Time { get; set; }
public string Description { get; set; }
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Observatory.UI.ViewModels;
namespace Observatory.UI.Models
{
public class CoreModel
{
public string Name { get; set; }
public ViewModelBase UI { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observatory.UI.Models
{
public class NotificationModel
{
public string Title { get; set; }
public string Detail { get; set; }
}
}

View File

@ -0,0 +1,31 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Avalonia.Metadata;
using Observatory.UI.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observatory.UI
{
public class TabTemplateSelector : IDataTemplate
{
public bool SupportsRecycling => false;
[Content]
public Dictionary<string, IDataTemplate> Templates { get; } = new Dictionary<string, IDataTemplate>();
public IControl Build(object param)
{
return new BasicUIView(); //Templates[param].Build(param);
}
public bool Match(object data)
{
return data is BasicUIView;
}
}
}

View File

@ -0,0 +1,32 @@
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using Observatory.UI.ViewModels;
using System;
namespace Observatory.UI
{
public class ViewLocator : IDataTemplate
{
public bool SupportsRecycling => false;
public IControl Build(object data)
{
var name = data.GetType().FullName!.Replace("ViewModel", "View");
var type = Type.GetType(name);
if (type != null)
{
return (Control)Activator.CreateInstance(type)!;
}
else
{
return new TextBlock { Text = "Not Found: " + name };
}
}
public bool Match(object data)
{
return data is ViewModelBase;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
using Observatory.UI.Models;
using ReactiveUI;
using System.Reactive.Linq;
using Observatory.Framework;
namespace Observatory.UI.ViewModels
{
public class BasicUIViewModel : ViewModelBase
{
private ObservableCollection<object> basicUIGrid;
public ObservableCollection<object> BasicUIGrid
{
get => basicUIGrid;
set
{
basicUIGrid = value;
this.RaisePropertyChanged(nameof(BasicUIGrid));
}
}
public BasicUIViewModel(ObservableCollection<object> BasicUIGrid)
{
this.BasicUIGrid = new();
this.BasicUIGrid = BasicUIGrid;
//// Create a timer and set a two second interval.
//var aTimer = new System.Timers.Timer();
//aTimer.Interval = 2000;
//// Hook up the Elapsed event for the timer.
//aTimer.Elapsed += OnTimedEvent;
//// Have the timer fire repeated events (true is the default)
//aTimer.AutoReset = true;
//// Start the timer
//aTimer.Enabled = true;
}
private PluginUI.UIType uiType;
public PluginUI.UIType UIType
{
get => uiType;
set
{
uiType = value;
this.RaisePropertyChanged(nameof(UIType));
}
}
private void OnTimedEvent(object sender, System.Timers.ElapsedEventArgs e)
{
basicUIGrid.Count();
}
}
}

View File

@ -0,0 +1,110 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Linq;
using Avalonia.Controls;
using Observatory.Framework.Interfaces;
using Observatory.UI.Models;
using ReactiveUI;
namespace Observatory.UI.ViewModels
{
public class CoreViewModel : ViewModelBase
{
private readonly ObservableCollection<IObservatoryNotifier> notifiers;
private readonly ObservableCollection<IObservatoryWorker> workers;
private readonly ObservableCollection<CoreModel> tabs;
private string toggleButtonText;
public CoreViewModel(IEnumerable<(IObservatoryWorker plugin, PluginManagement.PluginManager.PluginStatus signed)> workers, IEnumerable<(IObservatoryNotifier plugin, PluginManagement.PluginManager.PluginStatus signed)> notifiers)
{
this.notifiers = new ObservableCollection<IObservatoryNotifier>(notifiers.Select(p => p.plugin));
this.workers = new ObservableCollection<IObservatoryWorker>(workers.Select(p => p.plugin));
ToggleButtonText = "Start Monitor";
tabs = new ObservableCollection<CoreModel>();
foreach(var worker in workers.Select(p => p.plugin))
{
if (worker.PluginUI.PluginUIType == Framework.PluginUI.UIType.Basic)
{
CoreModel coreModel = new();
coreModel.Name = worker.ShortName;
coreModel.UI = new();
var uiViewModel = new BasicUIViewModel(worker.PluginUI.DataGrid)
{
UIType = worker.PluginUI.PluginUIType
};
coreModel.UI = uiViewModel;
tabs.Add(coreModel);
}
}
foreach(var notifier in notifiers.Select(p => p.plugin))
{
Panel notifierPanel = new Panel();
TextBlock notifierTextBlock = new TextBlock();
notifierTextBlock.Text = notifier.Name;
notifierPanel.Children.Add(notifierTextBlock);
//tabs.Add(new CoreModel() { Name = notifier.ShortName, UI = (ViewModelBase)notifier.UI });
}
tabs.Add(new CoreModel() { Name = "Core", UI = new BasicUIViewModel(new ObservableCollection<object>()) { UIType = Framework.PluginUI.UIType.Core } });
}
public void ReadAll()
{
foreach (var worker in workers)
{
worker.ReadAllStarted();
}
LogMonitor.GetInstance.ReadAllJournals();
}
public void ToggleMonitor()
{
var logMonitor = LogMonitor.GetInstance;
if (logMonitor.IsMonitoring())
{
logMonitor.Stop();
ToggleButtonText = "Start Monitor";
}
else
{
logMonitor.Start();
ToggleButtonText = "Stop Monitor";
}
}
public string ToggleButtonText
{
get => toggleButtonText;
set
{
if (toggleButtonText != value)
{
toggleButtonText = value;
this.RaisePropertyChanged(nameof(ToggleButtonText));
}
}
}
public ObservableCollection<IObservatoryWorker> Workers
{
get { return workers; }
}
public ObservableCollection<IObservatoryNotifier> Notifiers
{
get { return notifiers; }
}
public ObservableCollection<CoreModel> Tabs
{
get { return tabs; }
}
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Observatory.UI.ViewModels
{
public class MainWindowViewModel : ViewModelBase
{
public MainWindowViewModel(PluginManagement.PluginManager pluginManager)
{
core = new CoreViewModel(pluginManager.workerPlugins, pluginManager.notifyPlugins);
}
public CoreViewModel core { get; }
}
}

View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Observatory.UI.ViewModels
{
public class NotificationViewModel : ViewModelBase
{
public NotificationViewModel(string title, string detail)
{
Notification = new() { Title = title, Detail = detail };
}
public Models.NotificationModel Notification;
}
}

View File

@ -0,0 +1,11 @@
using ReactiveUI;
using System;
using System.Collections.Generic;
using System.Text;
namespace Observatory.UI.ViewModels
{
public class ViewModelBase : ReactiveObject
{
}
}

View File

@ -0,0 +1,10 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Observatory.UI.Views.BasicUIView">
<Panel Name="UIPanel">
</Panel>
</UserControl>

View File

@ -0,0 +1,404 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Markup.Xaml;
using System.Text.RegularExpressions;
using Observatory.Framework;
using Observatory.Framework.Interfaces;
using System.Linq;
using Avalonia.Interactivity;
using Avalonia.VisualTree;
namespace Observatory.UI.Views
{
public class BasicUIView : UserControl
{
public BasicUIView()
{
Initialized += OnInitialized;
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public static readonly DirectProperty<BasicUIView, PluginUI.UIType> UITypeProperty =
AvaloniaProperty.RegisterDirect<BasicUIView, PluginUI.UIType>(
nameof(UIType),
o => o.UIType,
(o, v) => o.UIType = v,
PluginUI.UIType.None,
BindingMode.OneWay
);
public PluginUI.UIType UIType
{
get
{
return _uitype;
}
set
{
_uitype = value;
UITypeChange();
}
}
private PluginUI.UIType _uitype;
private void ColumnGeneration(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
e.Column.Header = SplitCamelCase(e.PropertyName);
e.Column.CanUserReorder = true;
e.Column.CanUserResize = true;
e.Column.CanUserSort = true;
}
private void OnInitialized(object sender, System.EventArgs e)
{
}
private void UITypeChange()
{
var uiPanel = this.Find<Panel>("UIPanel");
switch (UIType)
{
case PluginUI.UIType.None:
break;
case PluginUI.UIType.Basic:
DataGrid dataGrid = new()
{
[!DataGrid.ItemsProperty] = new Binding("BasicUIGrid"),
SelectionMode = DataGridSelectionMode.Extended,
GridLinesVisibility = DataGridGridLinesVisibility.Vertical,
AutoGenerateColumns = true
};
dataGrid.AutoGeneratingColumn += ColumnGeneration;
uiPanel.Children.Clear();
uiPanel.Children.Add(dataGrid);
break;
case PluginUI.UIType.Avalonia:
break;
case PluginUI.UIType.Core:
uiPanel.Children.Clear();
ScrollViewer scrollViewer = new();
scrollViewer.Content = GenerateCoreUI();
uiPanel.Children.Add(scrollViewer);
break;
default:
break;
}
}
private Grid GenerateCoreUI()
{
Grid corePanel = new();
ColumnDefinitions columns = new()
{
new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) },
new ColumnDefinition() { Width = new GridLength(300) },
new ColumnDefinition() { Width = new GridLength(0, GridUnitType.Auto) }
};
corePanel.ColumnDefinitions = columns;
RowDefinitions rows = new()
{
new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) },
new RowDefinition() { Height = new GridLength(0, GridUnitType.Auto) }
};
corePanel.RowDefinitions = rows;
var pluginManager = PluginManagement.PluginManager.GetInstance;
#region Journal Location
TextBlock journalPathLabel = new()
{
Text = "Journal Path: ",
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
};
TextBox journalPath = new()
{
Text = Properties.Core.Default.JournalFolder
};
Button journalBrowse = new()
{
Content = "Browse",
Height = 30,
Width = 100,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right,
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center
};
journalBrowse.Click += (object source, RoutedEventArgs e) =>
{
OpenFolderDialog openFolderDialog = new()
{
Directory = journalPath.Text
};
var browseTask = openFolderDialog.ShowAsync((Window)((Button)source).GetVisualRoot());
string path = browseTask.Result;
if (path != string.Empty)
{
journalPath.Text = path;
Properties.Core.Default.JournalFolder = path;
Properties.Core.Default.Save();
}
};
corePanel.AddControl(journalPathLabel, 1, 0);
corePanel.AddControl(journalPath, 1, 1);
corePanel.AddControl(journalBrowse, 1, 2);
#endregion
#region Plugin List
DataGrid pluginList = new() { Margin = new Thickness(0, 20) };
pluginList.Columns.Add(new DataGridTextColumn()
{
Header = "Plugin",
Binding = new Binding("Name")
});
pluginList.Columns.Add(new DataGridTextColumn()
{
Header = "Version",
Binding = new Binding("Version")
});
pluginList.Columns.Add(new DataGridTextColumn()
{
Header = "Status",
Binding = new Binding("Status")
});
System.Collections.Generic.List<PluginView> allPlugins = new();
foreach(var (plugin, signed) in pluginManager.workerPlugins)
{
allPlugins.Add(new PluginView() { Name = plugin.Name, Version = plugin.Version, Status = GetStatusText(signed) });
}
foreach (var (plugin, signed) in pluginManager.notifyPlugins)
{
allPlugins.Add(new PluginView() { Name = plugin.Name, Version = plugin.Version, Status = GetStatusText(signed) });
}
pluginList.Items = allPlugins;
corePanel.AddControl(pluginList, 0, 0, 2);
#endregion
#region Plugin Settings
foreach(var plugin in pluginManager.workerPlugins.Select(p => p.plugin))
{
GeneratePluginSettingUI(corePanel, plugin);
}
#endregion
return corePanel;
}
private void GeneratePluginSettingUI(Grid gridPanel, IObservatoryPlugin plugin)
{
//var plugin = pluginSettings.Key;
var displayedSettings = PluginManagement.PluginManager.GetSettingDisplayNames(plugin.Settings);
if (displayedSettings.Count > 0)
{
Expander expander = new()
{
Header = $"{plugin.Name} - {plugin.Version}",
DataContext = plugin.Settings,
Margin = new Thickness(0, 20)
};
Grid settingsGrid = new();
ColumnDefinitions settingColumns = new()
{
new ColumnDefinition() { Width = new GridLength(3, GridUnitType.Star) },
new ColumnDefinition() { Width = new GridLength(3, GridUnitType.Star) },
new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) }
};
settingsGrid.ColumnDefinitions = settingColumns;
expander.Content = settingsGrid;
int nextRow = gridPanel.RowDefinitions.Count;
gridPanel.RowDefinitions.Add(new RowDefinition());
gridPanel.AddControl(expander, nextRow, 0, 3);
foreach (var setting in displayedSettings.Where(s => !System.Attribute.IsDefined(s.Key, typeof(SettingIgnore))))
{
if (setting.Key.PropertyType != typeof(bool) || settingsGrid.Children.Count % 2 == 0)
{
settingsGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(21) });
}
TextBlock label = new() { Text = setting.Value };
switch (setting.Key.GetValue(plugin.Settings))
{
case bool boolSetting:
CheckBox checkBox = new() { IsChecked = boolSetting, Content = label };
checkBox.Checked += (object sender, RoutedEventArgs e) =>
{
setting.Key.SetValue(plugin.Settings, true);
PluginManagement.PluginManager.GetInstance.SaveSettings(plugin, plugin.Settings);
};
checkBox.Unchecked += (object sender, RoutedEventArgs e) =>
{
setting.Key.SetValue(plugin.Settings, false);
PluginManagement.PluginManager.GetInstance.SaveSettings(plugin, plugin.Settings);
};
//settingsGrid.Children.Add(checkBox);
settingsGrid.AddControl(checkBox, settingsGrid.RowDefinitions.Count - 1, settingsGrid.Children.Count % 2 == 0 ? 0 : 1);
break;
case string stringSetting:
TextBox textBox = new() { Text = stringSetting };
settingsGrid.Children.Add(label);
settingsGrid.Children.Add(textBox);
break;
case int intSetting:
NumericUpDown numericUpDown = new() { Text = intSetting.ToString(), AllowSpin = true };
settingsGrid.Children.Add(label);
settingsGrid.Children.Add(numericUpDown);
break;
case System.IO.FileInfo fileSetting:
label.Text += ": ";
TextBox settingPath = new()
{
Text = fileSetting.FullName,
Width = 250,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right
};
Button settingBrowse = new()
{
Content = "Browse",
Height = 30,
Width = 100,
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Left,
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center
};
settingBrowse.Click += (object source, RoutedEventArgs e) =>
{
OpenFileDialog openFileDialog = new()
{
Directory = fileSetting.DirectoryName,
AllowMultiple = false
};
var browseTask = openFileDialog.ShowAsync((Window)((Button)source).GetVisualRoot());
if (browseTask.Result.Count() > 0)
{
string path = browseTask.Result[0];
settingPath.Text = path;
setting.Key.SetValue(plugin.Settings, new System.IO.FileInfo(path));
PluginManagement.PluginManager.GetInstance.SaveSettings(plugin, plugin.Settings);
}
};
StackPanel stackPanel = new() { Orientation = Avalonia.Layout.Orientation.Horizontal };
stackPanel.Children.Add(label);
stackPanel.Children.Add(settingPath);
settingsGrid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(21) });
//settingsGrid.AddControl(label, settingsGrid.RowDefinitions.Count - 1, 0, 2);
settingsGrid.AddControl(stackPanel, settingsGrid.RowDefinitions.Count - 1, 0, 2);
settingsGrid.AddControl(settingBrowse, settingsGrid.RowDefinitions.Count - 1, 2);
break;
}
//wrapPanel.Children.Add(panel);
}
}
}
private string GetStatusText(PluginManagement.PluginManager.PluginStatus status)
{
string statusText;
switch (status)
{
case PluginManagement.PluginManager.PluginStatus.Signed:
statusText = "Signed";
break;
case PluginManagement.PluginManager.PluginStatus.Unsigned:
statusText = "Unsigned";
break;
case PluginManagement.PluginManager.PluginStatus.InvalidSignature:
statusText = "Signature Invalid";
break;
case PluginManagement.PluginManager.PluginStatus.InvalidPlugin:
statusText = "No Interface";
break;
case PluginManagement.PluginManager.PluginStatus.InvalidLibrary:
statusText = "Invalid Library";
break;
default:
statusText = "Unknown";
break;
}
return statusText;
}
//From https://stackoverflow.com/questions/5796383/insert-spaces-between-words-on-a-camel-cased-token
private static string SplitCamelCase(string str)
{
return Regex.Replace(
Regex.Replace(
str,
@"(\P{Ll})(\P{Ll}\p{Ll})",
"$1 $2"
),
@"(\p{Ll})(\P{Ll})",
"$1 $2"
);
}
}
internal class PluginView
{
public string Name { get; set; }
public string Version { get; set; }
public string Status { get; set; }
}
internal static class GridExtention
{
public static void AddControl(this Grid grid, Control control, int row, int column, int span = 1)
{
grid.Children.Add(control);
Grid.SetColumnSpan(control, span);
Grid.SetColumn(control, column);
Grid.SetRow(control, row);
}
}
}

View File

@ -0,0 +1,43 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vw="clr-namespace:Observatory.UI.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Observatory.UI.Views.CoreView">
<Grid RowDefinitions="30,*,Auto">
<TextBlock Grid.Row="0" VerticalAlignment="Center" Padding="10,0,0,0">
Elite Observatory - v1.0core
</TextBlock>
<TabControl Name="CoreTabs"
Grid.Row="1" Grid.Column="1"
VerticalAlignment="Stretch"
TabStripPlacement="Left"
Items="{Binding Tabs}"
BorderBrush="Black"
BorderThickness="1">
<TabControl.ItemTemplate>
<DataTemplate>
<TabItem>
<TabItem.Header>
<TextBlock Text="{Binding Name}"/>
</TabItem.Header>
</TabItem>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<vw:BasicUIView DataContext="{Binding UI}" UIType="{Binding UIType}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<WrapPanel Height="50" Grid.Row="2" Grid.Column="0" VerticalAlignment="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Name="ToggleMonitor" Margin="10" Command="{Binding ToggleMonitor}" Content="{Binding ToggleButtonText}">
Start Monitor
</Button>
<Button Name="ReadAll" Margin="10" Command="{Binding ReadAll}">
Read All
</Button>
</WrapPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,21 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System.Linq;
namespace Observatory.UI.Views
{
public class CoreView : UserControl
{
public CoreView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,11 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Observatory.UI.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Observatory.UI.Views.MainWindow"
Title="Elite Observatory"
ExtendClientAreaToDecorationsHint="True"
Content="{Binding core}">
</Window>

View File

@ -0,0 +1,26 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System.Collections.Generic;
using System.Data;
namespace Observatory.UI.Views
{
public class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

View File

@ -0,0 +1,13 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Observatory.UI.Views.NotificationView"
Title="Notification">
<StackPanel DataContext="{Binding Notification}">
<TextBlock Text="{Binding Title}" />
<TextBlock Text="{Binding Detail}" />
</StackPanel>
</Window>

View File

@ -0,0 +1,22 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace Observatory.UI.Views
{
public partial class NotificationView : Window
{
public NotificationView()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}