WPF 기반 포터블 라디오 프로그램

심심풀이로 만들어본 WPF 기반 라디오 프로그램.

압축풀어서 KoreaRadioLivesmart.exe 를 실행하면 됩니다. 모든 검사를 한 깨끗한 파일입니다. 자동업데이트 지원


맥용 라디오 프로그램. MacOS, SwiftUI

아이폰용 라디오, iOS앱, swiftui

[MVVM] 9.MVVM과 WPF 디자인 패턴 및 UI 커스터마이징, 데이터 템플릿과 스타일을 활용한 UI 커스터마이징

Windows Presentation Foundation (WPF)은 Powerful한 UI 기반 애플리케이션을 개발할 수 있는 .NET Framework의 한 구성 요소입니다. WPF는 사용자 인터페이스(UI)를 여러 방식으로 정의할 수 있게 하여, 개발자가 기능적인 애플리케이션을 만들기 위해 무한한 가능성을 제공합니다. 이 블로그 글에서는 MVVM 패턴을 중심으로 WPF의 디자인 패턴을 심도 있게 분석하고, UI 커스터마이징을 위한 데이터 템플릿과 스타일을 활용하는 방법에 대해 살펴보겠습니다.

1. MVVM 디자인 패턴 개요

MVVM(Model-View-ViewModel)은 WPF 애플리케이션을 개발하는 데 가장 널리 사용되는 디자인 패턴입니다. MVVM은 UI와 비즈니스 로직을 분리하여 코드의 재사용성과 유지 보수성을 높이고, 개발자와 디자이너 간의 협업을 원활하게 해줍니다.

1.1 MVVM의 구조

  • Model: 애플리케이션의 데이터와 비즈니스 로직을 관장합니다. 데이터베이스나 외부 API에서 데이터를 가져오는 역할을 수행합니다.
  • View: 사용자에게 보여지는 UI 요소들입니다. XAML을 사용하여 정의되며, ViewModel과 바인딩을 통해 UI 요소와 데이터를 연결합니다.
  • ViewModel: Model과 View 간의 중재자 역할을 합니다. View와 Model의 상호작용을 처리하고, Data Binding을 통해 View에 데이터를 제공합니다.

1.2 MVVM의 이점

MVVM 패턴은 여러 가지 이점을 제공합니다:

  • 코드 분리: UI 코드와 비즈니스 로직이 분리되어 유지보수성과 코드 가독성이 높아집니다.
  • 재사용성: ViewModel을 통해 여러 View에서 동일한 데이터를 쉽게 재사용할 수 있습니다.
  • 디자인 협업: 디자이너는 View를 XAML로 독립적으로 구현할 수 있으며, 개발자는 ViewModel에서 비즈니스 로직을 처리합니다.

2. WPF에서의 Data Binding과 Commands

WPF의 핵심은 데이터 바인딩과 명령(Command)입니다. 데이터 바인딩을 통해 View와 ViewModel 간의 데이터를 동기화하고, Command를 통해 사용자 상호작용을 처리합니다.

2.1 데이터 바인딩

데이터 바인딩이란 View와 ViewModel 간의 데이터 통신을 설정한 것입니다. 예를 들어, ViewModel에 있는 속성이 변경되면 View에 자동으로 업데이트됩니다. 다음은 기본적인 데이터 바인딩의 예입니다.

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MVVM Example" Height="350" Width="525">
    <Grid>
        <TextBox Text="{Binding UserName, UpdateSourceTrigger=PropertyChanged}" />
        <Button Command="{Binding SubmitCommand}" Content="Submit" />
    </Grid>
</Window>

위의 예제에서 TextBox의 Text 속성은 ViewModel의 UserName 속성과 바인딩되어 있습니다. 사용자가 텍스트를 입력하면 ViewModel의 UserName 속성이 자동으로 업데이트됩니다.

2.2 Command 인터페이스

WPF에서 사용자 상호작용을 처리하기 위해 Command를 사용합니다. 다음은 ICommand 인터페이스를 구현한 간단한 예제입니다.

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Predicate<object> _canExecute;

    public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
    public void Execute(object parameter) => _execute(parameter);
    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
}

위의 RelayCommand 클래스는 ICommand 인터페이스를 구현하며, 비즈니스 로직을 실행하는 Execute 메소드를 포함하고 있습니다.

3. UI 커스터마이징 – 데이터 템플릿과 스타일

WPF에서는 데이터 템플릿(data templates)과 스타일(styles)을 사용하여 UI를 유연하게 커스터마이징할 수 있습니다.

3.1 데이터 템플릿

데이터 템플릿은 특정 데이터 유형에 대한 UI 표현을 정의합니다. 예를 들어, ListBox의 항목을 사용자 정의 방식으로 표시할 수 있습니다:

<ListBox ItemsSource="{Binding Users}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Name}" FontWeight="Bold" />
                <TextBlock Text="{Binding Email}" FontStyle="Italic" />
            </StackPanel>
        </DataTemplate>
    <ListBox.ItemTemplate>
</ListBox>

위 코드에서는 ListBox 내에서 항목의 데이터(사용자 이름 및 이메일)를 사용자 정의 방식으로 표시하고 있습니다.

3.2 스타일과 컨트롤 템플릿

스타일을 사용하여 WPF 컨트롤의 모양과 느낌을 조정할 수 있습니다. 예를 들어 모든 버튼의 기본 스타일을 정의할 수 있습니다:

<Window.Resources>
    <Style TargetType="Button">
        <Setter Property="Background" Value="DarkBlue"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="FontWeight" Value="Bold"/>
        </Style>
</Window.Resources>

이 스타일은 모든 버튼에 적용되며, 버튼의 배경색 및 글자색을 설정합니다.

4. 실전 예제: MVVM을 활용한 간단한 TODO 앱 구현

이제 MVVM 패턴을 사용하여 간단한 TODO 애플리케이션을 구현하는 예제를 살펴보겠습니다.

4.1 Model 클래스

public class TodoItem
{
    public string Title { get; set; }
    public bool IsCompleted { get; set; }
}

4.2 ViewModel 클래스

public class TodoViewModel : INotifyPropertyChanged
{
    private ObservableCollection<TodoItem> _todos;
    private string _newTodoTitle;

    public ObservableCollection<TodoItem> Todos
    {
        get { return _todos; }
        set { _todos = value; OnPropertyChanged(nameof(Todos)); }
    }

    public string NewTodoTitle
    {
        get { return _newTodoTitle; }
        set { _newTodoTitle = value; OnPropertyChanged(nameof(NewTodoTitle)); }
    }

    public ICommand AddTodoCommand { get; set; }

    public TodoViewModel()
    {
        Todos = new ObservableCollection<TodoItem>();
        AddTodoCommand = new RelayCommand(AddTodo);
    }

    private void AddTodo(object obj)
    {
        Todos.Add(new TodoItem { Title = NewTodoTitle });
        NewTodoTitle = string.Empty;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

4.3 View (XAML)

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="TODO App" Height="450" Width="400">
    <Window.DataContext>
        <local:TodoViewModel />
    </Window.DataContext>

    <Grid>
        <StackPanel>
            <TextBox Text="{Binding NewTodoTitle, UpdateSourceTrigger=PropertyChanged}" />
            <Button Command="{Binding AddTodoCommand}" Content="Add Todo" />
            <ListBox ItemsSource="{Binding Todos}">
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <CheckBox IsChecked="{Binding IsCompleted}" />
                            <TextBlock Text="{Binding Title}" Margin="5,0,0,0"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </StackPanel>
    </Grid>
</Window>

5. 결론

이 블로그 글에서는 MVVM 패턴을 통해 WPF에서 애플리케이션을 개발하는 방법을 설명하였으며, 데이터 템플릿과 스타일을 통해 UI를 커스터마이징하는 방법을 제시했습니다. MVVM을 사용하면 코드의 가독성과 유지 보수성을 높일 수 있을 뿐만 아니라, UI 디자인과 비즈니스 로직 간의 간섭을 최소화하여 효율적인 개발 환경을 제공합니다.

이번 예제를 바탕으로 여러분의 프로젝트에 MVVM 패턴을 적용하고, 데이터 템플릿 및 스타일을 통해 UI를 더욱 매력적으로 만들어 보세요. WPF는 매우 강력한 프레임워크이며, MVVM 디자인 패턴은 여러분의 애플리케이션 개발에 큰 도움이 될 것입니다.

기타 질문이나 피드백이 있으시면 댓글로 남겨주세요!

[MVVM] 8.유닛 테스트와 MVVM, ViewModel의 유닛 테스트 작성 요령

소프트웨어 개발에서 유닛 테스트는 코드를 작고 독립적인 단위로 테스트하여 버그를 조기에 발견하고 코드 품질을 유지하기 위해 필수적인 과정입니다. 특히, MVVM 패턴은 WPF (Windows Presentation Foundation) 애플리케이션의 구조적 설계를 위해 널리 사용되며, ViewModel은 MVVM의 핵심 구성 요소입니다. 이 글에서는 MVVM에서 ViewModel을 어떻게 유닛 테스트할 수 있는지, 그리고 효과적인 유닛 테스트를 작성하기 위한 요령을 자세히 설명합니다.

1. MVVM 패턴의 이해

MVVM(Model-View-ViewModel) 패턴은 WPF 애플리케이션의 아키텍처를 구조화하는 데 사용됩니다. 이 패턴은 다음과 같은 세 가지 주요 구성 요소로 나뉩니다:

  • Model: 애플리케이션의 데이터 및 비즈니스 로직을 포함합니다.
  • View: 사용자 인터페이스를 구성하며, 사용자와 상호작용합니다.
  • ViewModel: Model과 View 사이의 중재자로, UI 로직과 비즈니스 로직을 분리합니다.

이 구조는 단위 테스트를 보다 수월하게 해주며, 특히 ViewModel에서는 독립적인 비즈니스 로직을 테스트할 수 있습니다.

2. 유닛 테스트란?

유닛 테스트는 애플리케이션의 각 구성 요소를 독립적으로 검증하는 자동화된 테스트입니다. 이를 통해 개발자는 코드를 변경한 후에도 기존 기능이 올바르게 작동하는지 확인할 수 있습니다. C#에서는 NUnit, MSTest, xUnit 등 다양한 테스트 프레임워크를 사용하여 유닛 테스트를 작성할 수 있습니다.

3. 유닛 테스트의 장점

유닛 테스트의 주요 장점은 다음과 같습니다:

  • 버그 조기 발견: 코드 변경 부작용을 쉽게 찾아낼 수 있습니다.
  • 리팩토링의 안전성: 코드를 개선할 때 기존 동작을 그대로 유지하는지 검증할 수 있습니다.
  • 문서화: 테스트는 코드가 어떻게 작동하는지에 대한 문서 역할을 할 수 있습니다.
  • 개발 속도 향상: 자동화된 테스트로 인해 반복적인 수작업 테스트를 줄일 수 있습니다.

4. ViewModel의 테스트 준비

ViewModel의 테스트를 준비하기 위해서는 몇 가지 고려사항이 필요합니다:

  1. 의존성 주입: ViewModel에서 필요로 하는 서비스 또는 데이터 접근 객체에 대한 의존성을 주입하여 테스트 대역(mock)을 활용할 수 있도록 해야 합니다.
  2. 속성 바인딩: WPF의 데이터 바인딩을 활용하기 위해 INotifyPropertyChanged 인터페이스를 구현하여 속성의 변경을 감지하도록 합니다.

5. ViewModel 예제

다음은 간단한 ViewModel의 예제입니다. 이 ViewModel은 사용자 이름을 저장하고, 변경할 때마다 알림을 제공합니다:

using System.ComponentModel;

public class UserViewModel : INotifyPropertyChanged
{
    private string _userName;

    public string UserName
    {
        get { return _userName; }
        set
        {
            if (_userName != value)
            {
                _userName = value;
                OnPropertyChanged(nameof(UserName));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

이 ViewModel은 INotifyPropertyChanged를 구현하여 View에 속성 변화를 알립니다. 다음 섹션에서 이 ViewModel을 위한 유닛 테스트를 작성해 보겠습니다.

6. ViewModel 유닛 테스트 작성하기

이제 UserViewModel에 대한 유닛 테스트를 작성해 보겠습니다. NUnit을 사용할 경우, 다음과 같은 테스트 코드를 작성할 수 있습니다:

using NUnit.Framework;
using System.ComponentModel;

[TestFixture]
public class UserViewModelTests
{
    private UserViewModel _viewModel;

    [SetUp]
    public void SetUp()
    {
        _viewModel = new UserViewModel();
    }

    [Test]
    public void UserName_Should_Update_Property()
    {
        // Arrange
        string expected = "John Doe";

        // Act
        _viewModel.UserName = expected;

        // Assert
        Assert.AreEqual(expected, _viewModel.UserName);
    }

    [Test]
    public void UserName_Should_Raise_PropertyChanged_Event()
    {
        // Arrange
        bool eventRaised = false;
        _viewModel.PropertyChanged += (sender, e) =>
        {
            if (e.PropertyName == nameof(UserViewModel.UserName))
            {
                eventRaised = true;
            }
        };

        // Act
        _viewModel.UserName = "Jane Doe";

        // Assert
        Assert.IsTrue(eventRaised);
    }
}

7. 모의 객체(Mock Object) 사용하기

뷰모델이 더 복잡해지면 외부 서비스에 대한 의존성을 처리해야 할 수도 있습니다. 이때 모의 객체(mock object)를 사용하여 외부 의존성을 제거하고 ViewModel의 기능을 테스트할 수 있습니다. Moq 라이브러리를 사용하여 간단한 모의 객체를 생성해 보겠습니다:

using Moq;

public interface IUserService
{
    string GetUserName();
}

public class UserViewModel
{
    private readonly IUserService _userService;

    public UserViewModel(IUserService userService)
    {
        _userService = userService;
    }

    public string UserName => _userService.GetUserName();
}

// Test
[Test]
public void UserName_Returns_Correct_Value_From_Service()
{
    // Arrange
    var mockService = new Mock<IUserService>();
    mockService.Setup(s => s.GetUserName()).Returns("Mock User");
    var viewModel = new UserViewModel(mockService.Object);

    // Act
    var userName = viewModel.UserName;

    // Assert
    Assert.AreEqual("Mock User", userName);
}

위의 예제에서는 Moq를 사용하여 IUserService 인터페이스의 모의 객체를 생성하고, GetUserName 메서드가 반환할 값을 지정합니다. 이렇게 하면 외부 서비스에 의존하지 않고 ViewModel을 테스트할 수 있습니다.

8. 여러 테스트 시나리오 다루기

유닛 테스트를 작성할 때는 다양한 테스트 시나리오를 고려해야 합니다. 다음과 같은 상황을 테스트할 수 있습니다:

  • 범위 내의 유효한 입력 값
  • 경계 값 (예를 들어, 최소 및 최대 값)
  • 잘못된 입력 값
  • 예외가 발생하는 경우

예를 들어, UserViewModel에서 사용자 이름 입력의 유효성을 검사하는 메서드를 추가하고 이를 테스트할 수 있습니다.

public bool IsUserNameValid(string userName)
{
    return !string.IsNullOrWhiteSpace(userName) && userName.Length > 2;
}

// Test
[Test]
public void IsUserNameValid_Should_Return_False_When_Empty()
{
    Assert.IsFalse(_viewModel.IsUserNameValid(string.Empty));
}

9. 테스트 실행 및 CI/CD 통합

유닛 테스트는 코드 변경시 빠른 피드백을 제공하므로, 지속적인 통합(CI) 및 지속적인 배포(CD) 파이프라인에 통합하는 것이 좋습니다. GitHub Actions, Azure DevOps, Jenkins와 같은 CI/CD 도구를 활용하여 코드를 푸시할 때마다 자동으로 모든 유닛 테스트를 실행할 수 있습니다.

10. 마무리

MVVM 패턴에서 ViewModel의 유닛 테스트는 애플리케이션의 품질과 유지보수성을 높이는 데 매우 중요합니다. 이번 글을 통해 유닛 테스트의 개념, ViewModel에 대한 유닛 테스트 작성 방법, 모의 객체 사용 및 다양한 테스트 시나리오를 다루는 방법을 배웠습니다. 지속적인 테스트 및 CI/CD 통합을 통해 코드 품질을 지속적으로 유지하고, 향후 수정을 더욱 쉽게 수행하시길 바랍니다.

11. 추가 리소스

[MVVM] 5.MVVM과 .NET 6 7에서의 최신 WPF 기능 통합, .NET 6 7에서 WPF 최적화 및 성능 향상 기능

Windows Presentation Foundation(WPF)는 강력한 UI 프레임워크로, MVVM(Model-View-ViewModel) 아키텍처 패턴을 통해 개발자가 직관적으로 애플리케이션의 비즈니스 로직과 UI를 분리할 수 있게 해줍니다. 최근의 .NET 6 및 .NET 7 릴리스는 WPF 개발자에게 여러 새로운 기능과 성능 개선 사항을 제공하여, 현대 웹 애플리케이션과 비즈니스 요구에 부합하는 애플리케이션을 만드는 데 도움을 줍니다.

1. MVVM 아키텍처의 이해

MVVM 패턴은 Model, View, ViewModel 세 가지 구성 요소로 이루어져 있으며, 각각의 역할은 다음과 같습니다:

  • Model: 애플리케이션의 데이터와 비즈니스 로직을 관리합니다.
  • View: 사용자 인터페이스 요소를 구성하며, UI와 관련된 작업을 수행합니다.
  • ViewModel: Model과 View 간의 중재 역할을 수행하며, 데이터 바인딩을 통해 UI의 상태를 관리합니다.

2. .NET 6/7에서의 WPF 발전

.NET 6 및 .NET 7에서는 WPF의 성능 및 개발 경험을 크게 개선하기 위해 여러 가지 기능이 도입되었습니다. 이들 업데이트는 특히 MVVM 패턴으로 작업하는 개발자들에게 유용합니다.

2.1. .NET MAUI 통합

.NET MAUI(Multi-platform App UI)는 WPF 및 Xamarin.Forms의 기능을 통합하여 크로스 플랫폼 애플리케이션 개발을 지원합니다. .NET 6부터는 기존 WPF 애플리케이션을 MAUI로 쉽게 마이그레이션할 수 있으며, 이는 MVVM 패턴을 유지하는 데 큰 장점입니다. 필요한 경우, 개발자는 기존 프로덕션 코드를 재활용할 수 있습니다.

2.2. 성능 개선

.NET 6 및 .NET 7의 새로운 성능 최적화 도구는 WPF 응용 프로그램의 렌더링 및 데이터 바인딩 속도를 크게 향상시켰습니다. 이러한 성능 향상은 대량의 데이터를 처리하는 MVVM 구조에 특히 중요합니다. 개선된 데이터 바인딩은 UI 업데이트를 더 효율적으로 만들어 개발자는 보다 빠른 반응성을 가진 UI를 구축할 수 있습니다.

2.3. XAML Improvements

최신 .NET 버전에서 XAML은 더욱 효율적으로 재구성되었습니다. 새로운 구문과 확장된 기능으로 개발자들은 UI 요소를 더욱 쉽게 구성하고 관리할 수 있습니다. 새로운 XAML 문법은 프로퍼티 및 이벤트의 선언을 단순화하며, MVVM 구현 시 코드의 가독성과 유지 보수성을 향상시킵니다.

2.4. Hot Reload

Hot Reload는 개발자가 애플리케이션을 다시 빌드하지 않고도 UI를 실시간으로 수정할 수 있게 해주는 기능입니다. 이는 MVVM 구조에서 특히 유용하며, ViewModel 코드 변경 사항에 즉시 피드백을 받을 수 있습니다. 개발자는 더 빠르게 반복하고 실험할 수 있게 되며, 사용자 경험을 개선할 수 있습니다.

3. .NET 6/7에서의 WPF 최적화 및 성능 향상 기능

WPF 애플리케이션은 성능이 중요한 비즈니스 요구 사항을 수용합니다. 최근 .NET 버전에서는 여러 가지 최적화 기능이 추가되어 대규모 데이터 세트를 처리하는 성능을 향상시키고 있습니다.

3.1. Virtualization

뷰에서 표시되는 항목만 렌더링하고 나머지는 생략하는 Virtualization 기술은 대량의 데이터를 처리하는 WPF 애플리케이션에서 기능적으로 중요합니다. 새로운 WPF 기능은 Virtualization을 더욱 고도화하여, 스크롤링 성능과 메모리 효율성을 개선했습니다. 다음은 Virtualization을 적용한 ListView 예제입니다:

        
            <ListView ItemsSource="{Binding Items}" VirtualizingStackPanel.IsVirtualizing="True">
                <ListView.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}" />
                    </DataTemplate>
                </ListView.ItemTemplate>
            </ListView>
        
    

3.2. Async Data Loading

WPF에서 비동기 데이터 로딩을 지원하는 다른 최적화 접근법은 응용 프로그램의 UI가 데이터 로딩으로 인한 지연 없이 원활하게 작동할 수 있게 해줍니다. 비동기 패턴을 사용하여 ViewModel에서 데이터를 로드하는 예는 다음과 같습니다:

        
            public async Task LoadDataAsync()
            {
                Items = await DataService.GetItemsAsync();
                OnPropertyChanged(nameof(Items));
            }
        
    

3.3. Improved Binding Performance

.NET 6/7에서 WPF의 바인딩 성능이 크게 향상되었습니다. 이제 바인딩 성능을 개선하기 위해 새로운 최적화된 알고리즘이 사용됩니다. 이를 통해 MVVM 구현에서 View와 ViewModel 간의 상호작용이 더욱 부드럽고 성능에 영향이 적도록 설계되었습니다.

3.4. 새로운 그래픽 API

WPF는 Direct2D 및 DirectWrite와 같은 최신 그래픽 API를 지원하여 고화질 이미지 및 텍스트 렌더링을 제공합니다. 이를 통해 애플리케이션의 비주얼 품질을 높이고, 그래픽 성능을 극대화할 수 있습니다. With .NET 6, 고해상도 디스플레이를 위한 UI 개선이 이뤄졌습니다.

4. MVVM 구조를 활용한 예제 애플리케이션

실제로 어떤 애플리케이션에서 MVVM 패턴과 .NET 6/7의 기능을 어떻게 활용할 수 있는지를 보여주는 완전한 예제를 살펴보겠습니다. 다음은 간단한 TODO List 애플리케이션을 구현하는 과정입니다.

4.1. Model

        
            public class TodoItem
            {
                public string Title { get; set; }
                public bool IsCompleted { get; set; }
            }
        
    

4.2. ViewModel

        
            public class TodoViewModel : INotifyPropertyChanged
            {
                private ObservableCollection<TodoItem> _items;

                public ObservableCollection<TodoItem> Items
                {
                    get { return _items; }
                    set
                    {
                        _items = value;
                        OnPropertyChanged(nameof(Items));
                    }
                }

                public TodoViewModel()
                {
                    Items = new ObservableCollection<TodoItem>();
                    LoadData();
                }

                private void LoadData()
                {
                    // Data Loading Logic
                }

                public event PropertyChangedEventHandler PropertyChanged;
                protected void OnPropertyChanged(string propertyName)
                {
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        
    

4.3. View (XAML)

        
            <Window x:Class="TodoApp.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                Title="Todo List" Height="350" Width="525">
                <Grid>
                    <ListView ItemsSource="{Binding Items}" VirtualizingStackPanel.IsVirtualizing="True">
                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal">
                                    <CheckBox IsChecked="{Binding IsCompleted}" />
                                    <TextBlock Text="{Binding Title}" Margin="5,0" />
                                </StackPanel>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </Grid>
            </Window>
        
    

5. 결론

WPF를 사용한 MVVM 패턴은 현대 애플리케이션 개발에서 매우 중요한 요소입니다. .NET 6 및 7의 새로운 기능을 통해 개발자는 더욱 빠르고, 효율적이며, 사용자 친화적인 애플리케이션을 만들어낼 수 있습니다. 이러한 최신 업데이트들은 WPF 개발을 더욱 매력적으로 만들어, 다양한 비즈니스 요구를 충족할 수 있는 솔루션을 제시합니다.