Add DialogsPage and MoreControlsPage to ShellDemo

DialogsPage demonstrates:
- Alert dialogs (simple, confirmation)
- Action sheets (with destructive option)
- Input prompts (text, numeric)
- File pickers (single, multiple, images)
- Folder picker

MoreControlsPage demonstrates:
- Stepper (basic and custom range)
- RadioButton (vertical and horizontal groups)
- Image placeholders with aspect modes
- Clipboard (copy/paste)
- Share and Launcher services
- BoxView shapes and dividers

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-01 20:05:50 -05:00
parent 01270c6938
commit bc80436a34
5 changed files with 638 additions and 0 deletions

View File

@@ -41,6 +41,14 @@
<ShellContent ContentTemplate="{DataTemplate pages:GridsPage}" />
</FlyoutItem>
<FlyoutItem Title="Dialogs" Route="Dialogs">
<ShellContent ContentTemplate="{DataTemplate pages:DialogsPage}" />
</FlyoutItem>
<FlyoutItem Title="More Controls" Route="MoreControls">
<ShellContent ContentTemplate="{DataTemplate pages:MoreControlsPage}" />
</FlyoutItem>
<FlyoutItem Title="About" Route="About">
<ShellContent ContentTemplate="{DataTemplate pages:AboutPage}" />
</FlyoutItem>

View File

@@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ShellDemo.Pages.DialogsPage"
Title="Dialogs"
BackgroundColor="{StaticResource PageBackground}">
<Grid RowDefinitions="*,Auto">
<ScrollView Grid.Row="0">
<VerticalStackLayout Padding="20" Spacing="20">
<Label Text="Dialogs &amp; File Pickers" FontSize="24" FontAttributes="Bold" />
<!-- Alert Dialogs -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="12">
<Label Text="Alert Dialogs" FontSize="16" FontAttributes="Bold" />
<Button Text="Simple Alert"
BackgroundColor="{StaticResource PrimaryColor}"
TextColor="White"
Clicked="OnSimpleAlertClicked" />
<Button Text="Confirmation (Yes/No)"
BackgroundColor="{StaticResource WarningColor}"
TextColor="White"
Clicked="OnConfirmationClicked" />
<Label x:Name="AlertResultLabel" Text="Result: (none)" TextColor="{StaticResource TextSecondary}" FontSize="12" />
</VerticalStackLayout>
</Border>
<!-- Action Sheet -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="12">
<Label Text="Action Sheet" FontSize="16" FontAttributes="Bold" />
<Button Text="Show Action Sheet"
BackgroundColor="{StaticResource PurpleColor}"
TextColor="White"
Clicked="OnActionSheetClicked" />
<Button Text="With Destructive Option"
BackgroundColor="{StaticResource DangerColor}"
TextColor="White"
Clicked="OnDestructiveActionSheetClicked" />
<Label x:Name="ActionResultLabel" Text="Selection: (none)" TextColor="{StaticResource TextSecondary}" FontSize="12" />
</VerticalStackLayout>
</Border>
<!-- Prompt Dialog -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="12">
<Label Text="Input Prompts" FontSize="16" FontAttributes="Bold" />
<Button Text="Text Input Prompt"
BackgroundColor="{StaticResource SuccessColor}"
TextColor="White"
Clicked="OnTextPromptClicked" />
<Button Text="Numeric Prompt"
BackgroundColor="{StaticResource SuccessColor}"
TextColor="White"
Clicked="OnNumericPromptClicked" />
<Label x:Name="PromptResultLabel" Text="Input: (none)" TextColor="{StaticResource TextSecondary}" FontSize="12" />
</VerticalStackLayout>
</Border>
<!-- File Pickers -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="12">
<Label Text="File Pickers" FontSize="16" FontAttributes="Bold" />
<Button Text="Pick a File"
BackgroundColor="#795548"
TextColor="White"
Clicked="OnPickFileClicked" />
<Button Text="Pick Multiple Files"
BackgroundColor="#795548"
TextColor="White"
Clicked="OnPickMultipleFilesClicked" />
<Button Text="Pick Image"
BackgroundColor="#E91E63"
TextColor="White"
Clicked="OnPickImageClicked" />
<Button Text="Pick Folder"
BackgroundColor="#607D8B"
TextColor="White"
Clicked="OnPickFolderClicked" />
<Label x:Name="FileResultLabel" Text="Selected: (none)" TextColor="{StaticResource TextSecondary}" FontSize="12" LineBreakMode="TailTruncation" />
</VerticalStackLayout>
</Border>
</VerticalStackLayout>
</ScrollView>
<!-- Event Log -->
<Border Grid.Row="1" BackgroundColor="#F5F5F5" Padding="12" StrokeThickness="0">
<VerticalStackLayout>
<Label Text="Event Log:" FontSize="12" FontAttributes="Bold" />
<ScrollView HeightRequest="60">
<Label x:Name="EventLog" Text="Events will appear here..." FontSize="11" TextColor="{StaticResource TextSecondary}" LineBreakMode="WordWrap" />
</ScrollView>
</VerticalStackLayout>
</Border>
</Grid>
</ContentPage>

View File

@@ -0,0 +1,165 @@
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Storage;
namespace ShellDemo.Pages;
public partial class DialogsPage : ContentPage
{
private int _eventCount = 0;
public DialogsPage()
{
InitializeComponent();
}
private void LogEvent(string message)
{
_eventCount++;
var timestamp = DateTime.Now.ToString("HH:mm:ss");
EventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLog.Text}";
}
// Alert Dialogs
private async void OnSimpleAlertClicked(object? sender, EventArgs e)
{
await DisplayAlert("Information", "This is a simple alert dialog.", "OK");
AlertResultLabel.Text = "Result: Alert dismissed";
LogEvent("Simple alert shown");
}
private async void OnConfirmationClicked(object? sender, EventArgs e)
{
bool answer = await DisplayAlert("Confirm", "Do you want to proceed with this action?", "Yes", "No");
AlertResultLabel.Text = $"Result: {(answer ? "Yes" : "No")}";
LogEvent($"Confirmation: {(answer ? "Yes" : "No")}");
}
// Action Sheets
private async void OnActionSheetClicked(object? sender, EventArgs e)
{
string action = await DisplayActionSheet("Choose an option", "Cancel", null, "Option 1", "Option 2", "Option 3");
ActionResultLabel.Text = $"Selection: {action ?? "None"}";
LogEvent($"Action sheet: {action ?? "Cancelled"}");
}
private async void OnDestructiveActionSheetClicked(object? sender, EventArgs e)
{
string action = await DisplayActionSheet("Danger Zone", "Cancel", "Delete Everything", "Edit", "Share", "Archive");
ActionResultLabel.Text = $"Selection: {action ?? "None"}";
LogEvent($"Destructive action: {action ?? "Cancelled"}");
}
// Prompts
private async void OnTextPromptClicked(object? sender, EventArgs e)
{
string result = await DisplayPromptAsync("Name", "What is your name?", placeholder: "Enter name");
PromptResultLabel.Text = $"Input: {result ?? "(cancelled)"}";
LogEvent($"Text prompt: {result ?? "Cancelled"}");
}
private async void OnNumericPromptClicked(object? sender, EventArgs e)
{
string result = await DisplayPromptAsync("Age", "Enter your age:", keyboard: Keyboard.Numeric, placeholder: "0");
PromptResultLabel.Text = $"Input: {result ?? "(cancelled)"}";
LogEvent($"Numeric prompt: {result ?? "Cancelled"}");
}
// File Pickers
private async void OnPickFileClicked(object? sender, EventArgs e)
{
try
{
var result = await FilePicker.Default.PickAsync();
if (result != null)
{
FileResultLabel.Text = $"Selected: {result.FileName}";
LogEvent($"File: {result.FileName}");
}
else
{
FileResultLabel.Text = "Selected: (cancelled)";
LogEvent("File picker cancelled");
}
}
catch (Exception ex)
{
FileResultLabel.Text = $"Error: {ex.Message}";
LogEvent($"File picker error: {ex.Message}");
}
}
private async void OnPickMultipleFilesClicked(object? sender, EventArgs e)
{
try
{
var results = await FilePicker.Default.PickMultipleAsync();
if (results != null && results.Any())
{
FileResultLabel.Text = $"Selected: {results.Count()} files";
LogEvent($"Multiple files: {results.Count()} selected");
}
else
{
FileResultLabel.Text = "Selected: (cancelled)";
LogEvent("Multiple file picker cancelled");
}
}
catch (Exception ex)
{
FileResultLabel.Text = $"Error: {ex.Message}";
LogEvent($"Multiple file picker error: {ex.Message}");
}
}
private async void OnPickImageClicked(object? sender, EventArgs e)
{
try
{
var options = new PickOptions
{
PickerTitle = "Select an image",
FileTypes = FilePickerFileType.Images
};
var result = await FilePicker.Default.PickAsync(options);
if (result != null)
{
FileResultLabel.Text = $"Selected: {result.FileName}";
LogEvent($"Image: {result.FileName}");
}
else
{
FileResultLabel.Text = "Selected: (cancelled)";
LogEvent("Image picker cancelled");
}
}
catch (Exception ex)
{
FileResultLabel.Text = $"Error: {ex.Message}";
LogEvent($"Image picker error: {ex.Message}");
}
}
private async void OnPickFolderClicked(object? sender, EventArgs e)
{
try
{
var result = await FolderPicker.Default.PickAsync();
if (result.IsSuccessful)
{
FileResultLabel.Text = $"Selected: {result.Folder?.Path}";
LogEvent($"Folder: {result.Folder?.Name}");
}
else
{
FileResultLabel.Text = "Selected: (cancelled)";
LogEvent("Folder picker cancelled");
}
}
catch (Exception ex)
{
FileResultLabel.Text = $"Error: {ex.Message}";
LogEvent($"Folder picker error: {ex.Message}");
}
}
}

View File

@@ -0,0 +1,197 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ShellDemo.Pages.MoreControlsPage"
Title="More Controls"
BackgroundColor="{StaticResource PageBackground}">
<Grid RowDefinitions="*,Auto">
<ScrollView Grid.Row="0">
<VerticalStackLayout Padding="20" Spacing="20">
<Label Text="More Controls" FontSize="24" FontAttributes="Bold" />
<!-- Stepper -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="15">
<Label Text="Stepper" FontSize="16" FontAttributes="Bold" />
<HorizontalStackLayout Spacing="15">
<Stepper x:Name="BasicStepper" Minimum="0" Maximum="10" Increment="1" ValueChanged="OnStepperChanged" />
<Label x:Name="StepperValueLabel" Text="Value: 0" VerticalOptions="Center" />
</HorizontalStackLayout>
<Label Text="Custom Range (0-100, step 5):" FontSize="12" Margin="0,10,0,0" />
<HorizontalStackLayout Spacing="15">
<Stepper x:Name="CustomStepper" Minimum="0" Maximum="100" Increment="5" Value="50" ValueChanged="OnCustomStepperChanged" />
<Label x:Name="CustomStepperLabel" Text="Value: 50" VerticalOptions="Center" />
</HorizontalStackLayout>
</VerticalStackLayout>
</Border>
<!-- RadioButton -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="15">
<Label Text="RadioButton" FontSize="16" FontAttributes="Bold" />
<Label Text="Select a size:" FontSize="12" />
<VerticalStackLayout RadioButtonGroup.GroupName="SizeGroup" Spacing="5">
<RadioButton Content="Small" Value="S" CheckedChanged="OnRadioChanged" />
<RadioButton Content="Medium" Value="M" IsChecked="True" CheckedChanged="OnRadioChanged" />
<RadioButton Content="Large" Value="L" CheckedChanged="OnRadioChanged" />
<RadioButton Content="Extra Large" Value="XL" CheckedChanged="OnRadioChanged" />
</VerticalStackLayout>
<Label x:Name="RadioResultLabel" Text="Selected: Medium" TextColor="{StaticResource TextSecondary}" FontSize="12" />
<Label Text="Horizontal Radio Group:" FontSize="12" Margin="0,10,0,0" />
<HorizontalStackLayout RadioButtonGroup.GroupName="ColorGroup" Spacing="10">
<RadioButton Content="Red" CheckedChanged="OnColorRadioChanged" />
<RadioButton Content="Green" CheckedChanged="OnColorRadioChanged" />
<RadioButton Content="Blue" IsChecked="True" CheckedChanged="OnColorRadioChanged" />
</HorizontalStackLayout>
</VerticalStackLayout>
</Border>
<!-- Image -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="15">
<Label Text="Image &amp; ImageButton" FontSize="16" FontAttributes="Bold" />
<Label Text="Image with different aspects:" FontSize="12" />
<HorizontalStackLayout Spacing="10">
<VerticalStackLayout>
<BoxView WidthRequest="80" HeightRequest="80" BackgroundColor="#E3F2FD" />
<Label Text="AspectFit" FontSize="10" HorizontalOptions="Center" />
</VerticalStackLayout>
<VerticalStackLayout>
<BoxView WidthRequest="80" HeightRequest="80" BackgroundColor="#E8F5E9" />
<Label Text="AspectFill" FontSize="10" HorizontalOptions="Center" />
</VerticalStackLayout>
<VerticalStackLayout>
<BoxView WidthRequest="80" HeightRequest="80" BackgroundColor="#FFF3E0" />
<Label Text="Fill" FontSize="10" HorizontalOptions="Center" />
</VerticalStackLayout>
</HorizontalStackLayout>
<Label Text="Note: Replace BoxView with Image when you have image assets" FontSize="11" TextColor="{StaticResource TextSecondary}" FontAttributes="Italic" />
</VerticalStackLayout>
</Border>
<!-- Clipboard -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="12">
<Label Text="Clipboard" FontSize="16" FontAttributes="Bold" />
<Entry x:Name="ClipboardEntry" Placeholder="Type text to copy..." />
<HorizontalStackLayout Spacing="10">
<Button Text="Copy"
BackgroundColor="{StaticResource PrimaryColor}"
TextColor="White"
Clicked="OnCopyClicked" />
<Button Text="Paste"
BackgroundColor="{StaticResource SuccessColor}"
TextColor="White"
Clicked="OnPasteClicked" />
</HorizontalStackLayout>
<Label x:Name="ClipboardResultLabel" Text="Clipboard: (empty)" TextColor="{StaticResource TextSecondary}" FontSize="12" />
</VerticalStackLayout>
</Border>
<!-- Share -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="12">
<Label Text="Share &amp; Launcher" FontSize="16" FontAttributes="Bold" />
<Button Text="Share Text"
BackgroundColor="{StaticResource PurpleColor}"
TextColor="White"
Clicked="OnShareTextClicked" />
<Button Text="Open URL in Browser"
BackgroundColor="#FF5722"
TextColor="White"
Clicked="OnOpenUrlClicked" />
<Button Text="Open Email Client"
BackgroundColor="#795548"
TextColor="White"
Clicked="OnOpenEmailClicked" />
</VerticalStackLayout>
</Border>
<!-- BoxView & Shapes -->
<Border BackgroundColor="{StaticResource CardBackground}"
Stroke="{StaticResource BorderColor}"
StrokeThickness="1"
Padding="16">
<Border.StrokeShape>
<RoundRectangle CornerRadius="8" />
</Border.StrokeShape>
<VerticalStackLayout Spacing="15">
<Label Text="BoxView &amp; Shapes" FontSize="16" FontAttributes="Bold" />
<HorizontalStackLayout Spacing="15">
<BoxView WidthRequest="50" HeightRequest="50" BackgroundColor="Red" />
<BoxView WidthRequest="50" HeightRequest="50" BackgroundColor="Green" CornerRadius="10" />
<BoxView WidthRequest="50" HeightRequest="50" BackgroundColor="Blue" CornerRadius="25" />
<BoxView WidthRequest="80" HeightRequest="50" BackgroundColor="Orange" CornerRadius="5" />
</HorizontalStackLayout>
<Label Text="BoxView as divider:" FontSize="12" />
<BoxView HeightRequest="2" BackgroundColor="{StaticResource BorderColor}" />
<Label Text="BoxView with gradient-like effect (stacked):" FontSize="12" />
<Grid HeightRequest="30">
<BoxView BackgroundColor="#E3F2FD" />
<BoxView BackgroundColor="#BBDEFB" Margin="0,0,0,10" />
<BoxView BackgroundColor="#90CAF9" Margin="0,0,0,20" />
</Grid>
</VerticalStackLayout>
</Border>
</VerticalStackLayout>
</ScrollView>
<!-- Event Log -->
<Border Grid.Row="1" BackgroundColor="#F5F5F5" Padding="12" StrokeThickness="0">
<VerticalStackLayout>
<Label Text="Event Log:" FontSize="12" FontAttributes="Bold" />
<ScrollView HeightRequest="60">
<Label x:Name="EventLog" Text="Events will appear here..." FontSize="11" TextColor="{StaticResource TextSecondary}" LineBreakMode="WordWrap" />
</ScrollView>
</VerticalStackLayout>
</Border>
</Grid>
</ContentPage>

View File

@@ -0,0 +1,131 @@
using System;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.ApplicationModel.DataTransfer;
using Microsoft.Maui.Controls;
namespace ShellDemo.Pages;
public partial class MoreControlsPage : ContentPage
{
private int _eventCount = 0;
public MoreControlsPage()
{
InitializeComponent();
}
private void LogEvent(string message)
{
_eventCount++;
var timestamp = DateTime.Now.ToString("HH:mm:ss");
EventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLog.Text}";
}
// Stepper
private void OnStepperChanged(object? sender, ValueChangedEventArgs e)
{
StepperValueLabel.Text = $"Value: {(int)e.NewValue}";
LogEvent($"Stepper: {(int)e.NewValue}");
}
private void OnCustomStepperChanged(object? sender, ValueChangedEventArgs e)
{
CustomStepperLabel.Text = $"Value: {(int)e.NewValue}";
LogEvent($"Custom Stepper: {(int)e.NewValue}");
}
// RadioButton
private void OnRadioChanged(object? sender, CheckedChangedEventArgs e)
{
if (e.Value && sender is RadioButton rb)
{
RadioResultLabel.Text = $"Selected: {rb.Content}";
LogEvent($"Size: {rb.Content}");
}
}
private void OnColorRadioChanged(object? sender, CheckedChangedEventArgs e)
{
if (e.Value && sender is RadioButton rb)
{
LogEvent($"Color: {rb.Content}");
}
}
// Clipboard
private async void OnCopyClicked(object? sender, EventArgs e)
{
var text = ClipboardEntry.Text;
if (!string.IsNullOrEmpty(text))
{
await Clipboard.Default.SetTextAsync(text);
ClipboardResultLabel.Text = $"Copied: {text}";
LogEvent($"Copied to clipboard: {text}");
}
else
{
ClipboardResultLabel.Text = "Nothing to copy";
LogEvent("Copy failed: empty text");
}
}
private async void OnPasteClicked(object? sender, EventArgs e)
{
if (Clipboard.Default.HasText)
{
var text = await Clipboard.Default.GetTextAsync();
ClipboardEntry.Text = text;
ClipboardResultLabel.Text = $"Pasted: {text}";
LogEvent($"Pasted from clipboard: {text}");
}
else
{
ClipboardResultLabel.Text = "Clipboard is empty";
LogEvent("Paste failed: clipboard empty");
}
}
// Share & Launcher
private async void OnShareTextClicked(object? sender, EventArgs e)
{
try
{
await Share.Default.RequestAsync(new ShareTextRequest
{
Text = "Check out OpenMaui Linux - .NET MAUI for Linux!",
Title = "Share OpenMaui"
});
LogEvent("Share dialog opened");
}
catch (Exception ex)
{
LogEvent($"Share error: {ex.Message}");
}
}
private async void OnOpenUrlClicked(object? sender, EventArgs e)
{
try
{
await Launcher.Default.OpenAsync("https://github.com/pablotoledo/OpenMaui-Linux");
LogEvent("Opened URL in browser");
}
catch (Exception ex)
{
LogEvent($"Launcher error: {ex.Message}");
}
}
private async void OnOpenEmailClicked(object? sender, EventArgs e)
{
try
{
await Launcher.Default.OpenAsync("mailto:info@example.com?subject=OpenMaui%20Feedback");
LogEvent("Opened email client");
}
catch (Exception ex)
{
LogEvent($"Email error: {ex.Message}");
}
}
}