add Avalonia Dialogs

This commit is contained in:
2025-11-10 15:57:51 +01:00
parent 1ab775aa3e
commit c19c858de1
9 changed files with 431 additions and 1 deletions

View File

@@ -0,0 +1,47 @@
using Avalonia.Controls;
namespace Pilz.UI.AvaloniaUI.Dialogs;
public partial class AvaloniaDialogBase
{
public static T Show<T>(string? title, object? icon, object? tag = null) where T : AvaloniaFlyoutBase
{
return Show(CreatePanelInstance<T>(tag), title, icon);
}
public static Task<T> ShowDialog<T>(Window parent, string? title, object? icon, object? tag = null) where T : AvaloniaFlyoutBase
{
return ShowDialog(CreatePanelInstance<T>(tag), parent, title, icon);
}
public static T Show<T>(T dialogPanel, string? title, object? icon) where T : AvaloniaFlyoutBase
{
CreateForm(dialogPanel, title, icon, WindowStartupLocation.CenterScreen).Show();
return dialogPanel;
}
public static async Task<T> ShowDialog<T>(T dialogPanel, Window parent, string? title, object? icon) where T : AvaloniaFlyoutBase
{
await CreateForm(dialogPanel, title, icon, WindowStartupLocation.CenterOwner).ShowDialog(parent);
return dialogPanel;
}
private static T CreatePanelInstance<T>(object? tag) where T : AvaloniaFlyoutBase
{
var dialogPanel = Activator.CreateInstance<T>();
dialogPanel.Tag = tag;
return dialogPanel;
}
private static AvaloniaDialogBase CreateForm<T>(T dialogPanel, string? title, object? icon, WindowStartupLocation startPosition) where T : AvaloniaFlyoutBase
{
var dialog = new AvaloniaDialogBase
{
Title = title,
Icon = icon as WindowIcon,
WindowStartupLocation = startPosition,
};
dialog.SetContent(dialogPanel);
return dialog;
}
}

View File

@@ -0,0 +1,8 @@
<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="Pilz.UI.AvaloniaUI.Dialogs.AvaloniaDialogBase"
SizeToContent="WidthAndHeight">
</Window>

View File

@@ -0,0 +1,27 @@
using Avalonia.Controls;
namespace Pilz.UI.AvaloniaUI.Dialogs;
public partial class AvaloniaDialogBase : Window
{
protected AvaloniaFlyoutBase? dialogPanel;
public AvaloniaFlyoutBase DialogPanel => dialogPanel ?? throw new NullReferenceException();
public AvaloniaDialogBase()
{
InitializeComponent();
}
public void SetContent(AvaloniaFlyoutBase content)
{
dialogPanel = content;
dialogPanel.OnClose += DialogPanelOnOnClose;
Content = content;
}
private void DialogPanelOnOnClose(object? sender, EventArgs e)
{
Close(DialogPanel.Result);
}
}

View File

@@ -0,0 +1,33 @@
using Avalonia.Controls;
namespace Pilz.UI.AvaloniaUI.Dialogs;
public partial class AvaloniaFlyoutBase
{
// public static void Show<T>(Control controlToAssociate, object? tag = null)
// {
// Show<T>(controlToAssociate, null, null, tag: tag);
// }
//
// public static void Show<T>(Control controlToAssociate, string? title, RadSvgImage? icon, object? tag = null)
// {
// Show(controlToAssociate, typeof(T), title, icon, tag: tag);
// }
//
// public static void Show(Control controlToAssociate, Type flyoutContentType, string? title, RadSvgImage? icon, object? tag = null)
// {
// tagToAssign = tag;
// titleToAssing = title;
// iconToAssign = icon;
// ParentContext = controlToAssociate;
// CloseFlyout(); // Ensure it's closed!
// RadFlyoutManager.Show(controlToAssociate, flyoutContentType);
// }
//
// protected static void CloseFlyout()
// {
// if (typeof(RadFlyoutManager).GetField("flyoutInstance", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null) is FlyoutScreen instance
// && instance.IsActive)
// RadFlyoutManager.Close();
// }
}

View File

@@ -0,0 +1,78 @@
<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:controls="https://git.pilzinsel64.de/pilz-framework/pilz"
xmlns:dialogs="clr-namespace:Pilz.UI.AvaloniaUI.Dialogs"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Pilz.UI.AvaloniaUI.Dialogs.AvaloniaFlyoutBase">
<StackPanel Orientation="Vertical">
<!-- StackPanel: Header -->
<StackPanel
x:Name="StackPanelHeader"
Orientation="Horizontal"
HorizontalAlignment="Stretch"
Margin="3"
IsVisible="False"
>
<Image
x:Name="ImageTitle"
HorizontalAlignment="Left"
VerticalAlignment="Center"
/>
<TextBlock
HorizontalAlignment="Left"
VerticalAlignment="Center"
x:Name="LabelTitle"
/>
</StackPanel>
<!-- Panel: Content -->
<ContentPresenter
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="3"
Content="{Binding MainContent, RelativeSource={RelativeSource AncestorType=dialogs:AvaloniaFlyoutBase}}"
/>
<!-- StackPanel: Footer -->
<Grid
x:Name="StackPanelFooter"
RowDefinitions="Auto"
ColumnDefinitions="*,Auto,Auto"
HorizontalAlignment="Stretch"
>
<ContentPresenter
Grid.Column="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Margin="3"
Content="{Binding FooterContent, RelativeSource={RelativeSource AncestorType=dialogs:AvaloniaFlyoutBase}}"
/>
<!-- Button: Okay -->
<controls:ImageButton
Grid.Column="1"
x:Name="ButtonOkay"
HorizontalAlignment="Right"
Margin="3"
Text="Okay"
Click="ButtonOkay_OnClick"
/>
<!-- Button: Cancel -->
<controls:ImageButton
Grid.Column="2"
x:Name="ButtonCancel"
HorizontalAlignment="Right"
Margin="3"
Text="Cancel"
Click="ButtonCancel_OnClick"
/>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,197 @@
using System.ComponentModel;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Media;
namespace Pilz.UI.AvaloniaUI.Dialogs;
public partial class AvaloniaFlyoutBase : UserControl
{
protected readonly BackgroundWorker bgWorker_LoadData = new();
public delegate void OnCloseEventHandler(object? sender, EventArgs e);
public event OnCloseEventHandler? OnClose;
public static IImage? CancelImage { get; set; } = null;
public static IImage? ConfirmImage { get; set; } = null;
public static readonly StyledProperty<object?> MainContentProperty = AvaloniaProperty.Register<AvaloniaFlyoutBase, object?>(nameof(MainContent));
public static readonly StyledProperty<object?> FooterContentProperty = AvaloniaProperty.Register<AvaloniaFlyoutBase, object?>(nameof(FooterContent));
[Browsable(false)]
public object? Result { get; protected set; }
[DefaultValue(true)]
public bool RegisterDialogAccept { get; set; } = true;
[DefaultValue(true)]
public bool RegisterDialogCancel { get; set; } = true;
protected AvaloniaFlyoutBase()
{
InitializeComponent();
CancelButtonImage = CancelImage;
ConfirmButtonImage = ConfirmImage;
bgWorker_LoadData.DoWork += BgWorker_LoadData_DoWork;
bgWorker_LoadData.RunWorkerCompleted += BgWorker_LoadData_RunWorkerCompleted;
}
public object? MainContent
{
get => GetValue(MainContentProperty);
set => SetValue(MainContentProperty, value);
}
public object? FooterContent
{
get => GetValue(FooterContentProperty);
set => SetValue(FooterContentProperty, value);
}
[DefaultValue(true)]
public virtual bool ActionPanelVisible
{
get => StackPanelFooter.IsVisible;
set => StackPanelFooter.IsVisible = value;
}
[DefaultValue(true)]
public virtual bool CancelButtonVisible
{
get => ButtonCancel.IsVisible;
set => ButtonCancel.IsVisible = value;
}
[DefaultValue(true)]
public virtual bool ConfirmButtonVisible
{
get => ButtonOkay.IsVisible;
set => ButtonOkay.IsVisible = value;
}
[DefaultValue(true)]
public virtual bool CancelButtonEnable
{
get => ButtonCancel.IsEnabled;
set => ButtonCancel.IsEnabled = value;
}
[DefaultValue(true)]
public virtual bool ConfirmButtonEnable
{
get => ButtonOkay.IsEnabled;
set => ButtonOkay.IsEnabled = value;
}
[DefaultValue(null)]
public virtual IImage? CancelButtonImage
{
get => ButtonCancel.ImageSource;
set => ButtonCancel.ImageSource = value;
}
[DefaultValue(null)]
public virtual IImage? ConfirmButtonImage
{
get => ButtonOkay.ImageSource;
set => ButtonOkay.ImageSource = value;
}
[Localizable(true)]
[DefaultValue("Okay")]
public virtual string? ConfirmButtonText
{
get => ButtonOkay.Text;
set => ButtonOkay.Text = value;
}
[Localizable(true)]
[DefaultValue("Cancel")]
public virtual string? CancelButtonText
{
get => ButtonCancel.Text;
set => ButtonCancel.Text = value;
}
[Localizable(true)]
[DefaultValue("")]
public virtual string? Title
{
get => LabelTitle.Text;
set
{
LabelTitle.Text = value;
SetShowTitlePanel();
}
}
[DefaultValue(null)]
public virtual IImage? TitleIcon
{
get => ImageTitle.Source;
set
{
ImageTitle.Source = value;
SetShowTitlePanel();
}
}
protected virtual void BgWorker_LoadData_DoWork(object? sender, DoWorkEventArgs e)
{
OnLoadData(e);
}
protected virtual void BgWorker_LoadData_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
{
OnLoadDataCompleted(e);
}
protected virtual void LoadData()
{
bgWorker_LoadData.RunWorkerAsync();
}
protected virtual void LoadData(object? argument)
{
bgWorker_LoadData.RunWorkerAsync(argument);
}
protected virtual void OnLoadData(DoWorkEventArgs e)
{
}
protected virtual void OnLoadDataCompleted(RunWorkerCompletedEventArgs e)
{
}
protected void Close(object? result)
{
Result = result;
OnClose?.Invoke(this, EventArgs.Empty);
}
protected virtual bool ValidateOK()
{
return true;
}
protected virtual void SetShowTitlePanel()
{
StackPanelHeader.IsVisible = !string.IsNullOrWhiteSpace(Title) || TitleIcon != null;
}
protected virtual void ButtonOkay_OnClick(object? sender, RoutedEventArgs e)
{
if (ValidateOK())
Close(true);
}
protected virtual void ButtonCancel_OnClick(object? sender, RoutedEventArgs e)
{
Close(null);
}
}

27
Pilz/EnvironmentEx.cs Normal file
View File

@@ -0,0 +1,27 @@
using System.Diagnostics;
using Pilz.Runtime;
namespace Pilz;
public static class EnvironmentEx
{
private static string? processPath = null;
public static string? ProcessPath => processPath ??= GetProcessPath();
private static string? GetProcessPath()
{
if (RuntimeInformationsEx.IsOSPlatform(OSType.Linux, false)
&& Environment.GetEnvironmentVariable("APPIMAGE") is { } appImagePath
&& !string.IsNullOrWhiteSpace(appImagePath))
return appImagePath;
#if NET8_0_OR_GREATER
if (Environment.ProcessPath is { } processPath
&& !string.IsNullOrWhiteSpace(processPath))
return processPath;
#endif
return Process.GetCurrentProcess().MainModule?.FileName;
}
}

View File

@@ -5,7 +5,7 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>annotations</Nullable> <Nullable>annotations</Nullable>
<Version>2.5.3</Version> <Version>2.6.1</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'"> <PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">

View File

@@ -76,4 +76,17 @@ public static class RuntimeInformationsEx
{ {
return (checkRealOS ? RealOSType : OSType) == os; return (checkRealOS ? RealOSType : OSType) == os;
} }
public static string GetRuntimeIdentifier()
{
var arch = RuntimeInformation.OSArchitecture.ToString().ToLowerInvariant();
var os = OSType switch
{
OSType.Windows => "win",
OSType.Linux => "linux",
OSType.OSX => "osx",
_ => throw new NotSupportedException(),
};
return $"{os}-{arch}";
}
} }