I'm currently playing around with Avalonia and want to build reusable controls that can be used at multiple places all over my final application.
To keep it simple, I want a UserControl
named FilePicker
which is simply a Button
. Once clicked a file-chooser dialog should be opened (this code is not part of my question).
The control using the FilePicker
should bind to the selected file and do whatever it wants to do with it.
I've started with the current Avalonia template in Visual Studio 2022
which ends up in a folder-structure like this:
The FilePicker.axaml
<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:userControls="clr-namespace:FoodShiner.Avalonia.UserControls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="FoodShiner.Avalonia.UserControls.FilePicker"
x:DataType="userControls:FilePickerViewModel">
<Button Command="{Binding OpenFileDialog}">Test</Button>
</UserControl>
The code-behind FilePicker.axaml.cs
:
using Avalonia.Controls;
namespace FoodShiner.Avalonia.UserControls;
public partial class FilePicker : UserControl
{
public FilePicker()
{
InitializeComponent();
}
}
and the view-model FilePickerViewModel.cs
:
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.Input;
using FoodShiner.Avalonia.ViewModels;
namespace FoodShiner.Avalonia.UserControls
{
internal partial class FilePickerViewModel : ViewModelBase
{
[RelayCommand]
public async Task OpenFileDialog()
{
}
}
}
This compiles, builds and runs without any issues showing the button named Test
.
Once clicked I get
Attempt by method 'CompiledAvaloniaXaml.XamlIlTrampolines.FoodShiner.Avalonia:FoodShiner.Avalonia.UserControls.FilePickerViewModel+OpenFileDialog_0!CommandExecuteTrampoline(System.Object, System.Object)' to access method 'FoodShiner.Avalonia.UserControls.FilePickerViewModel.OpenFileDialog()' failed.
So I changed the modifier of my bound command to public
which then leads to
System.InvalidCastException: 'Unable to cast object of type 'FoodShiner.Avalonia.ViewModels.MainViewModel' to type 'FoodShiner.Avalonia.UserControls.FilePickerViewModel'.'
I'm starting to think that my whole concept is wrong here and this is not the way to go. On the other hand it seems like a "simple" thing to do by building a reusable control. I also tried a dedicated library (which might be released as nuget) but this one failed as well.
On the other hand the AavaloniaUI
could not provide me some helpful examples on how this can be done. Neither have XAML, WPF or the CommunityToolkit helped.
As I am a blazor-dev for quite a bit now this seems even more complicated as the concepts in blazor are a bit different
Here are the other files (as it might help)
MainView.axaml
<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:vm="clr-namespace:FoodShiner.Avalonia.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="FoodShiner.Avalonia.Views.MainView"
x:DataType="vm:MainViewModel">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:MainViewModel />
</Design.DataContext>
</UserControl>
and `MainWindow.axaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:uc="using:FoodShiner.Avalonia.UserControls"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="FoodShiner.Avalonia.Views.MainWindow"
Icon="/Assets/avalonia-logo.ico"
Title="FoodShiner.Avalonia"
RequestedThemeVariant="Dark"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center">
<uc:FilePicker />
</Window>
The CommunityToolkit.Mvvm's [RelayCommand] attribute will generate an IAsyncRelayCommand
named OpenFileDialogCommand for you.
So, the code below should work:
<Button Command="{Binding OpenFileDialogCommand}">Test</Button>