Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] RoutedCommand #21

Merged
merged 8 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Avalonia.Labs.sln
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Labs.Qr", "src\Ava
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Labs.Lottie", "src\Avalonia.Labs.Lottie\Avalonia.Labs.Lottie.csproj", "{633A51E9-65E7-4350-9AE6-E527FECD15F9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Labs.RoutedCommand", "src\Avalonia.Labs.RoutedCommand\Avalonia.Labs.RoutedCommand.csproj", "{3CCD9CC7-CAA3-4F97-86D2-A789107B7F30}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Labs.Panels", "src\Avalonia.Labs.Panels\Avalonia.Labs.Panels.csproj", "{BEDDF9B6-5B8D-4524-975C-485F87DDE7A2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Labs.ExpressionBuilder", "src\Avalonia.Labs.ExpressionBuilder\Avalonia.Labs.ExpressionBuilder.csproj", "{EDB6DA31-A37D-4E1B-9C1D-50B6178AB49C}"
Expand Down Expand Up @@ -93,6 +95,10 @@ Global
{633A51E9-65E7-4350-9AE6-E527FECD15F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{633A51E9-65E7-4350-9AE6-E527FECD15F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{633A51E9-65E7-4350-9AE6-E527FECD15F9}.Release|Any CPU.Build.0 = Release|Any CPU
{3CCD9CC7-CAA3-4F97-86D2-A789107B7F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CCD9CC7-CAA3-4F97-86D2-A789107B7F30}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CCD9CC7-CAA3-4F97-86D2-A789107B7F30}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CCD9CC7-CAA3-4F97-86D2-A789107B7F30}.Release|Any CPU.Build.0 = Release|Any CPU
{BEDDF9B6-5B8D-4524-975C-485F87DDE7A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BEDDF9B6-5B8D-4524-975C-485F87DDE7A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BEDDF9B6-5B8D-4524-975C-485F87DDE7A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
![Labs Header](https://github.com/AvaloniaUI/Avalonia.Labs/assets/552074/b9a462fc-8cb7-437b-9023-07e44ab0aabd)

# Avalonia Labs

Experimental Controls for [Avalonia](https://github.com/AvaloniaUI/Avalonia).

This repository serves as a staging ground for new controls for [Avalonia](https://github.com/AvaloniaUI/Avalonia), with the intention of including them in the core AvaloniaUI controls. The controls available here are unstable and are suspected to breaking changes as they are being worked on.

## Usage

Add the `Avalonia.Labs.Controls.ControlThemes` theme after your main app theme in `App.xaml.cs`.

```xaml
Expand All @@ -28,6 +30,7 @@ Add the `Avalonia.Labs.Controls.ControlThemes` theme after your main app theme i
- [Lottie Viewer](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.Lottie)
- [NavigationControl](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.Controls/Navigation)
- [Qr Code Generator](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.Qr)
- [RoutedCommand](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.RoutedCommand)
- [StepBar](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.Controls/StepBar)
- [SKCanvasView](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.Controls/SKCanvasView)
- [SwipeView](https://github.com/AvaloniaUI/Avalonia.Labs/tree/main/src/Avalonia.Labs.Controls/SwipeView)
Expand Down
10 changes: 10 additions & 0 deletions samples/Avalonia.Labs.Catalog/ApplicationCommands.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Avalonia.Labs.Input;

namespace Avalonia.Labs.Catalog;

public static class ApplicationCommands
{
public readonly static RoutedCommand Open = new RoutedCommand(nameof(Open));
public readonly static RoutedCommand Save = new RoutedCommand(nameof(Save));
public readonly static RoutedCommand Delete = new RoutedCommand(nameof(Delete));
}
1 change: 1 addition & 0 deletions samples/Avalonia.Labs.Catalog/Avalonia.Labs.Catalog.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<ProjectReference Include="..\..\src\Avalonia.Labs.Lottie\Avalonia.Labs.Lottie.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Labs.Panels\Avalonia.Labs.Panels.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Labs.Qr\Avalonia.Labs.Qr.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Labs.RoutedCommand\Avalonia.Labs.RoutedCommand.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Labs.ExpressionBuilder\Avalonia.Labs.ExpressionBuilder.csproj" />
</ItemGroup>

Expand Down
7 changes: 7 additions & 0 deletions samples/Avalonia.Labs.Catalog/IViewBinder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Avalonia.Labs.Catalog
{
internal interface IViewBinder
{
Visual? View { get; set; }
}
}
7 changes: 6 additions & 1 deletion samples/Avalonia.Labs.Catalog/ViewLocator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ public static void Register(Type type, Func<Control> factory)

if (type != null && ViewModelRegistry.TryGetValue(type, out var factory))
{
return factory();
var control = factory();
if (data is IViewBinder viewBinder)
{
viewBinder.View = control;
}
return control;
}

return new TextBlock { Text = type?.FullName };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using ReactiveUI;

namespace Avalonia.Labs.Catalog.ViewModels;

public class RouteCommandItemViewModel : ViewModelBase
{
private string? _text;
private bool _hasChanges;

public string? Text
{
get => _text;
set
{
this.RaiseAndSetIfChanged(ref _text, value);
HasChanges = true;
}
}

public int Id { get; internal set; }

public bool HasChanges { get => _hasChanges; private set => this.RaiseAndSetIfChanged(ref _hasChanges, value); }

public void Accept()
{
HasChanges = false;
}

public void Delete(object? parameter)
{

}
}
63 changes: 63 additions & 0 deletions samples/Avalonia.Labs.Catalog/ViewModels/RouteCommandViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Labs.Catalog.Views;
using Avalonia.Platform.Storage;

namespace Avalonia.Labs.Catalog.ViewModels;

public class RouteCommandViewModel : ViewModelBase, IViewBinder
{
static RouteCommandViewModel()
{
ViewLocator.Register(typeof(RouteCommandViewModel), () => new RouteCommandView());
}

public Visual? View { get; set; }

public bool CanOpen(object? parameter)
{
if (TopLevel.GetTopLevel(View)?.StorageProvider is { CanOpen: true } && parameter is RouteCommandItemViewModel item && item.HasChanges == false)
{
return true;
}
return false;
}

public async void Open(object? parameter)
{
if (TopLevel.GetTopLevel(View)?.StorageProvider is { CanOpen: true } provider)
{
var result = await provider.OpenFilePickerAsync(new FilePickerOpenOptions() { });

if (result?.FirstOrDefault() is IStorageFile file)
{
if (parameter is RouteCommandItemViewModel item)
{
item.Text = file.Name;
}
}
}
}

public bool CanSave(object? parameter) =>
parameter is RouteCommandItemViewModel { HasChanges: true };

public async void Save(object? parameter)
{
if (parameter is RouteCommandItemViewModel { HasChanges: true } item)
{
item.Accept();
}
await Task.CompletedTask;
}

public IReadOnlyList<RouteCommandItemViewModel> Dettails { get; } =
new RouteCommandItemViewModel[]
{
new() {Id = 1},
new() {Id = 2},
new() {Id = 3},
};
}
58 changes: 58 additions & 0 deletions samples/Avalonia.Labs.Catalog/Views/RouteCommandView.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:lab="clr-namespace:Avalonia.Labs.Catalog"
xmlns:rc="clr-namespace:Avalonia.Labs.Input;assembly=Avalonia.Labs.RoutedCommand"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
xmlns:viewModels="clr-namespace:Avalonia.Labs.Catalog.ViewModels"
x:Class="Avalonia.Labs.Catalog.Views.RouteCommandView"
x:DataType="viewModels:RouteCommandViewModel"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
>
<rc:RoutedCommandManager.Commands>
<rc:RoutedCommandBinding RoutedCommand="{x:Static lab:ApplicationCommands.Open}" ExecutingCommand="{Binding Open}" />
<rc:RoutedCommandBinding RoutedCommand="{x:Static lab:ApplicationCommands.Save}" ExecutingCommand="{Binding Save}" />
</rc:RoutedCommandManager.Commands>
<StackPanel>
<UniformGrid Columns="10">
<Button Content="Del"
Command="{x:Static lab:ApplicationCommands.Delete}"
Focusable="False"/>
</UniformGrid>
<ItemsControl Grid.IsSharedSizeScope="True"
ItemsSource="{Binding Dettails}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="viewModels:RouteCommandItemViewModel">
<Grid>
<rc:RoutedCommandManager.Commands>
<rc:RoutedCommandBinding RoutedCommand="{x:Static lab:ApplicationCommands.Delete}"
ExecutingCommand="{Binding Delete}"
ExecutingCommandParameter="{Binding .}"
/>
</rc:RoutedCommandManager.Commands>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="Command"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Id}"/>
<TextBox Text="{Binding Text}"
IsReadOnly="True"
Grid.Column="1"/>
<UniformGrid Columns="2"
Grid.Column="2">
<Button Content="Open"
Command="{x:Static lab:ApplicationCommands.Open}"
CommandParameter="{Binding .}"/>
<Button Content="Save"
Command="{x:Static lab:ApplicationCommands.Save}"
CommandParameter="{Binding .}"/>
</UniformGrid>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</UserControl>
11 changes: 11 additions & 0 deletions samples/Avalonia.Labs.Catalog/Views/RouteCommandView.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using Avalonia.Controls;

namespace Avalonia.Labs.Catalog.Views;

public partial class RouteCommandView : UserControl
{
public RouteCommandView()
{
InitializeComponent();
}
}
11 changes: 11 additions & 0 deletions samples/Avalonia.Labs.Catalog/Views/WelcomeView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,17 @@
<Label FontSize="18"
VerticalAlignment="Center">Qr Generator</Label>
</Button>
<Button HorizontalAlignment="Stretch"
Height="60"
Command="{Binding NavigateTo}"
CornerRadius="10"
Margin="0,5">
<Button.CommandParameter>
<viewModels:RouteCommandViewModel/>
</Button.CommandParameter>
<Label FontSize="18"
VerticalAlignment="Center">RoutedCommand</Label>
</Button>
<Button HorizontalAlignment="Stretch"
Height="60"
Command="{Binding NavigateTo}"
Expand Down
17 changes: 17 additions & 0 deletions src/Avalonia.Labs.RoutedCommand/Avalonia.Labs.RoutedCommand.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<IsPackable>true</IsPackable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<RootNamespace>Avalonia.Labs.Input</RootNamespace>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" Pack="true" PackagePath="\"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="$(AvaloniaVersion)" />
</ItemGroup>
</Project>
33 changes: 33 additions & 0 deletions src/Avalonia.Labs.RoutedCommand/CanExecuteRoutedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using Avalonia.Interactivity;
using System.Windows.Input;

namespace Avalonia.Labs.Input;

/// <summary>
/// Provides data for the <see cref="RoutedCommandManager.CanExecuteEvent"/> routed events.
/// </summary>
public sealed class CanExecuteRoutedEventArgs : RoutedEventArgs
{
/// <summary>
/// Gets the routed command that was invoked.
/// </summary>
public ICommand Command { get; }

/// <summary>
/// Gets data parameter of the command.
/// </summary>
public object? Parameter { get; }

/// <summary>
/// Gets or sets a value that indicates whether the <see cref="RoutedCommand"/> associated with this event can be executed on the command target.
/// </summary>
public bool CanExecute { get; set; }

internal CanExecuteRoutedEventArgs(ICommand command, object? parameter)
{
Command = command ?? throw new ArgumentNullException(nameof(command));
Parameter = parameter;
RoutedEvent = RoutedCommandManager.CanExecuteEvent;
}
}
28 changes: 28 additions & 0 deletions src/Avalonia.Labs.RoutedCommand/ExecutedRoutedEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using Avalonia.Interactivity;
using System.Windows.Input;

namespace Avalonia.Labs.Input;

/// <summary>
/// Provides data for the <see cref="RoutedCommandManager.ExecutedEvent"/> routed event.
/// </summary>
public sealed class ExecutedRoutedEventArgs : RoutedEventArgs
{
/// <summary>
/// Gets the routed command that was invoked.
/// </summary>
public ICommand Command { get; }

/// <summary>
/// Gets data parameter of the command.
/// </summary>
public object? Parameter { get; }

internal ExecutedRoutedEventArgs(ICommand command, object? parameter)
{
Command = command ?? throw new ArgumentNullException(nameof(command));
Parameter = parameter;
RoutedEvent = RoutedCommandManager.ExecutedEvent;
}
}
Loading
Loading