Browse Source

Added dialogs support

Kenric Nugteren 5 months ago
parent
commit
4e6401e4c5

+ 2 - 0
PRS.Avalonia/Directory.Packages.props

@@ -14,7 +14,9 @@
     <PackageVersion Include="Avalonia.Android" Version="11.2.3" />
     <PackageVersion Include="Avalonia.Themes.Fluent" Version="11.2.3" />
     <PackageVersion Include="Avalonia.Diagnostics" Version="11.2.3" />
+    <PackageVersion Include="AvaloniaDialogs" Version="3.6.1" />
     <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
+    <PackageVersion Include="DialogHost.Avalonia" Version="0.9.2" />
     <PackageVersion Include="Material.Avalonia" Version="3.9.2" />
     <PackageVersion Include="Material.Avalonia.DataGrid" Version="3.9.2" />
     <PackageVersion Include="Material.Avalonia.Dialogs" Version="3.9.2" />

+ 3 - 0
PRS.Avalonia/PRS.Avalonia/App.axaml

@@ -1,6 +1,7 @@
 <Application xmlns="https://github.com/avaloniaui"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:local="using:PRS.Avalonia"
+			 xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
              x:Class="PRS.Avalonia.App"
              RequestedThemeVariant="Default">
     <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
@@ -20,6 +21,8 @@
         <StyleInclude Source="avares://InABox.Avalonia/Theme/Classes/Separator.axaml" />
         <StyleInclude Source="avares://InABox.Avalonia/Theme/Classes/ListBox.axaml" />
 
+		<dialogHostAvalonia:DialogHostStyles/>
+
     </Application.Styles>
 
     <Application.Resources>

+ 37 - 0
PRS.Avalonia/PRS.Avalonia/Dialogs/MessageDialog.cs

@@ -0,0 +1,37 @@
+using InABox.Avalonia;
+using PRS.Avalonia.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PRS.Avalonia;
+
+public enum MessageDialogButtonPosition
+{
+    Left,
+    Right
+}
+
+public enum MessageDialogResult
+{
+    None,
+    OK,
+    Cancel,
+    Yes,
+    No,
+    Other
+}
+
+public static class MessageDialog
+{
+    public static async Task ShowMessage(string message/*, string title, ImageSource? image = null*/)
+    {
+        await MessageDialogViewModel.ShowMessage(message);
+        // var result = await Navigation.Popup<MessageDialogViewModel, MessageDialogResult>(model =>
+        // {
+        //     model.Message = message;
+        // });
+    }
+}

+ 53 - 0
PRS.Avalonia/PRS.Avalonia/Dialogs/MessageDialogView.axaml

@@ -0,0 +1,53 @@
+<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:dialogs="clr-namespace:PRS.Avalonia.Dialogs"
+             mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
+             x:Class="PRS.Avalonia.Dialogs.MessageDialogView"
+			 x:DataType="dialogs:MessageDialogViewModel"
+			 MinWidth="150">
+	<UserControl.Resources>
+		<DataTemplate x:Key="ButtonTemplate" DataType="dialogs:MessageDialogButton">
+			<Button Content="{Binding Content}"
+					Command="{Binding $parent[ItemsControl].((dialogs:MessageDialogViewModel)DataContext).ButtonClickCommand}"
+					CommandParameter="{Binding}"/>
+		</DataTemplate>
+	</UserControl.Resources>
+	<Grid>
+		<Grid.RowDefinitions>
+			<RowDefinition Height="*" MinHeight="100"/>
+			<RowDefinition Height="Auto"/>
+		</Grid.RowDefinitions>
+		<Grid.ColumnDefinitions>
+			<ColumnDefinition Width="*"/>
+		</Grid.ColumnDefinitions>
+		<TextBlock Text="{Binding Message}" Grid.Row="0"
+				   VerticalAlignment="Center" TextWrapping="Wrap" Margin="5"/>
+		<Grid Grid.Row="1">
+			<Grid.ColumnDefinitions>
+				<ColumnDefinition Width="Auto"/>
+				<ColumnDefinition Width="*"/>
+				<ColumnDefinition Width="Auto"/>
+			</Grid.ColumnDefinitions>
+			<ItemsControl ItemsSource="{Binding LeftButtons}"
+						  ItemTemplate="{StaticResource ButtonTemplate}"
+						  Grid.Column="0">
+				<ItemsControl.ItemsPanel>
+					<ItemsPanelTemplate>
+						<StackPanel Orientation="Horizontal"/>
+					</ItemsPanelTemplate>
+				</ItemsControl.ItemsPanel>
+			</ItemsControl>
+			<ItemsControl ItemsSource="{Binding RightButtons}"
+						  ItemTemplate="{StaticResource ButtonTemplate}"
+						  Grid.Column="2">
+				<ItemsControl.ItemsPanel>
+					<ItemsPanelTemplate>
+						<StackPanel Orientation="Horizontal"/>
+					</ItemsPanelTemplate>
+				</ItemsControl.ItemsPanel>
+			</ItemsControl>
+		</Grid>
+	</Grid>
+</UserControl>

+ 13 - 0
PRS.Avalonia/PRS.Avalonia/Dialogs/MessageDialogView.axaml.cs

@@ -0,0 +1,13 @@
+using Avalonia;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+
+namespace PRS.Avalonia.Dialogs;
+
+public partial class MessageDialogView : UserControl
+{
+    public MessageDialogView()
+    {
+        InitializeComponent();
+    }
+}

+ 172 - 0
PRS.Avalonia/PRS.Avalonia/Dialogs/MessageDialogViewModel.cs

@@ -0,0 +1,172 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using InABox.Avalonia;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace PRS.Avalonia.Dialogs;
+
+public partial class MessageDialogViewModel : PopupViewModel<MessageDialogResult>
+{
+    [ObservableProperty]
+    private string _message;
+
+    public ObservableCollection<MessageDialogButton> Buttons { get; private set; } = new();
+
+    public IEnumerable<MessageDialogButton> LeftButtons => Buttons.Where(x => x.Position == MessageDialogButtonPosition.Left);
+    public IEnumerable<MessageDialogButton> RightButtons => Buttons.Where(x => x.Position == MessageDialogButtonPosition.Right);
+
+    public MessageDialogViewModel()
+    {
+        Message = "";
+    }
+
+    [RelayCommand]
+    private void ButtonClick(MessageDialogButton button)
+    {
+        button.Action(this, button);
+    }
+
+    public MessageDialogViewModel AddButton(MessageDialogButton button)
+    {
+        Buttons.Add(button);
+        return this;
+    }
+
+    public MessageDialogViewModel AddOKButton(string content = "OK")
+    {
+        Buttons.Add(new MessageDialogButton(content, OKButton_Click, MessageDialogButtonPosition.Right));
+        return this;
+    }
+
+    public MessageDialogViewModel AddCancelButton(string content = "Cancel")
+    {
+        Buttons.Add(new MessageDialogButton(content, CancelButton_Click, MessageDialogButtonPosition.Right));
+        return this;
+    }
+
+    public MessageDialogViewModel AddYesButton(string content = "Yes")
+    {
+        Buttons.Add(new MessageDialogButton(content, YesButton_Click, MessageDialogButtonPosition.Right));
+        return this;
+    }
+
+    public MessageDialogViewModel AddNoButton(string content = "No")
+    {
+        Buttons.Add(new MessageDialogButton(content, NoButton_Click, MessageDialogButtonPosition.Right));
+        return this;
+    }
+
+    private void YesButton_Click(MessageDialogViewModel window, MessageDialogButton button)
+    {
+        window.Close(MessageDialogResult.Yes);
+    }
+
+    private void NoButton_Click(MessageDialogViewModel window, MessageDialogButton button)
+    {
+        window.Close(MessageDialogResult.No);
+    }
+
+    private void CancelButton_Click(MessageDialogViewModel window, MessageDialogButton button)
+    {
+        window.Close(MessageDialogResult.Cancel);
+    }
+
+    private void OKButton_Click(MessageDialogViewModel window, MessageDialogButton button)
+    {
+        window.Close(MessageDialogResult.OK);
+    }
+
+    #region Static Constructors
+
+    public static MessageDialogViewModel New()
+    {
+        return new MessageDialogViewModel();
+    }
+
+    public static MessageDialogViewModel NewMessage(string message)
+    {
+        return new MessageDialogViewModel()
+            .Message(message)
+            .AddOKButton();
+    }
+    public static async Task ShowMessage(string message)
+    {
+        await NewMessage(message).Display();
+    }
+
+    public static MessageDialogViewModel NewOKCancel(string message)
+    {
+        return new MessageDialogViewModel()
+            .Message(message)
+            .AddOKButton()
+            .AddCancelButton();
+    }
+    public static async Task<bool> ShowOKCancel(string message)
+    {
+        return await NewOKCancel(message).Display() == MessageDialogResult.OK;
+    }
+
+    public static MessageDialogViewModel NewYesNo(string message)
+    {
+        return new MessageDialogViewModel()
+            .Message(message)
+            .AddYesButton()
+            .AddNoButton();
+    }
+    public static async Task<bool> ShowYesNo(string message)
+    {
+        return await NewYesNo(message)
+            .Display() == MessageDialogResult.Yes;
+    }
+    public static MessageDialogViewModel NewYesNoCancel(string message)
+    {
+        return new MessageDialogViewModel()
+            .Message(message)
+            .AddYesButton()
+            .AddNoButton()
+            .AddCancelButton();
+    }
+    public static async Task<MessageDialogResult> ShowYesNoCancel(string message)
+    {
+        return await NewYesNoCancel(message).Display();
+    }
+
+    #endregion
+}
+
+public partial class MessageDialogButton : ObservableObject
+{
+    public delegate void MessageDialogButtonDelegate(MessageDialogViewModel window, MessageDialogButton button);
+
+    public MessageDialogButtonPosition Position { get; set; }
+
+    [ObservableProperty]
+    private string _content;
+
+    public MessageDialogButtonDelegate Action { get; set; }
+
+    public MessageDialogButton(string content, MessageDialogButtonDelegate action, MessageDialogButtonPosition position)
+    {
+        Content = content;
+        Action = action;
+        Position = position;
+    }
+}
+public static class MessageDialogBuilder
+{
+    public static MessageDialogViewModel Message(this MessageDialogViewModel window, string message)
+    {
+        window.Message = message;
+        return window;
+    }
+
+    public static async Task<MessageDialogResult> Display(this MessageDialogViewModel window)
+    {
+        return await Navigation.Popup<MessageDialogViewModel, MessageDialogResult>(window, false);
+    }
+}

+ 75 - 71
PRS.Avalonia/PRS.Avalonia/MainView.axaml

@@ -3,91 +3,95 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:avalonia="clr-namespace:PRS.Avalonia"
+			 xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
              xmlns:components="clr-namespace:InABox.Avalonia.Components;assembly=InABox.Avalonia"
              mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
              x:Class="PRS.Avalonia.Modules.MainView"
              x:DataType="avalonia:MainViewModel">
 
-    <Grid Background="{StaticResource PrsSurfaceBackground}">
-        <Grid.RowDefinitions>
-            <RowDefinition Height="45" />
-            <RowDefinition Height="*" />
-        </Grid.RowDefinitions>
+	<dialogHostAvalonia:DialogHost CloseOnClickAway="True">
+		<Grid Background="{StaticResource PrsSurfaceBackground}">
+			<Grid.RowDefinitions>
+				<RowDefinition Height="45" />
+				<RowDefinition Height="*" />
+			</Grid.RowDefinitions>
 
-        <Grid
-            Grid.Row="0"
-            Grid.Column="0"
-            Background="{StaticResource PrsMenuBackground}">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="*" />
-            </Grid.RowDefinitions>
+			<Grid
+				Grid.Row="0"
+				Grid.Column="0"
+				Background="{StaticResource PrsMenuBackground}">
+				<Grid.RowDefinitions>
+					<RowDefinition Height="*" />
+				</Grid.RowDefinitions>
 
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="Auto" />
-                <ColumnDefinition Width="Auto" />
-                <ColumnDefinition Width="*" />
-                <ColumnDefinition Width="Auto" />
-                <ColumnDefinition Width="Auto" />
-            </Grid.ColumnDefinitions>
+				<Grid.ColumnDefinitions>
+					<ColumnDefinition Width="Auto" />
+					<ColumnDefinition Width="Auto" />
+					<ColumnDefinition Width="*" />
+					<ColumnDefinition Width="Auto" />
+					<ColumnDefinition Width="Auto" />
+				</Grid.ColumnDefinitions>
 
-            <Button
-                Grid.Row="0"
-                Grid.Column="0"
-                Margin="5,0,0,0"
-                Classes="Transparent"
-                HorizontalAlignment="Center"
-                VerticalAlignment="Center"
-                IsVisible="{Binding BackButtonVisible}"
-                Command="{Binding BackButtonPressedCommand}">
-                <Image>
-                    <Image.Source>
-                        <SvgImage Source="../Images/arrow_white_left.svg" />
-                    </Image.Source>
-                </Image>
-            </Button>
+				<Button
+					Grid.Row="0"
+					Grid.Column="0"
+					Margin="5,0,0,0"
+					Classes="Transparent"
+					HorizontalAlignment="Center"
+					VerticalAlignment="Center"
+					IsVisible="{Binding BackButtonVisible}"
+					Command="{Binding BackButtonPressedCommand}">
+					<Image>
+						<Image.Source>
+							<SvgImage Source="../Images/arrow_white_left.svg" />
+						</Image.Source>
+					</Image>
+				</Button>
 
-            <components:AvaloniaMenuPanel
-                Grid.Row="0"
-                Grid.Column="1"
-                Items="{Binding SecondaryMenu}" />
+				<components:AvaloniaMenuPanel
+					Grid.Row="0"
+					Grid.Column="1"
+					Items="{Binding SecondaryMenu}" />
 
-            <Label
-                Grid.Row="0"
-                Grid.Column="2"
-                Content="{Binding Title}"
-                VerticalContentAlignment="Center"
-                Margin="5,0,0,0"
-                FontSize="{StaticResource PrsFontSizeLarge}"
-                FontWeight="{StaticResource PrsFontWeightBold}"
-                Foreground="{StaticResource PrsMainMenuForeground}" />
+				<Label
+					Grid.Row="0"
+					Grid.Column="2"
+					Content="{Binding Title}"
+					VerticalContentAlignment="Center"
+					Margin="5,0,0,0"
+					FontSize="{StaticResource PrsFontSizeLarge}"
+					FontWeight="{StaticResource PrsFontWeightBold}"
+					Foreground="{StaticResource PrsMainMenuForeground}" />
 
-            <ItemsControl
-                x:Name="NotificationsPanel"
-                Grid.Row="0"
-                Grid.Column="3"
-                Classes="MenuPanel" />
+				<ItemsControl
+					x:Name="NotificationsPanel"
+					Grid.Row="0"
+					Grid.Column="3"
+					Classes="MenuPanel" />
 
-            <components:AvaloniaMenuPanel
-                Grid.Row="0"
-                Grid.Column="4"
-                Margin="0,0,5,0"
-                Items="{Binding PrimaryMenu}" />
+				<components:AvaloniaMenuPanel
+					Grid.Row="0"
+					Grid.Column="4"
+					Margin="0,0,5,0"
+					Items="{Binding PrimaryMenu}" />
 
-        </Grid>
+			</Grid>
 
-        <TransitioningContentControl
-            Grid.Row="1"
-            Grid.Column="0"
-            Margin="{StaticResource PrsControlSpacing}"
-            Content="{Binding Content}"
-            IsTransitionReversed="{Binding ReverseTransition}"
-            TransitionCompleted="TransitioningContentControl_OnTransitionCompleted">
-            <TransitioningContentControl.PageTransition>
-                <!-- <CrossFade Duration="0:00:00.50"/> -->
-                <PageSlide Orientation="Horizontal" Duration="0:00:00.500" />
-            </TransitioningContentControl.PageTransition>
-        </TransitioningContentControl>
+			<TransitioningContentControl
+				Grid.Row="1"
+				Grid.Column="0"
+				Margin="{StaticResource PrsControlSpacing}"
+				Content="{Binding Content}"
+				IsTransitionReversed="{Binding ReverseTransition}"
+				TransitionCompleted="TransitioningContentControl_OnTransitionCompleted">
+				<TransitioningContentControl.PageTransition>
+					<!-- <CrossFade Duration="0:00:00.50"/> -->
+					<PageSlide Orientation="Horizontal" Duration="0:00:00.500" />
+				</TransitioningContentControl.PageTransition>
+			</TransitioningContentControl>
+
+		</Grid>
+	</dialogHostAvalonia:DialogHost>
 
-    </Grid>
 
 </UserControl>

+ 2 - 0
PRS.Avalonia/PRS.Avalonia/PRS.Avalonia.csproj

@@ -302,9 +302,11 @@
         <PackageReference Include="Avalonia.Themes.Fluent" />
         <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
         <PackageReference Include="CommunityToolkit.Mvvm" />
+        <PackageReference Include="DialogHost.Avalonia" />
         <PackageReference Include="Material.Avalonia" />
         <PackageReference Include="Material.Avalonia.DataGrid" />
         <PackageReference Include="Material.Avalonia.Dialogs" />
+        <PackageReference Include="SkiaSharp" />
         <PackageReference Include="SkiaSharp.Views" />
     </ItemGroup>
 

+ 29 - 0
PRS.Avalonia/PRS.Avalonia/ViewModelBase.cs

@@ -5,8 +5,10 @@ using System.Reflection;
 using System.Threading;
 using System.Threading.Tasks;
 using Avalonia.Svg.Skia;
+using Comal.Classes;
 using CommunityToolkit.Mvvm.ComponentModel;
 using CommunityToolkit.Mvvm.Input;
+using DialogHostAvalonia;
 using InABox.Avalonia;
 using InABox.Avalonia.Components;
 using InABox.Configuration;
@@ -213,4 +215,31 @@ public abstract partial class ViewModelBase : ObservableObject, IViewModelBase
     }
     
     
+}
+
+public abstract class PopupViewModel : ViewModelBase, IPopupViewModel
+{
+    public bool IsClosed { get; private set; }
+
+    public void Close()
+    {
+        IsClosed = true;
+        DialogHost.GetDialogSession(null)?.Close();
+    }
+}
+
+public abstract class PopupViewModel<TResult> : PopupViewModel, IPopupViewModel<TResult>
+{
+    private TResult? _result;
+
+    public TResult? GetResult()
+    {
+        return _result ?? default;
+    }
+
+    public void Close(TResult result)
+    {
+        _result = result;
+        Close();
+    }
 }