Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8b2df000b | |||
| 0c9563ccb5 | |||
| dcabf31c7b | |||
| 8a3e3f6283 | |||
| 73ee0b2bb3 | |||
| 8e51e5ba22 | |||
| 01dec998f8 | |||
| cdda247309 | |||
| 751b3d544d | |||
| ca48355f8a | |||
| 26b39a403a | |||
| e9d4b84790 | |||
| 71f6aa1179 | |||
| fc113166d6 | |||
| 8531f2b972 | |||
| 50cc186d6d | |||
| f87ea17a6f |
72
.gitea/workflows/ci.yml
Normal file
@@ -0,0 +1,72 @@
|
||||
# OpenMaui Linux Samples CI
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
|
||||
jobs:
|
||||
build-linux-x64:
|
||||
name: Build Samples (Linux x64)
|
||||
runs-on: linux-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build ShellDemo
|
||||
run: dotnet build ShellDemo/ShellDemo.csproj --configuration Release
|
||||
|
||||
- name: Build TodoApp
|
||||
run: dotnet build TodoApp/TodoApp.csproj --configuration Release
|
||||
|
||||
- name: Build WebViewDemo
|
||||
run: dotnet build WebViewDemo/WebViewDemo.csproj --configuration Release
|
||||
|
||||
build-linux-arm64:
|
||||
name: Build Samples (Linux arm64)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build in Docker (arm64)
|
||||
run: |
|
||||
docker run --rm --platform linux/arm64 \
|
||||
-v "$PWD:/src" -w /src \
|
||||
mcr.microsoft.com/dotnet/sdk:9.0 \
|
||||
bash -c "dotnet restore && dotnet build --configuration Release"
|
||||
|
||||
build-windows:
|
||||
name: Build Samples (Windows)
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build all samples
|
||||
run: dotnet build --configuration Release
|
||||
|
||||
build-macos:
|
||||
name: Build Samples (macOS)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Restore dependencies
|
||||
run: dotnet restore
|
||||
|
||||
- name: Build all samples
|
||||
run: dotnet build --configuration Release
|
||||
155
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,155 @@
|
||||
# OpenMaui Linux Samples Release - Build AppImages
|
||||
name: Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
env:
|
||||
DOTNET_NOLOGO: true
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: true
|
||||
GITEA_PACKAGES_URL: https://git.marketally.com/api/packages/AuroraOSS/nuget/index.json
|
||||
|
||||
jobs:
|
||||
build-appimages-x64:
|
||||
name: Build AppImages (Linux x64)
|
||||
runs-on: linux-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install appimagetool
|
||||
run: |
|
||||
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O /tmp/appimagetool
|
||||
chmod +x /tmp/appimagetool
|
||||
sudo mv /tmp/appimagetool /usr/local/bin/appimagetool
|
||||
|
||||
- name: Add Gitea NuGet source
|
||||
run: |
|
||||
dotnet nuget add source "$GITEA_PACKAGES_URL" --name gitea || true
|
||||
|
||||
- name: Install OpenMaui.AppImage tool
|
||||
run: |
|
||||
dotnet tool install --global OpenMaui.AppImage || \
|
||||
dotnet tool install --global OpenMaui.AppImage --add-source "$GITEA_PACKAGES_URL" || \
|
||||
dotnet tool update --global OpenMaui.AppImage
|
||||
|
||||
- name: Publish ShellDemo
|
||||
run: dotnet publish ShellDemo/ShellDemo.csproj -c Release -r linux-x64 --self-contained -o ./publish/ShellDemo
|
||||
|
||||
- name: Publish TodoApp
|
||||
run: dotnet publish TodoApp/TodoApp.csproj -c Release -r linux-x64 --self-contained -o ./publish/TodoApp
|
||||
|
||||
- name: Publish WebViewDemo
|
||||
run: dotnet publish WebViewDemo/WebViewDemo.csproj -c Release -r linux-x64 --self-contained -o ./publish/WebViewDemo
|
||||
|
||||
- name: Create ShellDemo AppImage
|
||||
run: |
|
||||
mkdir -p ./appimages
|
||||
~/.dotnet/tools/openmaui-appimage \
|
||||
-i ./publish/ShellDemo \
|
||||
-o ./appimages/ShellDemo-x64.AppImage \
|
||||
-n "Shell Demo"
|
||||
|
||||
- name: Create TodoApp AppImage
|
||||
run: |
|
||||
~/.dotnet/tools/openmaui-appimage \
|
||||
-i ./publish/TodoApp \
|
||||
-o ./appimages/TodoApp-x64.AppImage \
|
||||
-n "Todo App"
|
||||
|
||||
- name: Create WebViewDemo AppImage
|
||||
run: |
|
||||
~/.dotnet/tools/openmaui-appimage \
|
||||
-i ./publish/WebViewDemo \
|
||||
-o ./appimages/WebViewDemo-x64.AppImage \
|
||||
-n "WebView Demo"
|
||||
|
||||
- name: Upload AppImages
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: appimages-x64
|
||||
path: ./appimages/*.AppImage
|
||||
|
||||
build-appimages-arm64:
|
||||
name: Build AppImages (Linux arm64)
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Build and package in Docker (arm64)
|
||||
run: |
|
||||
mkdir -p ./appimages
|
||||
|
||||
docker run --rm --platform linux/arm64 \
|
||||
-v "$PWD:/src" -w /src \
|
||||
-e GITEA_PACKAGES_URL="$GITEA_PACKAGES_URL" \
|
||||
mcr.microsoft.com/dotnet/sdk:9.0 \
|
||||
bash -c "
|
||||
set -e
|
||||
|
||||
# Install appimagetool
|
||||
apt-get update && apt-get install -y wget file
|
||||
wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-aarch64.AppImage -O /tmp/appimagetool
|
||||
chmod +x /tmp/appimagetool
|
||||
mv /tmp/appimagetool /usr/local/bin/appimagetool
|
||||
|
||||
# Add Gitea source and install tool
|
||||
dotnet nuget add source \"\$GITEA_PACKAGES_URL\" --name gitea || true
|
||||
dotnet tool install --global OpenMaui.AppImage || \
|
||||
dotnet tool install --global OpenMaui.AppImage --add-source \"\$GITEA_PACKAGES_URL\" || true
|
||||
export PATH=\"\$PATH:/root/.dotnet/tools\"
|
||||
|
||||
# Publish samples
|
||||
dotnet publish ShellDemo/ShellDemo.csproj -c Release -r linux-arm64 --self-contained -o ./publish/ShellDemo
|
||||
dotnet publish TodoApp/TodoApp.csproj -c Release -r linux-arm64 --self-contained -o ./publish/TodoApp
|
||||
dotnet publish WebViewDemo/WebViewDemo.csproj -c Release -r linux-arm64 --self-contained -o ./publish/WebViewDemo
|
||||
|
||||
# Create AppImages
|
||||
mkdir -p ./appimages
|
||||
openmaui-appimage -i ./publish/ShellDemo -o ./appimages/ShellDemo-arm64.AppImage -n 'Shell Demo'
|
||||
openmaui-appimage -i ./publish/TodoApp -o ./appimages/TodoApp-arm64.AppImage -n 'Todo App'
|
||||
openmaui-appimage -i ./publish/WebViewDemo -o ./appimages/WebViewDemo-arm64.AppImage -n 'WebView Demo'
|
||||
"
|
||||
|
||||
- name: Upload AppImages
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: appimages-arm64
|
||||
path: ./appimages/*.AppImage
|
||||
|
||||
create-release:
|
||||
name: Attach to Release
|
||||
needs: [build-appimages-x64, build-appimages-arm64]
|
||||
runs-on: linux-latest
|
||||
if: github.event_name == 'release'
|
||||
steps:
|
||||
- name: Download x64 AppImages
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: appimages-x64
|
||||
path: ./release
|
||||
|
||||
- name: Download arm64 AppImages
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: appimages-arm64
|
||||
path: ./release
|
||||
|
||||
- name: List release files
|
||||
run: ls -la ./release/
|
||||
|
||||
- name: Upload to Gitea Release
|
||||
run: |
|
||||
for file in ./release/*.AppImage; do
|
||||
filename=$(basename "$file")
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$file" \
|
||||
"https://git.marketally.com/api/v1/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets?name=$filename"
|
||||
done
|
||||
9
.gitignore
vendored
@@ -51,3 +51,12 @@ TestResults/
|
||||
|
||||
# Publish output
|
||||
publish/
|
||||
|
||||
# Flatpak build artifacts
|
||||
.flatpak-builder/
|
||||
*.flatpak
|
||||
repo/
|
||||
|
||||
# AppImage artifacts
|
||||
*.AppImage
|
||||
*.AppDir/
|
||||
|
||||
5
Directory.Build.targets
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project>
|
||||
<!-- Auto-import OpenMaui targets for all sample projects on Linux -->
|
||||
<Import Project="../maui-linux/build/OpenMaui.Controls.Linux.targets" Condition="$([MSBuild]::IsOSPlatform('Linux'))" />
|
||||
</Project>
|
||||
15
NuGet.config
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
<add key="gitea" value="https://git.marketally.com/api/packages/AuroraOSS/nuget/index.json" />
|
||||
</packageSources>
|
||||
<packageSourceMapping>
|
||||
<packageSource key="nuget.org">
|
||||
<package pattern="*" />
|
||||
</packageSource>
|
||||
<packageSource key="gitea">
|
||||
<package pattern="OpenMaui.*" />
|
||||
</packageSource>
|
||||
</packageSourceMapping>
|
||||
</configuration>
|
||||
792
README.md
@@ -1,76 +1,782 @@
|
||||
# OpenMaui Linux Samples
|
||||
|
||||
Sample applications demonstrating [OpenMaui Linux](https://git.marketally.com/open-maui/maui-linux) - .NET MAUI on Linux.
|
||||
Sample applications demonstrating [OpenMaui Linux](https://git.marketally.com/open-maui/maui-linux) - .NET MAUI on Linux desktop with SkiaSharp rendering.
|
||||
|
||||
## Samples
|
||||
## Table of Contents
|
||||
|
||||
| Sample | Description |
|
||||
|--------|-------------|
|
||||
| [TodoApp](./TodoApp/) | Full-featured task manager with NavigationPage, XAML data binding, and CollectionView |
|
||||
| [ShellDemo](./ShellDemo/) | Comprehensive control showcase with Shell navigation and flyout menu |
|
||||
- [Overview](#overview)
|
||||
- [Sample Applications](#sample-applications)
|
||||
- [Requirements](#requirements)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Building and Deployment](#building-and-deployment)
|
||||
- [Sample Details](#sample-details)
|
||||
- [TodoApp](#todoapp)
|
||||
- [ShellDemo](#shelldemo)
|
||||
- [WebViewDemo](#webviewdemo)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Development Guide](#development-guide)
|
||||
- [API Usage Examples](#api-usage-examples)
|
||||
- [Related Resources](#related-resources)
|
||||
- [License](#license)
|
||||
|
||||
## Overview
|
||||
|
||||
This repository contains production-ready sample applications showcasing **OpenMaui Linux** - a .NET MAUI implementation for Linux desktop environments. These samples demonstrate how to build cross-platform applications using familiar MAUI APIs with native Linux rendering via SkiaSharp and optional GTK integration.
|
||||
|
||||
**Key Features:**
|
||||
- Full .NET MAUI API compatibility
|
||||
- SkiaSharp-based rendering for high-performance graphics
|
||||
- X11 window management support
|
||||
- GTK integration for WebView and native dialogs
|
||||
- Light/Dark theme support with dynamic switching
|
||||
- Navigation (NavigationPage, Shell, push/pop)
|
||||
- Data binding and MVVM patterns
|
||||
- Comprehensive control coverage
|
||||
|
||||
## Sample Applications
|
||||
|
||||
| Sample | Description | Key Features |
|
||||
|--------|-------------|--------------|
|
||||
| [TodoApp](./TodoApp/) | Full-featured task manager | NavigationPage, XAML data binding, CollectionView, value converters, theme switching |
|
||||
| [ShellDemo](./ShellDemo/) | Comprehensive control showcase | Shell navigation, flyout menu, all core controls, event logging |
|
||||
| [WebViewDemo](./WebViewDemo/) | Web browser with WebKitGTK | WebView, JavaScript evaluation, GTK integration, HTML rendering |
|
||||
|
||||
## Requirements
|
||||
|
||||
- .NET 9.0 SDK
|
||||
- Linux with X11 (Ubuntu, Fedora, etc.)
|
||||
- SkiaSharp dependencies: `libfontconfig1-dev libfreetype6-dev`
|
||||
**Software:**
|
||||
- .NET 9.0 SDK or later
|
||||
- Linux with X11 display server (Wayland not yet supported)
|
||||
- Supported distributions: Ubuntu 20.04+, Fedora 35+, Debian 11+, or similar
|
||||
|
||||
**System Dependencies:**
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get install libfontconfig1-dev libfreetype6-dev
|
||||
|
||||
# Fedora/RHEL
|
||||
sudo dnf install fontconfig-devel freetype-devel
|
||||
|
||||
# For WebView support (WebViewDemo)
|
||||
sudo apt-get install libwebkit2gtk-4.0-dev # Ubuntu/Debian
|
||||
sudo dnf install webkit2gtk3-devel # Fedora/RHEL
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### Clone the Repository
|
||||
|
||||
```bash
|
||||
git clone https://git.marketally.com/open-maui/maui-linux-samples.git
|
||||
cd maui-linux-samples
|
||||
```
|
||||
|
||||
### Install .NET SDK
|
||||
|
||||
If you don't have .NET 9.0 SDK installed:
|
||||
|
||||
```bash
|
||||
# Download and install .NET SDK
|
||||
wget https://dot.net/v1/dotnet-install.sh
|
||||
chmod +x dotnet-install.sh
|
||||
./dotnet-install.sh --channel 9.0
|
||||
|
||||
# Add to PATH (add to ~/.bashrc for persistence)
|
||||
export DOTNET_ROOT=$HOME/.dotnet
|
||||
export PATH=$DOTNET_ROOT:$PATH
|
||||
```
|
||||
|
||||
### Verify Installation
|
||||
|
||||
```bash
|
||||
dotnet --version # Should show 9.0.x or later
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Clone the samples
|
||||
git clone https://git.marketally.com/open-maui/maui-linux-samples.git
|
||||
cd maui-linux-samples
|
||||
### Run TodoApp
|
||||
|
||||
# Run TodoApp
|
||||
```bash
|
||||
cd TodoApp
|
||||
dotnet run
|
||||
```
|
||||
|
||||
# Or run ShellDemo
|
||||
The todo application will launch with sample tasks. You can:
|
||||
- Add new tasks with the "+" button
|
||||
- Tap tasks to view/edit details
|
||||
- Mark tasks as completed
|
||||
- Delete tasks
|
||||
- Toggle between light and dark themes
|
||||
|
||||
### Run ShellDemo
|
||||
|
||||
```bash
|
||||
cd ../ShellDemo
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## Building for Deployment
|
||||
The control gallery will open with a flyout menu. Navigate through different pages to explore:
|
||||
- Button styles and events
|
||||
- Text input controls (Entry, Editor, SearchBar)
|
||||
- Selection controls (CheckBox, Switch, Slider)
|
||||
- Pickers (Picker, DatePicker, TimePicker)
|
||||
- CollectionView with dynamic data
|
||||
- Progress indicators
|
||||
- Grid layouts with various configurations
|
||||
|
||||
### Run WebViewDemo
|
||||
|
||||
```bash
|
||||
# Build for Linux ARM64
|
||||
dotnet publish -c Release -r linux-arm64
|
||||
|
||||
# Build for Linux x64
|
||||
dotnet publish -c Release -r linux-x64
|
||||
cd ../WebViewDemo
|
||||
dotnet run
|
||||
```
|
||||
|
||||
## TodoApp
|
||||
A web browser window will open. You can:
|
||||
- Navigate to any URL
|
||||
- Use back/forward buttons
|
||||
- Load custom HTML content
|
||||
- Execute JavaScript
|
||||
- Toggle themes
|
||||
|
||||
A complete task management application demonstrating:
|
||||
- NavigationPage with toolbar and back navigation
|
||||
- CollectionView with data binding and selection
|
||||
- XAML value converters for dynamic styling
|
||||
- DisplayAlert dialogs
|
||||
- Grid layouts with star sizing
|
||||
- Entry and Editor text input
|
||||
## Building and Deployment
|
||||
|
||||

|
||||
### Debug Build
|
||||
|
||||
## ShellDemo
|
||||
```bash
|
||||
# Build without running
|
||||
dotnet build
|
||||
|
||||
A comprehensive control gallery demonstrating:
|
||||
- Shell with flyout menu navigation
|
||||
- All core MAUI controls (Button, Entry, CheckBox, Switch, Slider, etc.)
|
||||
- Picker, DatePicker, TimePicker
|
||||
- CollectionView with various item types
|
||||
- ProgressBar and ActivityIndicator
|
||||
- Grid layouts
|
||||
- Real-time event logging
|
||||
# Output location: bin/Debug/net9.0/
|
||||
```
|
||||
|
||||

|
||||
### Release Build
|
||||
|
||||
## Related
|
||||
```bash
|
||||
# Build optimized release version
|
||||
dotnet build -c Release
|
||||
```
|
||||
|
||||
- [OpenMaui Linux Framework](https://git.marketally.com/open-maui/maui-linux) - The core framework
|
||||
- [NuGet Package](https://www.nuget.org/packages/OpenMaui.Controls.Linux) - Install via NuGet
|
||||
### Publish for Distribution
|
||||
|
||||
Create self-contained executables for specific Linux architectures:
|
||||
|
||||
```bash
|
||||
# Linux x64 (most desktops)
|
||||
dotnet publish -c Release -r linux-x64 --self-contained
|
||||
|
||||
# Linux ARM64 (Raspberry Pi, ARM servers)
|
||||
dotnet publish -c Release -r linux-arm64 --self-contained
|
||||
|
||||
# Framework-dependent (requires .NET runtime installed)
|
||||
dotnet publish -c Release -r linux-x64 --no-self-contained
|
||||
```
|
||||
|
||||
Published applications will be in `bin/Release/net9.0/linux-x64/publish/` and can be distributed as standalone executables.
|
||||
|
||||
### Create Desktop Launcher
|
||||
|
||||
```bash
|
||||
# Make the run script executable
|
||||
chmod +x TodoApp/run.sh
|
||||
|
||||
# Create a .desktop file for your application
|
||||
cat > ~/.local/share/applications/openmaui-todo.desktop << EOF
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=OpenMaui Todo
|
||||
Exec=/path/to/maui-linux-samples/TodoApp/run.sh
|
||||
Icon=utilities-terminal
|
||||
Terminal=false
|
||||
Categories=Utility;
|
||||
EOF
|
||||
```
|
||||
|
||||
## Sample Details
|
||||
|
||||
### TodoApp
|
||||
|
||||
A complete task management application demonstrating production-ready patterns.
|
||||
|
||||
**Features:**
|
||||
- **NavigationPage** with toolbar and back navigation
|
||||
- **CollectionView** with item selection and data templates
|
||||
- **XAML data binding** with INotifyPropertyChanged
|
||||
- **Value converters** for dynamic styling (alternating rows, completed tasks)
|
||||
- **DisplayAlert** dialogs for confirmations
|
||||
- **Theme switching** (Light/Dark) with AppThemeBinding
|
||||
- **Grid layouts** with star sizing
|
||||
- **Entry and Editor** for text input
|
||||
- **Alternating row colors** with theme support
|
||||
|
||||
**Architecture:**
|
||||
- `TodoItem.cs` - Data model with property change notifications
|
||||
- `TodoService.cs` - Business logic with ObservableCollection
|
||||
- `TodoListPage.xaml` - Main list view with data templates
|
||||
- `TodoDetailPage.xaml` - Detail/edit view
|
||||
- `NewTodoPage.xaml` - Create new task view
|
||||
- Value converters for visual state management
|
||||
|
||||
**Code Example - Data Binding:**
|
||||
|
||||
```xaml
|
||||
<!-- TodoListPage.xaml -->
|
||||
<CollectionView ItemsSource="{Binding Todos}" SelectionMode="Single">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Padding="15,10"
|
||||
BackgroundColor="{Binding Index, Converter={StaticResource AlternatingRowColorConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="4"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- Status indicator -->
|
||||
<BoxView Grid.Column="0"
|
||||
BackgroundColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}, ConverterParameter=indicator}"/>
|
||||
|
||||
<!-- Task content -->
|
||||
<VerticalStackLayout Grid.Column="1" Spacing="4">
|
||||
<Label Text="{Binding Title}"
|
||||
FontSize="16"
|
||||
TextColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}}"
|
||||
TextDecorations="{Binding IsCompleted, Converter={StaticResource CompletedToTextDecorationsConverter}}"/>
|
||||
<Label Text="{Binding Notes}"
|
||||
FontSize="12"
|
||||
MaxLines="2"
|
||||
LineBreakMode="TailTruncation"
|
||||
TextColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}, ConverterParameter=notes}"/>
|
||||
</VerticalStackLayout>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
```
|
||||
|
||||
**Logging:**
|
||||
Application logs are written to `~/todoapp.log` for debugging.
|
||||
|
||||
### ShellDemo
|
||||
|
||||
A comprehensive control gallery showcasing all supported MAUI controls with Shell navigation.
|
||||
|
||||
**Features:**
|
||||
- **Shell navigation** with flyout menu
|
||||
- **All core controls**: Button, Entry, Editor, SearchBar, CheckBox, Switch, Slider, Stepper
|
||||
- **Pickers**: Picker, DatePicker, TimePicker
|
||||
- **CollectionView** with various data types and selection modes
|
||||
- **ProgressBar and ActivityIndicator**
|
||||
- **Grid layouts** with demonstrations of:
|
||||
- Auto, Star, and Absolute sizing
|
||||
- Row and column spanning
|
||||
- Spacing and padding
|
||||
- Mixed sizing strategies
|
||||
- **Real-time event logging** for all interactions
|
||||
- **Push/pop navigation** examples
|
||||
- **Theme switching** support
|
||||
|
||||
**Navigation Structure:**
|
||||
|
||||
```csharp
|
||||
// AppShell.xaml.cs - Shell with flyout menu
|
||||
public partial class AppShell : Shell
|
||||
{
|
||||
public AppShell()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Register routes for push navigation
|
||||
Routing.RegisterRoute("detail", typeof(DetailPage));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Code Example - Grid Layouts:**
|
||||
|
||||
```csharp
|
||||
// GridsPage.xaml.cs - Programmatic grid creation
|
||||
private View CreateStarSizingDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
|
||||
}
|
||||
};
|
||||
|
||||
// Add cells with proportional sizing: 25% | 50% | 25%
|
||||
grid.Children.Add(CreateCell("1*", "#BBDEFB"));
|
||||
grid.Children.Add(CreateCell("2* (double)", "#C8E6C9"));
|
||||
grid.Children.Add(CreateCell("1*", "#FFECB3"));
|
||||
|
||||
return grid;
|
||||
}
|
||||
```
|
||||
|
||||
**Logging:**
|
||||
Application logs are written to `~/shelldemo.log` for debugging.
|
||||
|
||||
### WebViewDemo
|
||||
|
||||
A web browser application demonstrating WebView integration with WebKitGTK.
|
||||
|
||||
**Features:**
|
||||
- **WebView** with full HTML5 support
|
||||
- **WebKitGTK** backend (same engine as GNOME Web)
|
||||
- **Navigation controls** (back, forward, reload)
|
||||
- **URL entry** with automatic https:// prefix
|
||||
- **JavaScript evaluation** via `EvaluateJavaScriptAsync`
|
||||
- **HTML content loading** from strings
|
||||
- **Progress indicator** with animations
|
||||
- **Theme switching** support
|
||||
- **GTK mode** for native WebView rendering
|
||||
|
||||
**Important:** Requires GTK mode enabled in `Program.cs`:
|
||||
|
||||
```csharp
|
||||
// WebViewDemo/Program.cs
|
||||
LinuxApplication.Run(app, args, options =>
|
||||
{
|
||||
options.UseGtk = true; // Required for WebView
|
||||
});
|
||||
```
|
||||
|
||||
**Code Example - JavaScript Evaluation:**
|
||||
|
||||
```csharp
|
||||
private async void OnEvalJsClicked(object? sender, EventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await MainWebView.EvaluateJavaScriptAsync("document.title");
|
||||
StatusLabel.Text = $"JS Result: {result ?? "(null)"}";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusLabel.Text = $"JS Error: {ex.Message}";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Code Example - Loading HTML:**
|
||||
|
||||
```csharp
|
||||
private void OnLoadHtmlClicked(object? sender, EventArgs e)
|
||||
{
|
||||
var html = @"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial; margin: 40px; }
|
||||
button { padding: 15px 30px; font-size: 1.1em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from OpenMaui!</h1>
|
||||
<button onclick=""alert('Hello from JavaScript!')"">Click Me!</button>
|
||||
</body>
|
||||
</html>";
|
||||
|
||||
MainWebView.Source = new HtmlWebViewSource { Html = html };
|
||||
}
|
||||
```
|
||||
|
||||
**Logging:**
|
||||
Application logs are written to `~/webviewdemo.log` for debugging.
|
||||
|
||||
## Project Structure
|
||||
|
||||
Each sample follows a similar structure:
|
||||
|
||||
```
|
||||
SampleApp/
|
||||
├── Program.cs # Linux platform entry point
|
||||
├── MauiProgram.cs # MAUI app configuration
|
||||
├── App.xaml # Application resources and theme definitions
|
||||
├── App.xaml.cs # Application lifecycle
|
||||
├── SampleApp.csproj # Project configuration
|
||||
├── Pages/ # XAML pages and code-behind
|
||||
│ ├── MainPage.xaml
|
||||
│ └── MainPage.xaml.cs
|
||||
├── Resources/ # Images, fonts, icons
|
||||
│ ├── AppIcon/
|
||||
│ └── Images/
|
||||
└── run.sh # Launcher script
|
||||
```
|
||||
|
||||
### Project Configuration
|
||||
|
||||
All samples use conditional compilation for cross-platform support:
|
||||
|
||||
```xml
|
||||
<!-- SampleApp.csproj -->
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Linux: Use OpenMaui -->
|
||||
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))">
|
||||
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Local development -->
|
||||
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux')) AND Exists('../../maui-linux/OpenMaui.Controls.Linux.csproj')">
|
||||
<ProjectReference Include="../../maui-linux/OpenMaui.Controls.Linux.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- CI/CD or standalone -->
|
||||
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux')) AND !Exists('../../maui-linux/OpenMaui.Controls.Linux.csproj')">
|
||||
<PackageReference Include="OpenMaui.Controls.Linux" Version="*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
## Development Guide
|
||||
|
||||
### Creating a New Linux MAUI App
|
||||
|
||||
1. **Create project structure:**
|
||||
|
||||
```bash
|
||||
mkdir MyApp
|
||||
cd MyApp
|
||||
dotnet new console -n MyApp
|
||||
```
|
||||
|
||||
2. **Edit MyApp.csproj:**
|
||||
|
||||
```xml
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<EnableDefaultXamlItems>true</EnableDefaultXamlItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenMaui.Controls.Linux" Version="*" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
```
|
||||
|
||||
3. **Create Program.cs:**
|
||||
|
||||
```csharp
|
||||
using Microsoft.Maui.Platform.Linux;
|
||||
|
||||
namespace MyApp;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
var app = MauiProgram.CreateMauiApp();
|
||||
LinuxApplication.Run(app, args);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. **Create MauiProgram.cs:**
|
||||
|
||||
```csharp
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace MyApp;
|
||||
|
||||
public static class MauiProgram
|
||||
{
|
||||
public static MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder.UseMauiApp<App>();
|
||||
builder.UseLinux();
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **Create App.xaml and App.xaml.cs** (see samples for examples)
|
||||
|
||||
6. **Build and run:**
|
||||
|
||||
```bash
|
||||
dotnet build
|
||||
dotnet run
|
||||
```
|
||||
|
||||
### Exception Handling
|
||||
|
||||
All samples include comprehensive exception handling:
|
||||
|
||||
```csharp
|
||||
// Global exception handlers in Program.cs
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
var ex = e.ExceptionObject as Exception;
|
||||
Console.WriteLine($"[FATAL] Unhandled exception: {ex?.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {ex?.StackTrace}");
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (sender, e) =>
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Unobserved task exception: {e.Exception?.Message}");
|
||||
e.SetObserved(); // Prevent crash
|
||||
};
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
All samples redirect console output to log files in the user's home directory:
|
||||
|
||||
```csharp
|
||||
var logPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
"myapp.log");
|
||||
using var logWriter = new StreamWriter(logPath, append: false) { AutoFlush = true };
|
||||
var multiWriter = new MultiTextWriter(Console.Out, logWriter);
|
||||
Console.SetOut(multiWriter);
|
||||
Console.SetError(multiWriter);
|
||||
```
|
||||
|
||||
## API Usage Examples
|
||||
|
||||
### Navigation
|
||||
|
||||
**NavigationPage (TodoApp):**
|
||||
|
||||
```csharp
|
||||
// App.xaml.cs - Create NavigationPage
|
||||
protected override Window CreateWindow(IActivationState? activationState)
|
||||
{
|
||||
NavigationPage = new NavigationPage(new TodoListPage())
|
||||
{
|
||||
BarBackgroundColor = Color.FromArgb("#5C6BC0"),
|
||||
BarTextColor = Colors.White
|
||||
};
|
||||
return new Window(NavigationPage);
|
||||
}
|
||||
|
||||
// Push a page
|
||||
await Navigation.PushAsync(new TodoDetailPage(todo));
|
||||
|
||||
// Pop back
|
||||
await Navigation.PopAsync();
|
||||
```
|
||||
|
||||
**Shell Navigation (ShellDemo):**
|
||||
|
||||
```csharp
|
||||
// AppShell.xaml.cs - Register routes
|
||||
Routing.RegisterRoute("detail", typeof(DetailPage));
|
||||
|
||||
// Navigate using route
|
||||
await Shell.Current.GoToAsync("detail?item=MyItem");
|
||||
|
||||
// Navigate with parameters
|
||||
await Shell.Current.GoToAsync($"detail?item={Uri.EscapeDataString(itemName)}");
|
||||
|
||||
// Use LinuxViewRenderer for direct navigation
|
||||
LinuxViewRenderer.NavigateToRoute("Buttons");
|
||||
LinuxViewRenderer.PushPage(new DetailPage());
|
||||
LinuxViewRenderer.PopPage();
|
||||
```
|
||||
|
||||
### Data Binding
|
||||
|
||||
**Observable Collections:**
|
||||
|
||||
```csharp
|
||||
public class TodoService
|
||||
{
|
||||
public ObservableCollection<TodoItem> Todos { get; } = new();
|
||||
|
||||
public TodoItem AddTodo(string title, string notes = "")
|
||||
{
|
||||
var todo = new TodoItem { Title = title, Notes = notes };
|
||||
Todos.Add(todo);
|
||||
return todo;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**INotifyPropertyChanged:**
|
||||
|
||||
```csharp
|
||||
public class TodoItem : INotifyPropertyChanged
|
||||
{
|
||||
private bool _isCompleted;
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get => _isCompleted;
|
||||
set
|
||||
{
|
||||
if (_isCompleted != value)
|
||||
{
|
||||
_isCompleted = value;
|
||||
OnPropertyChanged(nameof(IsCompleted));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public event PropertyChangedEventHandler? PropertyChanged;
|
||||
|
||||
protected void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Value Converters
|
||||
|
||||
**Theme-Aware Color Converter:**
|
||||
|
||||
```csharp
|
||||
public class CompletedToColorConverter : IValueConverter
|
||||
{
|
||||
private static readonly Color AccentColorLight = Color.FromArgb("#26A69A");
|
||||
private static readonly Color AccentColorDark = Color.FromArgb("#4DB6AC");
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
bool isDarkMode = Application.Current?.RequestedTheme == AppTheme.Dark;
|
||||
|
||||
if (isCompleted)
|
||||
return Color.FromArgb("#9E9E9E");
|
||||
|
||||
return isDarkMode ? AccentColorDark : AccentColorLight;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Register in App.xaml:**
|
||||
|
||||
```xaml
|
||||
<Application.Resources>
|
||||
<local:CompletedToColorConverter x:Key="CompletedToColorConverter"/>
|
||||
</Application.Resources>
|
||||
```
|
||||
|
||||
### Theme Switching
|
||||
|
||||
**Toggle Theme:**
|
||||
|
||||
```csharp
|
||||
private void OnThemeToggleClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (Application.Current == null) return;
|
||||
|
||||
var isDarkMode = Application.Current.UserAppTheme == AppTheme.Dark;
|
||||
Application.Current.UserAppTheme = isDarkMode ? AppTheme.Light : AppTheme.Dark;
|
||||
}
|
||||
```
|
||||
|
||||
**AppThemeBinding in XAML:**
|
||||
|
||||
```xaml
|
||||
<Border BackgroundColor="{AppThemeBinding Light=#FFFFFF, Dark=#1E1E1E}">
|
||||
<Label TextColor="{AppThemeBinding Light=#212121, Dark=#FFFFFF}"
|
||||
Text="Theme-aware text"/>
|
||||
</Border>
|
||||
```
|
||||
|
||||
**Programmatic AppThemeBinding:**
|
||||
|
||||
```csharp
|
||||
label.SetAppThemeColor(Label.TextColorProperty,
|
||||
Colors.Black, // Light theme
|
||||
Colors.White); // Dark theme
|
||||
```
|
||||
|
||||
### Dialogs
|
||||
|
||||
**DisplayAlert (using LinuxDialogService):**
|
||||
|
||||
```csharp
|
||||
private async void OnDeleteClicked(object? sender, EventArgs e)
|
||||
{
|
||||
var confirmed = await LinuxDialogService.ShowAlertAsync(
|
||||
"Delete Task",
|
||||
$"Are you sure you want to delete \"{_todo.Title}\"?",
|
||||
"Delete",
|
||||
"Cancel");
|
||||
|
||||
if (confirmed)
|
||||
{
|
||||
_service.DeleteTodo(_todo);
|
||||
await Navigation.PopAsync();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### WebView
|
||||
|
||||
**Load URL:**
|
||||
|
||||
```csharp
|
||||
MainWebView.Source = new UrlWebViewSource { Url = "https://dotnet.microsoft.com" };
|
||||
```
|
||||
|
||||
**Load HTML:**
|
||||
|
||||
```csharp
|
||||
MainWebView.Source = new HtmlWebViewSource
|
||||
{
|
||||
Html = "<html><body><h1>Hello!</h1></body></html>"
|
||||
};
|
||||
```
|
||||
|
||||
**Execute JavaScript:**
|
||||
|
||||
```csharp
|
||||
var result = await MainWebView.EvaluateJavaScriptAsync("document.title");
|
||||
```
|
||||
|
||||
**Navigation Events:**
|
||||
|
||||
```csharp
|
||||
private void OnNavigating(object? sender, WebNavigatingEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"Navigating to: {e.Url}");
|
||||
// e.Cancel = true; // Cancel navigation if needed
|
||||
}
|
||||
|
||||
private void OnNavigated(object? sender, WebNavigatedEventArgs e)
|
||||
{
|
||||
Console.WriteLine($"Navigated: {e.Result} - {e.Url}");
|
||||
}
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- **[OpenMaui Linux Framework](https://git.marketally.com/open-maui/maui-linux)** - The core framework repository
|
||||
- **[NuGet Package](https://www.nuget.org/packages/OpenMaui.Controls.Linux)** - Install via NuGet
|
||||
- **[.NET MAUI Documentation](https://learn.microsoft.com/dotnet/maui/)** - Official Microsoft documentation
|
||||
- **[SkiaSharp Documentation](https://learn.microsoft.com/xamarin/xamarin-forms/user-interface/graphics/skiasharp/)** - Graphics rendering engine
|
||||
|
||||
## License
|
||||
|
||||
MIT License - See [LICENSE](LICENSE) for details.
|
||||
|
||||
Copyright (c) 2024 OpenMaui
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,78 +0,0 @@
|
||||
// ShellDemo App - Comprehensive Control Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
/// <summary>
|
||||
/// Main application class with Shell navigation.
|
||||
/// </summary>
|
||||
public class App : Application
|
||||
{
|
||||
public App()
|
||||
{
|
||||
MainPage = new AppShell();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shell definition with flyout menu - comprehensive control demo.
|
||||
/// </summary>
|
||||
public class AppShell : Shell
|
||||
{
|
||||
public AppShell()
|
||||
{
|
||||
FlyoutBehavior = FlyoutBehavior.Flyout;
|
||||
Title = "OpenMaui Controls Demo";
|
||||
|
||||
// Register routes for push navigation (pages not in flyout)
|
||||
Routing.RegisterRoute("detail", typeof(DetailPage));
|
||||
|
||||
// Home
|
||||
Items.Add(CreateFlyoutItem("Home", typeof(HomePage)));
|
||||
|
||||
// Buttons Demo
|
||||
Items.Add(CreateFlyoutItem("Buttons", typeof(ButtonsPage)));
|
||||
|
||||
// Text Input Demo
|
||||
Items.Add(CreateFlyoutItem("Text Input", typeof(TextInputPage)));
|
||||
|
||||
// Selection Controls Demo
|
||||
Items.Add(CreateFlyoutItem("Selection", typeof(SelectionPage)));
|
||||
|
||||
// Pickers Demo
|
||||
Items.Add(CreateFlyoutItem("Pickers", typeof(PickersPage)));
|
||||
|
||||
// Lists Demo
|
||||
Items.Add(CreateFlyoutItem("Lists", typeof(ListsPage)));
|
||||
|
||||
// Progress Demo
|
||||
Items.Add(CreateFlyoutItem("Progress", typeof(ProgressPage)));
|
||||
|
||||
// Grids Demo
|
||||
Items.Add(CreateFlyoutItem("Grids", typeof(GridsPage)));
|
||||
|
||||
// About
|
||||
Items.Add(CreateFlyoutItem("About", typeof(AboutPage)));
|
||||
}
|
||||
|
||||
private FlyoutItem CreateFlyoutItem(string title, Type pageType)
|
||||
{
|
||||
// Route is required for Shell.GoToAsync navigation to work
|
||||
var route = title.Replace(" ", "");
|
||||
return new FlyoutItem
|
||||
{
|
||||
Title = title,
|
||||
Route = route,
|
||||
Items =
|
||||
{
|
||||
new ShellContent
|
||||
{
|
||||
Title = title,
|
||||
Route = route,
|
||||
ContentTemplate = new DataTemplate(pageType)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
150
ShellDemo/App.xaml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="ShellDemo.App">
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
|
||||
<!-- Primary Colors -->
|
||||
<Color x:Key="PrimaryColor">#2196F3</Color>
|
||||
<Color x:Key="PrimaryDarkColor">#1976D2</Color>
|
||||
<Color x:Key="AccentColor">#FF4081</Color>
|
||||
<Color x:Key="SuccessColor">#4CAF50</Color>
|
||||
<Color x:Key="WarningColor">#FF9800</Color>
|
||||
<Color x:Key="DangerColor">#F44336</Color>
|
||||
<Color x:Key="PurpleColor">#9C27B0</Color>
|
||||
|
||||
<!-- Light Theme Colors -->
|
||||
<Color x:Key="PageBackgroundLight">#F8F8F8</Color>
|
||||
<Color x:Key="CardBackgroundLight">#FFFFFF</Color>
|
||||
<Color x:Key="TextPrimaryLight">#212121</Color>
|
||||
<Color x:Key="TextSecondaryLight">#757575</Color>
|
||||
<Color x:Key="BorderLight">#E0E0E0</Color>
|
||||
<Color x:Key="EntryBackgroundLight">#F9F9F9</Color>
|
||||
<Color x:Key="ShellBackgroundLight">#FFFFFF</Color>
|
||||
<Color x:Key="FlyoutBackgroundLight">#FFFFFF</Color>
|
||||
<Color x:Key="FlyoutHeaderBackgroundLight">#1976D2</Color>
|
||||
<Color x:Key="ProgressTrackLight">#E0E0E0</Color>
|
||||
|
||||
<!-- Dark Theme Colors -->
|
||||
<Color x:Key="PageBackgroundDark">#121212</Color>
|
||||
<Color x:Key="CardBackgroundDark">#1E1E1E</Color>
|
||||
<Color x:Key="TextPrimaryDark">#FFFFFF</Color>
|
||||
<Color x:Key="TextSecondaryDark">#B0B0B0</Color>
|
||||
<Color x:Key="BorderDark">#424242</Color>
|
||||
<Color x:Key="EntryBackgroundDark">#2C2C2C</Color>
|
||||
<Color x:Key="ShellBackgroundDark">#1E1E1E</Color>
|
||||
<Color x:Key="FlyoutBackgroundDark">#1E1E1E</Color>
|
||||
<Color x:Key="FlyoutHeaderBackgroundDark">#0D47A1</Color>
|
||||
<Color x:Key="ProgressTrackDark">#424242</Color>
|
||||
|
||||
<!-- Themed Entry Style -->
|
||||
<Style x:Key="ThemedEntry" TargetType="Entry">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource EntryBackgroundLight}, Dark={StaticResource EntryBackgroundDark}}" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<!-- Themed Editor Style -->
|
||||
<Style x:Key="ThemedEditor" TargetType="Editor">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource EntryBackgroundLight}, Dark={StaticResource EntryBackgroundDark}}" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<!-- Title Label Style -->
|
||||
<Style x:Key="TitleLabel" TargetType="Label">
|
||||
<Setter Property="FontSize" Value="24" />
|
||||
<Setter Property="FontAttributes" Value="Bold" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<!-- Subtitle Label Style -->
|
||||
<Style x:Key="SubtitleLabel" TargetType="Label">
|
||||
<Setter Property="FontSize" Value="16" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<!-- Body Label Style -->
|
||||
<Style x:Key="BodyLabel" TargetType="Label">
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<!-- Caption Label Style -->
|
||||
<Style x:Key="CaptionLabel" TargetType="Label">
|
||||
<Setter Property="FontSize" Value="12" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<!-- Themed Frame Style -->
|
||||
<Style x:Key="ThemedFrame" TargetType="Frame">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}" />
|
||||
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="Padding" Value="15" />
|
||||
</Style>
|
||||
|
||||
<!-- Themed Border Style -->
|
||||
<Style x:Key="ThemedBorder" TargetType="Border">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}" />
|
||||
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}" />
|
||||
<Setter Property="StrokeThickness" Value="1" />
|
||||
</Style>
|
||||
|
||||
<!-- Themed Card Border Style -->
|
||||
<Style x:Key="ThemedCard" TargetType="Border">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}" />
|
||||
<Setter Property="StrokeThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="15" />
|
||||
<Setter Property="StrokeShape">
|
||||
<Setter.Value>
|
||||
<RoundRectangle CornerRadius="8" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- Themed ProgressBar Style -->
|
||||
<Style x:Key="ThemedProgressBar" TargetType="ProgressBar">
|
||||
<Setter Property="ProgressColor" Value="{StaticResource PrimaryColor}" />
|
||||
</Style>
|
||||
|
||||
<!-- Primary Button Style -->
|
||||
<Style x:Key="PrimaryButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource PrimaryColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="CornerRadius" Value="5" />
|
||||
</Style>
|
||||
|
||||
<!-- Secondary Button Style -->
|
||||
<Style x:Key="SecondaryButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light=#E0E0E0, Dark=#424242}" />
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Setter Property="CornerRadius" Value="5" />
|
||||
</Style>
|
||||
|
||||
<!-- Success Button Style -->
|
||||
<Style x:Key="SuccessButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource SuccessColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="CornerRadius" Value="5" />
|
||||
</Style>
|
||||
|
||||
<!-- Danger Button Style -->
|
||||
<Style x:Key="DangerButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource DangerColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="CornerRadius" Value="5" />
|
||||
</Style>
|
||||
|
||||
<!-- Themed BoxView Divider -->
|
||||
<Style x:Key="ThemedDivider" TargetType="BoxView">
|
||||
<Setter Property="HeightRequest" Value="1" />
|
||||
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
|
||||
</Application>
|
||||
18
ShellDemo/App.xaml.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
// App.xaml.cs - Main Application code-behind
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override Window CreateWindow(IActivationState? activationState)
|
||||
{
|
||||
return new Window(new AppShell());
|
||||
}
|
||||
}
|
||||
104
ShellDemo/AppShell.xaml
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="clr-namespace:ShellDemo"
|
||||
x:Class="ShellDemo.AppShell"
|
||||
Title="OpenMaui Controls Demo"
|
||||
FlyoutBehavior="Flyout"
|
||||
FlyoutBackgroundColor="{AppThemeBinding Light={StaticResource FlyoutBackgroundLight}, Dark={StaticResource FlyoutBackgroundDark}}"
|
||||
Shell.BackgroundColor="{AppThemeBinding Light={StaticResource ShellBackgroundLight}, Dark={StaticResource ShellBackgroundDark}}"
|
||||
Shell.ForegroundColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
Shell.TitleColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}">
|
||||
|
||||
<!-- Flyout Header -->
|
||||
<Shell.FlyoutHeader>
|
||||
<Grid BackgroundColor="{AppThemeBinding Light=#E8E8E8, Dark=#2A2A2A}"
|
||||
HeightRequest="120">
|
||||
<VerticalStackLayout VerticalOptions="Center" HorizontalOptions="Fill">
|
||||
<Image Source="logo_only.png"
|
||||
WidthRequest="80"
|
||||
HeightRequest="80"
|
||||
HorizontalOptions="Center" />
|
||||
<Label Text="OpenMaui"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
HorizontalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
TextColor="{AppThemeBinding Light=#212121, Dark=#E0E0E0}" />
|
||||
<Label Text="Controls Demo"
|
||||
FontSize="11"
|
||||
HorizontalOptions="Center"
|
||||
HorizontalTextAlignment="Center"
|
||||
TextColor="{AppThemeBinding Light=#757575, Dark=#B0B0B0}" />
|
||||
</VerticalStackLayout>
|
||||
</Grid>
|
||||
</Shell.FlyoutHeader>
|
||||
|
||||
<!-- Flyout Footer -->
|
||||
<Shell.FlyoutFooter>
|
||||
<Grid BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
HeightRequest="50"
|
||||
Padding="16,0">
|
||||
<Label Text="{Binding AppVersion}"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
VerticalOptions="Center"
|
||||
HorizontalOptions="Start" />
|
||||
</Grid>
|
||||
</Shell.FlyoutFooter>
|
||||
|
||||
<!-- Home -->
|
||||
<FlyoutItem Title="Home" Route="Home"
|
||||
Icon="{AppThemeBinding Light=home_light.svg, Dark=home_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:HomePage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Buttons -->
|
||||
<FlyoutItem Title="Buttons" Route="Buttons"
|
||||
Icon="{AppThemeBinding Light=smart_button_light.svg, Dark=smart_button_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:ButtonsPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Text Input -->
|
||||
<FlyoutItem Title="Text Input" Route="TextInput"
|
||||
Icon="{AppThemeBinding Light=edit_note_light.svg, Dark=edit_note_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:TextInputPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Selection -->
|
||||
<FlyoutItem Title="Selection" Route="Selection"
|
||||
Icon="{AppThemeBinding Light=check_box_light.svg, Dark=check_box_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:SelectionPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Pickers -->
|
||||
<FlyoutItem Title="Pickers" Route="Pickers"
|
||||
Icon="{AppThemeBinding Light=calendar_month_light.svg, Dark=calendar_month_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:PickersPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Lists -->
|
||||
<FlyoutItem Title="Lists" Route="Lists"
|
||||
Icon="{AppThemeBinding Light=format_list_bulleted_light.svg, Dark=format_list_bulleted_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:ListsPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Progress -->
|
||||
<FlyoutItem Title="Progress" Route="Progress"
|
||||
Icon="{AppThemeBinding Light=hourglass_empty_light.svg, Dark=hourglass_empty_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:ProgressPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- Grids -->
|
||||
<FlyoutItem Title="Grids" Route="Grids"
|
||||
Icon="{AppThemeBinding Light=grid_view_light.svg, Dark=grid_view_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:GridsPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
<!-- About -->
|
||||
<FlyoutItem Title="About" Route="About"
|
||||
Icon="{AppThemeBinding Light=info_light.svg, Dark=info_dark.svg}">
|
||||
<ShellContent ContentTemplate="{DataTemplate local:AboutPage}" />
|
||||
</FlyoutItem>
|
||||
|
||||
</Shell>
|
||||
25
ShellDemo/AppShell.xaml.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// AppShell - Shell navigation with flyout menu
|
||||
|
||||
using System.Reflection;
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class AppShell : Shell
|
||||
{
|
||||
public string AppVersion { get; }
|
||||
|
||||
public AppShell()
|
||||
{
|
||||
// Get app version from assembly
|
||||
var version = Assembly.GetExecutingAssembly().GetName().Version;
|
||||
AppVersion = $"OpenMaui v{version?.Major ?? 1}.{version?.Minor ?? 0}.{version?.Build ?? 0}";
|
||||
|
||||
BindingContext = this;
|
||||
|
||||
InitializeComponent();
|
||||
|
||||
// Register routes for push navigation (pages not in flyout)
|
||||
Routing.RegisterRoute("detail", typeof(DetailPage));
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// AboutPage - Information about OpenMaui Linux
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class AboutPage : ContentPage
|
||||
{
|
||||
public AboutPage()
|
||||
{
|
||||
Title = "About";
|
||||
|
||||
Content = new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "OpenMaui Linux",
|
||||
FontSize = 32,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
TextColor = Color.FromArgb("#1A237E"),
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = "Version 1.0.0",
|
||||
FontSize = 16,
|
||||
TextColor = Colors.Gray,
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
},
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Label
|
||||
{
|
||||
Text = "OpenMaui Linux brings .NET MAUI to Linux desktops using SkiaSharp for rendering. " +
|
||||
"It provides a native Linux experience while maintaining compatibility with MAUI's cross-platform API.",
|
||||
FontSize = 14,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
},
|
||||
CreateInfoCard("Platform", "Linux (X11/Wayland)"),
|
||||
CreateInfoCard("Rendering", "SkiaSharp"),
|
||||
CreateInfoCard("Framework", ".NET MAUI"),
|
||||
CreateInfoCard("License", "MIT License"),
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Label
|
||||
{
|
||||
Text = "Features",
|
||||
FontSize = 20,
|
||||
FontAttributes = FontAttributes.Bold
|
||||
},
|
||||
CreateFeatureItem("Full XAML support with styles and resources"),
|
||||
CreateFeatureItem("Shell navigation with flyout menus"),
|
||||
CreateFeatureItem("All standard MAUI controls"),
|
||||
CreateFeatureItem("Data binding and MVVM"),
|
||||
CreateFeatureItem("Keyboard and mouse input"),
|
||||
CreateFeatureItem("High DPI support"),
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Label
|
||||
{
|
||||
Text = "https://github.com/pablotoledo/OpenMaui-Linux",
|
||||
FontSize = 12,
|
||||
TextColor = Colors.Blue,
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Frame CreateInfoCard(string label, string value)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||||
HasShadow = false,
|
||||
Content = new HorizontalStackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = label + ":",
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
WidthRequest = 100
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = value,
|
||||
TextColor = Colors.Gray
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateFeatureItem(string text)
|
||||
{
|
||||
return new HorizontalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "✓", TextColor = Color.FromArgb("#4CAF50"), FontSize = 16 },
|
||||
new Label { Text = text, FontSize = 14 }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
118
ShellDemo/Pages/AboutPage.xaml
Normal file
@@ -0,0 +1,118 @@
|
||||
<?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.AboutPage"
|
||||
Title="About"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ScrollView>
|
||||
<VerticalStackLayout Padding="20" Spacing="20">
|
||||
|
||||
<!-- Header -->
|
||||
<Label Text="OpenMaui Linux"
|
||||
FontSize="32"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light=#1A237E, Dark=#7986CB}"
|
||||
HorizontalOptions="Center" />
|
||||
|
||||
<Label Text="Version 1.0.0"
|
||||
FontSize="16"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalOptions="Center" />
|
||||
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<Label Text="OpenMaui Linux brings .NET MAUI to Linux desktops using SkiaSharp for rendering. It provides a native Linux experience while maintaining compatibility with MAUI's cross-platform API."
|
||||
FontSize="14"
|
||||
LineBreakMode="WordWrap"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Info Cards -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="Platform:" FontAttributes="Bold" WidthRequest="100"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Linux (X11/Wayland)" TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="Rendering:" FontAttributes="Bold" WidthRequest="100"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="SkiaSharp" TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="Framework:" FontAttributes="Bold" WidthRequest="100"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text=".NET MAUI" TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="License:" FontAttributes="Bold" WidthRequest="100"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="MIT License" TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<!-- Features Section -->
|
||||
<Label Text="Features"
|
||||
FontSize="20"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="•" TextColor="{StaticResource SuccessColor}" FontSize="16" />
|
||||
<Label Text="Full XAML support with styles and resources" FontSize="14" TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="•" TextColor="{StaticResource SuccessColor}" FontSize="16" />
|
||||
<Label Text="Shell navigation with flyout menus" FontSize="14" TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="•" TextColor="{StaticResource SuccessColor}" FontSize="16" />
|
||||
<Label Text="All standard MAUI controls" FontSize="14" TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="•" TextColor="{StaticResource SuccessColor}" FontSize="16" />
|
||||
<Label Text="Data binding and MVVM" FontSize="14" TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="•" TextColor="{StaticResource SuccessColor}" FontSize="16" />
|
||||
<Label Text="Keyboard and mouse input" FontSize="14"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="•" TextColor="{StaticResource SuccessColor}" FontSize="16" />
|
||||
<Label Text="High DPI support" FontSize="14"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<Label x:Name="WebsiteLink"
|
||||
Text="https://www.openmaui.net"
|
||||
FontSize="12"
|
||||
TextColor="{StaticResource PrimaryColor}"
|
||||
TextDecorations="Underline"
|
||||
HorizontalOptions="Center">
|
||||
<Label.GestureRecognizers>
|
||||
<TapGestureRecognizer Tapped="OnWebsiteLinkTapped" />
|
||||
</Label.GestureRecognizers>
|
||||
</Label>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage>
|
||||
25
ShellDemo/Pages/AboutPage.xaml.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
// AboutPage - Information about OpenMaui Linux
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class AboutPage : ContentPage
|
||||
{
|
||||
public AboutPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void OnWebsiteLinkTapped(object? sender, TappedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Microsoft.Maui.Platform.Linux.Services.Browser.OpenAsync("https://www.openmaui.net", BrowserLaunchMode.SystemPreferred);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Failed to open URL: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
// ButtonsPage - Comprehensive Button Control Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class ButtonsPage : ContentPage
|
||||
{
|
||||
private readonly Label _eventLog;
|
||||
private int _eventCount = 0;
|
||||
|
||||
public ButtonsPage()
|
||||
{
|
||||
Title = "Buttons Demo";
|
||||
|
||||
_eventLog = new Label
|
||||
{
|
||||
Text = "Events will appear here...",
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
};
|
||||
|
||||
Content = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
|
||||
new RowDefinition { Height = new GridLength(120) }
|
||||
},
|
||||
Children =
|
||||
{
|
||||
CreateMainContent(),
|
||||
CreateEventLogPanel()
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetRow((View)((Grid)Content).Children[0], 0);
|
||||
Grid.SetRow((View)((Grid)Content).Children[1], 1);
|
||||
}
|
||||
|
||||
private View CreateMainContent()
|
||||
{
|
||||
return new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Button Styles & Events", FontSize = 24, FontAttributes = FontAttributes.Bold },
|
||||
|
||||
// Basic Buttons
|
||||
CreateSection("Basic Buttons", CreateBasicButtons()),
|
||||
|
||||
// Styled Buttons
|
||||
CreateSection("Styled Buttons", CreateStyledButtons()),
|
||||
|
||||
// Button States
|
||||
CreateSection("Button States", CreateButtonStates()),
|
||||
|
||||
// Button with Icons (text simulation)
|
||||
CreateSection("Button Variations", CreateButtonVariations())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateBasicButtons()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var defaultBtn = new Button { Text = "Default Button" };
|
||||
defaultBtn.Clicked += (s, e) => LogEvent("Default Button clicked");
|
||||
defaultBtn.Pressed += (s, e) => LogEvent("Default Button pressed");
|
||||
defaultBtn.Released += (s, e) => LogEvent("Default Button released");
|
||||
|
||||
var textBtn = new Button { Text = "Text Only", BackgroundColor = Colors.Transparent, TextColor = Colors.Blue };
|
||||
textBtn.Clicked += (s, e) => LogEvent("Text Button clicked");
|
||||
|
||||
layout.Children.Add(defaultBtn);
|
||||
layout.Children.Add(textBtn);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateStyledButtons()
|
||||
{
|
||||
var layout = new HorizontalStackLayout { Spacing = 10 };
|
||||
|
||||
var colors = new[]
|
||||
{
|
||||
("#2196F3", "Primary"),
|
||||
("#4CAF50", "Success"),
|
||||
("#FF9800", "Warning"),
|
||||
("#F44336", "Danger"),
|
||||
("#9C27B0", "Purple")
|
||||
};
|
||||
|
||||
foreach (var (color, name) in colors)
|
||||
{
|
||||
var btn = new Button
|
||||
{
|
||||
Text = name,
|
||||
BackgroundColor = Color.FromArgb(color),
|
||||
TextColor = Colors.White,
|
||||
CornerRadius = 5
|
||||
};
|
||||
btn.Clicked += (s, e) => LogEvent($"{name} button clicked");
|
||||
layout.Children.Add(btn);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateButtonStates()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var enabledBtn = new Button { Text = "Enabled Button", IsEnabled = true };
|
||||
enabledBtn.Clicked += (s, e) => LogEvent("Enabled button clicked");
|
||||
|
||||
var disabledBtn = new Button { Text = "Disabled Button", IsEnabled = false };
|
||||
|
||||
var toggleBtn = new Button { Text = "Toggle Above Button" };
|
||||
toggleBtn.Clicked += (s, e) =>
|
||||
{
|
||||
disabledBtn.IsEnabled = !disabledBtn.IsEnabled;
|
||||
disabledBtn.Text = disabledBtn.IsEnabled ? "Now Enabled!" : "Disabled Button";
|
||||
LogEvent($"Toggled button to: {(disabledBtn.IsEnabled ? "Enabled" : "Disabled")}");
|
||||
};
|
||||
|
||||
layout.Children.Add(enabledBtn);
|
||||
layout.Children.Add(disabledBtn);
|
||||
layout.Children.Add(toggleBtn);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateButtonVariations()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var wideBtn = new Button
|
||||
{
|
||||
Text = "Wide Button",
|
||||
HorizontalOptions = LayoutOptions.Fill,
|
||||
BackgroundColor = Color.FromArgb("#673AB7"),
|
||||
TextColor = Colors.White
|
||||
};
|
||||
wideBtn.Clicked += (s, e) => LogEvent("Wide button clicked");
|
||||
|
||||
var tallBtn = new Button
|
||||
{
|
||||
Text = "Tall Button",
|
||||
HeightRequest = 60,
|
||||
BackgroundColor = Color.FromArgb("#009688"),
|
||||
TextColor = Colors.White
|
||||
};
|
||||
tallBtn.Clicked += (s, e) => LogEvent("Tall button clicked");
|
||||
|
||||
var roundBtn = new Button
|
||||
{
|
||||
Text = "Round",
|
||||
WidthRequest = 80,
|
||||
HeightRequest = 80,
|
||||
CornerRadius = 40,
|
||||
BackgroundColor = Color.FromArgb("#E91E63"),
|
||||
TextColor = Colors.White
|
||||
};
|
||||
roundBtn.Clicked += (s, e) => LogEvent("Round button clicked");
|
||||
|
||||
layout.Children.Add(wideBtn);
|
||||
layout.Children.Add(tallBtn);
|
||||
layout.Children.Add(new HorizontalStackLayout { Children = { roundBtn } });
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private Frame CreateSection(string title, View content)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = title, FontSize = 16, FontAttributes = FontAttributes.Bold },
|
||||
content
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateEventLogPanel()
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||||
Padding = new Thickness(10),
|
||||
CornerRadius = 0,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Event Log:", FontSize = 12, FontAttributes = FontAttributes.Bold },
|
||||
new ScrollView
|
||||
{
|
||||
HeightRequest = 80,
|
||||
Content = _eventLog
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
_eventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{_eventLog.Text}";
|
||||
}
|
||||
}
|
||||
150
ShellDemo/Pages/ButtonsPage.xaml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?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.ButtonsPage"
|
||||
Title="Buttons Demo"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<Grid RowDefinitions="*,120">
|
||||
|
||||
<!-- Main Content -->
|
||||
<ScrollView Grid.Row="0">
|
||||
<VerticalStackLayout Padding="20" Spacing="20">
|
||||
|
||||
<Label Text="Button Styles & Events"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic Buttons Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Basic Buttons"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Button x:Name="DefaultButton"
|
||||
Text="Default Button"
|
||||
Clicked="OnDefaultButtonClicked" />
|
||||
|
||||
<Button x:Name="TextButton"
|
||||
Text="Text Only"
|
||||
BackgroundColor="Transparent"
|
||||
TextColor="{StaticResource PrimaryColor}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Styled Buttons Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Styled Buttons"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Button Text="Primary"
|
||||
Style="{StaticResource PrimaryButton}"
|
||||
Clicked="OnStyledButtonClicked" />
|
||||
<Button Text="Success"
|
||||
Style="{StaticResource SuccessButton}"
|
||||
Clicked="OnStyledButtonClicked" />
|
||||
<Button Text="Warning"
|
||||
BackgroundColor="{StaticResource WarningColor}"
|
||||
TextColor="White"
|
||||
CornerRadius="5"
|
||||
Clicked="OnStyledButtonClicked" />
|
||||
<Button Text="Danger"
|
||||
Style="{StaticResource DangerButton}"
|
||||
Clicked="OnStyledButtonClicked" />
|
||||
<Button Text="Purple"
|
||||
BackgroundColor="{StaticResource PurpleColor}"
|
||||
TextColor="White"
|
||||
CornerRadius="5"
|
||||
Clicked="OnStyledButtonClicked" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Button States Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Button States"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Button x:Name="EnabledButton"
|
||||
Text="Enabled Button"
|
||||
IsEnabled="True"
|
||||
Clicked="OnEnabledButtonClicked" />
|
||||
|
||||
<Button x:Name="DisabledButton"
|
||||
Text="Disabled Button"
|
||||
IsEnabled="False" />
|
||||
|
||||
<Button x:Name="ToggleButton"
|
||||
Text="Toggle Above Button"
|
||||
Style="{StaticResource SecondaryButton}"
|
||||
Clicked="OnToggleButtonClicked" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Button Variations Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Button Variations"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Button Text="Wide Button"
|
||||
HorizontalOptions="Fill"
|
||||
BackgroundColor="#673AB7"
|
||||
TextColor="White"
|
||||
Clicked="OnVariationButtonClicked" />
|
||||
|
||||
<Button Text="Tall Button"
|
||||
HeightRequest="60"
|
||||
BackgroundColor="#009688"
|
||||
TextColor="White"
|
||||
Clicked="OnVariationButtonClicked" />
|
||||
|
||||
<HorizontalStackLayout>
|
||||
<Button Text="Round"
|
||||
WidthRequest="80"
|
||||
HeightRequest="80"
|
||||
CornerRadius="40"
|
||||
BackgroundColor="#E91E63"
|
||||
TextColor="White"
|
||||
Clicked="OnVariationButtonClicked" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Event Log Panel -->
|
||||
<Border Grid.Row="1"
|
||||
BackgroundColor="{AppThemeBinding Light=#F5F5F5, Dark=#2C2C2C}"
|
||||
StrokeThickness="0"
|
||||
Padding="10">
|
||||
<VerticalStackLayout>
|
||||
<Label Text="Event Log:"
|
||||
FontSize="12"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<ScrollView HeightRequest="80">
|
||||
<Label x:Name="EventLogLabel"
|
||||
Text="Events will appear here..."
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
LineBreakMode="WordWrap" />
|
||||
</ScrollView>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
55
ShellDemo/Pages/ButtonsPage.xaml.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
// ButtonsPage - Comprehensive Button Control Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class ButtonsPage : ContentPage
|
||||
{
|
||||
private int _eventCount = 0;
|
||||
|
||||
public ButtonsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnDefaultButtonClicked(object? sender, EventArgs e)
|
||||
{
|
||||
LogEvent("Default Button clicked");
|
||||
}
|
||||
|
||||
private void OnStyledButtonClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is Button btn)
|
||||
{
|
||||
LogEvent($"{btn.Text} button clicked");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEnabledButtonClicked(object? sender, EventArgs e)
|
||||
{
|
||||
LogEvent("Enabled button clicked");
|
||||
}
|
||||
|
||||
private void OnToggleButtonClicked(object? sender, EventArgs e)
|
||||
{
|
||||
DisabledButton.IsEnabled = !DisabledButton.IsEnabled;
|
||||
DisabledButton.Text = DisabledButton.IsEnabled ? "Now Enabled!" : "Disabled Button";
|
||||
LogEvent($"Toggled button to: {(DisabledButton.IsEnabled ? "Enabled" : "Disabled")}");
|
||||
}
|
||||
|
||||
private void OnVariationButtonClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is Button btn)
|
||||
{
|
||||
LogEvent($"{btn.Text} button clicked");
|
||||
}
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
EventLogLabel.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLogLabel.Text}";
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
// ControlsPage - Demonstrates various MAUI controls
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class ControlsPage : ContentPage
|
||||
{
|
||||
public ControlsPage()
|
||||
{
|
||||
Title = "Controls";
|
||||
|
||||
Content = new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 15,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Control Gallery",
|
||||
FontSize = 24,
|
||||
FontAttributes = FontAttributes.Bold
|
||||
},
|
||||
|
||||
// Buttons
|
||||
CreateSection("Buttons", new View[]
|
||||
{
|
||||
CreateButtonRow()
|
||||
}),
|
||||
|
||||
// CheckBox & Switch
|
||||
CreateSection("Selection", new View[]
|
||||
{
|
||||
CreateCheckBoxRow(),
|
||||
CreateSwitchRow()
|
||||
}),
|
||||
|
||||
// Slider
|
||||
CreateSection("Slider", new View[]
|
||||
{
|
||||
CreateSliderRow()
|
||||
}),
|
||||
|
||||
// Picker
|
||||
CreateSection("Picker", new View[]
|
||||
{
|
||||
CreatePickerRow()
|
||||
}),
|
||||
|
||||
// Progress
|
||||
CreateSection("Progress", new View[]
|
||||
{
|
||||
CreateProgressRow()
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Frame CreateSection(string title, View[] content)
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
layout.Children.Add(new Label
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 18,
|
||||
FontAttributes = FontAttributes.Bold
|
||||
});
|
||||
|
||||
foreach (var view in content)
|
||||
{
|
||||
layout.Children.Add(view);
|
||||
}
|
||||
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
Content = layout
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateButtonRow()
|
||||
{
|
||||
var resultLabel = new Label { TextColor = Colors.Gray, FontSize = 12 };
|
||||
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var buttonRow = new HorizontalStackLayout { Spacing = 10 };
|
||||
|
||||
var primaryBtn = new Button { Text = "Primary", BackgroundColor = Color.FromArgb("#2196F3"), TextColor = Colors.White };
|
||||
primaryBtn.Clicked += (s, e) => resultLabel.Text = "Primary clicked!";
|
||||
|
||||
var successBtn = new Button { Text = "Success", BackgroundColor = Color.FromArgb("#4CAF50"), TextColor = Colors.White };
|
||||
successBtn.Clicked += (s, e) => resultLabel.Text = "Success clicked!";
|
||||
|
||||
var dangerBtn = new Button { Text = "Danger", BackgroundColor = Color.FromArgb("#F44336"), TextColor = Colors.White };
|
||||
dangerBtn.Clicked += (s, e) => resultLabel.Text = "Danger clicked!";
|
||||
|
||||
buttonRow.Children.Add(primaryBtn);
|
||||
buttonRow.Children.Add(successBtn);
|
||||
buttonRow.Children.Add(dangerBtn);
|
||||
|
||||
layout.Children.Add(buttonRow);
|
||||
layout.Children.Add(resultLabel);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateCheckBoxRow()
|
||||
{
|
||||
var layout = new HorizontalStackLayout { Spacing = 20 };
|
||||
|
||||
var cb1 = new CheckBox { IsChecked = true };
|
||||
var cb2 = new CheckBox { IsChecked = false };
|
||||
|
||||
layout.Children.Add(cb1);
|
||||
layout.Children.Add(new Label { Text = "Option 1", VerticalOptions = LayoutOptions.Center });
|
||||
layout.Children.Add(cb2);
|
||||
layout.Children.Add(new Label { Text = "Option 2", VerticalOptions = LayoutOptions.Center });
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateSwitchRow()
|
||||
{
|
||||
var label = new Label { Text = "Off", VerticalOptions = LayoutOptions.Center };
|
||||
var sw = new Switch { IsToggled = false };
|
||||
sw.Toggled += (s, e) => label.Text = e.Value ? "On" : "Off";
|
||||
|
||||
return new HorizontalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children = { sw, label }
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateSliderRow()
|
||||
{
|
||||
var label = new Label { Text = "Value: 50" };
|
||||
var slider = new Slider { Minimum = 0, Maximum = 100, Value = 50 };
|
||||
slider.ValueChanged += (s, e) => label.Text = $"Value: {(int)e.NewValue}";
|
||||
|
||||
return new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children = { slider, label }
|
||||
};
|
||||
}
|
||||
|
||||
private View CreatePickerRow()
|
||||
{
|
||||
var label = new Label { Text = "Selected: (none)", TextColor = Colors.Gray };
|
||||
var picker = new Picker { Title = "Select a fruit" };
|
||||
picker.Items.Add("Apple");
|
||||
picker.Items.Add("Banana");
|
||||
picker.Items.Add("Cherry");
|
||||
picker.Items.Add("Date");
|
||||
picker.Items.Add("Elderberry");
|
||||
|
||||
picker.SelectedIndexChanged += (s, e) =>
|
||||
{
|
||||
if (picker.SelectedIndex >= 0)
|
||||
label.Text = $"Selected: {picker.Items[picker.SelectedIndex]}";
|
||||
};
|
||||
|
||||
return new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children = { picker, label }
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateProgressRow()
|
||||
{
|
||||
var progress = new ProgressBar { Progress = 0.7 };
|
||||
var activity = new ActivityIndicator { IsRunning = true };
|
||||
|
||||
return new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
progress,
|
||||
new Label { Text = "70% Complete", FontSize = 12, TextColor = Colors.Gray },
|
||||
new HorizontalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
activity,
|
||||
new Label { Text = "Loading...", VerticalOptions = LayoutOptions.Center, TextColor = Colors.Gray }
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
132
ShellDemo/Pages/ControlsPage.xaml
Normal file
@@ -0,0 +1,132 @@
|
||||
<?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.ControlsPage"
|
||||
Title="Controls"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ScrollView>
|
||||
<VerticalStackLayout Padding="20" Spacing="15">
|
||||
|
||||
<Label Text="Control Gallery"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Buttons Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Buttons"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Button Text="Primary"
|
||||
Style="{StaticResource PrimaryButton}"
|
||||
Clicked="OnButtonClicked" />
|
||||
<Button Text="Success"
|
||||
Style="{StaticResource SuccessButton}"
|
||||
Clicked="OnButtonClicked" />
|
||||
<Button Text="Danger"
|
||||
Style="{StaticResource DangerButton}"
|
||||
Clicked="OnButtonClicked" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<Label x:Name="ButtonResultLabel"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Selection Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Selection"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- CheckBox Row -->
|
||||
<HorizontalStackLayout Spacing="20">
|
||||
<CheckBox x:Name="Checkbox1" IsChecked="True" />
|
||||
<Label Text="Option 1" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<CheckBox x:Name="Checkbox2" IsChecked="False" />
|
||||
<Label Text="Option 2" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Switch Row -->
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Switch x:Name="MainSwitch" IsToggled="False" Toggled="OnSwitchToggled" />
|
||||
<Label x:Name="SwitchLabel" Text="Off" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Slider Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Slider"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Slider x:Name="MainSlider" Minimum="0" Maximum="100" Value="50" ValueChanged="OnSliderValueChanged" />
|
||||
<Label x:Name="SliderLabel" Text="Value: 50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Picker Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Picker"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Picker x:Name="MainPicker"
|
||||
Title="Select a fruit"
|
||||
SelectedIndexChanged="OnPickerSelectedIndexChanged">
|
||||
<Picker.ItemsSource>
|
||||
<x:Array Type="{x:Type x:String}">
|
||||
<x:String>Apple</x:String>
|
||||
<x:String>Banana</x:String>
|
||||
<x:String>Cherry</x:String>
|
||||
<x:String>Date</x:String>
|
||||
<x:String>Elderberry</x:String>
|
||||
</x:Array>
|
||||
</Picker.ItemsSource>
|
||||
</Picker>
|
||||
<Label x:Name="PickerLabel" Text="Selected: (none)"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Progress Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="Progress"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<ProgressBar Progress="0.7" ProgressColor="{StaticResource PrimaryColor}" />
|
||||
<Label Text="70% Complete"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<ActivityIndicator IsRunning="True" Color="{StaticResource PrimaryColor}" />
|
||||
<Label Text="Loading..." VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage>
|
||||
39
ShellDemo/Pages/ControlsPage.xaml.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// ControlsPage - Demonstrates various MAUI controls
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class ControlsPage : ContentPage
|
||||
{
|
||||
public ControlsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnButtonClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (sender is Button btn)
|
||||
{
|
||||
ButtonResultLabel.Text = $"{btn.Text} clicked!";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSwitchToggled(object? sender, ToggledEventArgs e)
|
||||
{
|
||||
SwitchLabel.Text = e.Value ? "On" : "Off";
|
||||
}
|
||||
|
||||
private void OnSliderValueChanged(object? sender, ValueChangedEventArgs e)
|
||||
{
|
||||
SliderLabel.Text = $"Value: {(int)e.NewValue}";
|
||||
}
|
||||
|
||||
private void OnPickerSelectedIndexChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (MainPicker.SelectedIndex >= 0)
|
||||
{
|
||||
PickerLabel.Text = $"Selected: {MainPicker.ItemsSource[MainPicker.SelectedIndex]}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
// DetailPage - Demonstrates push/pop navigation
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
/// <summary>
|
||||
/// A detail page that can be pushed onto the navigation stack.
|
||||
/// </summary>
|
||||
public class DetailPage : ContentPage
|
||||
{
|
||||
private readonly string _itemName;
|
||||
|
||||
public DetailPage() : this("Detail Item")
|
||||
{
|
||||
}
|
||||
|
||||
public DetailPage(string itemName)
|
||||
{
|
||||
_itemName = itemName;
|
||||
Title = "Detail Page";
|
||||
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(30),
|
||||
Spacing = 20,
|
||||
VerticalOptions = LayoutOptions.Center,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Pushed Page",
|
||||
FontSize = 28,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
TextColor = Color.FromArgb("#9C27B0")
|
||||
},
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = $"You navigated to: {_itemName}",
|
||||
FontSize = 16,
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
},
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "This page was pushed onto the navigation stack using Shell.Current.GoToAsync()",
|
||||
FontSize = 14,
|
||||
TextColor = Colors.Gray,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
},
|
||||
|
||||
new BoxView
|
||||
{
|
||||
HeightRequest = 2,
|
||||
Color = Color.FromArgb("#E0E0E0"),
|
||||
Margin = new Thickness(0, 20)
|
||||
},
|
||||
|
||||
CreateBackButton(),
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "Use the back button above or the hardware/gesture back to pop this page",
|
||||
FontSize = 12,
|
||||
TextColor = Colors.Gray,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
Margin = new Thickness(0, 20, 0, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Button CreateBackButton()
|
||||
{
|
||||
var backBtn = new Button
|
||||
{
|
||||
Text = "Go Back (Pop)",
|
||||
BackgroundColor = Color.FromArgb("#9C27B0"),
|
||||
TextColor = Colors.White,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Padding = new Thickness(30, 10)
|
||||
};
|
||||
|
||||
backBtn.Clicked += (s, e) =>
|
||||
{
|
||||
// Pop this page off the navigation stack using LinuxViewRenderer
|
||||
Console.WriteLine("[DetailPage] Go Back clicked");
|
||||
var success = LinuxViewRenderer.PopPage();
|
||||
Console.WriteLine($"[DetailPage] PopPage result: {success}");
|
||||
};
|
||||
|
||||
return backBtn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query property for passing data to DetailPage.
|
||||
/// </summary>
|
||||
[QueryProperty(nameof(ItemName), "item")]
|
||||
public class DetailPageWithQuery : DetailPage
|
||||
{
|
||||
private string _itemName = "Item";
|
||||
|
||||
public string ItemName
|
||||
{
|
||||
get => _itemName;
|
||||
set
|
||||
{
|
||||
_itemName = value;
|
||||
// Update the title when the property is set
|
||||
Title = $"Detail: {value}";
|
||||
}
|
||||
}
|
||||
|
||||
public DetailPageWithQuery() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
48
ShellDemo/Pages/DetailPage.xaml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?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.DetailPage"
|
||||
Title="Detail Page"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<VerticalStackLayout Padding="30"
|
||||
Spacing="20"
|
||||
VerticalOptions="Center">
|
||||
|
||||
<Label Text="Pushed Page"
|
||||
FontSize="28"
|
||||
FontAttributes="Bold"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="{StaticResource PurpleColor}" />
|
||||
|
||||
<Label x:Name="ItemNameLabel"
|
||||
Text="You navigated to: Detail Item"
|
||||
FontSize="16"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Label Text="This page was pushed onto the navigation stack using Shell.Current.GoToAsync()"
|
||||
FontSize="14"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalTextAlignment="Center"
|
||||
LineBreakMode="WordWrap" />
|
||||
|
||||
<BoxView Style="{StaticResource ThemedDivider}" Margin="0,20" />
|
||||
|
||||
<Button x:Name="BackButton"
|
||||
Text="Go Back (Pop)"
|
||||
BackgroundColor="{StaticResource PurpleColor}"
|
||||
TextColor="White"
|
||||
HorizontalOptions="Center"
|
||||
Padding="30,10"
|
||||
CornerRadius="5"
|
||||
Clicked="OnBackClicked" />
|
||||
|
||||
<Label Text="Use the back button above or the hardware/gesture back to pop this page"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalTextAlignment="Center"
|
||||
Margin="0,20,0,0" />
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ContentPage>
|
||||
55
ShellDemo/Pages/DetailPage.xaml.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
// DetailPage - Demonstrates push/pop navigation
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
/// <summary>
|
||||
/// A detail page that can be pushed onto the navigation stack.
|
||||
/// </summary>
|
||||
public partial class DetailPage : ContentPage
|
||||
{
|
||||
private readonly string _itemName;
|
||||
|
||||
public DetailPage() : this("Detail Item")
|
||||
{
|
||||
}
|
||||
|
||||
public DetailPage(string itemName)
|
||||
{
|
||||
_itemName = itemName;
|
||||
InitializeComponent();
|
||||
ItemNameLabel.Text = $"You navigated to: {_itemName}";
|
||||
}
|
||||
|
||||
private void OnBackClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[DetailPage] Go Back clicked");
|
||||
var success = LinuxViewRenderer.PopPage();
|
||||
Console.WriteLine($"[DetailPage] PopPage result: {success}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query property for passing data to DetailPage.
|
||||
/// </summary>
|
||||
[QueryProperty(nameof(ItemName), "item")]
|
||||
public class DetailPageWithQuery : DetailPage
|
||||
{
|
||||
private string _itemName = "Item";
|
||||
|
||||
public string ItemName
|
||||
{
|
||||
get => _itemName;
|
||||
set
|
||||
{
|
||||
_itemName = value;
|
||||
Title = $"Detail: {value}";
|
||||
}
|
||||
}
|
||||
|
||||
public DetailPageWithQuery() : base()
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,594 +0,0 @@
|
||||
// GridsPage - Demonstrates Grid layouts with various options
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class GridsPage : ContentPage
|
||||
{
|
||||
public GridsPage()
|
||||
{
|
||||
Title = "Grids";
|
||||
|
||||
Content = new ScrollView
|
||||
{
|
||||
Orientation = ScrollOrientation.Both,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 25,
|
||||
Children =
|
||||
{
|
||||
CreateSectionHeader("Basic Grid (2x2)"),
|
||||
CreateBasicGrid(),
|
||||
|
||||
CreateSectionHeader("Column Definitions"),
|
||||
CreateColumnDefinitionsDemo(),
|
||||
|
||||
CreateSectionHeader("Row Definitions"),
|
||||
CreateRowDefinitionsDemo(),
|
||||
|
||||
CreateSectionHeader("Auto Rows (Empty vs Content)"),
|
||||
CreateAutoRowsDemo(),
|
||||
|
||||
CreateSectionHeader("Star Sizing (Proportional)"),
|
||||
CreateStarSizingDemo(),
|
||||
|
||||
CreateSectionHeader("Row & Column Spacing"),
|
||||
CreateSpacingDemo(),
|
||||
|
||||
CreateSectionHeader("Row & Column Span"),
|
||||
CreateSpanDemo(),
|
||||
|
||||
CreateSectionHeader("Mixed Sizing"),
|
||||
CreateMixedSizingDemo(),
|
||||
|
||||
CreateSectionHeader("Nested Grids"),
|
||||
CreateNestedGridDemo(),
|
||||
|
||||
new BoxView { HeightRequest = 20 } // Bottom padding
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Label CreateSectionHeader(string text)
|
||||
{
|
||||
return new Label
|
||||
{
|
||||
Text = text,
|
||||
FontSize = 18,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
TextColor = Color.FromArgb("#2196F3"),
|
||||
Margin = new Thickness(0, 10, 0, 5)
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateBasicGrid()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5")
|
||||
};
|
||||
|
||||
var cell1 = CreateCell("Row 0, Col 0", "#E3F2FD");
|
||||
var cell2 = CreateCell("Row 0, Col 1", "#E8F5E9");
|
||||
var cell3 = CreateCell("Row 1, Col 0", "#FFF3E0");
|
||||
var cell4 = CreateCell("Row 1, Col 1", "#FCE4EC");
|
||||
|
||||
Grid.SetRow(cell1, 0); Grid.SetColumn(cell1, 0);
|
||||
Grid.SetRow(cell2, 0); Grid.SetColumn(cell2, 1);
|
||||
Grid.SetRow(cell3, 1); Grid.SetColumn(cell3, 0);
|
||||
Grid.SetRow(cell4, 1); Grid.SetColumn(cell4, 1);
|
||||
|
||||
grid.Children.Add(cell1);
|
||||
grid.Children.Add(cell2);
|
||||
grid.Children.Add(cell3);
|
||||
grid.Children.Add(cell4);
|
||||
|
||||
return CreateDemoContainer(grid, "Equal columns using Star sizing");
|
||||
}
|
||||
|
||||
private View CreateColumnDefinitionsDemo()
|
||||
{
|
||||
var stack = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Auto width columns
|
||||
var autoGrid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition { Width = GridLength.Auto }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5")
|
||||
};
|
||||
|
||||
var a1 = CreateCell("Auto", "#BBDEFB");
|
||||
var a2 = CreateCell("Auto Width", "#C8E6C9");
|
||||
var a3 = CreateCell("A", "#FFECB3");
|
||||
Grid.SetColumn(a1, 0);
|
||||
Grid.SetColumn(a2, 1);
|
||||
Grid.SetColumn(a3, 2);
|
||||
autoGrid.Children.Add(a1);
|
||||
autoGrid.Children.Add(a2);
|
||||
autoGrid.Children.Add(a3);
|
||||
|
||||
stack.Children.Add(new Label { Text = "Auto: Sizes to content", FontSize = 12, TextColor = Colors.Gray });
|
||||
stack.Children.Add(autoGrid);
|
||||
|
||||
// Absolute width columns
|
||||
var absoluteGrid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(50) },
|
||||
new ColumnDefinition { Width = new GridLength(100) },
|
||||
new ColumnDefinition { Width = new GridLength(150) }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5")
|
||||
};
|
||||
|
||||
var b1 = CreateCell("50px", "#BBDEFB");
|
||||
var b2 = CreateCell("100px", "#C8E6C9");
|
||||
var b3 = CreateCell("150px", "#FFECB3");
|
||||
Grid.SetColumn(b1, 0);
|
||||
Grid.SetColumn(b2, 1);
|
||||
Grid.SetColumn(b3, 2);
|
||||
absoluteGrid.Children.Add(b1);
|
||||
absoluteGrid.Children.Add(b2);
|
||||
absoluteGrid.Children.Add(b3);
|
||||
|
||||
stack.Children.Add(new Label { Text = "Absolute: Fixed pixel widths (50, 100, 150)", FontSize = 12, TextColor = Colors.Gray, Margin = new Thickness(0, 10, 0, 0) });
|
||||
stack.Children.Add(absoluteGrid);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private View CreateRowDefinitionsDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
WidthRequest = 200,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(30) },
|
||||
new RowDefinition { Height = new GridLength(50) },
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = new GridLength(40) }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5")
|
||||
};
|
||||
|
||||
var r1 = CreateCell("30px height", "#BBDEFB");
|
||||
var r2 = CreateCell("50px height", "#C8E6C9");
|
||||
var r3 = CreateCell("Auto height\n(fits content)", "#FFECB3");
|
||||
var r4 = CreateCell("40px height", "#F8BBD9");
|
||||
|
||||
Grid.SetRow(r1, 0);
|
||||
Grid.SetRow(r2, 1);
|
||||
Grid.SetRow(r3, 2);
|
||||
Grid.SetRow(r4, 3);
|
||||
|
||||
grid.Children.Add(r1);
|
||||
grid.Children.Add(r2);
|
||||
grid.Children.Add(r3);
|
||||
grid.Children.Add(r4);
|
||||
|
||||
return CreateDemoContainer(grid, "Different row heights: 30px, 50px, Auto, 40px");
|
||||
}
|
||||
|
||||
private View CreateAutoRowsDemo()
|
||||
{
|
||||
var stack = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Grid with empty Auto row
|
||||
var emptyAutoGrid = new Grid
|
||||
{
|
||||
WidthRequest = 250,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(40) },
|
||||
new RowDefinition { Height = GridLength.Auto }, // Empty - should collapse
|
||||
new RowDefinition { Height = new GridLength(40) }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#E0E0E0")
|
||||
};
|
||||
|
||||
var r1 = CreateCell("Row 0: 40px", "#BBDEFB");
|
||||
// Row 1 is Auto with NO content - should be 0 height
|
||||
var r3 = CreateCell("Row 2: 40px", "#C8E6C9");
|
||||
|
||||
Grid.SetRow(r1, 0);
|
||||
Grid.SetRow(r3, 2); // Skip row 1
|
||||
|
||||
emptyAutoGrid.Children.Add(r1);
|
||||
emptyAutoGrid.Children.Add(r3);
|
||||
|
||||
stack.Children.Add(new Label { Text = "Empty Auto row (Row 1) should collapse to 0 height:", FontSize = 12, TextColor = Colors.Gray });
|
||||
stack.Children.Add(emptyAutoGrid);
|
||||
|
||||
// Grid with Auto row that has content
|
||||
var contentAutoGrid = new Grid
|
||||
{
|
||||
WidthRequest = 250,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(40) },
|
||||
new RowDefinition { Height = GridLength.Auto }, // Has content
|
||||
new RowDefinition { Height = new GridLength(40) }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#E0E0E0")
|
||||
};
|
||||
|
||||
var c1 = CreateCell("Row 0: 40px", "#BBDEFB");
|
||||
var c2 = CreateCell("Row 1: Auto (sized to this content)", "#FFECB3");
|
||||
var c3 = CreateCell("Row 2: 40px", "#C8E6C9");
|
||||
|
||||
Grid.SetRow(c1, 0);
|
||||
Grid.SetRow(c2, 1);
|
||||
Grid.SetRow(c3, 2);
|
||||
|
||||
contentAutoGrid.Children.Add(c1);
|
||||
contentAutoGrid.Children.Add(c2);
|
||||
contentAutoGrid.Children.Add(c3);
|
||||
|
||||
stack.Children.Add(new Label { Text = "Auto row with content sizes to fit:", FontSize = 12, TextColor = Colors.Gray, Margin = new Thickness(0, 10, 0, 0) });
|
||||
stack.Children.Add(contentAutoGrid);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private View CreateStarSizingDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5")
|
||||
};
|
||||
|
||||
var s1 = CreateCell("1*", "#BBDEFB");
|
||||
var s2 = CreateCell("2* (double)", "#C8E6C9");
|
||||
var s3 = CreateCell("1*", "#FFECB3");
|
||||
|
||||
Grid.SetColumn(s1, 0);
|
||||
Grid.SetColumn(s2, 1);
|
||||
Grid.SetColumn(s3, 2);
|
||||
|
||||
grid.Children.Add(s1);
|
||||
grid.Children.Add(s2);
|
||||
grid.Children.Add(s3);
|
||||
|
||||
return CreateDemoContainer(grid, "Star proportions: 1* | 2* | 1* = 25% | 50% | 25%");
|
||||
}
|
||||
|
||||
private View CreateSpacingDemo()
|
||||
{
|
||||
var stack = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// No spacing
|
||||
var noSpacing = new Grid
|
||||
{
|
||||
RowSpacing = 0,
|
||||
ColumnSpacing = 0,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
AddFourCells(noSpacing);
|
||||
stack.Children.Add(new Label { Text = "No spacing (RowSpacing=0, ColumnSpacing=0)", FontSize = 12, TextColor = Colors.Gray });
|
||||
stack.Children.Add(noSpacing);
|
||||
|
||||
// With spacing
|
||||
var withSpacing = new Grid
|
||||
{
|
||||
RowSpacing = 10,
|
||||
ColumnSpacing = 10,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
AddFourCells(withSpacing);
|
||||
stack.Children.Add(new Label { Text = "With spacing (RowSpacing=10, ColumnSpacing=10)", FontSize = 12, TextColor = Colors.Gray, Margin = new Thickness(0, 10, 0, 0) });
|
||||
stack.Children.Add(withSpacing);
|
||||
|
||||
// Different row/column spacing
|
||||
var mixedSpacing = new Grid
|
||||
{
|
||||
RowSpacing = 5,
|
||||
ColumnSpacing = 20,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
AddFourCells(mixedSpacing);
|
||||
stack.Children.Add(new Label { Text = "Mixed spacing (RowSpacing=5, ColumnSpacing=20)", FontSize = 12, TextColor = Colors.Gray, Margin = new Thickness(0, 10, 0, 0) });
|
||||
stack.Children.Add(mixedSpacing);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private View CreateSpanDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
RowSpacing = 5,
|
||||
ColumnSpacing = 5,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
|
||||
// Spanning header
|
||||
var header = CreateCell("ColumnSpan=3 (Header)", "#1976D2", Colors.White);
|
||||
Grid.SetRow(header, 0);
|
||||
Grid.SetColumn(header, 0);
|
||||
Grid.SetColumnSpan(header, 3);
|
||||
|
||||
// Left sidebar spanning 2 rows
|
||||
var sidebar = CreateCell("RowSpan=2\n(Sidebar)", "#388E3C", Colors.White);
|
||||
Grid.SetRow(sidebar, 1);
|
||||
Grid.SetColumn(sidebar, 0);
|
||||
Grid.SetRowSpan(sidebar, 2);
|
||||
|
||||
// Content cells
|
||||
var content1 = CreateCell("Content 1", "#E3F2FD");
|
||||
Grid.SetRow(content1, 1);
|
||||
Grid.SetColumn(content1, 1);
|
||||
|
||||
var content2 = CreateCell("Content 2", "#E8F5E9");
|
||||
Grid.SetRow(content2, 1);
|
||||
Grid.SetColumn(content2, 2);
|
||||
|
||||
var content3 = CreateCell("Content 3", "#FFF3E0");
|
||||
Grid.SetRow(content3, 2);
|
||||
Grid.SetColumn(content3, 1);
|
||||
|
||||
var content4 = CreateCell("Content 4", "#FCE4EC");
|
||||
Grid.SetRow(content4, 2);
|
||||
Grid.SetColumn(content4, 2);
|
||||
|
||||
grid.Children.Add(header);
|
||||
grid.Children.Add(sidebar);
|
||||
grid.Children.Add(content1);
|
||||
grid.Children.Add(content2);
|
||||
grid.Children.Add(content3);
|
||||
grid.Children.Add(content4);
|
||||
|
||||
return CreateDemoContainer(grid, "Header spans 3 columns, Sidebar spans 2 rows");
|
||||
}
|
||||
|
||||
private View CreateMixedSizingDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnSpacing = 5,
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(60) }, // Fixed
|
||||
new ColumnDefinition { Width = GridLength.Star }, // Fill
|
||||
new ColumnDefinition { Width = GridLength.Auto }, // Auto
|
||||
new ColumnDefinition { Width = new GridLength(60) } // Fixed
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5")
|
||||
};
|
||||
|
||||
var c1 = CreateCell("60px", "#BBDEFB");
|
||||
var c2 = CreateCell("Star (fills remaining)", "#C8E6C9");
|
||||
var c3 = CreateCell("Auto", "#FFECB3");
|
||||
var c4 = CreateCell("60px", "#F8BBD9");
|
||||
|
||||
Grid.SetColumn(c1, 0);
|
||||
Grid.SetColumn(c2, 1);
|
||||
Grid.SetColumn(c3, 2);
|
||||
Grid.SetColumn(c4, 3);
|
||||
|
||||
grid.Children.Add(c1);
|
||||
grid.Children.Add(c2);
|
||||
grid.Children.Add(c3);
|
||||
grid.Children.Add(c4);
|
||||
|
||||
return CreateDemoContainer(grid, "Mixed: 60px | Star | Auto | 60px");
|
||||
}
|
||||
|
||||
private View CreateNestedGridDemo()
|
||||
{
|
||||
var outerGrid = new Grid
|
||||
{
|
||||
RowSpacing = 10,
|
||||
ColumnSpacing = 10,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
},
|
||||
BackgroundColor = Color.FromArgb("#E0E0E0"),
|
||||
Padding = new Thickness(10)
|
||||
};
|
||||
|
||||
// Nested grid 1
|
||||
var innerGrid1 = new Grid
|
||||
{
|
||||
RowSpacing = 2,
|
||||
ColumnSpacing = 2,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
var i1a = CreateCell("A", "#BBDEFB", null, 8);
|
||||
var i1b = CreateCell("B", "#90CAF9", null, 8);
|
||||
var i1c = CreateCell("C", "#64B5F6", null, 8);
|
||||
var i1d = CreateCell("D", "#42A5F5", null, 8);
|
||||
Grid.SetRow(i1a, 0); Grid.SetColumn(i1a, 0);
|
||||
Grid.SetRow(i1b, 0); Grid.SetColumn(i1b, 1);
|
||||
Grid.SetRow(i1c, 1); Grid.SetColumn(i1c, 0);
|
||||
Grid.SetRow(i1d, 1); Grid.SetColumn(i1d, 1);
|
||||
innerGrid1.Children.Add(i1a);
|
||||
innerGrid1.Children.Add(i1b);
|
||||
innerGrid1.Children.Add(i1c);
|
||||
innerGrid1.Children.Add(i1d);
|
||||
|
||||
// Nested grid 2
|
||||
var innerGrid2 = new Grid
|
||||
{
|
||||
RowSpacing = 2,
|
||||
ColumnSpacing = 2,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
var i2a = CreateCell("1", "#C8E6C9", null, 8);
|
||||
var i2b = CreateCell("2", "#A5D6A7", null, 8);
|
||||
var i2c = CreateCell("3", "#81C784", null, 8);
|
||||
var i2d = CreateCell("4", "#66BB6A", null, 8);
|
||||
Grid.SetRow(i2a, 0); Grid.SetColumn(i2a, 0);
|
||||
Grid.SetRow(i2b, 0); Grid.SetColumn(i2b, 1);
|
||||
Grid.SetRow(i2c, 1); Grid.SetColumn(i2c, 0);
|
||||
Grid.SetRow(i2d, 1); Grid.SetColumn(i2d, 1);
|
||||
innerGrid2.Children.Add(i2a);
|
||||
innerGrid2.Children.Add(i2b);
|
||||
innerGrid2.Children.Add(i2c);
|
||||
innerGrid2.Children.Add(i2d);
|
||||
|
||||
Grid.SetRow(innerGrid1, 0); Grid.SetColumn(innerGrid1, 0);
|
||||
Grid.SetRow(innerGrid2, 0); Grid.SetColumn(innerGrid2, 1);
|
||||
|
||||
var label1 = new Label { Text = "Outer Grid Row 1", HorizontalOptions = LayoutOptions.Center };
|
||||
var label2 = new Label { Text = "Spans both columns", HorizontalOptions = LayoutOptions.Center };
|
||||
Grid.SetRow(label1, 1); Grid.SetColumn(label1, 0);
|
||||
Grid.SetRow(label2, 1); Grid.SetColumn(label2, 1);
|
||||
|
||||
outerGrid.Children.Add(innerGrid1);
|
||||
outerGrid.Children.Add(innerGrid2);
|
||||
outerGrid.Children.Add(label1);
|
||||
outerGrid.Children.Add(label2);
|
||||
|
||||
return CreateDemoContainer(outerGrid, "Outer grid contains two nested 2x2 grids");
|
||||
}
|
||||
|
||||
private Border CreateCell(string text, string bgColor, Color? textColor = null, float fontSize = 12)
|
||||
{
|
||||
return new Border
|
||||
{
|
||||
BackgroundColor = Color.FromArgb(bgColor),
|
||||
Padding = new Thickness(10, 8),
|
||||
StrokeThickness = 0,
|
||||
Content = new Label
|
||||
{
|
||||
Text = text,
|
||||
FontSize = fontSize,
|
||||
TextColor = textColor ?? Colors.Black,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
VerticalTextAlignment = TextAlignment.Center
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void AddFourCells(Grid grid)
|
||||
{
|
||||
var c1 = CreateCell("0,0", "#BBDEFB");
|
||||
var c2 = CreateCell("0,1", "#C8E6C9");
|
||||
var c3 = CreateCell("1,0", "#FFECB3");
|
||||
var c4 = CreateCell("1,1", "#F8BBD9");
|
||||
|
||||
Grid.SetRow(c1, 0); Grid.SetColumn(c1, 0);
|
||||
Grid.SetRow(c2, 0); Grid.SetColumn(c2, 1);
|
||||
Grid.SetRow(c3, 1); Grid.SetColumn(c3, 0);
|
||||
Grid.SetRow(c4, 1); Grid.SetColumn(c4, 1);
|
||||
|
||||
grid.Children.Add(c1);
|
||||
grid.Children.Add(c2);
|
||||
grid.Children.Add(c3);
|
||||
grid.Children.Add(c4);
|
||||
}
|
||||
|
||||
private View CreateDemoContainer(View content, string description)
|
||||
{
|
||||
return new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = description, FontSize = 12, TextColor = Colors.Gray },
|
||||
content
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
13
ShellDemo/Pages/GridsPage.xaml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?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.GridsPage"
|
||||
Title="Grids"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ScrollView Orientation="Both">
|
||||
<VerticalStackLayout x:Name="MainContent" Spacing="25" Padding="20">
|
||||
<!-- Content will be added dynamically with proper theming -->
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage>
|
||||
431
ShellDemo/Pages/GridsPage.xaml.cs
Normal file
@@ -0,0 +1,431 @@
|
||||
// GridsPage - Demonstrates Grid layouts with various options
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class GridsPage : ContentPage
|
||||
{
|
||||
public GridsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
BuildContent();
|
||||
}
|
||||
|
||||
private void BuildContent()
|
||||
{
|
||||
MainContent.Children.Add(CreateSectionHeader("Basic Grid (2x2)"));
|
||||
MainContent.Children.Add(CreateBasicGrid());
|
||||
|
||||
MainContent.Children.Add(CreateSectionHeader("Column Definitions"));
|
||||
MainContent.Children.Add(CreateColumnDefinitionsDemo());
|
||||
|
||||
MainContent.Children.Add(CreateSectionHeader("Row Definitions"));
|
||||
MainContent.Children.Add(CreateRowDefinitionsDemo());
|
||||
|
||||
MainContent.Children.Add(CreateSectionHeader("Star Sizing (Proportional)"));
|
||||
MainContent.Children.Add(CreateStarSizingDemo());
|
||||
|
||||
MainContent.Children.Add(CreateSectionHeader("Row & Column Spacing"));
|
||||
MainContent.Children.Add(CreateSpacingDemo());
|
||||
|
||||
MainContent.Children.Add(CreateSectionHeader("Row & Column Span"));
|
||||
MainContent.Children.Add(CreateSpanDemo());
|
||||
|
||||
MainContent.Children.Add(CreateSectionHeader("Mixed Sizing"));
|
||||
MainContent.Children.Add(CreateMixedSizingDemo());
|
||||
|
||||
MainContent.Children.Add(new BoxView { HeightRequest = 20 });
|
||||
}
|
||||
|
||||
private Label CreateSectionHeader(string text)
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
Text = text,
|
||||
FontSize = 18,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
Margin = new Thickness(0, 10, 0, 5)
|
||||
};
|
||||
label.SetAppThemeColor(Label.TextColorProperty,
|
||||
Color.FromArgb("#2196F3"),
|
||||
Color.FromArgb("#64B5F6"));
|
||||
return label;
|
||||
}
|
||||
|
||||
private View CreateBasicGrid()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
grid.SetAppThemeColor(Grid.BackgroundColorProperty,
|
||||
Color.FromArgb("#F5F5F5"),
|
||||
Color.FromArgb("#2C2C2C"));
|
||||
|
||||
var cell1 = CreateCell("Row 0, Col 0", "#E3F2FD", "#1A3A5C");
|
||||
var cell2 = CreateCell("Row 0, Col 1", "#E8F5E9", "#1A3C1A");
|
||||
var cell3 = CreateCell("Row 1, Col 0", "#FFF3E0", "#3C2A1A");
|
||||
var cell4 = CreateCell("Row 1, Col 1", "#FCE4EC", "#3C1A2A");
|
||||
|
||||
Grid.SetRow(cell1, 0); Grid.SetColumn(cell1, 0);
|
||||
Grid.SetRow(cell2, 0); Grid.SetColumn(cell2, 1);
|
||||
Grid.SetRow(cell3, 1); Grid.SetColumn(cell3, 0);
|
||||
Grid.SetRow(cell4, 1); Grid.SetColumn(cell4, 1);
|
||||
|
||||
grid.Children.Add(cell1);
|
||||
grid.Children.Add(cell2);
|
||||
grid.Children.Add(cell3);
|
||||
grid.Children.Add(cell4);
|
||||
|
||||
return CreateDemoContainer(grid, "Equal columns using Star sizing");
|
||||
}
|
||||
|
||||
private View CreateColumnDefinitionsDemo()
|
||||
{
|
||||
var stack = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Auto width columns
|
||||
var autoGrid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition { Width = GridLength.Auto }
|
||||
}
|
||||
};
|
||||
autoGrid.SetAppThemeColor(Grid.BackgroundColorProperty,
|
||||
Color.FromArgb("#F5F5F5"),
|
||||
Color.FromArgb("#2C2C2C"));
|
||||
|
||||
var a1 = CreateCell("Auto", "#BBDEFB", "#1A3A5C");
|
||||
var a2 = CreateCell("Auto Width", "#C8E6C9", "#1A3C1A");
|
||||
var a3 = CreateCell("A", "#FFECB3", "#3C3A1A");
|
||||
Grid.SetColumn(a1, 0);
|
||||
Grid.SetColumn(a2, 1);
|
||||
Grid.SetColumn(a3, 2);
|
||||
autoGrid.Children.Add(a1);
|
||||
autoGrid.Children.Add(a2);
|
||||
autoGrid.Children.Add(a3);
|
||||
|
||||
var autoLabel = new Label { Text = "Auto: Sizes to content", FontSize = 12 };
|
||||
autoLabel.SetAppThemeColor(Label.TextColorProperty,
|
||||
Color.FromArgb("#757575"),
|
||||
Color.FromArgb("#B0B0B0"));
|
||||
stack.Children.Add(autoLabel);
|
||||
stack.Children.Add(autoGrid);
|
||||
|
||||
// Absolute width columns
|
||||
var absoluteGrid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(50) },
|
||||
new ColumnDefinition { Width = new GridLength(100) },
|
||||
new ColumnDefinition { Width = new GridLength(150) }
|
||||
}
|
||||
};
|
||||
absoluteGrid.SetAppThemeColor(Grid.BackgroundColorProperty,
|
||||
Color.FromArgb("#F5F5F5"),
|
||||
Color.FromArgb("#2C2C2C"));
|
||||
|
||||
var b1 = CreateCell("50px", "#BBDEFB", "#1A3A5C");
|
||||
var b2 = CreateCell("100px", "#C8E6C9", "#1A3C1A");
|
||||
var b3 = CreateCell("150px", "#FFECB3", "#3C3A1A");
|
||||
Grid.SetColumn(b1, 0);
|
||||
Grid.SetColumn(b2, 1);
|
||||
Grid.SetColumn(b3, 2);
|
||||
absoluteGrid.Children.Add(b1);
|
||||
absoluteGrid.Children.Add(b2);
|
||||
absoluteGrid.Children.Add(b3);
|
||||
|
||||
var absLabel = new Label { Text = "Absolute: Fixed pixel widths (50, 100, 150)", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) };
|
||||
absLabel.SetAppThemeColor(Label.TextColorProperty,
|
||||
Color.FromArgb("#757575"),
|
||||
Color.FromArgb("#B0B0B0"));
|
||||
stack.Children.Add(absLabel);
|
||||
stack.Children.Add(absoluteGrid);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private View CreateRowDefinitionsDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
WidthRequest = 200,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(30) },
|
||||
new RowDefinition { Height = new GridLength(50) },
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = new GridLength(40) }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
grid.SetAppThemeColor(Grid.BackgroundColorProperty,
|
||||
Color.FromArgb("#F5F5F5"),
|
||||
Color.FromArgb("#2C2C2C"));
|
||||
|
||||
var r1 = CreateCell("30px height", "#BBDEFB", "#1A3A5C");
|
||||
var r2 = CreateCell("50px height", "#C8E6C9", "#1A3C1A");
|
||||
var r3 = CreateCell("Auto height\n(fits content)", "#FFECB3", "#3C3A1A");
|
||||
var r4 = CreateCell("40px height", "#F8BBD9", "#3C1A3C");
|
||||
|
||||
Grid.SetRow(r1, 0);
|
||||
Grid.SetRow(r2, 1);
|
||||
Grid.SetRow(r3, 2);
|
||||
Grid.SetRow(r4, 3);
|
||||
|
||||
grid.Children.Add(r1);
|
||||
grid.Children.Add(r2);
|
||||
grid.Children.Add(r3);
|
||||
grid.Children.Add(r4);
|
||||
|
||||
return CreateDemoContainer(grid, "Different row heights: 30px, 50px, Auto, 40px");
|
||||
}
|
||||
|
||||
private View CreateStarSizingDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(2, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
|
||||
}
|
||||
};
|
||||
grid.SetAppThemeColor(Grid.BackgroundColorProperty,
|
||||
Color.FromArgb("#F5F5F5"),
|
||||
Color.FromArgb("#2C2C2C"));
|
||||
|
||||
var s1 = CreateCell("1*", "#BBDEFB", "#1A3A5C");
|
||||
var s2 = CreateCell("2* (double)", "#C8E6C9", "#1A3C1A");
|
||||
var s3 = CreateCell("1*", "#FFECB3", "#3C3A1A");
|
||||
|
||||
Grid.SetColumn(s1, 0);
|
||||
Grid.SetColumn(s2, 1);
|
||||
Grid.SetColumn(s3, 2);
|
||||
|
||||
grid.Children.Add(s1);
|
||||
grid.Children.Add(s2);
|
||||
grid.Children.Add(s3);
|
||||
|
||||
return CreateDemoContainer(grid, "Star proportions: 1* | 2* | 1* = 25% | 50% | 25%");
|
||||
}
|
||||
|
||||
private View CreateSpacingDemo()
|
||||
{
|
||||
var stack = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// No spacing
|
||||
var noSpacing = CreateSpacedGrid(0, 0);
|
||||
var noLabel = new Label { Text = "No spacing (RowSpacing=0, ColumnSpacing=0)", FontSize = 12 };
|
||||
noLabel.SetAppThemeColor(Label.TextColorProperty,
|
||||
Color.FromArgb("#757575"),
|
||||
Color.FromArgb("#B0B0B0"));
|
||||
stack.Children.Add(noLabel);
|
||||
stack.Children.Add(noSpacing);
|
||||
|
||||
// With spacing
|
||||
var withSpacing = CreateSpacedGrid(10, 10);
|
||||
var withLabel = new Label { Text = "With spacing (RowSpacing=10, ColumnSpacing=10)", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) };
|
||||
withLabel.SetAppThemeColor(Label.TextColorProperty,
|
||||
Color.FromArgb("#757575"),
|
||||
Color.FromArgb("#B0B0B0"));
|
||||
stack.Children.Add(withLabel);
|
||||
stack.Children.Add(withSpacing);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
private Grid CreateSpacedGrid(int rowSpacing, int columnSpacing)
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
RowSpacing = rowSpacing,
|
||||
ColumnSpacing = columnSpacing,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
|
||||
var c1 = CreateCell("0,0", "#BBDEFB", "#1A3A5C");
|
||||
var c2 = CreateCell("0,1", "#C8E6C9", "#1A3C1A");
|
||||
var c3 = CreateCell("1,0", "#FFECB3", "#3C3A1A");
|
||||
var c4 = CreateCell("1,1", "#F8BBD9", "#3C1A3C");
|
||||
|
||||
Grid.SetRow(c1, 0); Grid.SetColumn(c1, 0);
|
||||
Grid.SetRow(c2, 0); Grid.SetColumn(c2, 1);
|
||||
Grid.SetRow(c3, 1); Grid.SetColumn(c3, 0);
|
||||
Grid.SetRow(c4, 1); Grid.SetColumn(c4, 1);
|
||||
|
||||
grid.Children.Add(c1);
|
||||
grid.Children.Add(c2);
|
||||
grid.Children.Add(c3);
|
||||
grid.Children.Add(c4);
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
private View CreateSpanDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
RowSpacing = 5,
|
||||
ColumnSpacing = 5,
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Star }
|
||||
}
|
||||
};
|
||||
|
||||
// Spanning header
|
||||
var header = CreateCell("ColumnSpan=3 (Header)", "#1976D2", "#0D47A1", Colors.White);
|
||||
Grid.SetRow(header, 0);
|
||||
Grid.SetColumn(header, 0);
|
||||
Grid.SetColumnSpan(header, 3);
|
||||
|
||||
// Left sidebar spanning 2 rows
|
||||
var sidebar = CreateCell("RowSpan=2\n(Sidebar)", "#388E3C", "#1B5E20", Colors.White);
|
||||
Grid.SetRow(sidebar, 1);
|
||||
Grid.SetColumn(sidebar, 0);
|
||||
Grid.SetRowSpan(sidebar, 2);
|
||||
|
||||
// Content cells
|
||||
var content1 = CreateCell("Content 1", "#E3F2FD", "#1A3A5C");
|
||||
Grid.SetRow(content1, 1); Grid.SetColumn(content1, 1);
|
||||
|
||||
var content2 = CreateCell("Content 2", "#E8F5E9", "#1A3C1A");
|
||||
Grid.SetRow(content2, 1); Grid.SetColumn(content2, 2);
|
||||
|
||||
var content3 = CreateCell("Content 3", "#FFF3E0", "#3C2A1A");
|
||||
Grid.SetRow(content3, 2); Grid.SetColumn(content3, 1);
|
||||
|
||||
var content4 = CreateCell("Content 4", "#FCE4EC", "#3C1A2A");
|
||||
Grid.SetRow(content4, 2); Grid.SetColumn(content4, 2);
|
||||
|
||||
grid.Children.Add(header);
|
||||
grid.Children.Add(sidebar);
|
||||
grid.Children.Add(content1);
|
||||
grid.Children.Add(content2);
|
||||
grid.Children.Add(content3);
|
||||
grid.Children.Add(content4);
|
||||
|
||||
return CreateDemoContainer(grid, "Header spans 3 columns, Sidebar spans 2 rows");
|
||||
}
|
||||
|
||||
private View CreateMixedSizingDemo()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnSpacing = 5,
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(60) },
|
||||
new ColumnDefinition { Width = GridLength.Star },
|
||||
new ColumnDefinition { Width = GridLength.Auto },
|
||||
new ColumnDefinition { Width = new GridLength(60) }
|
||||
}
|
||||
};
|
||||
grid.SetAppThemeColor(Grid.BackgroundColorProperty,
|
||||
Color.FromArgb("#F5F5F5"),
|
||||
Color.FromArgb("#2C2C2C"));
|
||||
|
||||
var c1 = CreateCell("60px", "#BBDEFB", "#1A3A5C");
|
||||
var c2 = CreateCell("Star (fills remaining)", "#C8E6C9", "#1A3C1A");
|
||||
var c3 = CreateCell("Auto", "#FFECB3", "#3C3A1A");
|
||||
var c4 = CreateCell("60px", "#F8BBD9", "#3C1A3C");
|
||||
|
||||
Grid.SetColumn(c1, 0);
|
||||
Grid.SetColumn(c2, 1);
|
||||
Grid.SetColumn(c3, 2);
|
||||
Grid.SetColumn(c4, 3);
|
||||
|
||||
grid.Children.Add(c1);
|
||||
grid.Children.Add(c2);
|
||||
grid.Children.Add(c3);
|
||||
grid.Children.Add(c4);
|
||||
|
||||
return CreateDemoContainer(grid, "Mixed: 60px | Star | Auto | 60px");
|
||||
}
|
||||
|
||||
private Border CreateCell(string text, string lightBgColor, string darkBgColor, Color? textColor = null)
|
||||
{
|
||||
var label = new Label
|
||||
{
|
||||
Text = text,
|
||||
FontSize = 12,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
VerticalTextAlignment = TextAlignment.Center
|
||||
};
|
||||
|
||||
if (textColor != null)
|
||||
{
|
||||
label.TextColor = textColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
label.SetAppThemeColor(Label.TextColorProperty,
|
||||
Colors.Black,
|
||||
Colors.White);
|
||||
}
|
||||
|
||||
var border = new Border
|
||||
{
|
||||
Padding = new Thickness(10, 8),
|
||||
StrokeThickness = 0,
|
||||
Content = label
|
||||
};
|
||||
|
||||
border.SetAppThemeColor(Border.BackgroundColorProperty,
|
||||
Color.FromArgb(lightBgColor),
|
||||
Color.FromArgb(darkBgColor));
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
private View CreateDemoContainer(View content, string description)
|
||||
{
|
||||
var descLabel = new Label { Text = description, FontSize = 12 };
|
||||
descLabel.SetAppThemeColor(Label.TextColorProperty,
|
||||
Color.FromArgb("#757575"),
|
||||
Color.FromArgb("#B0B0B0"));
|
||||
|
||||
return new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children = { descLabel, content }
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
// HomePage - Welcome page for the demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class HomePage : ContentPage
|
||||
{
|
||||
public HomePage()
|
||||
{
|
||||
Title = "Home";
|
||||
|
||||
Content = new ScrollView
|
||||
{
|
||||
Orientation = ScrollOrientation.Both, // Enable horizontal scrolling when window is too narrow
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(30),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "OpenMaui Linux",
|
||||
FontSize = 32,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
TextColor = Color.FromArgb("#2196F3")
|
||||
},
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "Controls Demo",
|
||||
FontSize = 20,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
TextColor = Colors.Gray
|
||||
},
|
||||
|
||||
new BoxView
|
||||
{
|
||||
HeightRequest = 2,
|
||||
Color = Color.FromArgb("#E0E0E0"),
|
||||
Margin = new Thickness(0, 10)
|
||||
},
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "Welcome to the comprehensive controls demonstration for OpenMaui Linux. " +
|
||||
"This app showcases all the major UI controls available in the framework.",
|
||||
FontSize = 14,
|
||||
LineBreakMode = LineBreakMode.WordWrap,
|
||||
HorizontalTextAlignment = TextAlignment.Center
|
||||
},
|
||||
|
||||
CreateFeatureSection(),
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "Use the flyout menu (swipe from left or tap the hamburger icon) to navigate between different control demos.",
|
||||
FontSize = 12,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
Margin = new Thickness(0, 20, 0, 0)
|
||||
},
|
||||
|
||||
CreateQuickLinksSection(),
|
||||
|
||||
CreateNavigationDemoSection()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateFeatureSection()
|
||||
{
|
||||
var grid = new Grid
|
||||
{
|
||||
ColumnDefinitions =
|
||||
{
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) },
|
||||
new ColumnDefinition { Width = new GridLength(1, GridUnitType.Star) }
|
||||
},
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto },
|
||||
new RowDefinition { Height = GridLength.Auto }
|
||||
},
|
||||
ColumnSpacing = 15,
|
||||
RowSpacing = 15,
|
||||
Margin = new Thickness(0, 20)
|
||||
};
|
||||
|
||||
var features = new[]
|
||||
{
|
||||
("Buttons", "Various button styles and events"),
|
||||
("Text Input", "Entry, Editor, SearchBar"),
|
||||
("Selection", "CheckBox, Switch, Slider"),
|
||||
("Pickers", "Picker, DatePicker, TimePicker"),
|
||||
("Lists", "CollectionView with selection"),
|
||||
("Progress", "ProgressBar, ActivityIndicator")
|
||||
};
|
||||
|
||||
for (int i = 0; i < features.Length; i++)
|
||||
{
|
||||
var (title, desc) = features[i];
|
||||
var card = CreateFeatureCard(title, desc);
|
||||
Grid.SetRow(card, i / 2);
|
||||
Grid.SetColumn(card, i % 2);
|
||||
grid.Children.Add(card);
|
||||
}
|
||||
|
||||
return grid;
|
||||
}
|
||||
|
||||
private Frame CreateFeatureCard(string title, string description)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
HasShadow = true,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 14,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
TextColor = Color.FromArgb("#2196F3")
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = description,
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateQuickLinksSection()
|
||||
{
|
||||
var layout = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Margin = new Thickness(0, 20, 0, 0)
|
||||
};
|
||||
|
||||
layout.Children.Add(new Label
|
||||
{
|
||||
Text = "Quick Actions",
|
||||
FontSize = 16,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
});
|
||||
|
||||
var buttonRow = new HorizontalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
};
|
||||
|
||||
var buttonsBtn = new Button
|
||||
{
|
||||
Text = "Try Buttons",
|
||||
BackgroundColor = Color.FromArgb("#2196F3"),
|
||||
TextColor = Colors.White
|
||||
};
|
||||
buttonsBtn.Clicked += (s, e) => LinuxViewRenderer.NavigateToRoute("Buttons");
|
||||
|
||||
var listsBtn = new Button
|
||||
{
|
||||
Text = "Try Lists",
|
||||
BackgroundColor = Color.FromArgb("#4CAF50"),
|
||||
TextColor = Colors.White
|
||||
};
|
||||
listsBtn.Clicked += (s, e) => LinuxViewRenderer.NavigateToRoute("Lists");
|
||||
|
||||
buttonRow.Children.Add(buttonsBtn);
|
||||
buttonRow.Children.Add(listsBtn);
|
||||
layout.Children.Add(buttonRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateNavigationDemoSection()
|
||||
{
|
||||
var frame = new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(20),
|
||||
BackgroundColor = Color.FromArgb("#F3E5F5"),
|
||||
Margin = new Thickness(0, 20, 0, 0),
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 15,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Navigation Stack Demo",
|
||||
FontSize = 18,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
TextColor = Color.FromArgb("#9C27B0"),
|
||||
HorizontalOptions = LayoutOptions.Center
|
||||
},
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "Demonstrate push/pop navigation using Shell.GoToAsync()",
|
||||
FontSize = 12,
|
||||
TextColor = Colors.Gray,
|
||||
HorizontalTextAlignment = TextAlignment.Center
|
||||
},
|
||||
|
||||
CreatePushButton("Push Detail Page", "detail"),
|
||||
|
||||
new Label
|
||||
{
|
||||
Text = "Click the button to push a new page onto the navigation stack. " +
|
||||
"Use the back button or 'Go Back' to pop it off.",
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
HorizontalTextAlignment = TextAlignment.Center,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
private Button CreatePushButton(string text, string route)
|
||||
{
|
||||
var btn = new Button
|
||||
{
|
||||
Text = text,
|
||||
BackgroundColor = Color.FromArgb("#9C27B0"),
|
||||
TextColor = Colors.White,
|
||||
HorizontalOptions = LayoutOptions.Center,
|
||||
Padding = new Thickness(30, 10)
|
||||
};
|
||||
|
||||
btn.Clicked += (s, e) =>
|
||||
{
|
||||
Console.WriteLine($"[HomePage] Push button clicked, navigating to {route}");
|
||||
// Use LinuxViewRenderer.PushPage for Skia-based navigation
|
||||
var success = LinuxViewRenderer.PushPage(new DetailPage());
|
||||
Console.WriteLine($"[HomePage] PushPage result: {success}");
|
||||
};
|
||||
|
||||
return btn;
|
||||
}
|
||||
}
|
||||
120
ShellDemo/Pages/HomePage.xaml
Normal file
@@ -0,0 +1,120 @@
|
||||
<?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.HomePage"
|
||||
Title="Home"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ScrollView Orientation="Vertical">
|
||||
<VerticalStackLayout Padding="30" Spacing="20">
|
||||
|
||||
<!-- Header -->
|
||||
<Label Text="OpenMaui Linux"
|
||||
FontSize="32"
|
||||
FontAttributes="Bold"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="{StaticResource PrimaryColor}" />
|
||||
|
||||
<Label Text="Controls Demo"
|
||||
FontSize="20"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<!-- Theme Toggle -->
|
||||
<HorizontalStackLayout HorizontalOptions="Center" Spacing="10">
|
||||
<Label Text="Light"
|
||||
FontSize="14"
|
||||
VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Switch x:Name="ThemeSwitch"
|
||||
Toggled="OnThemeSwitchToggled"
|
||||
OnColor="{StaticResource PrimaryColor}" />
|
||||
<Label Text="Dark"
|
||||
FontSize="14"
|
||||
VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<BoxView Style="{StaticResource ThemedDivider}" Margin="0,10" />
|
||||
|
||||
<Label Text="Welcome to the comprehensive controls demonstration for OpenMaui Linux. This app showcases all the major UI controls available in the framework."
|
||||
FontSize="14"
|
||||
LineBreakMode="WordWrap"
|
||||
HorizontalTextAlignment="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Feature Grid -->
|
||||
<Grid x:Name="FeatureGrid"
|
||||
ColumnDefinitions="*,*"
|
||||
RowDefinitions="Auto,Auto,Auto"
|
||||
ColumnSpacing="15"
|
||||
RowSpacing="15"
|
||||
Margin="0,20" />
|
||||
|
||||
<Label Text="Use the flyout menu (swipe from left or tap the hamburger icon) to navigate between different control demos."
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
LineBreakMode="WordWrap"
|
||||
HorizontalTextAlignment="Center"
|
||||
Margin="0,20,0,0" />
|
||||
|
||||
<!-- Quick Actions Section -->
|
||||
<VerticalStackLayout Spacing="10" Margin="0,20,0,0">
|
||||
<Label Text="Quick Actions"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
HorizontalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<HorizontalStackLayout Spacing="10" HorizontalOptions="Center">
|
||||
<Button x:Name="TryButtonsBtn"
|
||||
Text="Try Buttons"
|
||||
Style="{StaticResource PrimaryButton}"
|
||||
Clicked="OnTryButtonsClicked" />
|
||||
<Button x:Name="TryListsBtn"
|
||||
Text="Try Lists"
|
||||
Style="{StaticResource SuccessButton}"
|
||||
Clicked="OnTryListsClicked" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
|
||||
<!-- Navigation Demo Section -->
|
||||
<Border BackgroundColor="{AppThemeBinding Light=#F3E5F5, Dark=#2D1F3D}"
|
||||
StrokeThickness="0"
|
||||
Padding="20"
|
||||
Margin="0,20,0,0">
|
||||
<Border.StrokeShape>
|
||||
<RoundRectangle CornerRadius="8" />
|
||||
</Border.StrokeShape>
|
||||
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="Navigation Stack Demo"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{StaticResource PurpleColor}"
|
||||
HorizontalOptions="Center" />
|
||||
|
||||
<Label Text="Demonstrate push/pop navigation using Shell.GoToAsync()"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalTextAlignment="Center" />
|
||||
|
||||
<Button x:Name="PushDetailBtn"
|
||||
Text="Push Detail Page"
|
||||
BackgroundColor="{StaticResource PurpleColor}"
|
||||
TextColor="White"
|
||||
HorizontalOptions="Center"
|
||||
Padding="30,10"
|
||||
Clicked="OnPushDetailClicked" />
|
||||
|
||||
<Label Text="Click the button to push a new page onto the navigation stack. Use the back button or 'Go Back' to pop it off."
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalTextAlignment="Center"
|
||||
LineBreakMode="WordWrap" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage>
|
||||
154
ShellDemo/Pages/HomePage.xaml.cs
Normal file
@@ -0,0 +1,154 @@
|
||||
// HomePage - Welcome page for the demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Controls.Shapes;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class HomePage : ContentPage
|
||||
{
|
||||
private bool _isUpdatingThemeSwitch;
|
||||
|
||||
public HomePage()
|
||||
{
|
||||
InitializeComponent();
|
||||
CreateFeatureCards();
|
||||
UpdateThemeSwitchState();
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
UpdateThemeSwitchState();
|
||||
}
|
||||
|
||||
private void UpdateThemeSwitchState()
|
||||
{
|
||||
_isUpdatingThemeSwitch = true;
|
||||
var isDark = Application.Current?.UserAppTheme == AppTheme.Dark ||
|
||||
(Application.Current?.UserAppTheme == AppTheme.Unspecified &&
|
||||
Application.Current?.RequestedTheme == AppTheme.Dark);
|
||||
ThemeSwitch.IsToggled = isDark;
|
||||
_isUpdatingThemeSwitch = false;
|
||||
}
|
||||
|
||||
private void OnThemeSwitchToggled(object? sender, ToggledEventArgs e)
|
||||
{
|
||||
if (_isUpdatingThemeSwitch || Application.Current == null) return;
|
||||
|
||||
Application.Current.UserAppTheme = e.Value ? AppTheme.Dark : AppTheme.Light;
|
||||
}
|
||||
|
||||
private void CreateFeatureCards()
|
||||
{
|
||||
// Title, Description, Route
|
||||
var features = new[]
|
||||
{
|
||||
("Buttons", "Various button styles and events", "Buttons"),
|
||||
("Text Input", "Entry, Editor, SearchBar", "TextInput"),
|
||||
("Selection", "CheckBox, Switch, Slider", "Selection"),
|
||||
("Pickers", "Picker, DatePicker, TimePicker", "Pickers"),
|
||||
("Lists", "CollectionView with selection", "Lists"),
|
||||
("Progress", "ProgressBar, ActivityIndicator", "Progress")
|
||||
};
|
||||
|
||||
for (int i = 0; i < features.Length; i++)
|
||||
{
|
||||
var (title, desc, route) = features[i];
|
||||
var card = CreateFeatureCard(title, desc, route);
|
||||
Grid.SetRow(card, i / 2);
|
||||
Grid.SetColumn(card, i % 2);
|
||||
FeatureGrid.Children.Add(card);
|
||||
}
|
||||
}
|
||||
|
||||
private Border CreateFeatureCard(string title, string description, string route)
|
||||
{
|
||||
// Use AppThemeBinding for card colors
|
||||
var cardBackground = new AppThemeBindingExtension
|
||||
{
|
||||
Light = Application.Current?.Resources["CardBackgroundLight"] as Color ?? Colors.White,
|
||||
Dark = Application.Current?.Resources["CardBackgroundDark"] as Color ?? Color.FromArgb("#1E1E1E")
|
||||
};
|
||||
|
||||
var textPrimary = new AppThemeBindingExtension
|
||||
{
|
||||
Light = Application.Current?.Resources["TextPrimaryLight"] as Color ?? Colors.Black,
|
||||
Dark = Application.Current?.Resources["TextPrimaryDark"] as Color ?? Colors.White
|
||||
};
|
||||
|
||||
var textSecondary = new AppThemeBindingExtension
|
||||
{
|
||||
Light = Application.Current?.Resources["TextSecondaryLight"] as Color ?? Colors.Gray,
|
||||
Dark = Application.Current?.Resources["TextSecondaryDark"] as Color ?? Color.FromArgb("#B0B0B0")
|
||||
};
|
||||
|
||||
var titleLabel = new Label
|
||||
{
|
||||
Text = title,
|
||||
FontSize = 14,
|
||||
FontAttributes = FontAttributes.Bold,
|
||||
TextColor = Color.FromArgb("#2196F3")
|
||||
};
|
||||
|
||||
var descLabel = new Label
|
||||
{
|
||||
Text = description,
|
||||
FontSize = 11,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
};
|
||||
descLabel.SetBinding(Label.TextColorProperty, new Binding { Source = textSecondary, Path = "." });
|
||||
descLabel.SetAppThemeColor(Label.TextColorProperty,
|
||||
Application.Current?.Resources["TextSecondaryLight"] as Color ?? Colors.Gray,
|
||||
Application.Current?.Resources["TextSecondaryDark"] as Color ?? Color.FromArgb("#B0B0B0"));
|
||||
|
||||
var border = new Border
|
||||
{
|
||||
Padding = new Thickness(15),
|
||||
StrokeThickness = 0,
|
||||
StrokeShape = new RoundRectangle { CornerRadius = 8 },
|
||||
Shadow = new Shadow
|
||||
{
|
||||
Brush = new SolidColorBrush(Colors.Black),
|
||||
Opacity = 0.1f,
|
||||
Radius = 4,
|
||||
Offset = new Point(0, 2)
|
||||
},
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children = { titleLabel, descLabel }
|
||||
}
|
||||
};
|
||||
|
||||
border.SetAppThemeColor(Border.BackgroundColorProperty,
|
||||
Application.Current?.Resources["CardBackgroundLight"] as Color ?? Colors.White,
|
||||
Application.Current?.Resources["CardBackgroundDark"] as Color ?? Color.FromArgb("#1E1E1E"));
|
||||
|
||||
// Add tap gesture for navigation
|
||||
var tapGesture = new TapGestureRecognizer();
|
||||
tapGesture.Tapped += (s, e) => LinuxViewRenderer.NavigateToRoute(route);
|
||||
border.GestureRecognizers.Add(tapGesture);
|
||||
|
||||
return border;
|
||||
}
|
||||
|
||||
private void OnTryButtonsClicked(object? sender, EventArgs e)
|
||||
{
|
||||
LinuxViewRenderer.NavigateToRoute("Buttons");
|
||||
}
|
||||
|
||||
private void OnTryListsClicked(object? sender, EventArgs e)
|
||||
{
|
||||
LinuxViewRenderer.NavigateToRoute("Lists");
|
||||
}
|
||||
|
||||
private void OnPushDetailClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[HomePage] Push button clicked, navigating to detail");
|
||||
var success = LinuxViewRenderer.PushPage(new DetailPage());
|
||||
Console.WriteLine($"[HomePage] PushPage result: {success}");
|
||||
}
|
||||
}
|
||||
@@ -1,249 +0,0 @@
|
||||
// ListsPage - CollectionView and ListView Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class ListsPage : ContentPage
|
||||
{
|
||||
private readonly Label _eventLog;
|
||||
private int _eventCount = 0;
|
||||
|
||||
public ListsPage()
|
||||
{
|
||||
Title = "Lists";
|
||||
|
||||
_eventLog = new Label
|
||||
{
|
||||
Text = "Events will appear here...",
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
};
|
||||
|
||||
Content = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
|
||||
new RowDefinition { Height = new GridLength(120) }
|
||||
},
|
||||
Children =
|
||||
{
|
||||
CreateMainContent(),
|
||||
CreateEventLogPanel()
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetRow((View)((Grid)Content).Children[0], 0);
|
||||
Grid.SetRow((View)((Grid)Content).Children[1], 1);
|
||||
}
|
||||
|
||||
private View CreateMainContent()
|
||||
{
|
||||
return new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "List Controls", FontSize = 24, FontAttributes = FontAttributes.Bold },
|
||||
|
||||
CreateSection("CollectionView - Fruits", CreateFruitsCollectionView()),
|
||||
CreateSection("CollectionView - Colors", CreateColorsCollectionView()),
|
||||
CreateSection("CollectionView - Contacts", CreateContactsCollectionView())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateFruitsCollectionView()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var fruits = new List<string>
|
||||
{
|
||||
"Apple", "Banana", "Cherry", "Date", "Elderberry",
|
||||
"Fig", "Grape", "Honeydew", "Kiwi", "Lemon",
|
||||
"Mango", "Nectarine", "Orange", "Papaya", "Quince"
|
||||
};
|
||||
|
||||
var selectedLabel = new Label { Text = "Tap a fruit to select", TextColor = Colors.Gray };
|
||||
|
||||
var collectionView = new CollectionView
|
||||
{
|
||||
ItemsSource = fruits,
|
||||
HeightRequest = 200,
|
||||
SelectionMode = SelectionMode.Single,
|
||||
BackgroundColor = Color.FromArgb("#FAFAFA")
|
||||
};
|
||||
|
||||
collectionView.SelectionChanged += (s, e) =>
|
||||
{
|
||||
if (e.CurrentSelection.Count > 0)
|
||||
{
|
||||
var item = e.CurrentSelection[0]?.ToString();
|
||||
selectedLabel.Text = $"Selected: {item}";
|
||||
LogEvent($"Fruit selected: {item}");
|
||||
}
|
||||
};
|
||||
|
||||
layout.Children.Add(collectionView);
|
||||
layout.Children.Add(selectedLabel);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateColorsCollectionView()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var colors = new List<ColorItem>
|
||||
{
|
||||
new("Red", "#F44336"),
|
||||
new("Pink", "#E91E63"),
|
||||
new("Purple", "#9C27B0"),
|
||||
new("Deep Purple", "#673AB7"),
|
||||
new("Indigo", "#3F51B5"),
|
||||
new("Blue", "#2196F3"),
|
||||
new("Cyan", "#00BCD4"),
|
||||
new("Teal", "#009688"),
|
||||
new("Green", "#4CAF50"),
|
||||
new("Light Green", "#8BC34A"),
|
||||
new("Lime", "#CDDC39"),
|
||||
new("Yellow", "#FFEB3B"),
|
||||
new("Amber", "#FFC107"),
|
||||
new("Orange", "#FF9800"),
|
||||
new("Deep Orange", "#FF5722")
|
||||
};
|
||||
|
||||
var collectionView = new CollectionView
|
||||
{
|
||||
ItemsSource = colors,
|
||||
HeightRequest = 180,
|
||||
SelectionMode = SelectionMode.Single,
|
||||
BackgroundColor = Colors.White
|
||||
};
|
||||
|
||||
collectionView.SelectionChanged += (s, e) =>
|
||||
{
|
||||
if (e.CurrentSelection.Count > 0 && e.CurrentSelection[0] is ColorItem item)
|
||||
{
|
||||
LogEvent($"Color selected: {item.Name} ({item.Hex})");
|
||||
}
|
||||
};
|
||||
|
||||
layout.Children.Add(collectionView);
|
||||
layout.Children.Add(new Label { Text = "Scroll to see all colors", FontSize = 11, TextColor = Colors.Gray });
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateContactsCollectionView()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 10 };
|
||||
|
||||
var contacts = new List<ContactItem>
|
||||
{
|
||||
new("Alice Johnson", "alice@example.com", "Engineering"),
|
||||
new("Bob Smith", "bob@example.com", "Marketing"),
|
||||
new("Carol Williams", "carol@example.com", "Design"),
|
||||
new("David Brown", "david@example.com", "Sales"),
|
||||
new("Eva Martinez", "eva@example.com", "Engineering"),
|
||||
new("Frank Lee", "frank@example.com", "Support"),
|
||||
new("Grace Kim", "grace@example.com", "HR"),
|
||||
new("Henry Wilson", "henry@example.com", "Finance")
|
||||
};
|
||||
|
||||
var collectionView = new CollectionView
|
||||
{
|
||||
ItemsSource = contacts,
|
||||
HeightRequest = 200,
|
||||
SelectionMode = SelectionMode.Single,
|
||||
BackgroundColor = Colors.White
|
||||
};
|
||||
|
||||
collectionView.SelectionChanged += (s, e) =>
|
||||
{
|
||||
if (e.CurrentSelection.Count > 0 && e.CurrentSelection[0] is ContactItem contact)
|
||||
{
|
||||
LogEvent($"Contact: {contact.Name} - {contact.Department}");
|
||||
}
|
||||
};
|
||||
|
||||
layout.Children.Add(collectionView);
|
||||
|
||||
// Action buttons
|
||||
var buttonRow = new HorizontalStackLayout { Spacing = 10 };
|
||||
var addBtn = new Button { Text = "Add Contact", BackgroundColor = Colors.Green, TextColor = Colors.White };
|
||||
addBtn.Clicked += (s, e) => LogEvent("Add contact clicked");
|
||||
var deleteBtn = new Button { Text = "Delete Selected", BackgroundColor = Colors.Red, TextColor = Colors.White };
|
||||
deleteBtn.Clicked += (s, e) => LogEvent("Delete contact clicked");
|
||||
buttonRow.Children.Add(addBtn);
|
||||
buttonRow.Children.Add(deleteBtn);
|
||||
layout.Children.Add(buttonRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private Frame CreateSection(string title, View content)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = title, FontSize = 16, FontAttributes = FontAttributes.Bold },
|
||||
content
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateEventLogPanel()
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||||
Padding = new Thickness(10),
|
||||
CornerRadius = 0,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Event Log:", FontSize = 12, FontAttributes = FontAttributes.Bold },
|
||||
new ScrollView
|
||||
{
|
||||
HeightRequest = 80,
|
||||
Content = _eventLog
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
_eventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{_eventLog.Text}";
|
||||
}
|
||||
}
|
||||
|
||||
public record ColorItem(string Name, string Hex)
|
||||
{
|
||||
public override string ToString() => Name;
|
||||
}
|
||||
|
||||
public record ContactItem(string Name, string Email, string Department)
|
||||
{
|
||||
public override string ToString() => $"{Name} ({Department})";
|
||||
}
|
||||
133
ShellDemo/Pages/ListsPage.xaml
Normal file
@@ -0,0 +1,133 @@
|
||||
<?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.ListsPage"
|
||||
Title="Lists"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<Grid RowDefinitions="*,120">
|
||||
|
||||
<!-- Main Content -->
|
||||
<ScrollView Grid.Row="0">
|
||||
<VerticalStackLayout Padding="20" Spacing="20">
|
||||
|
||||
<Label Text="List Controls"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Fruits CollectionView -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="CollectionView - Fruits"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<CollectionView x:Name="FruitsCollectionView"
|
||||
HeightRequest="200"
|
||||
SelectionMode="Single"
|
||||
BackgroundColor="{AppThemeBinding Light=#FAFAFA, Dark=#2C2C2C}"
|
||||
SelectionChanged="OnFruitsSelectionChanged">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<Label Text="{Binding}"
|
||||
Padding="10"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
|
||||
<Label x:Name="FruitsSelectedLabel"
|
||||
Text="Tap a fruit to select"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Colors CollectionView -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="CollectionView - Colors"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<CollectionView x:Name="ColorsCollectionView"
|
||||
HeightRequest="180"
|
||||
SelectionMode="Single"
|
||||
BackgroundColor="{AppThemeBinding Light=White, Dark=#1E1E1E}"
|
||||
SelectionChanged="OnColorsSelectionChanged">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<Label Text="{Binding}"
|
||||
Padding="10"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
|
||||
<Label Text="Scroll to see all colors"
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Contacts CollectionView -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="10">
|
||||
<Label Text="CollectionView - Contacts"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<CollectionView x:Name="ContactsCollectionView"
|
||||
HeightRequest="200"
|
||||
SelectionMode="Single"
|
||||
BackgroundColor="{AppThemeBinding Light=White, Dark=#1E1E1E}"
|
||||
SelectionChanged="OnContactsSelectionChanged">
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate x:DataType="x:String">
|
||||
<Label Text="{Binding}"
|
||||
Padding="10"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
|
||||
<!-- Action buttons -->
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Button Text="Add Contact"
|
||||
Style="{StaticResource SuccessButton}"
|
||||
Clicked="OnAddContactClicked" />
|
||||
<Button Text="Delete Selected"
|
||||
Style="{StaticResource DangerButton}"
|
||||
Clicked="OnDeleteContactClicked" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Event Log Panel -->
|
||||
<Border Grid.Row="1"
|
||||
BackgroundColor="{AppThemeBinding Light=#F5F5F5, Dark=#2C2C2C}"
|
||||
StrokeThickness="0"
|
||||
Padding="10">
|
||||
<VerticalStackLayout>
|
||||
<Label Text="Event Log:"
|
||||
FontSize="12"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<ScrollView HeightRequest="80">
|
||||
<Label x:Name="EventLogLabel"
|
||||
Text="Events will appear here..."
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
LineBreakMode="WordWrap" />
|
||||
</ScrollView>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
104
ShellDemo/Pages/ListsPage.xaml.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
// ListsPage - CollectionView and ListView Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class ListsPage : ContentPage
|
||||
{
|
||||
private int _eventCount = 0;
|
||||
private ObservableCollection<string> _contacts;
|
||||
|
||||
public ListsPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Set up fruits
|
||||
var fruits = new List<string>
|
||||
{
|
||||
"Apple", "Banana", "Cherry", "Date", "Elderberry",
|
||||
"Fig", "Grape", "Honeydew", "Kiwi", "Lemon",
|
||||
"Mango", "Nectarine", "Orange", "Papaya", "Quince"
|
||||
};
|
||||
FruitsCollectionView.ItemsSource = fruits;
|
||||
|
||||
// Set up colors
|
||||
var colors = new List<string>
|
||||
{
|
||||
"Red", "Pink", "Purple", "Deep Purple", "Indigo",
|
||||
"Blue", "Cyan", "Teal", "Green", "Light Green",
|
||||
"Lime", "Yellow", "Amber", "Orange", "Deep Orange"
|
||||
};
|
||||
ColorsCollectionView.ItemsSource = colors;
|
||||
|
||||
// Set up contacts
|
||||
_contacts = new ObservableCollection<string>
|
||||
{
|
||||
"Alice Johnson (Engineering)",
|
||||
"Bob Smith (Marketing)",
|
||||
"Carol Williams (Design)",
|
||||
"David Brown (Sales)",
|
||||
"Eva Martinez (Engineering)",
|
||||
"Frank Lee (Support)",
|
||||
"Grace Kim (HR)",
|
||||
"Henry Wilson (Finance)"
|
||||
};
|
||||
ContactsCollectionView.ItemsSource = _contacts;
|
||||
}
|
||||
|
||||
private void OnFruitsSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.CurrentSelection.Count > 0)
|
||||
{
|
||||
var item = e.CurrentSelection[0]?.ToString();
|
||||
FruitsSelectedLabel.Text = $"Selected: {item}";
|
||||
LogEvent($"Fruit selected: {item}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnColorsSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.CurrentSelection.Count > 0)
|
||||
{
|
||||
var item = e.CurrentSelection[0]?.ToString();
|
||||
LogEvent($"Color selected: {item}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContactsSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
if (e.CurrentSelection.Count > 0)
|
||||
{
|
||||
var contact = e.CurrentSelection[0]?.ToString();
|
||||
LogEvent($"Contact: {contact}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAddContactClicked(object? sender, EventArgs e)
|
||||
{
|
||||
var newContact = $"New Contact {_contacts.Count + 1} (New)";
|
||||
_contacts.Add(newContact);
|
||||
LogEvent($"Added: {newContact}");
|
||||
}
|
||||
|
||||
private void OnDeleteContactClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (ContactsCollectionView.SelectedItem is string selected && _contacts.Contains(selected))
|
||||
{
|
||||
_contacts.Remove(selected);
|
||||
LogEvent($"Deleted: {selected}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogEvent("No contact selected to delete");
|
||||
}
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
EventLogLabel.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLogLabel.Text}";
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
// PickersPage - Picker, DatePicker, TimePicker Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class PickersPage : ContentPage
|
||||
{
|
||||
private readonly Label _eventLog;
|
||||
private int _eventCount = 0;
|
||||
|
||||
public PickersPage()
|
||||
{
|
||||
Title = "Pickers";
|
||||
|
||||
_eventLog = new Label
|
||||
{
|
||||
Text = "Events will appear here...",
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
};
|
||||
|
||||
Content = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
|
||||
new RowDefinition { Height = new GridLength(120) }
|
||||
},
|
||||
Children =
|
||||
{
|
||||
CreateMainContent(),
|
||||
CreateEventLogPanel()
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetRow((View)((Grid)Content).Children[0], 0);
|
||||
Grid.SetRow((View)((Grid)Content).Children[1], 1);
|
||||
}
|
||||
|
||||
private View CreateMainContent()
|
||||
{
|
||||
return new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Picker Controls", FontSize = 24, FontAttributes = FontAttributes.Bold },
|
||||
|
||||
CreateSection("Picker", CreatePickerDemo()),
|
||||
CreateSection("DatePicker", CreateDatePickerDemo()),
|
||||
CreateSection("TimePicker", CreateTimePickerDemo())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreatePickerDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Basic picker
|
||||
var selectedLabel = new Label { Text = "Selected: (none)", TextColor = Colors.Gray };
|
||||
var picker1 = new Picker { Title = "Select a fruit" };
|
||||
picker1.Items.Add("Apple");
|
||||
picker1.Items.Add("Banana");
|
||||
picker1.Items.Add("Cherry");
|
||||
picker1.Items.Add("Date");
|
||||
picker1.Items.Add("Elderberry");
|
||||
picker1.Items.Add("Fig");
|
||||
picker1.Items.Add("Grape");
|
||||
picker1.SelectedIndexChanged += (s, e) =>
|
||||
{
|
||||
if (picker1.SelectedIndex >= 0)
|
||||
{
|
||||
var item = picker1.Items[picker1.SelectedIndex];
|
||||
selectedLabel.Text = $"Selected: {item}";
|
||||
LogEvent($"Fruit selected: {item}");
|
||||
}
|
||||
};
|
||||
layout.Children.Add(picker1);
|
||||
layout.Children.Add(selectedLabel);
|
||||
|
||||
// Picker with default selection
|
||||
layout.Children.Add(new Label { Text = "With Default Selection:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var picker2 = new Picker { Title = "Select a color" };
|
||||
picker2.Items.Add("Red");
|
||||
picker2.Items.Add("Green");
|
||||
picker2.Items.Add("Blue");
|
||||
picker2.Items.Add("Yellow");
|
||||
picker2.Items.Add("Purple");
|
||||
picker2.SelectedIndex = 2; // Blue
|
||||
picker2.SelectedIndexChanged += (s, e) =>
|
||||
{
|
||||
if (picker2.SelectedIndex >= 0)
|
||||
LogEvent($"Color selected: {picker2.Items[picker2.SelectedIndex]}");
|
||||
};
|
||||
layout.Children.Add(picker2);
|
||||
|
||||
// Styled picker
|
||||
layout.Children.Add(new Label { Text = "Styled Picker:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var picker3 = new Picker
|
||||
{
|
||||
Title = "Select size",
|
||||
TextColor = Colors.DarkBlue,
|
||||
TitleColor = Colors.Gray
|
||||
};
|
||||
picker3.Items.Add("Small");
|
||||
picker3.Items.Add("Medium");
|
||||
picker3.Items.Add("Large");
|
||||
picker3.Items.Add("Extra Large");
|
||||
picker3.SelectedIndexChanged += (s, e) =>
|
||||
{
|
||||
if (picker3.SelectedIndex >= 0)
|
||||
LogEvent($"Size selected: {picker3.Items[picker3.SelectedIndex]}");
|
||||
};
|
||||
layout.Children.Add(picker3);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateDatePickerDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Basic date picker
|
||||
var dateLabel = new Label { Text = $"Selected: {DateTime.Today:d}" };
|
||||
var datePicker1 = new DatePicker { Date = DateTime.Today };
|
||||
datePicker1.DateSelected += (s, e) =>
|
||||
{
|
||||
dateLabel.Text = $"Selected: {e.NewDate:d}";
|
||||
LogEvent($"Date selected: {e.NewDate:d}");
|
||||
};
|
||||
layout.Children.Add(datePicker1);
|
||||
layout.Children.Add(dateLabel);
|
||||
|
||||
// Date picker with range
|
||||
layout.Children.Add(new Label { Text = "With Date Range (this month only):", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var startOfMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
|
||||
var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);
|
||||
var datePicker2 = new DatePicker
|
||||
{
|
||||
MinimumDate = startOfMonth,
|
||||
MaximumDate = endOfMonth,
|
||||
Date = DateTime.Today
|
||||
};
|
||||
datePicker2.DateSelected += (s, e) => LogEvent($"Date (limited): {e.NewDate:d}");
|
||||
layout.Children.Add(datePicker2);
|
||||
|
||||
// Styled date picker
|
||||
layout.Children.Add(new Label { Text = "Styled DatePicker:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var datePicker3 = new DatePicker
|
||||
{
|
||||
Date = DateTime.Today.AddDays(7),
|
||||
TextColor = Colors.DarkGreen
|
||||
};
|
||||
datePicker3.DateSelected += (s, e) => LogEvent($"Styled date: {e.NewDate:d}");
|
||||
layout.Children.Add(datePicker3);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateTimePickerDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Basic time picker
|
||||
var timeLabel = new Label { Text = $"Selected: {DateTime.Now:t}" };
|
||||
var timePicker1 = new TimePicker { Time = DateTime.Now.TimeOfDay };
|
||||
timePicker1.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(TimePicker.Time))
|
||||
{
|
||||
var time = timePicker1.Time;
|
||||
timeLabel.Text = $"Selected: {time:hh\\:mm}";
|
||||
LogEvent($"Time selected: {time:hh\\:mm}");
|
||||
}
|
||||
};
|
||||
layout.Children.Add(timePicker1);
|
||||
layout.Children.Add(timeLabel);
|
||||
|
||||
// Styled time picker
|
||||
layout.Children.Add(new Label { Text = "Styled TimePicker:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var timePicker2 = new TimePicker
|
||||
{
|
||||
Time = new TimeSpan(14, 30, 0),
|
||||
TextColor = Colors.DarkBlue
|
||||
};
|
||||
timePicker2.PropertyChanged += (s, e) =>
|
||||
{
|
||||
if (e.PropertyName == nameof(TimePicker.Time))
|
||||
LogEvent($"Styled time: {timePicker2.Time:hh\\:mm}");
|
||||
};
|
||||
layout.Children.Add(timePicker2);
|
||||
|
||||
// Morning alarm example
|
||||
layout.Children.Add(new Label { Text = "Alarm Time:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var alarmRow = new HorizontalStackLayout { Spacing = 10 };
|
||||
var alarmPicker = new TimePicker { Time = new TimeSpan(7, 0, 0) };
|
||||
var alarmBtn = new Button { Text = "Set Alarm", BackgroundColor = Colors.Orange, TextColor = Colors.White };
|
||||
alarmBtn.Clicked += (s, e) => LogEvent($"Alarm set for {alarmPicker.Time:hh\\:mm}");
|
||||
alarmRow.Children.Add(alarmPicker);
|
||||
alarmRow.Children.Add(alarmBtn);
|
||||
layout.Children.Add(alarmRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private Frame CreateSection(string title, View content)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = title, FontSize = 16, FontAttributes = FontAttributes.Bold },
|
||||
content
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateEventLogPanel()
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||||
Padding = new Thickness(10),
|
||||
CornerRadius = 0,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Event Log:", FontSize = 12, FontAttributes = FontAttributes.Bold },
|
||||
new ScrollView
|
||||
{
|
||||
HeightRequest = 80,
|
||||
Content = _eventLog
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
_eventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{_eventLog.Text}";
|
||||
}
|
||||
}
|
||||
185
ShellDemo/Pages/PickersPage.xaml
Normal file
@@ -0,0 +1,185 @@
|
||||
<?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.PickersPage"
|
||||
Title="Pickers"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<Grid RowDefinitions="*,120">
|
||||
|
||||
<!-- Main Content -->
|
||||
<ScrollView Grid.Row="0">
|
||||
<VerticalStackLayout Padding="20" Spacing="20">
|
||||
|
||||
<Label Text="Picker Controls"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Picker Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="Picker"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic picker -->
|
||||
<Picker x:Name="FruitPicker"
|
||||
Title="Select a fruit"
|
||||
SelectedIndexChanged="OnFruitPickerChanged">
|
||||
<Picker.ItemsSource>
|
||||
<x:Array Type="{x:Type x:String}">
|
||||
<x:String>Apple</x:String>
|
||||
<x:String>Banana</x:String>
|
||||
<x:String>Cherry</x:String>
|
||||
<x:String>Date</x:String>
|
||||
<x:String>Elderberry</x:String>
|
||||
<x:String>Fig</x:String>
|
||||
<x:String>Grape</x:String>
|
||||
</x:Array>
|
||||
</Picker.ItemsSource>
|
||||
</Picker>
|
||||
<Label x:Name="FruitSelectedLabel"
|
||||
Text="Selected: (none)"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<!-- Picker with default selection -->
|
||||
<Label Text="With Default Selection:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Picker x:Name="ColorPicker"
|
||||
Title="Select a color"
|
||||
SelectedIndex="2"
|
||||
SelectedIndexChanged="OnColorPickerChanged">
|
||||
<Picker.ItemsSource>
|
||||
<x:Array Type="{x:Type x:String}">
|
||||
<x:String>Red</x:String>
|
||||
<x:String>Green</x:String>
|
||||
<x:String>Blue</x:String>
|
||||
<x:String>Yellow</x:String>
|
||||
<x:String>Purple</x:String>
|
||||
</x:Array>
|
||||
</Picker.ItemsSource>
|
||||
</Picker>
|
||||
|
||||
<!-- Styled picker -->
|
||||
<Label Text="Styled Picker:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Picker x:Name="SizePicker"
|
||||
Title="Select size"
|
||||
TextColor="{AppThemeBinding Light=DarkBlue, Dark=LightBlue}"
|
||||
TitleColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
SelectedIndexChanged="OnSizePickerChanged">
|
||||
<Picker.ItemsSource>
|
||||
<x:Array Type="{x:Type x:String}">
|
||||
<x:String>Small</x:String>
|
||||
<x:String>Medium</x:String>
|
||||
<x:String>Large</x:String>
|
||||
<x:String>Extra Large</x:String>
|
||||
</x:Array>
|
||||
</Picker.ItemsSource>
|
||||
</Picker>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- DatePicker Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="DatePicker"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic date picker -->
|
||||
<DatePicker x:Name="BasicDatePicker"
|
||||
DateSelected="OnDateSelected" />
|
||||
<Label x:Name="DateSelectedLabel"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Date picker with range -->
|
||||
<Label Text="With Date Range (this month only):"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<DatePicker x:Name="RangeDatePicker"
|
||||
DateSelected="OnRangeDateSelected" />
|
||||
|
||||
<!-- Styled date picker -->
|
||||
<Label Text="Styled DatePicker:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<DatePicker x:Name="StyledDatePicker"
|
||||
TextColor="{AppThemeBinding Light=DarkGreen, Dark=LightGreen}"
|
||||
DateSelected="OnStyledDateSelected" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- TimePicker Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="TimePicker"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic time picker -->
|
||||
<TimePicker x:Name="BasicTimePicker"
|
||||
PropertyChanged="OnTimePickerPropertyChanged" />
|
||||
<Label x:Name="TimeSelectedLabel"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Styled time picker -->
|
||||
<Label Text="Styled TimePicker:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<TimePicker x:Name="StyledTimePicker"
|
||||
Time="14:30:00"
|
||||
TextColor="{AppThemeBinding Light=DarkBlue, Dark=LightBlue}"
|
||||
PropertyChanged="OnStyledTimePickerPropertyChanged" />
|
||||
|
||||
<!-- Alarm time -->
|
||||
<Label Text="Alarm Time:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<TimePicker x:Name="AlarmTimePicker" Time="07:00:00" />
|
||||
<Button Text="Set Alarm"
|
||||
BackgroundColor="{StaticResource WarningColor}"
|
||||
TextColor="White"
|
||||
Clicked="OnSetAlarmClicked" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Event Log Panel -->
|
||||
<Border Grid.Row="1"
|
||||
BackgroundColor="{AppThemeBinding Light=#F5F5F5, Dark=#2C2C2C}"
|
||||
StrokeThickness="0"
|
||||
Padding="10">
|
||||
<VerticalStackLayout>
|
||||
<Label Text="Event Log:"
|
||||
FontSize="12"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<ScrollView HeightRequest="80">
|
||||
<Label x:Name="EventLogLabel"
|
||||
Text="Events will appear here..."
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
LineBreakMode="WordWrap" />
|
||||
</ScrollView>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
107
ShellDemo/Pages/PickersPage.xaml.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
// PickersPage - Picker, DatePicker, TimePicker Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class PickersPage : ContentPage
|
||||
{
|
||||
private int _eventCount = 0;
|
||||
|
||||
public PickersPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
// Set up date picker with current date
|
||||
BasicDatePicker.Date = DateTime.Today;
|
||||
DateSelectedLabel.Text = $"Selected: {DateTime.Today:d}";
|
||||
|
||||
// Set up range date picker
|
||||
var startOfMonth = new DateTime(DateTime.Today.Year, DateTime.Today.Month, 1);
|
||||
var endOfMonth = startOfMonth.AddMonths(1).AddDays(-1);
|
||||
RangeDatePicker.MinimumDate = startOfMonth;
|
||||
RangeDatePicker.MaximumDate = endOfMonth;
|
||||
RangeDatePicker.Date = DateTime.Today;
|
||||
|
||||
// Set up styled date picker
|
||||
StyledDatePicker.Date = DateTime.Today.AddDays(7);
|
||||
|
||||
// Set up time picker
|
||||
BasicTimePicker.Time = DateTime.Now.TimeOfDay;
|
||||
TimeSelectedLabel.Text = $"Selected: {DateTime.Now:t}";
|
||||
}
|
||||
|
||||
private void OnFruitPickerChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (FruitPicker.SelectedIndex >= 0)
|
||||
{
|
||||
var item = FruitPicker.ItemsSource[FruitPicker.SelectedIndex]?.ToString();
|
||||
FruitSelectedLabel.Text = $"Selected: {item}";
|
||||
LogEvent($"Fruit selected: {item}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnColorPickerChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (ColorPicker.SelectedIndex >= 0)
|
||||
{
|
||||
var item = ColorPicker.ItemsSource[ColorPicker.SelectedIndex]?.ToString();
|
||||
LogEvent($"Color selected: {item}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSizePickerChanged(object? sender, EventArgs e)
|
||||
{
|
||||
if (SizePicker.SelectedIndex >= 0)
|
||||
{
|
||||
var item = SizePicker.ItemsSource[SizePicker.SelectedIndex]?.ToString();
|
||||
LogEvent($"Size selected: {item}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDateSelected(object? sender, DateChangedEventArgs e)
|
||||
{
|
||||
DateSelectedLabel.Text = $"Selected: {e.NewDate:d}";
|
||||
LogEvent($"Date selected: {e.NewDate:d}");
|
||||
}
|
||||
|
||||
private void OnRangeDateSelected(object? sender, DateChangedEventArgs e)
|
||||
{
|
||||
LogEvent($"Date (limited): {e.NewDate:d}");
|
||||
}
|
||||
|
||||
private void OnStyledDateSelected(object? sender, DateChangedEventArgs e)
|
||||
{
|
||||
LogEvent($"Styled date: {e.NewDate:d}");
|
||||
}
|
||||
|
||||
private void OnTimePickerPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(TimePicker.Time))
|
||||
{
|
||||
var time = BasicTimePicker.Time;
|
||||
TimeSelectedLabel.Text = $"Selected: {time:hh\\:mm}";
|
||||
LogEvent($"Time selected: {time:hh\\:mm}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnStyledTimePickerPropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName == nameof(TimePicker.Time))
|
||||
{
|
||||
LogEvent($"Styled time: {StyledTimePicker.Time:hh\\:mm}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSetAlarmClicked(object? sender, EventArgs e)
|
||||
{
|
||||
LogEvent($"Alarm set for {AlarmTimePicker.Time:hh\\:mm}");
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
EventLogLabel.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLogLabel.Text}";
|
||||
}
|
||||
}
|
||||
@@ -1,261 +0,0 @@
|
||||
// ProgressPage - ProgressBar and ActivityIndicator Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class ProgressPage : ContentPage
|
||||
{
|
||||
private readonly Label _eventLog;
|
||||
private int _eventCount = 0;
|
||||
private ProgressBar? _animatedProgress;
|
||||
private bool _isAnimating = false;
|
||||
|
||||
public ProgressPage()
|
||||
{
|
||||
Title = "Progress";
|
||||
|
||||
_eventLog = new Label
|
||||
{
|
||||
Text = "Events will appear here...",
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
};
|
||||
|
||||
Content = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
|
||||
new RowDefinition { Height = new GridLength(120) }
|
||||
},
|
||||
Children =
|
||||
{
|
||||
CreateMainContent(),
|
||||
CreateEventLogPanel()
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetRow((View)((Grid)Content).Children[0], 0);
|
||||
Grid.SetRow((View)((Grid)Content).Children[1], 1);
|
||||
}
|
||||
|
||||
private View CreateMainContent()
|
||||
{
|
||||
return new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Progress Indicators", FontSize = 24, FontAttributes = FontAttributes.Bold },
|
||||
|
||||
CreateSection("ProgressBar", CreateProgressBarDemo()),
|
||||
CreateSection("ActivityIndicator", CreateActivityIndicatorDemo()),
|
||||
CreateSection("Interactive Demo", CreateInteractiveDemo())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateProgressBarDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Various progress values
|
||||
var values = new[] { 0.0, 0.25, 0.5, 0.75, 1.0 };
|
||||
foreach (var value in values)
|
||||
{
|
||||
var row = new HorizontalStackLayout { Spacing = 10 };
|
||||
var progress = new ProgressBar { Progress = value, WidthRequest = 200 };
|
||||
var label = new Label { Text = $"{value * 100:0}%", VerticalOptions = LayoutOptions.Center, WidthRequest = 50 };
|
||||
row.Children.Add(progress);
|
||||
row.Children.Add(label);
|
||||
layout.Children.Add(row);
|
||||
}
|
||||
|
||||
// Colored progress bars
|
||||
layout.Children.Add(new Label { Text = "Colored Progress Bars:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
|
||||
var colors = new[] { Colors.Red, Colors.Green, Colors.Blue, Colors.Orange, Colors.Purple };
|
||||
foreach (var color in colors)
|
||||
{
|
||||
var progress = new ProgressBar { Progress = 0.7, ProgressColor = color };
|
||||
layout.Children.Add(progress);
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateActivityIndicatorDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Running indicator
|
||||
var runningRow = new HorizontalStackLayout { Spacing = 15 };
|
||||
var runningIndicator = new ActivityIndicator { IsRunning = true };
|
||||
runningRow.Children.Add(runningIndicator);
|
||||
runningRow.Children.Add(new Label { Text = "Loading...", VerticalOptions = LayoutOptions.Center });
|
||||
layout.Children.Add(runningRow);
|
||||
|
||||
// Toggle indicator
|
||||
var toggleRow = new HorizontalStackLayout { Spacing = 15 };
|
||||
var toggleIndicator = new ActivityIndicator { IsRunning = false };
|
||||
var toggleBtn = new Button { Text = "Start/Stop" };
|
||||
toggleBtn.Clicked += (s, e) =>
|
||||
{
|
||||
toggleIndicator.IsRunning = !toggleIndicator.IsRunning;
|
||||
LogEvent($"ActivityIndicator: {(toggleIndicator.IsRunning ? "Started" : "Stopped")}");
|
||||
};
|
||||
toggleRow.Children.Add(toggleIndicator);
|
||||
toggleRow.Children.Add(toggleBtn);
|
||||
layout.Children.Add(toggleRow);
|
||||
|
||||
// Colored indicators
|
||||
layout.Children.Add(new Label { Text = "Colored Indicators:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var colorRow = new HorizontalStackLayout { Spacing = 20 };
|
||||
var indicatorColors = new[] { Colors.Red, Colors.Green, Colors.Blue, Colors.Orange };
|
||||
foreach (var color in indicatorColors)
|
||||
{
|
||||
var indicator = new ActivityIndicator { IsRunning = true, Color = color };
|
||||
colorRow.Children.Add(indicator);
|
||||
}
|
||||
layout.Children.Add(colorRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateInteractiveDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Slider-controlled progress
|
||||
var progressLabel = new Label { Text = "Progress: 50%" };
|
||||
_animatedProgress = new ProgressBar { Progress = 0.5 };
|
||||
|
||||
var slider = new Slider { Minimum = 0, Maximum = 100, Value = 50 };
|
||||
slider.ValueChanged += (s, e) =>
|
||||
{
|
||||
var value = e.NewValue / 100.0;
|
||||
_animatedProgress.Progress = value;
|
||||
progressLabel.Text = $"Progress: {e.NewValue:0}%";
|
||||
};
|
||||
|
||||
layout.Children.Add(_animatedProgress);
|
||||
layout.Children.Add(slider);
|
||||
layout.Children.Add(progressLabel);
|
||||
|
||||
// Animated progress buttons
|
||||
var buttonRow = new HorizontalStackLayout { Spacing = 10, Margin = new Thickness(0, 10, 0, 0) };
|
||||
|
||||
var resetBtn = new Button { Text = "Reset", BackgroundColor = Colors.Gray, TextColor = Colors.White };
|
||||
resetBtn.Clicked += async (s, e) =>
|
||||
{
|
||||
_animatedProgress.Progress = 0;
|
||||
slider.Value = 0;
|
||||
LogEvent("Progress reset to 0%");
|
||||
};
|
||||
|
||||
var animateBtn = new Button { Text = "Animate to 100%", BackgroundColor = Colors.Blue, TextColor = Colors.White };
|
||||
animateBtn.Clicked += async (s, e) =>
|
||||
{
|
||||
if (_isAnimating) return;
|
||||
_isAnimating = true;
|
||||
LogEvent("Animation started");
|
||||
|
||||
for (int i = (int)(slider.Value); i <= 100; i += 5)
|
||||
{
|
||||
_animatedProgress.Progress = i / 100.0;
|
||||
slider.Value = i;
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
_isAnimating = false;
|
||||
LogEvent("Animation completed");
|
||||
};
|
||||
|
||||
var simulateBtn = new Button { Text = "Simulate Download", BackgroundColor = Colors.Green, TextColor = Colors.White };
|
||||
simulateBtn.Clicked += async (s, e) =>
|
||||
{
|
||||
if (_isAnimating) return;
|
||||
_isAnimating = true;
|
||||
LogEvent("Download simulation started");
|
||||
|
||||
_animatedProgress.Progress = 0;
|
||||
slider.Value = 0;
|
||||
|
||||
var random = new Random();
|
||||
double progress = 0;
|
||||
while (progress < 1.0)
|
||||
{
|
||||
progress += random.NextDouble() * 0.1;
|
||||
if (progress > 1.0) progress = 1.0;
|
||||
_animatedProgress.Progress = progress;
|
||||
slider.Value = progress * 100;
|
||||
await Task.Delay(200 + random.Next(300));
|
||||
}
|
||||
|
||||
_isAnimating = false;
|
||||
LogEvent("Download simulation completed");
|
||||
};
|
||||
|
||||
buttonRow.Children.Add(resetBtn);
|
||||
buttonRow.Children.Add(animateBtn);
|
||||
buttonRow.Children.Add(simulateBtn);
|
||||
layout.Children.Add(buttonRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private Frame CreateSection(string title, View content)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = title, FontSize = 16, FontAttributes = FontAttributes.Bold },
|
||||
content
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateEventLogPanel()
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||||
Padding = new Thickness(10),
|
||||
CornerRadius = 0,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Event Log:", FontSize = 12, FontAttributes = FontAttributes.Bold },
|
||||
new ScrollView
|
||||
{
|
||||
HeightRequest = 80,
|
||||
Content = _eventLog
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
_eventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{_eventLog.Text}";
|
||||
}
|
||||
}
|
||||
167
ShellDemo/Pages/ProgressPage.xaml
Normal file
@@ -0,0 +1,167 @@
|
||||
<?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.ProgressPage"
|
||||
Title="Progress"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<Grid RowDefinitions="*,120">
|
||||
|
||||
<!-- Main Content -->
|
||||
<ScrollView Grid.Row="0">
|
||||
<VerticalStackLayout Padding="20" Spacing="20">
|
||||
|
||||
<Label Text="Progress Indicators"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- ProgressBar Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="ProgressBar"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Various progress values -->
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<ProgressBar Progress="0" WidthRequest="200" />
|
||||
<Label Text="0%" VerticalOptions="Center" WidthRequest="50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<ProgressBar Progress="0.25" WidthRequest="200" />
|
||||
<Label Text="25%" VerticalOptions="Center" WidthRequest="50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<ProgressBar Progress="0.5" WidthRequest="200" />
|
||||
<Label Text="50%" VerticalOptions="Center" WidthRequest="50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<ProgressBar Progress="0.75" WidthRequest="200" />
|
||||
<Label Text="75%" VerticalOptions="Center" WidthRequest="50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<ProgressBar Progress="1.0" WidthRequest="200" />
|
||||
<Label Text="100%" VerticalOptions="Center" WidthRequest="50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Colored progress bars -->
|
||||
<Label Text="Colored Progress Bars:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<ProgressBar Progress="0.7" ProgressColor="Red" />
|
||||
<ProgressBar Progress="0.7" ProgressColor="Green" />
|
||||
<ProgressBar Progress="0.7" ProgressColor="Blue" />
|
||||
<ProgressBar Progress="0.7" ProgressColor="Orange" />
|
||||
<ProgressBar Progress="0.7" ProgressColor="Purple" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- ActivityIndicator Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="ActivityIndicator"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Running indicator -->
|
||||
<HorizontalStackLayout Spacing="15">
|
||||
<ActivityIndicator IsRunning="True" />
|
||||
<Label Text="Loading..." VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Toggle indicator -->
|
||||
<HorizontalStackLayout Spacing="15">
|
||||
<ActivityIndicator x:Name="ToggleIndicator" IsRunning="False" />
|
||||
<Button Text="Start/Stop"
|
||||
Style="{StaticResource SecondaryButton}"
|
||||
Clicked="OnToggleIndicatorClicked" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Colored indicators -->
|
||||
<Label Text="Colored Indicators:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<HorizontalStackLayout Spacing="20">
|
||||
<ActivityIndicator IsRunning="True" Color="Red" />
|
||||
<ActivityIndicator IsRunning="True" Color="Green" />
|
||||
<ActivityIndicator IsRunning="True" Color="Blue" />
|
||||
<ActivityIndicator IsRunning="True" Color="Orange" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Interactive Demo Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="Interactive Demo"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Slider-controlled progress -->
|
||||
<ProgressBar x:Name="AnimatedProgress" Progress="0.5" />
|
||||
<Slider x:Name="ProgressSlider"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="50"
|
||||
ValueChanged="OnProgressSliderValueChanged" />
|
||||
<Label x:Name="ProgressLabel"
|
||||
Text="Progress: 50%"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Animated progress buttons -->
|
||||
<HorizontalStackLayout Spacing="10" Margin="0,10,0,0">
|
||||
<Button Text="Reset"
|
||||
Style="{StaticResource SecondaryButton}"
|
||||
Clicked="OnResetClicked" />
|
||||
<Button Text="Animate to 100%"
|
||||
Style="{StaticResource PrimaryButton}"
|
||||
Clicked="OnAnimateClicked" />
|
||||
<Button Text="Simulate Download"
|
||||
Style="{StaticResource SuccessButton}"
|
||||
Clicked="OnSimulateDownloadClicked" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Event Log Panel -->
|
||||
<Border Grid.Row="1"
|
||||
BackgroundColor="{AppThemeBinding Light=#F5F5F5, Dark=#2C2C2C}"
|
||||
StrokeThickness="0"
|
||||
Padding="10">
|
||||
<VerticalStackLayout>
|
||||
<Label Text="Event Log:"
|
||||
FontSize="12"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<ScrollView HeightRequest="80">
|
||||
<Label x:Name="EventLogLabel"
|
||||
Text="Events will appear here..."
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
LineBreakMode="WordWrap" />
|
||||
</ScrollView>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
83
ShellDemo/Pages/ProgressPage.xaml.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
// ProgressPage - ProgressBar and ActivityIndicator Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class ProgressPage : ContentPage
|
||||
{
|
||||
private int _eventCount = 0;
|
||||
private bool _isAnimating = false;
|
||||
|
||||
public ProgressPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnToggleIndicatorClicked(object? sender, EventArgs e)
|
||||
{
|
||||
ToggleIndicator.IsRunning = !ToggleIndicator.IsRunning;
|
||||
LogEvent($"ActivityIndicator: {(ToggleIndicator.IsRunning ? "Started" : "Stopped")}");
|
||||
}
|
||||
|
||||
private void OnProgressSliderValueChanged(object? sender, ValueChangedEventArgs e)
|
||||
{
|
||||
AnimatedProgress.Progress = e.NewValue / 100.0;
|
||||
ProgressLabel.Text = $"Progress: {(int)e.NewValue}%";
|
||||
}
|
||||
|
||||
private void OnResetClicked(object? sender, EventArgs e)
|
||||
{
|
||||
AnimatedProgress.Progress = 0;
|
||||
ProgressSlider.Value = 0;
|
||||
LogEvent("Progress reset to 0%");
|
||||
}
|
||||
|
||||
private async void OnAnimateClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (_isAnimating) return;
|
||||
_isAnimating = true;
|
||||
LogEvent("Animation started");
|
||||
|
||||
for (int i = (int)ProgressSlider.Value; i <= 100; i += 5)
|
||||
{
|
||||
AnimatedProgress.Progress = i / 100.0;
|
||||
ProgressSlider.Value = i;
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
_isAnimating = false;
|
||||
LogEvent("Animation completed");
|
||||
}
|
||||
|
||||
private async void OnSimulateDownloadClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (_isAnimating) return;
|
||||
_isAnimating = true;
|
||||
LogEvent("Download simulation started");
|
||||
|
||||
AnimatedProgress.Progress = 0;
|
||||
ProgressSlider.Value = 0;
|
||||
|
||||
var random = new Random();
|
||||
double progress = 0;
|
||||
while (progress < 1.0)
|
||||
{
|
||||
progress += random.NextDouble() * 0.1;
|
||||
if (progress > 1.0) progress = 1.0;
|
||||
AnimatedProgress.Progress = progress;
|
||||
ProgressSlider.Value = progress * 100;
|
||||
await Task.Delay(200 + random.Next(300));
|
||||
}
|
||||
|
||||
_isAnimating = false;
|
||||
LogEvent("Download simulation completed");
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
EventLogLabel.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLogLabel.Text}";
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
// SelectionPage - CheckBox, Switch, Slider Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class SelectionPage : ContentPage
|
||||
{
|
||||
private readonly Label _eventLog;
|
||||
private int _eventCount = 0;
|
||||
|
||||
public SelectionPage()
|
||||
{
|
||||
Title = "Selection Controls";
|
||||
|
||||
_eventLog = new Label
|
||||
{
|
||||
Text = "Events will appear here...",
|
||||
FontSize = 11,
|
||||
TextColor = Colors.Gray,
|
||||
LineBreakMode = LineBreakMode.WordWrap
|
||||
};
|
||||
|
||||
Content = new Grid
|
||||
{
|
||||
RowDefinitions =
|
||||
{
|
||||
new RowDefinition { Height = new GridLength(1, GridUnitType.Star) },
|
||||
new RowDefinition { Height = new GridLength(120) }
|
||||
},
|
||||
Children =
|
||||
{
|
||||
CreateMainContent(),
|
||||
CreateEventLogPanel()
|
||||
}
|
||||
};
|
||||
|
||||
Grid.SetRow((View)((Grid)Content).Children[0], 0);
|
||||
Grid.SetRow((View)((Grid)Content).Children[1], 1);
|
||||
}
|
||||
|
||||
private View CreateMainContent()
|
||||
{
|
||||
return new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 20,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Selection Controls", FontSize = 24, FontAttributes = FontAttributes.Bold },
|
||||
|
||||
CreateSection("CheckBox", CreateCheckBoxDemo()),
|
||||
CreateSection("Switch", CreateSwitchDemo()),
|
||||
CreateSection("Slider", CreateSliderDemo())
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateCheckBoxDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Basic checkboxes
|
||||
var basicRow = new HorizontalStackLayout { Spacing = 20 };
|
||||
|
||||
var cb1 = new CheckBox { IsChecked = false };
|
||||
cb1.CheckedChanged += (s, e) => LogEvent($"Checkbox 1: {(e.Value ? "Checked" : "Unchecked")}");
|
||||
basicRow.Children.Add(cb1);
|
||||
basicRow.Children.Add(new Label { Text = "Option 1", VerticalOptions = LayoutOptions.Center });
|
||||
|
||||
var cb2 = new CheckBox { IsChecked = true };
|
||||
cb2.CheckedChanged += (s, e) => LogEvent($"Checkbox 2: {(e.Value ? "Checked" : "Unchecked")}");
|
||||
basicRow.Children.Add(cb2);
|
||||
basicRow.Children.Add(new Label { Text = "Option 2 (default checked)", VerticalOptions = LayoutOptions.Center });
|
||||
|
||||
layout.Children.Add(basicRow);
|
||||
|
||||
// Colored checkboxes
|
||||
var colorRow = new HorizontalStackLayout { Spacing = 20 };
|
||||
var colors = new[] { Colors.Red, Colors.Green, Colors.Blue, Colors.Purple };
|
||||
foreach (var color in colors)
|
||||
{
|
||||
var cb = new CheckBox { Color = color, IsChecked = true };
|
||||
cb.CheckedChanged += (s, e) => LogEvent($"{color} checkbox: {(e.Value ? "Checked" : "Unchecked")}");
|
||||
colorRow.Children.Add(cb);
|
||||
}
|
||||
layout.Children.Add(new Label { Text = "Colored Checkboxes:", FontSize = 12 });
|
||||
layout.Children.Add(colorRow);
|
||||
|
||||
// Disabled checkbox
|
||||
var disabledRow = new HorizontalStackLayout { Spacing = 10 };
|
||||
var disabledCb = new CheckBox { IsChecked = true, IsEnabled = false };
|
||||
disabledRow.Children.Add(disabledCb);
|
||||
disabledRow.Children.Add(new Label { Text = "Disabled (checked)", VerticalOptions = LayoutOptions.Center, TextColor = Colors.Gray });
|
||||
layout.Children.Add(disabledRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateSwitchDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Basic switch
|
||||
var basicRow = new HorizontalStackLayout { Spacing = 15 };
|
||||
var statusLabel = new Label { Text = "Off", VerticalOptions = LayoutOptions.Center, WidthRequest = 50 };
|
||||
var sw1 = new Switch { IsToggled = false };
|
||||
sw1.Toggled += (s, e) =>
|
||||
{
|
||||
statusLabel.Text = e.Value ? "On" : "Off";
|
||||
LogEvent($"Switch toggled: {(e.Value ? "ON" : "OFF")}");
|
||||
};
|
||||
basicRow.Children.Add(sw1);
|
||||
basicRow.Children.Add(statusLabel);
|
||||
layout.Children.Add(basicRow);
|
||||
|
||||
// Colored switches
|
||||
var colorRow = new HorizontalStackLayout { Spacing = 20 };
|
||||
var switchColors = new[] { Colors.Green, Colors.Orange, Colors.Purple };
|
||||
foreach (var color in switchColors)
|
||||
{
|
||||
var sw = new Switch { IsToggled = true, OnColor = color };
|
||||
sw.Toggled += (s, e) => LogEvent($"{color} switch: {(e.Value ? "ON" : "OFF")}");
|
||||
colorRow.Children.Add(sw);
|
||||
}
|
||||
layout.Children.Add(new Label { Text = "Colored Switches:", FontSize = 12 });
|
||||
layout.Children.Add(colorRow);
|
||||
|
||||
// Disabled switch
|
||||
var disabledRow = new HorizontalStackLayout { Spacing = 10 };
|
||||
var disabledSw = new Switch { IsToggled = true, IsEnabled = false };
|
||||
disabledRow.Children.Add(disabledSw);
|
||||
disabledRow.Children.Add(new Label { Text = "Disabled (on)", VerticalOptions = LayoutOptions.Center, TextColor = Colors.Gray });
|
||||
layout.Children.Add(disabledRow);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private View CreateSliderDemo()
|
||||
{
|
||||
var layout = new VerticalStackLayout { Spacing = 15 };
|
||||
|
||||
// Basic slider
|
||||
var valueLabel = new Label { Text = "Value: 50" };
|
||||
var slider1 = new Slider { Minimum = 0, Maximum = 100, Value = 50 };
|
||||
slider1.ValueChanged += (s, e) =>
|
||||
{
|
||||
valueLabel.Text = $"Value: {(int)e.NewValue}";
|
||||
LogEvent($"Slider value: {(int)e.NewValue}");
|
||||
};
|
||||
layout.Children.Add(slider1);
|
||||
layout.Children.Add(valueLabel);
|
||||
|
||||
// Slider with custom range
|
||||
layout.Children.Add(new Label { Text = "Temperature (0-40°C):", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var tempLabel = new Label { Text = "20°C" };
|
||||
var tempSlider = new Slider { Minimum = 0, Maximum = 40, Value = 20 };
|
||||
tempSlider.ValueChanged += (s, e) =>
|
||||
{
|
||||
tempLabel.Text = $"{(int)e.NewValue}°C";
|
||||
LogEvent($"Temperature: {(int)e.NewValue}°C");
|
||||
};
|
||||
layout.Children.Add(tempSlider);
|
||||
layout.Children.Add(tempLabel);
|
||||
|
||||
// Colored slider
|
||||
layout.Children.Add(new Label { Text = "Colored Slider:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var colorSlider = new Slider
|
||||
{
|
||||
Minimum = 0,
|
||||
Maximum = 100,
|
||||
Value = 75,
|
||||
MinimumTrackColor = Colors.Green,
|
||||
MaximumTrackColor = Colors.LightGray,
|
||||
ThumbColor = Colors.DarkGreen
|
||||
};
|
||||
colorSlider.ValueChanged += (s, e) => LogEvent($"Colored slider: {(int)e.NewValue}");
|
||||
layout.Children.Add(colorSlider);
|
||||
|
||||
// Disabled slider
|
||||
layout.Children.Add(new Label { Text = "Disabled Slider:", FontSize = 12, Margin = new Thickness(0, 10, 0, 0) });
|
||||
var disabledSlider = new Slider { Minimum = 0, Maximum = 100, Value = 30, IsEnabled = false };
|
||||
layout.Children.Add(disabledSlider);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
private Frame CreateSection(string title, View content)
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
BackgroundColor = Colors.White,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 10,
|
||||
Children =
|
||||
{
|
||||
new Label { Text = title, FontSize = 16, FontAttributes = FontAttributes.Bold },
|
||||
content
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private View CreateEventLogPanel()
|
||||
{
|
||||
return new Frame
|
||||
{
|
||||
BackgroundColor = Color.FromArgb("#F5F5F5"),
|
||||
Padding = new Thickness(10),
|
||||
CornerRadius = 0,
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label { Text = "Event Log:", FontSize = 12, FontAttributes = FontAttributes.Bold },
|
||||
new ScrollView
|
||||
{
|
||||
HeightRequest = 80,
|
||||
Content = _eventLog
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
_eventLog.Text = $"[{timestamp}] {_eventCount}. {message}\n{_eventLog.Text}";
|
||||
}
|
||||
}
|
||||
157
ShellDemo/Pages/SelectionPage.xaml
Normal file
@@ -0,0 +1,157 @@
|
||||
<?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.SelectionPage"
|
||||
Title="Selection Controls"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<Grid RowDefinitions="*,120">
|
||||
|
||||
<!-- Main Content -->
|
||||
<ScrollView Grid.Row="0">
|
||||
<VerticalStackLayout Padding="20" Spacing="20">
|
||||
|
||||
<Label Text="Selection Controls"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- CheckBox Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="CheckBox"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic checkboxes -->
|
||||
<HorizontalStackLayout Spacing="20">
|
||||
<CheckBox x:Name="Checkbox1" IsChecked="False" CheckedChanged="OnCheckboxChanged" />
|
||||
<Label Text="Option 1" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<CheckBox x:Name="Checkbox2" IsChecked="True" CheckedChanged="OnCheckboxChanged" />
|
||||
<Label Text="Option 2 (default checked)" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Colored checkboxes -->
|
||||
<Label Text="Colored Checkboxes:"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<HorizontalStackLayout Spacing="20">
|
||||
<CheckBox Color="Red" IsChecked="True" CheckedChanged="OnColoredCheckboxChanged" />
|
||||
<CheckBox Color="Green" IsChecked="True" CheckedChanged="OnColoredCheckboxChanged" />
|
||||
<CheckBox Color="Blue" IsChecked="True" CheckedChanged="OnColoredCheckboxChanged" />
|
||||
<CheckBox Color="Purple" IsChecked="True" CheckedChanged="OnColoredCheckboxChanged" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Disabled checkbox -->
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<CheckBox IsChecked="True" IsEnabled="False" />
|
||||
<Label Text="Disabled (checked)" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Switch Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="Switch"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic switch -->
|
||||
<HorizontalStackLayout Spacing="15">
|
||||
<Switch x:Name="BasicSwitch" IsToggled="False" Toggled="OnSwitchToggled" />
|
||||
<Label x:Name="SwitchStatusLabel" Text="Off" VerticalOptions="Center" WidthRequest="50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Colored switches -->
|
||||
<Label Text="Colored Switches:"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<HorizontalStackLayout Spacing="20">
|
||||
<Switch IsToggled="True" OnColor="Green" Toggled="OnColoredSwitchToggled" />
|
||||
<Switch IsToggled="True" OnColor="Orange" Toggled="OnColoredSwitchToggled" />
|
||||
<Switch IsToggled="True" OnColor="Purple" Toggled="OnColoredSwitchToggled" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<!-- Disabled switch -->
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Switch IsToggled="True" IsEnabled="False" />
|
||||
<Label Text="Disabled (on)" VerticalOptions="Center"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</HorizontalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
<!-- Slider Section -->
|
||||
<Border Style="{StaticResource ThemedCard}">
|
||||
<VerticalStackLayout Spacing="15">
|
||||
<Label Text="Slider"
|
||||
FontSize="16"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Basic slider -->
|
||||
<Slider x:Name="BasicSlider" Minimum="0" Maximum="100" Value="50" ValueChanged="OnSliderValueChanged" />
|
||||
<Label x:Name="SliderValueLabel" Text="Value: 50"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Temperature slider -->
|
||||
<Label Text="Temperature (0-40°C):"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Slider x:Name="TempSlider" Minimum="0" Maximum="40" Value="20" ValueChanged="OnTempSliderValueChanged" />
|
||||
<Label x:Name="TempLabel" Text="20°C"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<!-- Colored slider -->
|
||||
<Label Text="Colored Slider:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Slider Minimum="0" Maximum="100" Value="75"
|
||||
MinimumTrackColor="Green"
|
||||
MaximumTrackColor="LightGray"
|
||||
ThumbColor="DarkGreen"
|
||||
ValueChanged="OnColoredSliderValueChanged" />
|
||||
|
||||
<!-- Disabled slider -->
|
||||
<Label Text="Disabled Slider:"
|
||||
FontSize="12"
|
||||
Margin="0,10,0,0"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Slider Minimum="0" Maximum="100" Value="30" IsEnabled="False" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Event Log Panel -->
|
||||
<Border Grid.Row="1"
|
||||
BackgroundColor="{AppThemeBinding Light=#F5F5F5, Dark=#2C2C2C}"
|
||||
StrokeThickness="0"
|
||||
Padding="10">
|
||||
<VerticalStackLayout>
|
||||
<Label Text="Event Log:"
|
||||
FontSize="12"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<ScrollView HeightRequest="80">
|
||||
<Label x:Name="EventLogLabel"
|
||||
Text="Events will appear here..."
|
||||
FontSize="11"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
LineBreakMode="WordWrap" />
|
||||
</ScrollView>
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
65
ShellDemo/Pages/SelectionPage.xaml.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
// SelectionPage - CheckBox, Switch, Slider Demo
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class SelectionPage : ContentPage
|
||||
{
|
||||
private int _eventCount = 0;
|
||||
|
||||
public SelectionPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnCheckboxChanged(object? sender, CheckedChangedEventArgs e)
|
||||
{
|
||||
if (sender == Checkbox1)
|
||||
LogEvent($"Checkbox 1: {(e.Value ? "Checked" : "Unchecked")}");
|
||||
else if (sender == Checkbox2)
|
||||
LogEvent($"Checkbox 2: {(e.Value ? "Checked" : "Unchecked")}");
|
||||
}
|
||||
|
||||
private void OnColoredCheckboxChanged(object? sender, CheckedChangedEventArgs e)
|
||||
{
|
||||
if (sender is CheckBox cb)
|
||||
LogEvent($"{cb.Color} checkbox: {(e.Value ? "Checked" : "Unchecked")}");
|
||||
}
|
||||
|
||||
private void OnSwitchToggled(object? sender, ToggledEventArgs e)
|
||||
{
|
||||
SwitchStatusLabel.Text = e.Value ? "On" : "Off";
|
||||
LogEvent($"Switch toggled: {(e.Value ? "ON" : "OFF")}");
|
||||
}
|
||||
|
||||
private void OnColoredSwitchToggled(object? sender, ToggledEventArgs e)
|
||||
{
|
||||
if (sender is Switch sw)
|
||||
LogEvent($"{sw.OnColor} switch: {(e.Value ? "ON" : "OFF")}");
|
||||
}
|
||||
|
||||
private void OnSliderValueChanged(object? sender, ValueChangedEventArgs e)
|
||||
{
|
||||
SliderValueLabel.Text = $"Value: {(int)e.NewValue}";
|
||||
LogEvent($"Slider value: {(int)e.NewValue}");
|
||||
}
|
||||
|
||||
private void OnTempSliderValueChanged(object? sender, ValueChangedEventArgs e)
|
||||
{
|
||||
TempLabel.Text = $"{(int)e.NewValue}°C";
|
||||
LogEvent($"Temperature: {(int)e.NewValue}°C");
|
||||
}
|
||||
|
||||
private void OnColoredSliderValueChanged(object? sender, ValueChangedEventArgs e)
|
||||
{
|
||||
LogEvent($"Colored slider: {(int)e.NewValue}");
|
||||
}
|
||||
|
||||
private void LogEvent(string message)
|
||||
{
|
||||
_eventCount++;
|
||||
var timestamp = DateTime.Now.ToString("HH:mm:ss");
|
||||
EventLogLabel.Text = $"[{timestamp}] {_eventCount}. {message}\n{EventLogLabel.Text}";
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
// TextInputPage - Demonstrates text input controls
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public class TextInputPage : ContentPage
|
||||
{
|
||||
private Label _entryOutput;
|
||||
private Label _searchOutput;
|
||||
private Label _editorOutput;
|
||||
|
||||
public TextInputPage()
|
||||
{
|
||||
Title = "Text Input";
|
||||
|
||||
_entryOutput = new Label { TextColor = Colors.Gray, FontSize = 12 };
|
||||
_searchOutput = new Label { TextColor = Colors.Gray, FontSize = 12 };
|
||||
_editorOutput = new Label { TextColor = Colors.Gray, FontSize = 12 };
|
||||
|
||||
Content = new ScrollView
|
||||
{
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Padding = new Thickness(20),
|
||||
Spacing = 15,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Text Input Controls",
|
||||
FontSize = 24,
|
||||
FontAttributes = FontAttributes.Bold
|
||||
},
|
||||
new Label
|
||||
{
|
||||
Text = "Click on any field and start typing. All keyboard input is handled by the framework.",
|
||||
FontSize = 14,
|
||||
TextColor = Colors.Gray
|
||||
},
|
||||
|
||||
// Entry Section
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Label { Text = "Entry (Single Line)", FontSize = 18, FontAttributes = FontAttributes.Bold },
|
||||
CreateEntry("Enter your name...", e => _entryOutput.Text = $"You typed: {e.Text}"),
|
||||
_entryOutput,
|
||||
|
||||
CreateEntry("Enter your email...", null, Keyboard.Email),
|
||||
new Label { Text = "Email keyboard type", FontSize = 12, TextColor = Colors.Gray },
|
||||
|
||||
CreatePasswordEntry("Enter password..."),
|
||||
new Label { Text = "Password field (text hidden)", FontSize = 12, TextColor = Colors.Gray },
|
||||
|
||||
// SearchBar Section
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Label { Text = "SearchBar", FontSize = 18, FontAttributes = FontAttributes.Bold },
|
||||
CreateSearchBar(),
|
||||
_searchOutput,
|
||||
|
||||
// Editor Section
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Label { Text = "Editor (Multi-line)", FontSize = 18, FontAttributes = FontAttributes.Bold },
|
||||
CreateEditor(),
|
||||
_editorOutput,
|
||||
|
||||
// Instructions
|
||||
new BoxView { HeightRequest = 1, Color = Colors.LightGray },
|
||||
new Frame
|
||||
{
|
||||
BackgroundColor = Color.FromArgb("#E3F2FD"),
|
||||
CornerRadius = 8,
|
||||
Padding = new Thickness(15),
|
||||
Content = new VerticalStackLayout
|
||||
{
|
||||
Spacing = 5,
|
||||
Children =
|
||||
{
|
||||
new Label
|
||||
{
|
||||
Text = "Keyboard Shortcuts",
|
||||
FontAttributes = FontAttributes.Bold
|
||||
},
|
||||
new Label { Text = "Ctrl+A: Select all" },
|
||||
new Label { Text = "Ctrl+C: Copy" },
|
||||
new Label { Text = "Ctrl+V: Paste" },
|
||||
new Label { Text = "Ctrl+X: Cut" },
|
||||
new Label { Text = "Home/End: Move to start/end" },
|
||||
new Label { Text = "Shift+Arrow: Select text" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private Entry CreateEntry(string placeholder, Action<Entry>? onTextChanged, Keyboard? keyboard = null)
|
||||
{
|
||||
var entry = new Entry
|
||||
{
|
||||
Placeholder = placeholder,
|
||||
FontSize = 14
|
||||
};
|
||||
|
||||
if (keyboard != null)
|
||||
{
|
||||
entry.Keyboard = keyboard;
|
||||
}
|
||||
|
||||
if (onTextChanged != null)
|
||||
{
|
||||
entry.TextChanged += (s, e) => onTextChanged(entry);
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
private Entry CreatePasswordEntry(string placeholder)
|
||||
{
|
||||
return new Entry
|
||||
{
|
||||
Placeholder = placeholder,
|
||||
FontSize = 14,
|
||||
IsPassword = true
|
||||
};
|
||||
}
|
||||
|
||||
private SearchBar CreateSearchBar()
|
||||
{
|
||||
var searchBar = new SearchBar
|
||||
{
|
||||
Placeholder = "Search for items..."
|
||||
};
|
||||
|
||||
searchBar.TextChanged += (s, e) =>
|
||||
{
|
||||
_searchOutput.Text = $"Searching: {e.NewTextValue}";
|
||||
};
|
||||
|
||||
searchBar.SearchButtonPressed += (s, e) =>
|
||||
{
|
||||
_searchOutput.Text = $"Search submitted: {searchBar.Text}";
|
||||
};
|
||||
|
||||
return searchBar;
|
||||
}
|
||||
|
||||
private Editor CreateEditor()
|
||||
{
|
||||
var editor = new Editor
|
||||
{
|
||||
Placeholder = "Enter multiple lines of text here...\nPress Enter to create new lines.",
|
||||
HeightRequest = 120,
|
||||
FontSize = 14
|
||||
};
|
||||
|
||||
editor.TextChanged += (s, e) =>
|
||||
{
|
||||
var lineCount = string.IsNullOrEmpty(e.NewTextValue) ? 0 : e.NewTextValue.Split('\n').Length;
|
||||
_editorOutput.Text = $"Lines: {lineCount}, Characters: {e.NewTextValue?.Length ?? 0}";
|
||||
};
|
||||
|
||||
return editor;
|
||||
}
|
||||
}
|
||||
121
ShellDemo/Pages/TextInputPage.xaml
Normal file
@@ -0,0 +1,121 @@
|
||||
<?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.TextInputPage"
|
||||
Title="Text Input"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ScrollView>
|
||||
<VerticalStackLayout Padding="20" Spacing="15">
|
||||
|
||||
<Label Text="Text Input Controls"
|
||||
FontSize="24"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Label Text="Click on any field and start typing. All keyboard input is handled by the framework."
|
||||
FontSize="14"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<!-- Entry Section -->
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<Label Text="Entry (Single Line)"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Entry x:Name="NameEntry"
|
||||
Placeholder="Enter your name..."
|
||||
Style="{StaticResource ThemedEntry}"
|
||||
TextChanged="OnNameEntryTextChanged" />
|
||||
|
||||
<Label x:Name="EntryOutputLabel"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<Entry x:Name="EmailEntry"
|
||||
Placeholder="Enter your email..."
|
||||
Keyboard="Email"
|
||||
Style="{StaticResource ThemedEntry}" />
|
||||
|
||||
<Label Text="Email keyboard type"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<Entry x:Name="PasswordEntry"
|
||||
Placeholder="Enter password..."
|
||||
IsPassword="True"
|
||||
Style="{StaticResource ThemedEntry}" />
|
||||
|
||||
<Label Text="Password field (text hidden)"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<!-- SearchBar Section -->
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<Label Text="SearchBar"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<SearchBar x:Name="MainSearchBar"
|
||||
Placeholder="Search for items..."
|
||||
TextChanged="OnSearchBarTextChanged"
|
||||
SearchButtonPressed="OnSearchButtonPressed" />
|
||||
|
||||
<Label x:Name="SearchOutputLabel"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<!-- Editor Section -->
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<Label Text="Editor (Multi-line)"
|
||||
FontSize="18"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
|
||||
<Editor x:Name="MainEditor"
|
||||
Placeholder="Enter multiple lines of text here... Press Enter to create new lines."
|
||||
HeightRequest="120"
|
||||
Style="{StaticResource ThemedEditor}"
|
||||
TextChanged="OnEditorTextChanged" />
|
||||
|
||||
<Label x:Name="EditorOutputLabel"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
|
||||
<!-- Keyboard Shortcuts -->
|
||||
<BoxView Style="{StaticResource ThemedDivider}" />
|
||||
|
||||
<Border BackgroundColor="{AppThemeBinding Light=#E3F2FD, Dark=#1A3A5C}"
|
||||
StrokeThickness="0"
|
||||
Padding="15">
|
||||
<Border.StrokeShape>
|
||||
<RoundRectangle CornerRadius="8" />
|
||||
</Border.StrokeShape>
|
||||
|
||||
<VerticalStackLayout Spacing="5">
|
||||
<Label Text="Keyboard Shortcuts"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Ctrl+A: Select all"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Ctrl+C: Copy"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Ctrl+V: Paste"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Ctrl+X: Cut"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Home/End: Move to start/end"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Shift+Arrow: Select text"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
</Border>
|
||||
|
||||
</VerticalStackLayout>
|
||||
</ScrollView>
|
||||
</ContentPage>
|
||||
34
ShellDemo/Pages/TextInputPage.xaml.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// TextInputPage - Demonstrates text input controls
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
public partial class TextInputPage : ContentPage
|
||||
{
|
||||
public TextInputPage()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnNameEntryTextChanged(object? sender, TextChangedEventArgs e)
|
||||
{
|
||||
EntryOutputLabel.Text = $"You typed: {e.NewTextValue}";
|
||||
}
|
||||
|
||||
private void OnSearchBarTextChanged(object? sender, TextChangedEventArgs e)
|
||||
{
|
||||
SearchOutputLabel.Text = $"Searching: {e.NewTextValue}";
|
||||
}
|
||||
|
||||
private void OnSearchButtonPressed(object? sender, EventArgs e)
|
||||
{
|
||||
SearchOutputLabel.Text = $"Search submitted: {MainSearchBar.Text}";
|
||||
}
|
||||
|
||||
private void OnEditorTextChanged(object? sender, TextChangedEventArgs e)
|
||||
{
|
||||
var lineCount = string.IsNullOrEmpty(e.NewTextValue) ? 0 : e.NewTextValue.Split('\n').Length;
|
||||
EditorOutputLabel.Text = $"Lines: {lineCount}, Characters: {e.NewTextValue?.Length ?? 0}";
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Platforms/Linux/Program.cs - Linux platform entry point
|
||||
// Same pattern as Android's MainActivity or iOS's AppDelegate
|
||||
|
||||
using Microsoft.Maui.Platform.Linux;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// Create the shared MAUI app
|
||||
var app = MauiProgram.CreateMauiApp();
|
||||
|
||||
// Run on Linux platform
|
||||
LinuxApplication.Run(app, args);
|
||||
}
|
||||
}
|
||||
67
ShellDemo/Program.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
// Program.cs - Linux platform entry point
|
||||
|
||||
using Microsoft.Maui.Platform.Linux;
|
||||
|
||||
namespace ShellDemo;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// Redirect console output to a log file for debugging
|
||||
var logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "shelldemo.log");
|
||||
using var logWriter = new StreamWriter(logPath, append: false) { AutoFlush = true };
|
||||
var multiWriter = new MultiTextWriter(Console.Out, logWriter);
|
||||
Console.SetOut(multiWriter);
|
||||
Console.SetError(multiWriter);
|
||||
|
||||
// Global exception handler
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
var ex = e.ExceptionObject as Exception;
|
||||
Console.WriteLine($"[FATAL] Unhandled exception: {ex?.GetType().Name}: {ex?.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {ex?.StackTrace}");
|
||||
if (ex?.InnerException != null)
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Inner exception: {ex.InnerException.GetType().Name}: {ex.InnerException.Message}");
|
||||
Console.WriteLine($"[FATAL] Inner stack trace: {ex.InnerException.StackTrace}");
|
||||
}
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (sender, e) =>
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Unobserved task exception: {e.Exception?.GetType().Name}: {e.Exception?.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {e.Exception?.StackTrace}");
|
||||
e.SetObserved(); // Prevent crash
|
||||
};
|
||||
|
||||
Console.WriteLine($"[Program] Starting Shell Demo at {DateTime.Now}");
|
||||
Console.WriteLine($"[Program] Log file: {logPath}");
|
||||
|
||||
try
|
||||
{
|
||||
// Create the MAUI app with all handlers registered
|
||||
var app = MauiProgram.CreateMauiApp();
|
||||
|
||||
// Run on Linux platform
|
||||
LinuxApplication.Run(app, args);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Exception in Main: {ex.GetType().Name}: {ex.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {ex.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to write to both console and file
|
||||
class MultiTextWriter : TextWriter
|
||||
{
|
||||
private readonly TextWriter[] _writers;
|
||||
public MultiTextWriter(params TextWriter[] writers) => _writers = writers;
|
||||
public override System.Text.Encoding Encoding => System.Text.Encoding.UTF8;
|
||||
public override void Write(char value) { foreach (var w in _writers) w.Write(value); }
|
||||
public override void WriteLine(string? value) { foreach (var w in _writers) w.WriteLine(value); }
|
||||
public override void Flush() { foreach (var w in _writers) w.Flush(); }
|
||||
}
|
||||
4
ShellDemo/Resources/AppIcon/appicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="456" height="456" fill="#1B79C4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 184 B |
7
ShellDemo/Resources/AppIcon/appicon_combined.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="256" height="256" rx="48" fill="#1B79C4"/>
|
||||
<g transform="translate(48, 48) scale(6.67)">
|
||||
<path fill="white" d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 338 B |
7
ShellDemo/Resources/AppIcon/appiconfg.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Dashboard icon (Material Icons) -->
|
||||
<g transform="translate(88, 88) scale(11.67)">
|
||||
<path fill="#000000" d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 326 B |
1
ShellDemo/Resources/Images/calendar_month_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0 0v-80 80Zm280 240q-17 0-28.5-11.5T440-440q0-17 11.5-28.5T480-480q17 0 28.5 11.5T520-440q0 17-11.5 28.5T480-400Zm-160 0q-17 0-28.5-11.5T280-440q0-17 11.5-28.5T320-480q17 0 28.5 11.5T360-440q0 17-11.5 28.5T320-400Zm320 0q-17 0-28.5-11.5T600-440q0-17 11.5-28.5T640-480q17 0 28.5 11.5T680-440q0 17-11.5 28.5T640-400ZM480-240q-17 0-28.5-11.5T440-280q0-17 11.5-28.5T480-320q17 0 28.5 11.5T520-280q0 17-11.5 28.5T480-240Zm-160 0q-17 0-28.5-11.5T280-280q0-17 11.5-28.5T320-320q17 0 28.5 11.5T360-280q0 17-11.5 28.5T320-240Zm320 0q-17 0-28.5-11.5T600-280q0-17 11.5-28.5T640-320q17 0 28.5 11.5T680-280q0 17-11.5 28.5T640-240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 927 B |
1
ShellDemo/Resources/Images/calendar_month_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M200-80q-33 0-56.5-23.5T120-160v-560q0-33 23.5-56.5T200-800h40v-80h80v80h320v-80h80v80h40q33 0 56.5 23.5T840-720v560q0 33-23.5 56.5T760-80H200Zm0-80h560v-400H200v400Zm0-480h560v-80H200v80Zm0 0v-80 80Zm280 240q-17 0-28.5-11.5T440-440q0-17 11.5-28.5T480-480q17 0 28.5 11.5T520-440q0 17-11.5 28.5T480-400Zm-160 0q-17 0-28.5-11.5T280-440q0-17 11.5-28.5T320-480q17 0 28.5 11.5T360-440q0 17-11.5 28.5T320-400Zm320 0q-17 0-28.5-11.5T600-440q0-17 11.5-28.5T640-480q17 0 28.5 11.5T680-440q0 17-11.5 28.5T640-400ZM480-240q-17 0-28.5-11.5T440-280q0-17 11.5-28.5T480-320q17 0 28.5 11.5T520-280q0 17-11.5 28.5T480-240Zm-160 0q-17 0-28.5-11.5T280-280q0-17 11.5-28.5T320-320q17 0 28.5 11.5T360-280q0 17-11.5 28.5T320-240Zm320 0q-17 0-28.5-11.5T600-280q0-17 11.5-28.5T640-320q17 0 28.5 11.5T680-280q0 17-11.5 28.5T640-240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 927 B |
1
ShellDemo/Resources/Images/check_box_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="m424-312 282-282-56-56-226 226-114-114-56 56 170 170ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z"/></svg>
|
||||
|
After Width: | Height: | Size: 331 B |
1
ShellDemo/Resources/Images/check_box_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="m424-312 282-282-56-56-226 226-114-114-56 56 170 170ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z"/></svg>
|
||||
|
After Width: | Height: | Size: 331 B |
1
ShellDemo/Resources/Images/edit_note_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M160-400v-80h280v80H160Zm0-160v-80h440v80H160Zm0-160v-80h440v80H160Zm360 560v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q8 9 12.5 20t4.5 22q0 11-4 22.5T863-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z"/></svg>
|
||||
|
After Width: | Height: | Size: 394 B |
1
ShellDemo/Resources/Images/edit_note_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M160-400v-80h280v80H160Zm0-160v-80h440v80H160Zm0-160v-80h440v80H160Zm360 560v-123l221-220q9-9 20-13t22-4q12 0 23 4.5t20 13.5l37 37q8 9 12.5 20t4.5 22q0 11-4 22.5T863-380L643-160H520Zm300-263-37-37 37 37ZM580-220h38l121-122-18-19-19-18-122 121v38Zm141-141-19-18 37 37-18-19Z"/></svg>
|
||||
|
After Width: | Height: | Size: 394 B |
1
ShellDemo/Resources/Images/format_list_bulleted_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M360-200v-80h480v80H360Zm0-240v-80h480v80H360Zm0-240v-80h480v80H360ZM200-160q-33 0-56.5-23.5T120-240q0-33 23.5-56.5T200-320q33 0 56.5 23.5T280-240q0 33-23.5 56.5T200-160Zm0-240q-33 0-56.5-23.5T120-480q0-33 23.5-56.5T200-560q33 0 56.5 23.5T280-480q0 33-23.5 56.5T200-400Zm0-240q-33 0-56.5-23.5T120-720q0-33 23.5-56.5T200-800q33 0 56.5 23.5T280-720q0 33-23.5 56.5T200-640Z"/></svg>
|
||||
|
After Width: | Height: | Size: 491 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M360-200v-80h480v80H360Zm0-240v-80h480v80H360Zm0-240v-80h480v80H360ZM200-160q-33 0-56.5-23.5T120-240q0-33 23.5-56.5T200-320q33 0 56.5 23.5T280-240q0 33-23.5 56.5T200-160Zm0-240q-33 0-56.5-23.5T120-480q0-33 23.5-56.5T200-560q33 0 56.5 23.5T280-480q0 33-23.5 56.5T200-400Zm0-240q-33 0-56.5-23.5T120-720q0-33 23.5-56.5T200-800q33 0 56.5 23.5T280-720q0 33-23.5 56.5T200-640Z"/></svg>
|
||||
|
After Width: | Height: | Size: 491 B |
1
ShellDemo/Resources/Images/grid_view_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M120-520v-320h320v320H120Zm0 400v-320h320v320H120Zm400-400v-320h320v320H520Zm0 400v-320h320v320H520ZM200-600h160v-160H200v160Zm400 0h160v-160H600v160Zm0 400h160v-160H600v160Zm-400 0h160v-160H200v160Zm400-400Zm0 240Zm-240 0Zm0-240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
1
ShellDemo/Resources/Images/grid_view_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M120-520v-320h320v320H120Zm0 400v-320h320v320H120Zm400-400v-320h320v320H520Zm0 400v-320h320v320H520ZM200-600h160v-160H200v160Zm400 0h160v-160H600v160Zm0 400h160v-160H600v160Zm-400 0h160v-160H200v160Zm400-400Zm0 240Zm-240 0Zm0-240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 351 B |
1
ShellDemo/Resources/Images/home_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>
|
||||
|
After Width: | Height: | Size: 239 B |
1
ShellDemo/Resources/Images/home_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>
|
||||
|
After Width: | Height: | Size: 239 B |
1
ShellDemo/Resources/Images/hourglass_empty_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M320-160h320v-120q0-66-47-113t-113-47q-66 0-113 47t-47 113v120Zm160-360q66 0 113-47t47-113v-120H320v120q0 66 47 113t113 47ZM160-80v-80h80v-120q0-61 28.5-114.5T348-480q-51-32-79.5-85.5T240-680v-120h-80v-80h640v80h-80v120q0 61-28.5 114.5T612-480q51 32 79.5 85.5T720-280v120h80v80H160Z"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
1
ShellDemo/Resources/Images/hourglass_empty_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M320-160h320v-120q0-66-47-113t-113-47q-66 0-113 47t-47 113v120Zm160-360q66 0 113-47t47-113v-120H320v120q0 66 47 113t113 47ZM160-80v-80h80v-120q0-61 28.5-114.5T348-480q-51-32-79.5-85.5T240-680v-120h-80v-80h640v80h-80v120q0 61-28.5 114.5T612-480q51 32 79.5 85.5T720-280v120h80v80H160Z"/></svg>
|
||||
|
After Width: | Height: | Size: 403 B |
1
ShellDemo/Resources/Images/info_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 531 B |
1
ShellDemo/Resources/Images/info_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M440-280h80v-240h-80v240Zm40-320q17 0 28.5-11.5T520-640q0-17-11.5-28.5T480-680q-17 0-28.5 11.5T440-640q0 17 11.5 28.5T480-600Zm0 520q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>
|
||||
|
After Width: | Height: | Size: 531 B |
44
ShellDemo/Resources/Images/logo_only.svg
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="904px" height="904px" viewBox="0 0 904 904" style="enable-background:new 0 0 904 904;" xml:space="preserve">
|
||||
<path style="fill:#1B79C4;" d="M352.47,305.72c2.59,0.01,5.18,0,7.77-0.02c15.57-0.02,30.94,1.71,46.17,5.54
|
||||
c0.94,0.23,1.88,0.46,2.85,0.7c21.6,5.47,42.63,13.46,63.64,21.34c26.31,9.85,54.17,20.02,82.04,20.19c0.7,0.01,1.41,0.01,2.13,0.02
|
||||
c21.87,0.24,21.87,0.24,43.52-2.93c1.15-0.26,1.15-0.26,2.32-0.53c27.81-6.62,56.02-18.76,79.07-38.33
|
||||
c2.47-1.99,2.47-1.99,4.04-1.99c0.15,9.24,0.26,18.48,0.32,27.73c0.03,4.29,0.08,8.58,0.15,12.88c0.07,4.14,0.11,8.29,0.12,12.43
|
||||
c0.01,1.58,0.03,3.16,0.07,4.74c0.25,12.36,0.25,12.36-2.02,16.31c-1.58,1.62-3.07,2.82-4.91,4c-0.75,0.63-1.49,1.27-2.26,1.92
|
||||
c-2.37,1.8-4.75,3.58-7.15,5.34c-0.86,0.63-1.72,1.27-2.61,1.92c-24.4,17.65-51.89,29.41-80.47,33.48
|
||||
c-0.54,0.09-1.08,0.17-1.64,0.26c-7.03,0.98-14.12,0.87-21.19,0.88c-0.7,0-1.4,0-2.12,0c-11.18-0.01-22.02-0.68-33.05-2.95
|
||||
c-0.73-0.14-1.47-0.28-2.22-0.43c-21.91-4.28-43.2-12.21-64.33-20.02c-68.48-25.32-136.76-38.11-203.72,1.23
|
||||
c-8.4,5.1-16.65,10.72-24.29,17.22c-2.49,2-2.49,2-4.05,2c-0.24-9.43-0.42-18.86-0.53-28.29c-0.05-4.38-0.13-8.76-0.24-13.14
|
||||
c-0.11-4.23-0.17-8.46-0.2-12.7c-0.02-1.61-0.06-3.22-0.11-4.83c-0.4-12.22-0.4-12.22,2.05-16.13c2.73-3.18,5.86-5.34,9.22-7.53
|
||||
c1.55-1.17,3.1-2.34,4.65-3.51c1.55-1.09,3.1-2.17,4.66-3.24c0.84-0.58,1.68-1.16,2.55-1.75c20.5-13.88,42.33-23.81,65.73-28.72
|
||||
c0.57-0.12,1.13-0.24,1.72-0.36C330.94,306.24,341.52,305.65,352.47,305.72z"/>
|
||||
<path style="fill:#1D5DB3;" d="M462.25,230.05c36.11,13.71,36.11,13.71,73.29,22.46c0.56,0.1,1.12,0.19,1.7,0.29
|
||||
c48.86,8.29,104.44-2.97,144.83-37.25c1.6-1.17,1.6-1.17,3.95-1.17c0.2,8.99,0.35,17.98,0.45,26.97c0.04,4.18,0.1,8.35,0.2,12.52
|
||||
c0.09,4.03,0.15,8.06,0.17,12.1c0.01,1.54,0.05,3.07,0.09,4.6c0.35,12.01,0.35,12.01-2.78,16.23c-2.19,1.87-4.33,3.33-6.76,4.75
|
||||
c-1.08,0.8-2.14,1.61-3.2,2.44c-0.97,0.71-1.95,1.4-2.92,2.1c-0.54,0.38-1.07,0.76-1.62,1.16c-1.09,0.77-2.17,1.54-3.26,2.31
|
||||
c-1.39,0.97-2.76,1.96-4.14,2.95c-20.16,13.99-43.31,22.55-66.36,27.18c-0.56,0.11-1.11,0.23-1.69,0.34
|
||||
c-6.97,1.38-13.98,2.08-21.04,2.38c-0.91,0.04-0.91,0.04-1.83,0.08c-37.4,1.35-72.7-11.12-107.89-24.14
|
||||
c-69.35-25.65-137.32-37.43-204.66,2.34c-8.73,5.24-16.85,10.97-24.57,18.06c-1.19,0.94-1.19,0.94-2.76,0.94
|
||||
c-0.22-8.72-0.39-17.44-0.49-26.16c-0.05-4.05-0.12-8.1-0.22-12.15c-0.1-3.92-0.16-7.83-0.18-11.74c-0.02-1.49-0.05-2.97-0.1-4.46
|
||||
c-0.37-11.31-0.37-11.31,1.91-15.08c2.5-3.08,5.41-5.32,8.49-7.57c1.18-1.01,2.35-2.02,3.51-3.05
|
||||
C312.28,194.21,387.12,201.79,462.25,230.05z"/>
|
||||
<path style="fill:#0F839E;" d="M452.04,425.6c4.85,1.82,9.72,3.61,14.58,5.4c1.43,0.52,1.43,0.52,2.88,1.06
|
||||
c23.64,8.69,47.6,16.84,72.3,20.19c0.93,0.13,0.93,0.13,1.88,0.26c6.69,0.82,13.39,0.83,20.11,0.82c0.61,0,1.22,0,1.85,0
|
||||
c8.87-0.02,17.54-0.33,26.32-1.99c1.01-0.16,2.02-0.33,3.05-0.5c30.94-5.41,63.41-19.49,88.12-42.05c1.31-1.03,1.31-1.03,2.88-1.03
|
||||
c0.22,9.21,0.39,18.42,0.49,27.63c0.05,4.28,0.11,8.55,0.22,12.83c0.1,4.13,0.16,8.26,0.18,12.4c0.02,1.57,0.05,3.15,0.1,4.72
|
||||
c0.38,12.28,0.38,12.28-3.01,16.87c-2.39,2.1-4.75,3.8-7.39,5.45c-1.37,1.03-2.73,2.07-4.08,3.12c-1.31,0.95-2.63,1.9-3.96,2.83
|
||||
c-0.71,0.5-1.42,1.01-2.15,1.53c-6.11,4.29-12.31,8.14-18.81,11.58c-0.6,0.32-1.2,0.65-1.82,0.98
|
||||
c-36.64,19.49-78.25,26.49-118.1,18.09c-0.81-0.16-1.63-0.32-2.46-0.49c-23.79-4.8-46.79-13.74-69.75-22.22
|
||||
c-29.8-11-61.27-21.77-92.8-21.96c-1.03-0.01-1.03-0.01-2.08-0.02c-2.2-0.02-4.41-0.02-6.61-0.02c-1.13,0-1.13,0-2.29,0
|
||||
c-11.52,0.01-22.71,0.52-34.06,2.95c-1.12,0.23-1.12,0.23-2.26,0.46c-19.2,3.95-37.33,10.82-54.96,20.42
|
||||
c-0.88,0.47-0.88,0.47-1.77,0.95c-9.51,5.14-18.5,11.37-27.23,18.11c-1.5-1.74-0.92-4.89-0.93-7.19c-0.01-0.66-0.01-1.31-0.02-1.99
|
||||
c-0.02-2.18-0.03-4.37-0.04-6.55c0-0.75-0.01-1.49-0.01-2.26c-0.02-3.94-0.04-7.89-0.05-11.83c-0.01-4.07-0.05-8.15-0.09-12.22
|
||||
c-0.03-3.13-0.04-6.26-0.04-9.39c-0.01-1.5-0.02-3-0.04-4.5c-0.03-2.1-0.03-4.21-0.02-6.31c-0.01-1.2-0.01-2.4-0.02-3.63
|
||||
c0.6-3.91,1.8-5.17,4.39-7.67c0.56-0.54,1.12-1.09,1.69-1.65C300.38,393.44,379.63,398.41,452.04,425.6z"/>
|
||||
<path style="fill:#0F839E;" d="M230.65,512.16c0.26,0,0.52,0,0.78,0c0,3.36,0,6.72,0,10.19c0.78,0.26,1.55,0.52,2.35,0.78
|
||||
c-0.78,0.52-1.55,1.04-2.35,1.57c-0.78-0.78-0.78-0.78-0.86-3.43c0-1.09,0.01-2.19,0.03-3.28c0-0.56,0.01-1.11,0.01-1.69
|
||||
C230.62,514.92,230.63,513.54,230.65,512.16z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
10
ShellDemo/Resources/Images/openmaui_logo.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
|
||||
<defs>
|
||||
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#2196F3"/>
|
||||
<stop offset="100%" style="stop-color:#1976D2"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<circle cx="50" cy="50" r="45" fill="url(#gradient)"/>
|
||||
<text x="50" y="62" font-family="Arial, sans-serif" font-size="32" font-weight="bold" fill="white" text-anchor="middle">OM</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 479 B |
1
ShellDemo/Resources/Images/smart_button_dark.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M160-280q-33 0-56.5-23.5T80-360v-240q0-33 23.5-56.5T160-680h640q33 0 56.5 23.5T880-600v240q0 33-23.5 56.5T800-280h-40v-80h40v-240H160v240h240v80H160Zm420 80-44-96-96-44 96-44 44-96 44 96 96 44-96 44-44 96Zm100-200-25-55-55-25 55-25 25-55 25 55 55 25-55 25-25 55Z"/></svg>
|
||||
|
After Width: | Height: | Size: 383 B |
1
ShellDemo/Resources/Images/smart_button_light.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#212121" d="M160-280q-33 0-56.5-23.5T80-360v-240q0-33 23.5-56.5T160-680h640q33 0 56.5 23.5T880-600v240q0 33-23.5 56.5T800-280h-40v-80h40v-240H160v240h240v80H160Zm420 80-44-96-96-44 96-44 44-96 44 96 96 44-96 44-44 96Zm100-200-25-55-55-25 55-25 25-55 25 55 55 25-55 25-25 55Z"/></svg>
|
||||
|
After Width: | Height: | Size: 383 B |
@@ -6,10 +6,52 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<!-- Application Info -->
|
||||
<ApplicationTitle>OpenMaui Shell Demo</ApplicationTitle>
|
||||
<ApplicationId>com.openmaui.shelldemo</ApplicationId>
|
||||
<ApplicationVersion>1.0.0</ApplicationVersion>
|
||||
|
||||
<!-- MAUI Settings (conditional based on platform) -->
|
||||
<SingleProject>true</SingleProject>
|
||||
<EnableDefaultXamlItems>true</EnableDefaultXamlItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Non-Linux platforms: Use official MAUI workload -->
|
||||
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Linux'))">
|
||||
<UseMaui>true</UseMaui>
|
||||
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst;net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Linux: Use OpenMaui -->
|
||||
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))">
|
||||
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- OpenMaui Linux Platform - Local development (ProjectReference) -->
|
||||
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux')) AND Exists('../../maui-linux/OpenMaui.Controls.Linux.csproj') AND '$(UsePackageReference)' != 'true'">
|
||||
<ProjectReference Include="../../maui-linux/OpenMaui.Controls.Linux.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- OpenMaui Linux Platform - CI/CD builds (PackageReference) -->
|
||||
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux')) AND (!Exists('../../maui-linux/OpenMaui.Controls.Linux.csproj') OR '$(UsePackageReference)' == 'true')">
|
||||
<PackageReference Include="OpenMaui.Controls.Linux" Version="*" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- XAML Files -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenMaui.Controls.Linux" Version="1.0.0-preview.*" />
|
||||
<MauiXaml Update="**/*.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- App Icon -->
|
||||
<ItemGroup>
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#FFFFFF" BackgroundColor="#1B79C4" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Embedded Resources (images) -->
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\Images\*.svg" />
|
||||
<EmbeddedResource Include="Resources\Images\*.png" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
8
ShellDemo/run.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set .NET environment for desktop launcher compatibility
|
||||
export DOTNET_ROOT="$HOME/.dotnet"
|
||||
export PATH="$DOTNET_ROOT:$PATH"
|
||||
|
||||
cd "$(dirname "$0")/bin/Debug/net9.0"
|
||||
exec ./ShellDemo "$@"
|
||||
@@ -1,21 +0,0 @@
|
||||
// TodoApp - Main Application with NavigationPage
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace TodoApp;
|
||||
|
||||
public class App : Application
|
||||
{
|
||||
public static NavigationPage? NavigationPage { get; private set; }
|
||||
|
||||
public App()
|
||||
{
|
||||
NavigationPage = new NavigationPage(new TodoListPage())
|
||||
{
|
||||
Title = "OpenMaui Todo App",
|
||||
BarBackgroundColor = Color.FromArgb("#2196F3"),
|
||||
BarTextColor = Colors.White
|
||||
};
|
||||
MainPage = NavigationPage;
|
||||
}
|
||||
}
|
||||
76
TodoApp/App.xaml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="TodoApp.App">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<!-- Primary Brand Colors -->
|
||||
<Color x:Key="PrimaryColor">#5C6BC0</Color>
|
||||
<Color x:Key="PrimaryDarkColor">#3949AB</Color>
|
||||
<Color x:Key="AccentColor">#26A69A</Color>
|
||||
<Color x:Key="DangerColor">#EF5350</Color>
|
||||
<Color x:Key="CompletedColor">#9E9E9E</Color>
|
||||
|
||||
<!-- Light Theme Colors -->
|
||||
<Color x:Key="PageBackgroundLight">#F5F7FA</Color>
|
||||
<Color x:Key="CardBackgroundLight">#FFFFFF</Color>
|
||||
<Color x:Key="TextPrimaryLight">#212121</Color>
|
||||
<Color x:Key="TextSecondaryLight">#757575</Color>
|
||||
<Color x:Key="BorderLight">#E8EAF6</Color>
|
||||
<Color x:Key="DividerLight">#E0E0E0</Color>
|
||||
<Color x:Key="AlternateRowLight">#F5F5F5</Color>
|
||||
|
||||
<!-- Dark Theme Colors -->
|
||||
<Color x:Key="PageBackgroundDark">#121212</Color>
|
||||
<Color x:Key="CardBackgroundDark">#1E1E1E</Color>
|
||||
<Color x:Key="TextPrimaryDark">#FFFFFF</Color>
|
||||
<Color x:Key="TextSecondaryDark">#B0B0B0</Color>
|
||||
<Color x:Key="BorderDark">#424242</Color>
|
||||
<Color x:Key="DividerDark">#424242</Color>
|
||||
<Color x:Key="AlternateRowDark">#2A2A2A</Color>
|
||||
|
||||
<!-- Navigation Bar Colors -->
|
||||
<Color x:Key="NavBarBackgroundLight">#5C6BC0</Color>
|
||||
<Color x:Key="NavBarBackgroundDark">#3949AB</Color>
|
||||
|
||||
<!-- Themed Styles -->
|
||||
<Style x:Key="ThemedCard" TargetType="Border">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}" />
|
||||
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}" />
|
||||
<Setter Property="StrokeThickness" Value="1" />
|
||||
<Setter Property="Padding" Value="16,12" />
|
||||
<Setter Property="StrokeShape">
|
||||
<Setter.Value>
|
||||
<RoundRectangle CornerRadius="10" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ThemedEntry" TargetType="Entry">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ThemedEditor" TargetType="Editor">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="PrimaryButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource PrimaryColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="FontAttributes" Value="Bold" />
|
||||
<Setter Property="Padding" Value="20,12" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="DangerButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource DangerColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="FontAttributes" Value="Bold" />
|
||||
<Setter Property="Padding" Value="20,12" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
38
TodoApp/App.xaml.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
// TodoApp - Main Application with NavigationPage and Theme Support
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace TodoApp;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public static NavigationPage? NavigationPage { get; private set; }
|
||||
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override Window CreateWindow(IActivationState? activationState)
|
||||
{
|
||||
// Determine current theme for navigation bar colors
|
||||
var isDarkMode = Current?.RequestedTheme == AppTheme.Dark;
|
||||
var barBackground = isDarkMode ? Color.FromArgb("#3949AB") : Color.FromArgb("#5C6BC0");
|
||||
|
||||
NavigationPage = new NavigationPage(new TodoListPage())
|
||||
{
|
||||
Title = "OpenMaui Todo App",
|
||||
BarBackgroundColor = barBackground,
|
||||
BarTextColor = Colors.White
|
||||
};
|
||||
|
||||
// Update navigation bar when theme changes
|
||||
Current!.RequestedThemeChanged += (s, e) =>
|
||||
{
|
||||
var dark = e.RequestedTheme == AppTheme.Dark;
|
||||
NavigationPage.BarBackgroundColor = dark ? Color.FromArgb("#3949AB") : Color.FromArgb("#5C6BC0");
|
||||
};
|
||||
|
||||
return new Window(NavigationPage);
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,12 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="TodoApp.NewTodoPage"
|
||||
Title="New Task"
|
||||
BackgroundColor="#F5F7FA">
|
||||
|
||||
<ContentPage.Resources>
|
||||
<Color x:Key="PrimaryColor">#5C6BC0</Color>
|
||||
<Color x:Key="AccentColor">#26A69A</Color>
|
||||
<Color x:Key="TextPrimary">#212121</Color>
|
||||
<Color x:Key="TextSecondary">#757575</Color>
|
||||
<Color x:Key="CardBackground">#FFFFFF</Color>
|
||||
<Color x:Key="BorderColor">#E8EAF6</Color>
|
||||
</ContentPage.Resources>
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="Save" Clicked="OnSaveClicked" />
|
||||
<ToolbarItem Text="Save"
|
||||
IconImageSource="save_white.svg"
|
||||
Clicked="OnSaveClicked" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,*" RowSpacing="16" Padding="20">
|
||||
@@ -24,10 +17,10 @@
|
||||
<VerticalStackLayout Grid.Row="0" Spacing="4">
|
||||
<Label Text="Create a new task"
|
||||
FontSize="24"
|
||||
TextColor="{StaticResource TextPrimary}" />
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Label Text="Fill in the details below"
|
||||
FontSize="14"
|
||||
TextColor="{StaticResource TextSecondary}" />
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</VerticalStackLayout>
|
||||
|
||||
<!-- Title Section -->
|
||||
@@ -36,8 +29,8 @@
|
||||
FontSize="13"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{StaticResource PrimaryColor}" />
|
||||
<Border BackgroundColor="{StaticResource CardBackground}"
|
||||
Stroke="{StaticResource BorderColor}"
|
||||
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||
StrokeThickness="1"
|
||||
Padding="16,12">
|
||||
<Border.StrokeShape>
|
||||
@@ -46,8 +39,8 @@
|
||||
<Entry x:Name="TitleEntry"
|
||||
Placeholder="What needs to be done?"
|
||||
FontSize="18"
|
||||
TextColor="{StaticResource TextPrimary}"
|
||||
PlaceholderColor="{StaticResource TextSecondary}" />
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</Border>
|
||||
</VerticalStackLayout>
|
||||
|
||||
@@ -60,8 +53,8 @@
|
||||
|
||||
<!-- Notes Section (fills remaining space) -->
|
||||
<Border Grid.Row="3"
|
||||
BackgroundColor="{StaticResource CardBackground}"
|
||||
Stroke="{StaticResource BorderColor}"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||
StrokeThickness="1"
|
||||
Padding="16,12">
|
||||
<Border.StrokeShape>
|
||||
@@ -70,8 +63,8 @@
|
||||
<Editor x:Name="NotesEditor"
|
||||
Placeholder="Add notes (optional)..."
|
||||
FontSize="14"
|
||||
TextColor="{StaticResource TextPrimary}"
|
||||
PlaceholderColor="{StaticResource TextSecondary}"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
VerticalOptions="Fill" />
|
||||
</Border>
|
||||
|
||||
@@ -3,21 +3,15 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="TodoApp.TodoDetailPage"
|
||||
Title="Task Details"
|
||||
BackgroundColor="#F5F7FA">
|
||||
|
||||
<ContentPage.Resources>
|
||||
<Color x:Key="PrimaryColor">#5C6BC0</Color>
|
||||
<Color x:Key="AccentColor">#26A69A</Color>
|
||||
<Color x:Key="DangerColor">#EF5350</Color>
|
||||
<Color x:Key="TextPrimary">#212121</Color>
|
||||
<Color x:Key="TextSecondary">#757575</Color>
|
||||
<Color x:Key="CardBackground">#FFFFFF</Color>
|
||||
<Color x:Key="BorderColor">#E8EAF6</Color>
|
||||
</ContentPage.Resources>
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="Delete" Clicked="OnDeleteClicked" />
|
||||
<ToolbarItem Text="Save" Clicked="OnSaveClicked" />
|
||||
<ToolbarItem Text="Delete"
|
||||
IconImageSource="delete_white.svg"
|
||||
Clicked="OnDeleteClicked" />
|
||||
<ToolbarItem Text="Save"
|
||||
IconImageSource="save_white.svg"
|
||||
Clicked="OnSaveClicked" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto,Auto" RowSpacing="16" Padding="20">
|
||||
@@ -28,8 +22,8 @@
|
||||
FontSize="13"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{StaticResource PrimaryColor}" />
|
||||
<Border BackgroundColor="{StaticResource CardBackground}"
|
||||
Stroke="{StaticResource BorderColor}"
|
||||
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||
StrokeThickness="1"
|
||||
Padding="16,12">
|
||||
<Border.StrokeShape>
|
||||
@@ -38,8 +32,8 @@
|
||||
<Entry x:Name="TitleEntry"
|
||||
Placeholder="Task title"
|
||||
FontSize="18"
|
||||
TextColor="{StaticResource TextPrimary}"
|
||||
PlaceholderColor="{StaticResource TextSecondary}" />
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
</Border>
|
||||
</VerticalStackLayout>
|
||||
|
||||
@@ -52,8 +46,8 @@
|
||||
|
||||
<!-- Notes Section (fills remaining space) -->
|
||||
<Border Grid.Row="2"
|
||||
BackgroundColor="{StaticResource CardBackground}"
|
||||
Stroke="{StaticResource BorderColor}"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||
StrokeThickness="1"
|
||||
Padding="16,12">
|
||||
<Border.StrokeShape>
|
||||
@@ -62,8 +56,8 @@
|
||||
<Editor x:Name="NotesEditor"
|
||||
Placeholder="Add notes here..."
|
||||
FontSize="14"
|
||||
TextColor="{StaticResource TextPrimary}"
|
||||
PlaceholderColor="{StaticResource TextSecondary}"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
VerticalOptions="Fill" />
|
||||
</Border>
|
||||
|
||||
@@ -73,8 +67,8 @@
|
||||
FontSize="13"
|
||||
FontAttributes="Bold"
|
||||
TextColor="{StaticResource PrimaryColor}" />
|
||||
<Border BackgroundColor="{StaticResource CardBackground}"
|
||||
Stroke="{StaticResource BorderColor}"
|
||||
<Border BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||
StrokeThickness="1"
|
||||
Padding="16,12">
|
||||
<Border.StrokeShape>
|
||||
@@ -88,7 +82,8 @@
|
||||
<Label x:Name="StatusLabel"
|
||||
Text="In Progress"
|
||||
FontSize="16"
|
||||
TextColor="{StaticResource TextPrimary}"
|
||||
LineBreakMode="NoWrap"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
VerticalOptions="Center" />
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
@@ -98,7 +93,8 @@
|
||||
<Label Grid.Row="4"
|
||||
x:Name="CreatedLabel"
|
||||
FontSize="13"
|
||||
TextColor="{StaticResource TextSecondary}"
|
||||
LineBreakMode="NoWrap"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalOptions="Center"
|
||||
HorizontalTextAlignment="Center" />
|
||||
|
||||
@@ -4,20 +4,10 @@
|
||||
xmlns:local="clr-namespace:TodoApp"
|
||||
x:Class="TodoApp.TodoListPage"
|
||||
Title="My Tasks"
|
||||
BackgroundColor="#F5F7FA">
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<!-- Colors -->
|
||||
<Color x:Key="PrimaryColor">#5C6BC0</Color>
|
||||
<Color x:Key="PrimaryDark">#3949AB</Color>
|
||||
<Color x:Key="AccentColor">#26A69A</Color>
|
||||
<Color x:Key="TextPrimary">#212121</Color>
|
||||
<Color x:Key="TextSecondary">#757575</Color>
|
||||
<Color x:Key="CardBackground">#FFFFFF</Color>
|
||||
<Color x:Key="DividerColor">#E0E0E0</Color>
|
||||
<Color x:Key="CompletedColor">#9E9E9E</Color>
|
||||
|
||||
<!-- Converters -->
|
||||
<local:AlternatingRowColorConverter x:Key="AlternatingRowColorConverter" />
|
||||
<local:CompletedToColorConverter x:Key="CompletedToColorConverter" />
|
||||
@@ -27,7 +17,9 @@
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="+ Add" Clicked="OnAddClicked" />
|
||||
<ToolbarItem Text="Add"
|
||||
IconImageSource="add_white.svg"
|
||||
Clicked="OnAddClicked" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<Grid RowDefinitions="*,Auto" Padding="0">
|
||||
@@ -47,11 +39,11 @@
|
||||
Padding="40">
|
||||
<Label Text="No tasks yet"
|
||||
FontSize="22"
|
||||
TextColor="{StaticResource TextSecondary}"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalOptions="Center" />
|
||||
<Label Text="Tap '+ Add' to create your first task"
|
||||
FontSize="14"
|
||||
TextColor="{StaticResource TextSecondary}"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
HorizontalOptions="Center"
|
||||
Margin="0,8,0,0" />
|
||||
</VerticalStackLayout>
|
||||
@@ -62,7 +54,7 @@
|
||||
<!-- Card-style item -->
|
||||
<Grid Padding="0,6" BackgroundColor="Transparent">
|
||||
<Border StrokeThickness="0"
|
||||
BackgroundColor="{StaticResource CardBackground}"
|
||||
BackgroundColor="{AppThemeBinding Light=#FFFFFF, Dark=#1E1E1E}"
|
||||
Padding="16,14"
|
||||
Opacity="{Binding IsCompleted, Converter={StaticResource CompletedToOpacityConverter}}">
|
||||
<Border.StrokeShape>
|
||||
@@ -75,7 +67,7 @@
|
||||
WidthRequest="8"
|
||||
HeightRequest="44"
|
||||
Margin="0"
|
||||
BackgroundColor="{Binding IsCompleted, Converter={StaticResource CompletedToColorConverter}, ConverterParameter=indicator}"
|
||||
BackgroundColor="#26A69A"
|
||||
VerticalOptions="Center">
|
||||
<Border.StrokeShape>
|
||||
<RoundRectangle CornerRadius="4" />
|
||||
@@ -106,22 +98,30 @@
|
||||
</CollectionView.ItemTemplate>
|
||||
</CollectionView>
|
||||
|
||||
<!-- Footer Stats -->
|
||||
<!-- Footer Stats with Theme Toggle -->
|
||||
<Border Grid.Row="1"
|
||||
BackgroundColor="{StaticResource PrimaryColor}"
|
||||
StrokeThickness="0"
|
||||
Padding="24,14"
|
||||
Padding="20,10"
|
||||
Margin="0">
|
||||
<Grid ColumnDefinitions="Auto,Auto" ColumnSpacing="6" HorizontalOptions="Center" VerticalOptions="Center">
|
||||
<Grid ColumnDefinitions="*,Auto" VerticalOptions="Center">
|
||||
<!-- Stats on the left -->
|
||||
<Label Grid.Column="0"
|
||||
Text="Tasks:"
|
||||
FontSize="15"
|
||||
TextColor="White" />
|
||||
<Label Grid.Column="1"
|
||||
x:Name="StatsLabel"
|
||||
FontSize="15"
|
||||
TextColor="White"
|
||||
Opacity="0.9" />
|
||||
VerticalOptions="Center"
|
||||
LineBreakMode="NoWrap" />
|
||||
|
||||
<!-- Theme Toggle on the right -->
|
||||
<ImageButton Grid.Column="1"
|
||||
x:Name="ThemeToggleButton"
|
||||
Source="light_mode_white.svg"
|
||||
WidthRequest="32"
|
||||
HeightRequest="32"
|
||||
BackgroundColor="Transparent"
|
||||
Clicked="OnThemeToggleClicked"
|
||||
VerticalOptions="Center" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
228
TodoApp/Pages/TodoListPage.xaml.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
// TodoListPage - Main page for viewing todos with XAML support
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace TodoApp;
|
||||
|
||||
public partial class TodoListPage : ContentPage
|
||||
{
|
||||
private readonly TodoService _service = TodoService.Instance;
|
||||
private bool _isNavigating; // Guard against double navigation
|
||||
|
||||
public TodoListPage()
|
||||
{
|
||||
Console.WriteLine("[TodoListPage] Constructor starting");
|
||||
InitializeComponent();
|
||||
|
||||
TodoCollectionView.ItemsSource = _service.Todos;
|
||||
UpdateStats();
|
||||
|
||||
// Subscribe to theme changes to verify event is firing
|
||||
if (Application.Current != null)
|
||||
{
|
||||
Application.Current.RequestedThemeChanged += (s, e) =>
|
||||
{
|
||||
Console.WriteLine($"[TodoListPage] RequestedThemeChanged event received! NewTheme={e.RequestedTheme}");
|
||||
};
|
||||
}
|
||||
|
||||
Console.WriteLine("[TodoListPage] Constructor finished");
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
base.OnAppearing();
|
||||
|
||||
// Reset navigation guard when page reappears
|
||||
_isNavigating = false;
|
||||
|
||||
// Refresh indexes for alternating row colors
|
||||
_service.RefreshIndexes();
|
||||
|
||||
// Refresh the collection view
|
||||
TodoCollectionView.ItemsSource = null;
|
||||
TodoCollectionView.ItemsSource = _service.Todos;
|
||||
UpdateStats();
|
||||
UpdateThemeIcon();
|
||||
}
|
||||
|
||||
private async void OnAddClicked(object sender, EventArgs e)
|
||||
{
|
||||
await Navigation.PushAsync(new NewTodoPage());
|
||||
}
|
||||
|
||||
private async void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
// Guard against double navigation
|
||||
if (_isNavigating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.CurrentSelection.FirstOrDefault() is TodoItem todo)
|
||||
{
|
||||
_isNavigating = true;
|
||||
TodoCollectionView.SelectedItem = null; // Deselect immediately
|
||||
await Navigation.PushAsync(new TodoDetailPage(todo));
|
||||
// Note: _isNavigating is reset in OnAppearing when we return
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStats()
|
||||
{
|
||||
var completed = _service.CompletedCount;
|
||||
var total = _service.TotalCount;
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
StatsLabel.Text = "No tasks";
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsLabel.Text = $"Tasks: {completed} of {total} completed";
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateThemeIcon()
|
||||
{
|
||||
// Check UserAppTheme first, fall back to RequestedTheme
|
||||
var userTheme = Application.Current?.UserAppTheme ?? AppTheme.Unspecified;
|
||||
var effectiveTheme = userTheme != AppTheme.Unspecified ? userTheme : Application.Current?.RequestedTheme ?? AppTheme.Light;
|
||||
var isDarkMode = effectiveTheme == AppTheme.Dark;
|
||||
|
||||
// Show sun icon in dark mode (to switch to light), moon icon in light mode (to switch to dark)
|
||||
ThemeToggleButton.Source = isDarkMode ? "light_mode_white.svg" : "dark_mode_white.svg";
|
||||
Console.WriteLine($"[TodoListPage] UpdateThemeIcon: UserAppTheme={userTheme}, RequestedTheme={Application.Current?.RequestedTheme}, isDarkMode={isDarkMode}");
|
||||
}
|
||||
|
||||
private void OnThemeToggleClicked(object? sender, EventArgs e)
|
||||
{
|
||||
if (Application.Current == null) return;
|
||||
|
||||
// Check current effective theme
|
||||
var userTheme = Application.Current.UserAppTheme;
|
||||
var effectiveTheme = userTheme != AppTheme.Unspecified ? userTheme : Application.Current.RequestedTheme;
|
||||
var isDarkMode = effectiveTheme == AppTheme.Dark;
|
||||
|
||||
// Toggle to the opposite theme
|
||||
var newTheme = isDarkMode ? AppTheme.Light : AppTheme.Dark;
|
||||
Application.Current.UserAppTheme = newTheme;
|
||||
|
||||
Console.WriteLine($"[TodoListPage] Theme toggled from {effectiveTheme} to {newTheme}");
|
||||
UpdateThemeIcon();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for alternating row background colors with Light/Dark mode support.
|
||||
/// </summary>
|
||||
public class AlternatingRowColorConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isDarkMode = Application.Current?.RequestedTheme == AppTheme.Dark;
|
||||
|
||||
if (value is int index)
|
||||
{
|
||||
if (isDarkMode)
|
||||
{
|
||||
return index % 2 == 0 ? Color.FromArgb("#1E1E1E") : Color.FromArgb("#2A2A2A");
|
||||
}
|
||||
return index % 2 == 0 ? Colors.White : Color.FromArgb("#F5F5F5");
|
||||
}
|
||||
return isDarkMode ? Color.FromArgb("#1E1E1E") : Colors.White;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for completed task text color and indicator color with Light/Dark mode support.
|
||||
/// </summary>
|
||||
public class CompletedToColorConverter : IValueConverter
|
||||
{
|
||||
// Light theme colors
|
||||
private static readonly Color AccentColorLight = Color.FromArgb("#26A69A");
|
||||
private static readonly Color AccentColorDark = Color.FromArgb("#4DB6AC");
|
||||
private static readonly Color CompletedColor = Color.FromArgb("#9E9E9E");
|
||||
private static readonly Color TextPrimaryLight = Color.FromArgb("#212121");
|
||||
private static readonly Color TextSecondaryLight = Color.FromArgb("#757575");
|
||||
|
||||
// Dark theme colors
|
||||
private static readonly Color TextPrimaryDark = Color.FromArgb("#FFFFFF");
|
||||
private static readonly Color TextSecondaryDark = Color.FromArgb("#B0B0B0");
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
string param = parameter as string ?? "";
|
||||
bool isDarkMode = Application.Current?.RequestedTheme == AppTheme.Dark;
|
||||
|
||||
// Indicator bar color - theme-aware accent color
|
||||
if (param == "indicator")
|
||||
{
|
||||
var color = isCompleted ? CompletedColor : (isDarkMode ? AccentColorDark : AccentColorLight);
|
||||
Console.WriteLine($"[CompletedToColorConverter] indicator: isCompleted={isCompleted}, isDarkMode={isDarkMode}, color={color}");
|
||||
return color;
|
||||
}
|
||||
|
||||
// Text colors with theme support
|
||||
if (isCompleted)
|
||||
{
|
||||
return CompletedColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (param == "notes")
|
||||
{
|
||||
return isDarkMode ? TextSecondaryDark : TextSecondaryLight;
|
||||
}
|
||||
return isDarkMode ? TextPrimaryDark : TextPrimaryLight;
|
||||
}
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for completed task text decorations (strikethrough).
|
||||
/// </summary>
|
||||
public class CompletedToTextDecorationsConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
return isCompleted ? TextDecorations.Strikethrough : TextDecorations.None;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for completed task opacity (slightly faded when complete).
|
||||
/// </summary>
|
||||
public class CompletedToOpacityConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
return isCompleted ? 0.7 : 1.0;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
4
TodoApp/Resources/AppIcon/appicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="456" height="456" fill="#0F839E"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 184 B |
7
TodoApp/Resources/AppIcon/appicon_combined.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256" height="256" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="256" height="256" rx="48" fill="#0F839E"/>
|
||||
<g transform="translate(48, 48) scale(6.67)">
|
||||
<path fill="white" d="M22 5.18L10.59 16.6l-4.24-4.24-1.41 1.41 5.66 5.66 12.83-12.83L22 5.18zM12 20c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8m0-18C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 472 B |
7
TodoApp/Resources/AppIcon/appiconfg.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- Task/Check circle icon (Material Icons) -->
|
||||
<g transform="translate(88, 88) scale(11.67)">
|
||||
<path fill="#000000" d="M22 5.18L10.59 16.6l-4.24-4.24-1.41 1.41 5.66 5.66 12.83-12.83L22 5.18zM12 20c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8m0-18C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 468 B |
1
TodoApp/Resources/Images/add_white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>
|
||||
|
After Width: | Height: | Size: 178 B |
1
TodoApp/Resources/Images/dark_mode_white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M480-120q-150 0-255-105T120-480q0-150 105-255t255-105q14 0 27.5 1t26.5 3q-41 29-65.5 75.5T444-660q0 90 63 153t153 63q55 0 101-24.5t75-65.5q2 13 3 26.5t1 27.5q0 150-105 255T480-120Zm0-80q88 0 158-48.5T740-375q-20 5-40 8t-40 3q-123 0-209.5-86.5T364-660q0-20 3-40t8-40q-78 32-126.5 102T200-480q0 116 82 198t198 82Zm-10-270Z"/></svg>
|
||||
|
After Width: | Height: | Size: 442 B |
1
TodoApp/Resources/Images/delete_white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M280-120q-33 0-56.5-23.5T200-200v-520h-40v-80h200v-40h240v40h200v80h-40v520q0 33-23.5 56.5T680-120H280Zm400-600H280v520h400v-520ZM360-280h80v-360h-80v360Zm160 0h80v-360h-80v360ZM280-720v520-520Z"/></svg>
|
||||
|
After Width: | Height: | Size: 315 B |
1
TodoApp/Resources/Images/light_mode_white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M480-360q50 0 85-35t35-85q0-50-35-85t-85-35q-50 0-85 35t-35 85q0 50 35 85t85 35Zm0 80q-83 0-141.5-58.5T280-480q0-83 58.5-141.5T480-680q83 0 141.5 58.5T680-480q0 83-58.5 141.5T480-280ZM200-440H40v-80h160v80Zm720 0H760v-80h160v80ZM440-760v-160h80v160h-80Zm0 720v-160h80v160h-80ZM256-650l-101-97 57-59 96 100-52 56Zm492 496-97-101 53-55 101 97-57 59Zm-98-550 97-101 59 57-100 96-56-52ZM154-212l101-97 55 53-97 101-59-57Zm326-268Z"/></svg>
|
||||
|
After Width: | Height: | Size: 548 B |
1
TodoApp/Resources/Images/save_white.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path fill="#FFFFFF" d="M382-240 154-468l57-57 171 171 367-367 57 57-424 424Z"/></svg>
|
||||
|
After Width: | Height: | Size: 174 B |
@@ -6,10 +6,51 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
||||
<!-- Application Info -->
|
||||
<ApplicationTitle>OpenMaui Todo</ApplicationTitle>
|
||||
<ApplicationId>com.openmaui.todoapp</ApplicationId>
|
||||
<ApplicationVersion>1.0.0</ApplicationVersion>
|
||||
|
||||
<!-- MAUI Settings (conditional based on platform) -->
|
||||
<SingleProject>true</SingleProject>
|
||||
<EnableDefaultXamlItems>true</EnableDefaultXamlItems>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Non-Linux platforms: Use official MAUI workload -->
|
||||
<PropertyGroup Condition="!$([MSBuild]::IsOSPlatform('Linux'))">
|
||||
<UseMaui>true</UseMaui>
|
||||
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst;net9.0-windows10.0.19041.0</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Linux: Use OpenMaui -->
|
||||
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))">
|
||||
<RuntimeIdentifiers>linux-x64;linux-arm64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- OpenMaui Linux Platform - Local development (ProjectReference) -->
|
||||
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux')) AND Exists('../../maui-linux/OpenMaui.Controls.Linux.csproj') AND '$(UsePackageReference)' != 'true'">
|
||||
<ProjectReference Include="../../maui-linux/OpenMaui.Controls.Linux.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- OpenMaui Linux Platform - CI/CD builds (PackageReference) -->
|
||||
<ItemGroup Condition="$([MSBuild]::IsOSPlatform('Linux')) AND (!Exists('../../maui-linux/OpenMaui.Controls.Linux.csproj') OR '$(UsePackageReference)' == 'true')">
|
||||
<PackageReference Include="OpenMaui.Controls.Linux" Version="*" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- XAML Files -->
|
||||
<ItemGroup>
|
||||
<PackageReference Include="OpenMaui.Controls.Linux" Version="1.0.0-preview.*" />
|
||||
<MauiXaml Update="**/*.xaml" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- App Icon -->
|
||||
<ItemGroup>
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#FFFFFF" BackgroundColor="#0F839E" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Images -->
|
||||
<ItemGroup>
|
||||
<MauiImage Include="Resources\Images\*.svg" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -78,4 +78,15 @@ public class TodoItem : INotifyPropertyChanged
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces all bindings to re-evaluate by notifying all properties changed.
|
||||
/// Used when external factors (like app theme) change.
|
||||
/// </summary>
|
||||
public void RefreshBindings()
|
||||
{
|
||||
OnPropertyChanged(nameof(IsCompleted));
|
||||
OnPropertyChanged(nameof(Title));
|
||||
OnPropertyChanged(nameof(Notes));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
// TodoListPage - Main page for viewing todos with XAML support
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
using Microsoft.Maui.Graphics;
|
||||
using System.Globalization;
|
||||
|
||||
namespace TodoApp;
|
||||
|
||||
public partial class TodoListPage : ContentPage
|
||||
{
|
||||
private readonly TodoService _service = TodoService.Instance;
|
||||
|
||||
public TodoListPage()
|
||||
{
|
||||
Console.WriteLine("[TodoListPage] Constructor starting");
|
||||
InitializeComponent();
|
||||
|
||||
TodoCollectionView.ItemsSource = _service.Todos;
|
||||
UpdateStats();
|
||||
|
||||
Console.WriteLine("[TodoListPage] Constructor finished");
|
||||
}
|
||||
|
||||
protected override void OnAppearing()
|
||||
{
|
||||
Console.WriteLine("[TodoListPage] OnAppearing called - refreshing CollectionView");
|
||||
base.OnAppearing();
|
||||
|
||||
// Refresh indexes for alternating row colors
|
||||
_service.RefreshIndexes();
|
||||
|
||||
// Refresh the collection view
|
||||
TodoCollectionView.ItemsSource = null;
|
||||
TodoCollectionView.ItemsSource = _service.Todos;
|
||||
Console.WriteLine($"[TodoListPage] ItemsSource set with {_service.Todos.Count} items");
|
||||
UpdateStats();
|
||||
}
|
||||
|
||||
private async void OnAddClicked(object sender, EventArgs e)
|
||||
{
|
||||
await Navigation.PushAsync(new NewTodoPage());
|
||||
}
|
||||
|
||||
private async void OnSelectionChanged(object? sender, SelectionChangedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"[TodoListPage] OnSelectionChanged: {e.CurrentSelection.Count} items selected");
|
||||
if (e.CurrentSelection.FirstOrDefault() is TodoItem todo)
|
||||
{
|
||||
Console.WriteLine($"[TodoListPage] Navigating to TodoDetailPage for: {todo.Title}");
|
||||
TodoCollectionView.SelectedItem = null; // Deselect
|
||||
var detailPage = new TodoDetailPage(todo);
|
||||
Console.WriteLine($"[TodoListPage] Created TodoDetailPage, pushing...");
|
||||
await Navigation.PushAsync(detailPage);
|
||||
Console.WriteLine($"[TodoListPage] Navigation complete");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[TodoListPage] EXCEPTION in OnSelectionChanged: {ex.GetType().Name}: {ex.Message}");
|
||||
Console.WriteLine($"[TodoListPage] Stack trace: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStats()
|
||||
{
|
||||
var completed = _service.CompletedCount;
|
||||
var total = _service.TotalCount;
|
||||
|
||||
if (total == 0)
|
||||
{
|
||||
StatsLabel.Text = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsLabel.Text = $"{completed} of {total} completed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for alternating row background colors.
|
||||
/// </summary>
|
||||
public class AlternatingRowColorConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is int index)
|
||||
{
|
||||
return index % 2 == 0 ? Colors.White : Color.FromArgb("#F5F5F5");
|
||||
}
|
||||
return Colors.White;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for completed task text color and indicator color.
|
||||
/// </summary>
|
||||
public class CompletedToColorConverter : IValueConverter
|
||||
{
|
||||
// Define colors
|
||||
private static readonly Color PrimaryColor = Color.FromArgb("#5C6BC0");
|
||||
private static readonly Color AccentColor = Color.FromArgb("#26A69A");
|
||||
private static readonly Color CompletedColor = Color.FromArgb("#9E9E9E");
|
||||
private static readonly Color TextPrimary = Color.FromArgb("#212121");
|
||||
private static readonly Color TextSecondary = Color.FromArgb("#757575");
|
||||
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
string param = parameter as string ?? "";
|
||||
|
||||
// Indicator bar color
|
||||
if (param == "indicator")
|
||||
{
|
||||
return isCompleted ? CompletedColor : AccentColor;
|
||||
}
|
||||
|
||||
// Text colors
|
||||
if (isCompleted)
|
||||
{
|
||||
return CompletedColor;
|
||||
}
|
||||
else
|
||||
{
|
||||
return param == "notes" ? TextSecondary : TextPrimary;
|
||||
}
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for completed task text decorations (strikethrough).
|
||||
/// </summary>
|
||||
public class CompletedToTextDecorationsConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
return isCompleted ? TextDecorations.Strikethrough : TextDecorations.None;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converter for completed task opacity (slightly faded when complete).
|
||||
/// </summary>
|
||||
public class CompletedToOpacityConverter : IValueConverter
|
||||
{
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
bool isCompleted = value is bool b && b;
|
||||
return isCompleted ? 0.7 : 1.0;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
8
TodoApp/run.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Set .NET environment for desktop launcher compatibility
|
||||
export DOTNET_ROOT="$HOME/.dotnet"
|
||||
export PATH="$DOTNET_ROOT:$PATH"
|
||||
|
||||
cd "$(dirname "$0")/bin/Debug/net9.0"
|
||||
exec ./TodoApp "$@"
|
||||
71
WebViewDemo/App.xaml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
x:Class="WebViewDemo.App">
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<!-- Primary Brand Colors -->
|
||||
<Color x:Key="PrimaryColor">#5C6BC0</Color>
|
||||
<Color x:Key="PrimaryDarkColor">#3949AB</Color>
|
||||
<Color x:Key="AccentColor">#26A69A</Color>
|
||||
<Color x:Key="ButtonColor">#667EEA</Color>
|
||||
|
||||
<!-- Light Theme Colors -->
|
||||
<Color x:Key="PageBackgroundLight">#F5F7FA</Color>
|
||||
<Color x:Key="CardBackgroundLight">#FFFFFF</Color>
|
||||
<Color x:Key="TextPrimaryLight">#212121</Color>
|
||||
<Color x:Key="TextSecondaryLight">#757575</Color>
|
||||
<Color x:Key="BorderLight">#E0E0E0</Color>
|
||||
|
||||
<!-- Dark Theme Colors -->
|
||||
<Color x:Key="PageBackgroundDark">#121212</Color>
|
||||
<Color x:Key="CardBackgroundDark">#1E1E1E</Color>
|
||||
<Color x:Key="TextPrimaryDark">#FFFFFF</Color>
|
||||
<Color x:Key="TextSecondaryDark">#B0B0B0</Color>
|
||||
<Color x:Key="BorderDark">#424242</Color>
|
||||
|
||||
<!-- Navigation Bar Colors -->
|
||||
<Color x:Key="NavBarBackgroundLight">#5C6BC0</Color>
|
||||
<Color x:Key="NavBarBackgroundDark">#3949AB</Color>
|
||||
|
||||
<!-- Themed Styles -->
|
||||
<Style x:Key="ThemedCard" TargetType="Border">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}" />
|
||||
<Setter Property="StrokeThickness" Value="0" />
|
||||
<Setter Property="Padding" Value="12" />
|
||||
<Setter Property="StrokeShape">
|
||||
<Setter.Value>
|
||||
<RoundRectangle CornerRadius="8" />
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="ThemedEntry" TargetType="Entry">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="NavButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource ButtonColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="HeightRequest" Value="36" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="PrimaryButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource PrimaryColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="HeightRequest" Value="36" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
</Style>
|
||||
|
||||
<Style x:Key="AccentButton" TargetType="Button">
|
||||
<Setter Property="BackgroundColor" Value="{StaticResource AccentColor}" />
|
||||
<Setter Property="TextColor" Value="White" />
|
||||
<Setter Property="HeightRequest" Value="36" />
|
||||
<Setter Property="FontSize" Value="13" />
|
||||
</Style>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
30
WebViewDemo/App.xaml.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// App.xaml.cs - Main Application with Theme Support
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace WebViewDemo;
|
||||
|
||||
public partial class App : Application
|
||||
{
|
||||
public App()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
protected override Window CreateWindow(IActivationState? activationState)
|
||||
{
|
||||
return new Window(new WebViewPage())
|
||||
{
|
||||
Title = "OpenMaui Browser"
|
||||
};
|
||||
}
|
||||
|
||||
public static void ToggleTheme()
|
||||
{
|
||||
if (Current is null) return;
|
||||
|
||||
Current.UserAppTheme = Current.UserAppTheme == AppTheme.Dark
|
||||
? AppTheme.Light
|
||||
: AppTheme.Dark;
|
||||
}
|
||||
}
|
||||
22
WebViewDemo/MauiProgram.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
// MauiProgram.cs - MAUI app configuration
|
||||
|
||||
using Microsoft.Maui.Hosting;
|
||||
using Microsoft.Maui.Platform.Linux.Hosting;
|
||||
|
||||
namespace WebViewDemo;
|
||||
|
||||
public static class MauiProgram
|
||||
{
|
||||
public static MauiApp CreateMauiApp()
|
||||
{
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
|
||||
// Configure the app
|
||||
builder.UseMauiApp<App>();
|
||||
|
||||
// Add Linux platform support with all handlers
|
||||
builder.UseLinux();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
153
WebViewDemo/Pages/WebViewPage.xaml
Normal file
@@ -0,0 +1,153 @@
|
||||
<?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="WebViewDemo.WebViewPage"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource PageBackgroundLight}, Dark={StaticResource PageBackgroundDark}}">
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto,*,Auto">
|
||||
|
||||
<!-- Browser Toolbar -->
|
||||
<Grid Grid.Row="0"
|
||||
ColumnDefinitions="Auto,Auto,Auto,*,Auto,Auto,Auto,Auto"
|
||||
ColumnSpacing="4"
|
||||
Padding="8"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}">
|
||||
|
||||
<!-- Back Button -->
|
||||
<ImageButton Grid.Column="0"
|
||||
x:Name="BackButton"
|
||||
Source="{AppThemeBinding Light=arrow_back_light.svg, Dark=arrow_back_dark.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnBackClicked"
|
||||
ToolTipProperties.Text="Go Back" />
|
||||
|
||||
<!-- Forward Button -->
|
||||
<ImageButton Grid.Column="1"
|
||||
x:Name="ForwardButton"
|
||||
Source="{AppThemeBinding Light=arrow_forward_light.svg, Dark=arrow_forward_dark.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnForwardClicked"
|
||||
ToolTipProperties.Text="Go Forward" />
|
||||
|
||||
<!-- Reload Button -->
|
||||
<ImageButton Grid.Column="2"
|
||||
x:Name="ReloadButton"
|
||||
Source="{AppThemeBinding Light=refresh_light.svg, Dark=refresh_dark.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnReloadClicked"
|
||||
ToolTipProperties.Text="Reload Page" />
|
||||
|
||||
<!-- URL Bar -->
|
||||
<Border Grid.Column="3"
|
||||
BackgroundColor="{AppThemeBinding Light={StaticResource CardBackgroundLight}, Dark={StaticResource CardBackgroundDark}}"
|
||||
StrokeThickness="1"
|
||||
Stroke="{AppThemeBinding Light={StaticResource BorderLight}, Dark={StaticResource BorderDark}}"
|
||||
Padding="8,4"
|
||||
Margin="4,0"
|
||||
VerticalOptions="Center">
|
||||
<Border.StrokeShape>
|
||||
<RoundRectangle CornerRadius="18" />
|
||||
</Border.StrokeShape>
|
||||
|
||||
<Entry x:Name="UrlEntry"
|
||||
Placeholder="Enter URL or search..."
|
||||
Text="https://dotnet.microsoft.com"
|
||||
FontSize="14"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextPrimaryLight}, Dark={StaticResource TextPrimaryDark}}"
|
||||
PlaceholderColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Completed="OnUrlSubmitted" />
|
||||
</Border>
|
||||
|
||||
<!-- Go Button -->
|
||||
<ImageButton Grid.Column="4"
|
||||
x:Name="GoButton"
|
||||
Source="{AppThemeBinding Light=send_light.svg, Dark=send_dark.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="{StaticResource AccentColor}"
|
||||
CornerRadius="18"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnGoClicked"
|
||||
ToolTipProperties.Text="Navigate" />
|
||||
|
||||
<!-- Load HTML Demo Button -->
|
||||
<ImageButton Grid.Column="5"
|
||||
x:Name="LoadHtmlButton"
|
||||
Source="{AppThemeBinding Light=code_light.svg, Dark=code_dark.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnLoadHtmlClicked"
|
||||
ToolTipProperties.Text="Load Demo HTML" />
|
||||
|
||||
<!-- Eval JS Demo Button -->
|
||||
<ImageButton Grid.Column="6"
|
||||
x:Name="EvalJsButton"
|
||||
Source="{AppThemeBinding Light=play_arrow_light.svg, Dark=play_arrow_dark.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnEvalJsClicked"
|
||||
ToolTipProperties.Text="Run JavaScript" />
|
||||
|
||||
<!-- Theme Toggle Button -->
|
||||
<ImageButton Grid.Column="7"
|
||||
x:Name="ThemeToggleButton"
|
||||
Source="{AppThemeBinding Light=dark_mode.svg, Dark=light_mode.svg}"
|
||||
WidthRequest="36"
|
||||
HeightRequest="36"
|
||||
Padding="8"
|
||||
BackgroundColor="Transparent"
|
||||
VerticalOptions="Center"
|
||||
Clicked="OnThemeToggleClicked"
|
||||
ToolTipProperties.Text="Toggle Theme" />
|
||||
</Grid>
|
||||
|
||||
<!-- Loading Progress Bar (own row above WebView) -->
|
||||
<ProgressBar Grid.Row="1"
|
||||
x:Name="LoadingProgress"
|
||||
HeightRequest="3"
|
||||
ProgressColor="{StaticResource AccentColor}"
|
||||
Progress="0"
|
||||
IsVisible="False" />
|
||||
|
||||
<!-- WebView - Main Content Area -->
|
||||
<WebView Grid.Row="2"
|
||||
x:Name="MainWebView"
|
||||
VerticalOptions="Fill"
|
||||
HorizontalOptions="Fill"
|
||||
Navigating="OnNavigating"
|
||||
Navigated="OnNavigated" />
|
||||
|
||||
<!-- Status Bar -->
|
||||
<Grid Grid.Row="3"
|
||||
Padding="8,4"
|
||||
BackgroundColor="{AppThemeBinding Light=#E8E8E8, Dark=#1A1A1A}">
|
||||
<Label x:Name="StatusLabel"
|
||||
Text="Ready"
|
||||
FontSize="12"
|
||||
TextColor="{AppThemeBinding Light={StaticResource TextSecondaryLight}, Dark={StaticResource TextSecondaryDark}}"
|
||||
VerticalOptions="Center" />
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</ContentPage>
|
||||
271
WebViewDemo/Pages/WebViewPage.xaml.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
// WebViewPage - Main page demonstrating WebView with WebKitGTK
|
||||
|
||||
using Microsoft.Maui.Controls;
|
||||
|
||||
namespace WebViewDemo;
|
||||
|
||||
public partial class WebViewPage : ContentPage
|
||||
{
|
||||
private CancellationTokenSource? _progressCts;
|
||||
|
||||
public WebViewPage()
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Constructor starting");
|
||||
InitializeComponent();
|
||||
|
||||
// Set initial URL
|
||||
MainWebView.Source = new UrlWebViewSource { Url = "https://dotnet.microsoft.com" };
|
||||
|
||||
// Configure URL entry to select all on double-click (like a browser address bar)
|
||||
EntryExtensions.SetSelectAllOnDoubleClick(UrlEntry, true);
|
||||
|
||||
Console.WriteLine("[WebViewPage] Constructor finished");
|
||||
}
|
||||
|
||||
private void OnBackClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Back button clicked");
|
||||
if (MainWebView.CanGoBack)
|
||||
{
|
||||
MainWebView.GoBack();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnForwardClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Forward button clicked");
|
||||
if (MainWebView.CanGoForward)
|
||||
{
|
||||
MainWebView.GoForward();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnReloadClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Reload button clicked");
|
||||
MainWebView.Reload();
|
||||
}
|
||||
|
||||
private void OnGoClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Navigate();
|
||||
}
|
||||
|
||||
private void OnUrlSubmitted(object? sender, EventArgs e)
|
||||
{
|
||||
Navigate();
|
||||
}
|
||||
|
||||
private void Navigate()
|
||||
{
|
||||
var url = UrlEntry.Text?.Trim();
|
||||
if (string.IsNullOrEmpty(url))
|
||||
return;
|
||||
|
||||
// Add https:// if not present
|
||||
if (!url.StartsWith("http://") && !url.StartsWith("https://"))
|
||||
url = "https://" + url;
|
||||
|
||||
Console.WriteLine($"[WebViewPage] Navigating to: {url}");
|
||||
MainWebView.Source = new UrlWebViewSource { Url = url };
|
||||
UrlEntry.Text = url;
|
||||
}
|
||||
|
||||
private void OnNavigating(object? sender, WebNavigatingEventArgs e)
|
||||
{
|
||||
StatusLabel.Text = $"Loading: {e.Url}";
|
||||
Console.WriteLine($"[WebViewPage] Navigating to: {e.Url}");
|
||||
|
||||
// Start progress animation
|
||||
StartProgressAnimation();
|
||||
}
|
||||
|
||||
private void OnNavigated(object? sender, WebNavigatedEventArgs e)
|
||||
{
|
||||
// Stop progress animation and complete
|
||||
StopProgressAnimation(e.Result == WebNavigationResult.Success);
|
||||
|
||||
StatusLabel.Text = e.Result == WebNavigationResult.Success
|
||||
? $"Loaded: {e.Url}"
|
||||
: $"Failed: {e.Result}";
|
||||
|
||||
UrlEntry.Text = e.Url;
|
||||
Console.WriteLine($"[WebViewPage] Navigated: {e.Result} - {e.Url}");
|
||||
}
|
||||
|
||||
private void StartProgressAnimation()
|
||||
{
|
||||
// Cancel any existing animation
|
||||
_progressCts?.Cancel();
|
||||
_progressCts = new CancellationTokenSource();
|
||||
|
||||
// Show and reset progress bar
|
||||
LoadingProgress.Progress = 0;
|
||||
LoadingProgress.IsVisible = true;
|
||||
|
||||
// Animate progress (simulated since WebView doesn't report actual progress)
|
||||
var token = _progressCts.Token;
|
||||
Dispatcher.Dispatch(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// Quick initial progress
|
||||
await LoadingProgress.ProgressTo(0.3, 200, Easing.CubicOut);
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
// Slower middle progress
|
||||
await LoadingProgress.ProgressTo(0.6, 500, Easing.Linear);
|
||||
if (token.IsCancellationRequested) return;
|
||||
|
||||
// Even slower as we wait
|
||||
await LoadingProgress.ProgressTo(0.85, 1000, Easing.CubicIn);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
// Expected when navigation completes
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void StopProgressAnimation(bool success)
|
||||
{
|
||||
_progressCts?.Cancel();
|
||||
|
||||
Dispatcher.Dispatch(async () =>
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
// Complete the progress bar
|
||||
await LoadingProgress.ProgressTo(1.0, 150, Easing.CubicOut);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
// Hide the progress bar
|
||||
LoadingProgress.IsVisible = false;
|
||||
LoadingProgress.Progress = 0;
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnLoadHtmlClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Load HTML button clicked");
|
||||
StatusLabel.Text = "Loading demo HTML...";
|
||||
|
||||
// Show quick progress for HTML loading
|
||||
LoadingProgress.Progress = 0;
|
||||
LoadingProgress.IsVisible = true;
|
||||
await LoadingProgress.ProgressTo(0.5, 100, Easing.CubicOut);
|
||||
|
||||
var html = @"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>OpenMaui WebView</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
margin: 40px;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
min-height: 100vh;
|
||||
}
|
||||
h1 {
|
||||
font-size: 2.5em;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
p {
|
||||
font-size: 1.2em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.feature-list {
|
||||
background: rgba(255,255,255,0.1);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
li {
|
||||
margin: 10px 0;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
button {
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 15px 30px;
|
||||
font-size: 1.1em;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
margin-top: 20px;
|
||||
}
|
||||
button:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello from OpenMaui Linux!</h1>
|
||||
<p>This HTML content is rendered by WebKitGTK inside your .NET MAUI application.</p>
|
||||
|
||||
<div class='feature-list'>
|
||||
<h2>WebView Features:</h2>
|
||||
<ul>
|
||||
<li>Full HTML5 support</li>
|
||||
<li>CSS3 animations and transitions</li>
|
||||
<li>JavaScript execution</li>
|
||||
<li>Navigation history (back/forward)</li>
|
||||
<li>WebGL and canvas support</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<button onclick=""alert('Hello from JavaScript!')"">Click Me!</button>
|
||||
|
||||
<p style='margin-top: 30px; opacity: 0.8;'>
|
||||
Powered by WebKitGTK - the same engine used by GNOME Web (Epiphany)
|
||||
</p>
|
||||
</body>
|
||||
</html>";
|
||||
|
||||
MainWebView.Source = new HtmlWebViewSource { Html = html };
|
||||
|
||||
// Complete progress
|
||||
await LoadingProgress.ProgressTo(1.0, 100, Easing.CubicOut);
|
||||
await Task.Delay(100);
|
||||
LoadingProgress.IsVisible = false;
|
||||
|
||||
StatusLabel.Text = "Loaded demo HTML";
|
||||
UrlEntry.Text = "about:demo";
|
||||
}
|
||||
|
||||
private async void OnEvalJsClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Run JS button clicked");
|
||||
|
||||
try
|
||||
{
|
||||
var result = await MainWebView.EvaluateJavaScriptAsync("document.title");
|
||||
StatusLabel.Text = $"JS Result: {result ?? "(null)"}";
|
||||
Console.WriteLine($"[WebViewPage] JS Eval result: {result}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StatusLabel.Text = $"JS Error: {ex.Message}";
|
||||
Console.WriteLine($"[WebViewPage] JS Error: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnThemeToggleClicked(object? sender, EventArgs e)
|
||||
{
|
||||
Console.WriteLine("[WebViewPage] Theme toggle clicked");
|
||||
Console.WriteLine($"[WebViewPage] Before: UserAppTheme={Application.Current?.UserAppTheme}, RequestedTheme={Application.Current?.RequestedTheme}");
|
||||
|
||||
App.ToggleTheme();
|
||||
|
||||
Console.WriteLine($"[WebViewPage] After: UserAppTheme={Application.Current?.UserAppTheme}, RequestedTheme={Application.Current?.RequestedTheme}");
|
||||
|
||||
var theme = Application.Current?.UserAppTheme == AppTheme.Dark ? "Dark" : "Light";
|
||||
StatusLabel.Text = $"Theme: {theme}";
|
||||
|
||||
// Debug: Check what the ImageButton Source is now
|
||||
Console.WriteLine($"[WebViewPage] ThemeToggleButton.Source = {ThemeToggleButton.Source}");
|
||||
}
|
||||
}
|
||||
70
WebViewDemo/Program.cs
Normal file
@@ -0,0 +1,70 @@
|
||||
// Program.cs - Linux platform entry point
|
||||
|
||||
using Microsoft.Maui.Platform.Linux;
|
||||
|
||||
namespace WebViewDemo;
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
// Redirect console output to a log file for debugging
|
||||
var logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "webviewdemo.log");
|
||||
using var logWriter = new StreamWriter(logPath, append: false) { AutoFlush = true };
|
||||
var multiWriter = new MultiTextWriter(Console.Out, logWriter);
|
||||
Console.SetOut(multiWriter);
|
||||
Console.SetError(multiWriter);
|
||||
|
||||
// Global exception handler
|
||||
AppDomain.CurrentDomain.UnhandledException += (sender, e) =>
|
||||
{
|
||||
var ex = e.ExceptionObject as Exception;
|
||||
Console.WriteLine($"[FATAL] Unhandled exception: {ex?.GetType().Name}: {ex?.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {ex?.StackTrace}");
|
||||
if (ex?.InnerException != null)
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Inner exception: {ex.InnerException.GetType().Name}: {ex.InnerException.Message}");
|
||||
Console.WriteLine($"[FATAL] Inner stack trace: {ex.InnerException.StackTrace}");
|
||||
}
|
||||
};
|
||||
|
||||
TaskScheduler.UnobservedTaskException += (sender, e) =>
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Unobserved task exception: {e.Exception?.GetType().Name}: {e.Exception?.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {e.Exception?.StackTrace}");
|
||||
e.SetObserved(); // Prevent crash
|
||||
};
|
||||
|
||||
Console.WriteLine($"[Program] Starting WebView Demo at {DateTime.Now}");
|
||||
Console.WriteLine($"[Program] Log file: {logPath}");
|
||||
|
||||
try
|
||||
{
|
||||
// Create the MAUI app with all handlers registered
|
||||
var app = MauiProgram.CreateMauiApp();
|
||||
|
||||
// Run on Linux platform with GTK mode for WebView support
|
||||
LinuxApplication.Run(app, args, options =>
|
||||
{
|
||||
options.UseGtk = true;
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[FATAL] Exception in Main: {ex.GetType().Name}: {ex.Message}");
|
||||
Console.WriteLine($"[FATAL] Stack trace: {ex.StackTrace}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to write to both console and file
|
||||
class MultiTextWriter : TextWriter
|
||||
{
|
||||
private readonly TextWriter[] _writers;
|
||||
public MultiTextWriter(params TextWriter[] writers) => _writers = writers;
|
||||
public override System.Text.Encoding Encoding => System.Text.Encoding.UTF8;
|
||||
public override void Write(char value) { foreach (var w in _writers) w.Write(value); }
|
||||
public override void WriteLine(string? value) { foreach (var w in _writers) w.WriteLine(value); }
|
||||
public override void Flush() { foreach (var w in _writers) w.Flush(); }
|
||||
}
|
||||
4
WebViewDemo/Resources/AppIcon/appicon.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="456" height="456" fill="#1D5DB3"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 184 B |