티스토리 뷰
■ 목차
1. MvvmDialogSample 들어가기
크로스플랫폼 닷넷 UI 프레임워크인 아바로니아 맛보기를 넘어서서 본격적인 활용에 들어가기 위한 학습 방법으로 필자는 예제 프로그램 리뷰를 하나씩 수행하기로 했다. 기술 자료가 넉넉하고, 비주얼스튜디오의 WPF 디자이너와 같은 도구가 있다면 넘어갈 수도 있는 문제겠지만 지금까지 접하지 않았던 새로운 도구와 친숙해지는 방법은 역시 예제를 통하는 것이 좋은 방법이 아닌가 싶다. 공식 예제 코드는 깃허브 https://github.com/AvaloniaUI/Avalonia.Samples 에서 받을 수 있다. 커뮤니티에서 발굴해 놓은 예제와 참조 프로젝트도 있는데 깃허브 https://github.com/AvaloniaCommunity/awesome-avalonia 에서 확인할 수 있다.

MvvmDialogSample은 MVVM(Models, Views, View Models) 패턴으로 개발 시 대화창의 동작이 필요한 경우 참조할만한 예제이다. 예를 들면 작업에 필요한 파일을 선택하거나, 항목 삭제 시 확인하는 대화창 등이다. 예제에서는 파일 선택 대화창을 사용한다. Reactive UI 프레임워크의 Interactions 제네릭 클래스를 사용하는 방법과 CommunityToolkit.Mvvm 패키지 및 자체 Interaction 제네릭 클래스를 사용하는 방법 두 가지를 제시한다.
2. 프로그램 동작 및 구조

"Interaction" 탭과 "Custom Interaction" 탭의 동작은 동일하다. 하단의 [Select Files] 버튼을 클릭하면 파일 선택 창이 나오고 선택한 파일의 목록을 리스트박스에 표시하는 동작을 수행한다.

프로젝트는 MVVM 패턴에서 별도의 모델 없이 한 개의 뷰모델 및 뷰와 Core 폴더의 자체 Interaction 제네릭 클래스로 구성된 형태이다. 메인 뷰에서 두 개의 탭을 서브 뷰로 두고 각 서브 뷰별로 뷰 모델과 대응하는 구조이다. 두 개의 서브 뷰는 디자인에서는 <UserControl> 태그로 동일하게 정의하지만 뷰의 소스코드를 보면 각각 Reactive UI의 ReactiveUserControl <InteractionViewModel>과 아발로니아의 UserControl을 상속받는다는 차이점이 있다. 뷰의 소스코드에 대화창 처리를 위한 비동기 핸들러가 각각 구현되어 있다.
3. 두 가지 Interaction 구현 방법
public class InteractionViewModel : ViewModelBase
{
public InteractionViewModel()
{
_SelectFilesInteraction = new Interaction<string?, string[]?>();
SelectFilesCommand = ReactiveCommand.CreateFromTask(SelectFiles);
}
private string[]? _SelectedFiles;
public string[]? SelectedFiles
{
get { return _SelectedFiles; }
set { this.RaiseAndSetIfChanged(ref _SelectedFiles, value); }
}
private readonly Interaction<string?, string[]?> _SelectFilesInteraction;
public Interaction<string?, string[]?> SelectFilesInteraction => this._SelectFilesInteraction;
public ICommand SelectFilesCommand { get; }
private async Task SelectFiles()
{
SelectedFiles = await _SelectFilesInteraction.Handle("Hello from Avalonia");
}
}
"Interaction" 탭은 Reactive UI를 사용하는 뷰로 뷰모델에서는 Interaction 제네릭 클래스를 기반으로 인스턴스를 생성하고 버튼 클릭을 위한 명령을 생성한다.
public partial class InteractionView : ReactiveUserControl<InteractionViewModel>
{
public InteractionView()
{
InitializeComponent();
this.WhenActivated(d =>
{
d(ViewModel!.SelectFilesInteraction.RegisterHandler(this.InteractionHandler));
});
}
private async Task InteractionHandler(IInteractionContext<string?, string[]?> context)
{
var topLevel = TopLevel.GetTopLevel(this);
var storageFiles = await topLevel!.StorageProvider.OpenFilePickerAsync(
new FilePickerOpenOptions()
{
AllowMultiple = true,
Title = context.Input
});
context.SetOutput(storageFiles.Select(x => x.Name).ToArray());
}
}
"Interaction" 탭의 뷰 코드는 ReactiveUserControl<InteractionViewModel>를 상속받아 작성하며 뷰가 동작하는 시점에 뷰모델에서 생성한 Interaction 인스턴스를 기반으로 대화창 처리를 위한 비동기 핸들러를 등록하는 방식이다.
public partial class CustomInteractionViewModel : ObservableObject
{
[ObservableProperty]
string[]? _SelectedFiles;
public Interaction<string, string[]?> SelectFilesInteraction { get; } = new Interaction<string, string[]?>();
[RelayCommand]
private async Task SelectFilesAsync()
{
SelectedFiles = await SelectFilesInteraction.HandleAsync("Hello from Avalonia");
}
}
"Custom Interaction" 탭은 CommunityToolkit.Mvvm 패키지와 core 폴더에 있는 자체적인 Interaction 제네릭 클래스를 사용하는 방법으로 위의 코드처럼 어노테이션을 기술하는 방법으로 간편하게 적용할 수 있다. [ObservableProperty] 어노테이션으로 뷰에서는 SelectedFiles 속성을 바인딩할 수 있고, [RelayCommand] 어노테이션으로 뷰에서는 SelectFilesCommand를 명령으로 바인딩할 수 있다.
public partial class CustomInteractionView : UserControl
{
public CustomInteractionView()
{
InitializeComponent();
}
IDisposable? _selectFilesInteractionDisposable;
protected override void OnDataContextChanged(EventArgs e)
{
_selectFilesInteractionDisposable?.Dispose();
if (DataContext is CustomInteractionViewModel vm)
{
_selectFilesInteractionDisposable = vm.SelectFilesInteraction.RegisterHandler(InteractionHandler);
}
base.OnDataContextChanged(e);
}
private async Task<string[]?> InteractionHandler(string input)
{
var topLevel = TopLevel.GetTopLevel(this);
var storageFiles = await topLevel!.StorageProvider.OpenFilePickerAsync(
new FilePickerOpenOptions()
{
AllowMultiple = true,
Title = input
});
return storageFiles?.Select(x => x.Name)?.ToArray();
}
}
" Custom Interaction" 탭의 뷰 코드는 UserControl를 상속받으며 OnDataContextChanged를 재작성하는 방식으로 대화창 처리를 위한 비동기 핸들러를 등록한다.
4. 뷰와 아발로니아 요소들
예제는 메인 뷰에서 서브 뷰 두 개를 내포하는 방식이고 두 가지의 뷰는 뷰모델만 다를 뿐 거의 동일하다.
<TabControl>
<TabItem Header="Interaction">
<v:InteractionView DataContext="{Binding InteractionSample}" />
</TabItem>
<TabItem Header="Custom Interaction">
<v:CustomInteractionView DataContext="{Binding CustomInteractionViewModel}" />
</TabItem>
</TabControl>
<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="using:MvvmDialogSample.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="MvvmDialogSample.Views.CustomInteractionView"
x:CompileBindings="True"
x:DataType="vm:CustomInteractionViewModel">
<Grid RowDefinitions="Auto,*,Auto">
<TextBlock Text="Selected Files:" />
<ListBox ItemsSource="{Binding SelectedFiles}" Grid.Row="1" />
<Button Content="Select Files"
Command="{Binding SelectFilesCommand}"
Grid.Row="2" />
</Grid>
</UserControl>
- TabControl : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_TabControl
내부에 TabItem 항목으로 탭 요소를 둔다. TabStripPlacement 속성을 "Bottom"으로 지정하면 탭을 하단에 표시한다. - TabItem : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_TabItem
Header 속성으로 탭 제목 지정. - UserControl : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_UserControl
사용자 컨트롤을 정의하기 위한 베이스 클래스로 Window 컨트롤처럼 하나의 뷰 클래스 단위가 되고 뷰모델과 연동한다.
UserControl.Resources 등 사용 - Grid : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_Grid
행과 열로 구성되는 표 형태의 영역을 정의하는 데 사용한다. RowDefinitions, ColumnDefinitions으로 행과 열의 크기를 지정. - TextBlock : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_TextBlock
텍스트 출력, 태그 내에 출력할 문자열을 입력하거나 Text 속성에 정적/동적 텍스트 입력. FontWeight, FontSize, VerticalAlignment, TextAlignment, TextWrapping 등 사용. - ListBox : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_ListBox
항목 선택이 가능한 목록 컨트롤. ItemsSource, SelectedItem, Background, Margin, ItemsPanel, ItemTemplate 등 사용 - Button : https://api-docs.avaloniaui.net/docs/T_Avalonia_Controls_Button
표준 버튼 컨트롤. HorizontalAlignment, VerticalAlignment, Content, Command, CommandParameter, IsVisible, FontSize, CornerRadius, Background, Classes 등 사용
'프로그래밍' 카테고리의 다른 글
| 아발로니아 UI 예제 프로그램 리뷰 7 - SimpleToDoList (0) | 2025.11.10 |
|---|---|
| 아발로니아 UI 예제 프로그램 리뷰 6 - DialogManagerSample (0) | 2025.11.07 |
| 아발로니아 UI 예제 프로그램 리뷰 4 - ValueConversionSample (0) | 2025.10.27 |
| 아발로니아 UI 예제 프로그램 리뷰 3 - ValidationSample (0) | 2025.10.22 |
| 아발로니아 UI 예제 프로그램 리뷰 2 - CommandSample (0) | 2025.10.17 |