1. 프로젝트 구성 파일
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.9" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.9" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.9" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.9" />
<PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageReference Include="ReactiveUI.SourceGenerators" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
2. 모델 정의
using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace AvaRea.Models
{
public partial class UserProfile : ReactiveObject
{
[Reactive] private string _username;
[Reactive] private int _userAge;
[Reactive] private string _userEmail;
}
}
3. 뷰모델 (ReactiveUI 소스 생성기 활용)
using AvaRea.Models;
using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
namespace AvaRea.ViewModels
{
public partial class DashboardViewModel : ReactiveObject
{
private int newItemIndex = 0;
public ObservableCollection<UserProfile> Users { get; set; } = new();
[ObservableAsProperty]
private int _totalUsers;
[Reactive]
private UserProfile _selectedUser;
[ReactiveCommand]
private void CreateUser()
{
Users.Add(new UserProfile
{
Username = $"사용자{newItemIndex++}",
UserAge = 25 + newItemIndex,
UserEmail = $"user{newItemIndex}@example.com"
});
}
public DashboardViewModel()
{
var initialData = new[]
{
new UserProfile { Username = "김철수", UserAge = 28, UserEmail = "kim@example.com" },
new UserProfile { Username = "박지영", UserAge = 32, UserEmail = "park@example.com" }
};
Users.AddRange(initialData);
var countObserver = Users
.ToObservableChangeSet()
.Select(_ => Users.Count)
.ToProperty(this, x => x.TotalUsers);
}
}
using ReactiveUI;
namespace AvaRea.ViewModels
{
public class BaseViewModel : ReactiveObject
{
}
}
}
4. 뷰 정의
<Window
x:Class="AvaRea.Views.MainView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:model="clr-namespace:AvaRea.Models"
xmlns:vm="clr-namespace:AvaRea.ViewModels"
d:DesignHeight="450"
d:DesignWidth="800"
x:DataType="vm:DashboardViewModel"
mc:Ignorable="d">
<Window.DataTemplates>
<DataTemplate DataType="model:UserProfile">
<StackPanel
Margin="5"
Orientation="Horizontal"
Spacing="10">
<Ellipse
Width="32"
Height="32"
Fill="LightBlue" />
<StackPanel>
<TextBlock FontWeight="Bold" Text="{Binding Username}" />
<TextBlock
FontSize="12"
Foreground="Gray"
Text="{Binding UserEmail}" />
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.DataTemplates>
<Design.DataContext>
<vm:DashboardViewModel />
</Design.DataContext>
<StackPanel>
<TextBlock Text="{Binding TotalUsers}" />
<ListBox
ItemsSource="{Binding Users}"
SelectedItem="{Binding SelectedUser}" />
<Button Command="{Binding CreateUserCommand}" Content="등록" />
</StackPanel>
</Window>
using Avalonia.Controls;
namespace AvaRea.Views
{
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
}
}
}
5. 애플리케이션 설정 파일
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaRea.App"
RequestedThemeVariant="Default">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
6. 애플리케이션 코드
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using AvaRea.ViewModels;
using AvaRea.Views;
namespace AvaRea
{
public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainView
{
DataContext = new DashboardViewModel()
};
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
singleViewPlatform.MainView = new MainView
{
DataContext = new DashboardViewModel()
};
}
base.OnFrameworkInitializationCompleted();
}
}
}
7. 프로그램 진입점
using System;
using Avalonia;
using ReactiveUI.Avalonia;
namespace AvaRea.Desktop
{
class Program
{
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace()
.UseReactiveUI();
}
}