반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#WPF에서 MVVM패턴으로 
프로그래밍 할때 DataContext
에 대해 자세히
알아보자.

 

 

반응형
내용 요약

 

C# WPF에서 MVVM 패턴을 사용할 때, View와 ViewModel 간의 DataContext 설정은 매우 중요한 부분이다. MVVM(Model-View-ViewModel) 패턴은 UI와 비즈니스 로직을 분리하여 유지보수성과 테스트 용이성을 높이는 데 도움을 주는데, 이 글에서는 DataContext를 설정하는 다양한 방법에 대해 설명하도록 하겠다.

 

목차
1. 기본적인 DataContext 설정
2. 코드 비하인드에서 DataContext 설정
3. Dependency Injection을 통한 DataContext 설정
4. ViewModelLocator 패턴
5. 구조체와 클래스의 DataContext 설정
6. ViewModel의 속성 변경 알림
7. ViewModel의 생성자 매개변수
8. ViewModel의 명령
9. XAML에서 명령 바인딩

 

1. 기본적인 DataContext 설정

 

WPF에서 View와 ViewModel 간의 연결은 주로 DataContext 속성을 통해 이루어진다. DataContext는 View가 바인딩할 데이터의 소스를 지정한다. 가장 간단한 방법은 XAML에서 직접 설정하는 것이다.

 

<예제 1: XAML에서 DataContext 설정>

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <local:MyViewModel />
    </Window.DataContext>
    <Grid>
        <TextBox Text="{Binding MyProperty}" />
    </Grid>
</Window>

위의 예제에서 MyViewModel은 ViewModel 클래스이며, MyProperty는 ViewModel의 속성입니다. 이 방법은 View가 로드될 때 ViewModel의 인스턴스가 생성되고, DataContext가 설정된다.

 

2. 코드 비하인드에서 DataContext 설정

 

XAML에서 DataContext를 설정하는 대신, 코드 비하인드에서 설정할 수도 있다. 이 방법은 ViewModel의 인스턴스를 동적으로 생성해야 할 때 유용하다.

 

<예제 2: 코드 비하인드에서 DataContext 설정>

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MyViewModel();
    }
}

이 경우, MainWindow의 생성자에서 DataContext를 설정한다. 이 방법은 ViewModel의 생성자에 매개변수를 전달해야 할 때 유용하다.

 

3. Dependency Injection을 통한 DataContext 설정

 

Dependency Injection(DI)을 사용하여 ViewModel을 주입하는 방법도 있다.

이 방법은 특히 대규모 애플리케이션에서 유용하다.

 

<예제 3: DI를 통한 DataContext 설정>

public partial class MainWindow : Window
{
    private readonly IMyViewModel _viewModel;

    public MainWindow(IMyViewModel viewModel)
    {
        InitializeComponent();
        _viewModel = viewModel;
        this.DataContext = _viewModel;
    }
}

이 예제에서는 IMyViewModel 인터페이스를 통해 ViewModel을 주입받는다. DI 컨테이너를 사용하여 ViewModel의 인스턴스를 관리할 수 있다.

 

4. ViewModelLocator 패턴

 

ViewModelLocator 패턴을 사용하면 XAML에서 ViewModel을 쉽게 바인딩할 수 있다. 이 패턴은 ViewModel을 자동으로 찾고 설정하는 데 도움을 준다.

 

<예제 4: ViewModelLocator 설정>

public class ViewModelLocator
{
    public MyViewModel MyViewModel => new MyViewModel();
}

 

XAML에서 ViewModelLocator를 사용하여 DataContext를 설정할 수 있다.

<Window x:Class="MyApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyApp"
        Title="MainWindow" Height="350" Width="525"
        DataContext="{Binding MyViewModel, Source={StaticResource ViewModelLocator}}">
    <Grid>
        <TextBox Text="{Binding MyProperty}" />
    </Grid>
</Window>

 

 

5. 구조체와 클래스의 DataContext 설정

 

MVVM 패턴에서 ViewModel은 일반적으로 클래스 형태로 구현된다. 그러나 구조체를 사용할 수도 있다. 구조체는 값 타입이므로, 바인딩 시 주의가 필요하다.

 

<예제 5: 구조체를 ViewModel로 사용>

public struct MyViewModel
{
    public string MyProperty { get; set; }
}

 

구조체를 사용할 경우, 바인딩이 제대로 작동하지 않을 수 있다. 이는 구조체가 값 타입이기 때문에, 바인딩 시 변경 사항이 반영되지 않기 때문이다. 따라서, ViewModel은 클래스 형태로 구현하는 것이 좋다.

 

6. ViewModel의 속성 변경 알림

 

ViewModel의 속성이 변경될 때 UI에 알리기 위해 INotifyPropertyChanged 인터페이스를 구현해야 한다.

 

<예제 6: INotifyPropertyChanged 구현>

public class MyViewModel : INotifyPropertyChanged
{
    private string _myProperty;
    public string MyProperty
    {
        get => _myProperty;
        set
        {
            if (_myProperty != value)
            {
                _myProperty = value;
                OnPropertyChanged(nameof(MyProperty));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

이렇게 하면 MyProperty가 변경될 때 UI가 자동으로 업데이트됩니다.

 

7. ViewModel의 생성자 매개변수

 

ViewModel의 생성자에 매개변수를 전달하여 초기화할 수 있다. 이 경우, ViewModel을 생성할 때 필요한 데이터를 전달할 수 있다.

 

<예제 7: 생성자 매개변수 사용>

public class MyViewModel
{
    public MyViewModel(string initialValue)
    {
        MyProperty = initialValue;
    }

    public string MyProperty { get; set; }
}

이 경우, ViewModel을 생성할 때 초기값을 전달해야 한다.

8. ViewModel의 명령

 

MVVM 패턴에서는 UI의 동작을 ViewModel의 명령으로 처리한다. ICommand 인터페이스를 구현하여 명령을 정의할 수 있다.

 

<예제 8: 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 event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);

    public void Execute(object parameter) => _execute(parameter);
}

 

이제 ViewModel에서 명령을 정의할 수 있습니다.

public class MyViewModel : INotifyPropertyChanged
{
    public ICommand MyCommand { get; }

    public MyViewModel()
    {
        MyCommand = new RelayCommand(ExecuteMyCommand);
    }

    private void ExecuteMyCommand(object parameter)
    {
        // 명령 실행 로직
    }
}

 

9. XAML에서 명령 바인딩

 

XAML에서 명령을 바인딩하여 UI와 ViewModel 간의 상호작용을 구현할 수 있다.

 

<예제 9: XAML에서 명령 바인딩>

<Button Command="{Binding MyCommand}" Content="Click Me" />

이렇게 하면 버튼 클릭 시 ViewModel의 MyCommand가 실행된다

 


결론

 

WPF에서 MVVM 패턴을 사용할 때 View와 ViewModel 간의 DataContext 설정은 매우 중요하다. 다양한 방법으로 DataContext를 설정할 수 있으며, 각 방법은 특정 상황에 따라 유용하게 사용될 수 있다. ViewModel은 일반적으로 클래스 형태로 구현하며, INotifyPropertyChanged를 통해 UI와의 데이터 바인딩을 관리한다. 명령을 사용하여 UI의 동작을 ViewModel에서 처리할 수 있으며, XAML에서 쉽게 바인딩할 수 있다.

 

이러한 원칙을 따르면 WPF 애플리케이션을 보다 구조적이고 유지보수하기 쉽게 만들 수 있습니다. 따라서, MVVM 패턴을 잘 활용하여 효율적인 WPF 애플리케이션을 개발하시기 바랍니다.

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#WPF에서 MVVM패턴으로 
프로그래밍 할때 OnPropertyChanged
의 역할에 대해
알아보자.

 

 

반응형

 

내용 요약

 

MVVM (Model-View-ViewModel) 패턴은 WPF (Windows Presentation Foundation) 애플리케이션에서 데이터 바인딩을 통해 UI와 비즈니스 로직을 분리하는 데 사용된다. 이 패턴에서 INotifyPropertyChanged 인터페이스는 ViewModel이 속성의 변경을 View에 알리는 데 중요한 역할을 한다. 이 과정에서 OnPropertyChanged 메서드가 핵심적인 역할을 하는데, 이번 포스팅에서는 어떠한 역할을 하는지 알아보도록 하자.

 

목차
1.INotifyPropertyChanged 인터페이스
2.OnPropertyChanged 메서드
3.ViewModel의 속성 구현
4.XAML에서의 데이터 바인딩
5.예외 처리 및 성능 고려사항
6.예제: 복잡한 ViewModel
7.결론

 

1.INotifyPropertyChanged 인터페이스

 

INotifyPropertyChanged 인터페이스는 속성이 변경될 때 알림을 제공하는 메커니즘을 정의한다. 이 인터페이스는 다음과 같은 이벤트를 포함한다

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}

 

이벤트 PropertyChanged는 속성이 변경될 때 발생한다. ViewModel에서 속성이 변경되면 이 이벤트를 발생시켜 UI에 변경 사항을 알리게 된다.

 

2.OnPropertyChanged 메서드

 

OnPropertyChanged 메서드는 속성이 변경될 때 호출되어 PropertyChanged 이벤트를 발생시키는 역할을 한다. 일반적으로 ViewModel 클래스에서 다음과 같이 구현된다.

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

 

이 메서드는 propertyName 매개변수로 변경된 속성의 이름을 받아서 해당 속성이 변경되었음을 알린다. PropertyChanged 이벤트가 구독되어 있다면, 구독자에게 알림을 보낸다.

 

3.ViewModel의 속성 구현

 

ViewModel에서 속성을 구현할 때, 속성의 setter에서 OnPropertyChanged를 호출하여 속성이 변경되었음을 알린다. 예를 들어, 다음과 같은 ViewModel을 고려해 보자.

public class PersonViewModel : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

 

위의 예제에서 Name 속성의 setter는 _name의 값이 변경될 때만 OnPropertyChanged를 호출한다. 이렇게 하면 UI는 Name 속성이 변경되었음을 인식하고 자동으로 업데이트된다.

 

4.XAML에서의 데이터 바인딩

 

WPF에서는 XAML을 사용하여 UI를 정의하고, ViewModel의 속성과 UI 요소를 바인딩할 수 있다. 예를 들어, PersonViewModel을 사용하여 UI를 구성할 수 있다.

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MVVM Example" Height="200" Width="300">
    <Grid>
        <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
    </Grid>
</Window>

 

위의 XAML 코드에서 TextBox Text 속성은 Name 속성과 바인딩되어 있다. 사용자가 텍스트 박스에 입력할 때마다 Name 속성이 업데이트되고, OnPropertyChanged가 호출되어 UI가 자동으로 업데이트된다.

 

5.예외 처리 및 성능 고려사항

 

속성 변경 알림을 구현할 때는 몇 가지 고려사항이 있다.

  • 예외 처리: OnPropertyChanged 메서드에서 예외가 발생할 수 있으므로, 적절한 예외 처리를 구현하는 것이 좋다.
  • 성능: 많은 속성이 있는 ViewModel에서 모든 속성에 대해 OnPropertyChanged를 호출하면 성능에 영향을 줄 수 있으므로, 이 경우, 변경된 속성만 알리도록 최적화할 수 있다.
  •  
6.예제: 복잡한 ViewModel

 

복잡한 ViewModel에서는 여러 속성을 관리할 수 있다. 다음은 여러 속성을 가진 ViewModel의 예이다.

public class EmployeeViewModel : INotifyPropertyChanged
{
    private string _firstName;
    private string _lastName;
    private int _age;

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

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

    public int Age
    {
        get { return _age; }
        set
        {
            if (_age != value)
            {
                _age = value;
                OnPropertyChanged(nameof(Age));
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

 

이 ViewModel은 직원의 이름과 나이를 관리한다. 각 속성의 setter에서 OnPropertyChanged를 호출하여 UI에 변경 사항을 알린다

 

7.결론

 

OnPropertyChanged 메서드는 WPF MVVM 패턴에서 데이터 바인딩을 통해 UI와 ViewModel 간의 상호작용을 가능하게 하는 중요한 요소이다. 이를 통해 UI는 ViewModel의 상태를 자동으로 반영할 수 있으며, 개발자는 UI와 비즈니스 로직을 효과적으로 분리할 수 있다.

MVVM 패턴을 활용하면 유지보수성과 테스트 용이성이 향상되며, 더 나은 사용자 경험을 제공할 수 있다.

 

이와 같은 방식으로 OnPropertyChanged를 활용하여 WPF 애플리케이션을 개발하면, 데이터의 변경 사항을 UI에 실시간으로 반영할 수 있으므로 MVVM 패턴을 잘 이해하고 활용하도록 하자.

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

C#WPF에서 MVVM패턴으로 
프로그래밍 할때 Dependency Injection(DI)의
개념과 역할에 대해
알아보자.

 

반응형
내용 요약

 

MVVM (Model-View-ViewModel) 패턴은 WPF (Windows Presentation Foundation) 애플리케이션에서 UI와 비즈니스 로직을 분리하는 데 사용되는 아키텍처 패턴이다. 이 패턴은 코드의 재사용성과 테스트 용이성을 높이며, UI의 상태와 동작을 관리하는 ViewModel을 중심으로 구성된다. Dependency Injection (DI)은 이러한 MVVM 패턴을 구현할 때 매우 유용한 기법으로, 객체 간의 의존성을 관리하고, 코드의 결합도를 낮추는 데 도움을 준다.

 

목차
1.MVVM 패턴이란?
2.Dependency Injection 이란?
3.MVVM 패턴에서 Dependency Injection의 역할
4.WPF MVVM에서 DI 구현 예제
5.Dependency Injection 사용 시 주의할 점

 

1. MVVM 패턴이란?

 

MVVM(Model-View-ViewModel) 패턴은 WPF(Windows Presentation Foundation) 애플리케이션에서 자주 사용되는 아키텍처 패턴으로, 코드의 가독성과 유지보수성을 높이기 위해 설계되었다. MVVM은 세 가지 주요 컴포넌트로 구성된다.

 

Model: 애플리케이션의 데이터와 비즈니스 로직을 나타냄.

View: 사용자 인터페이스(UI) 요소를 담당하며, 데이터 바인딩(Data Binding)을 통해 ViewModel과 상호작용한다.

ViewModel: View와 Model 간의 중개 역할을 하며, View에 필요한 데이터를 제공하고 명령(Command)을 처리한다.

 

MVVM 패턴을 따를 때, View와 ViewModel은 느슨하게 결합되어야 하며, 이는 테스트 가능성과 확장성을 높이는 데 기여한다. 여기서 Dependency Injection(DI)은 ViewModel과 Model, 서비스 간의 의존성을 효과적으로 관리하는 데 중요한 역할을 한다.

 

2. Dependency Injection이란?

 

**Dependency Injection(DI)**는 객체 간의 의존성을 외부에서 주입하는 설계 패턴이다. 객체가 스스로 필요한 의존성을 생성하거나 관리하지 않고, 외부에서 주입받음으로써 코드의 유연성과 테스트 용이성을 높인다.

 

Dependency Injection의 핵심 개념

 

1. 의존성(Dependency): 한 클래스가 다른 클래스의 기능을 사용하려면 해당 클래스에 의존해야 한다. 예를 들어, ViewModel이 데이터 저장소를 사용해야 한다면, 이 저장소(Repository)가 ViewModel의 의존성이다.

2. 주입(Injection): 의존성을 직접 생성하지 않고 외부에서 필요한 객체를 주입한다.

3. 느슨한 결합(Loose Coupling): DI를 통해 클래스 간의 결합도를 낮추고, 변경 사항이 최소화되도록 설계한다.

 

DI의 종류

 

DI는 객체를 주입하는 방식에 따라 세 가지 유형으로 나뉜다.

생성자 주입(Constructor Injection): 의존성을 생성자의 매개변수로 전달.

속성 주입(Property Injection): 의존성을 객체의 속성을 통해 전달.

메서드 주입(Method Injection): 의존성을 메서드 호출 시 전달.

 

3. MVVM 패턴에서 Dependency Injection의 역할

 

WPF에서 MVVM 패턴을 사용할 때 Dependency Injection은 다음과 같은 역할을 한다:

1. ViewModel 생성 및 관리

DI는 ViewModel의 의존성을 주입하고 객체 생성을 관리한다.

이를 통해 ViewModel의 독립성을 유지하며, 단위 테스트가 용이해진다.

2. 서비스 및 데이터 액세스 계층의 주입

데이터 액세스 계층(예: Repository 패턴) 또는 서비스 클래스(예: API 호출)를 ViewModel에 주입하여 분리된 로직을 재사용할 수 있다.

3. 전역 상태 관리

DI 컨테이너를 사용하면 애플리케이션 전체에서 공유해야 하는 전역 서비스(예: 설정, 인증 상태)를 관리할 수 있다.

 

4. WPF MVVM에서 DI 구현 예제

 

다음은 DI를 사용하여 MVVM 패턴을 구현하는 간단한 예제이다.

 

(1) 프로젝트 설정

 

1. NuGet 패키지 설치

Microsoft.Extensions.DependencyInjection 패키지를 설치하여 DI 컨테이너를 사용한다.

 

(2) Model 정의

public class DataModel
{
    public string GetData() => "Hello, Dependency Injection!";
}

 

(3) Service Interface 정의

 

public interface IDataService
{
    string FetchData();
}

public class DataService : IDataService
{
    private readonly DataModel _dataModel;
    public DataService(DataModel dataModel)
    {
        _dataModel = dataModel;
    }
    public string FetchData() => _dataModel.GetData();
}

 

(4) ViewModel 정의

 

using System.ComponentModel;

public class MainViewModel : INotifyPropertyChanged
{
    private readonly IDataService _dataService;
    private string _displayData;
    public string DisplayData
    {
        get => _displayData;
        set
        {
            _displayData = value;
            OnPropertyChanged(nameof(DisplayData));
        }
    }

    public MainViewModel(IDataService dataService)
    {
        _dataService = dataService;
        LoadData();
    }

    private void LoadData()
    {
        DisplayData = _dataService.FetchData();
    }

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

 

(5) DI(의존성 주입) 컨테이너 설정

 

using Microsoft.Extensions.DependencyInjection;

public partial class App : Application
{
    public static ServiceProvider ServiceProvider { get; private set; }
    protected override void OnStartup(StartupEventArgs e)
    {
        var services = new ServiceCollection();

        // DI 등록
        services.AddSingleton<DataModel>();
        services.AddSingleton<IDataService, DataService>();
        services.AddSingleton<MainViewModel>();
        ServiceProvider = services.BuildServiceProvider();

        var mainWindow = new MainWindow
        {
            DataContext = ServiceProvider.GetRequiredService<MainViewModel>()
        };
        
        mainWindow.Show();
        base.OnStartup(e);
    }
}

 

(6) View (XAML) 정의

 

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Dependency Injection Example" Height="200" Width="400">
    <Grid>
        <TextBlock Text="{Binding DisplayData}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20"/>
    </Grid>
</Window>

 

5. Dependency Injection 사용 시 주의할 점

 

1. 과도한 의존성 주입

너무 많은 의존성을 주입하면 코드가 복잡해지고 관리가 어려워질 수 있으므로 필요한 최소한의 의존성만 주입하도록 설계해야 한다.

2. 서비스 수명 주기 관리

DI 컨테이너를 사용할 때 객체의 수명 주기(Transient, Scoped, Singleton)를 신중히 선택해야 한다. WPF에서는 대부분 Singleton이 적합하지만, 특정 경우에는 Transient가 유용할 수 있다.

3. DI 컨테이너에 과도한 의존

DI 컨테이너에 모든 것을 등록하려다 보면 의존성 주입의 장점이 감소할 수 있으니 중요한 의존성만 컨테이너에 등록해야 한다.

4. 유닛 테스트와 DI

DI는 유닛 테스트를 쉽게 만들어주지만, Mock 객체를 올바르게 사용하지 않으면 테스트가 복잡해질 수 있다. 인터페이스 기반 설계와 Mocking 라이브러리(예: Moq)를 활용하자.

5. 초기 설정 복잡성

DI를 도입하면 초기 설정이 복잡해질 수 있으므로, 프로젝트 초기 단계에서 DI를 도입할지 여부를 신중히 결정하자.

 


결론

 

WPF에서 MVVM 패턴을 사용할 때 Dependency Injection은 클래스 간의 결합도를 낮추고, 테스트 가능성과 유지보수성을 높이는 강력한 도구이다. DI는 ViewModel, Model, 서비스 계층 간의 의존성을 명확히 관리하고, 전역 상태를 효율적으로 다룰 수 있도록 돕는다. 하지만 과도한 의존성 주입을 피하고 서비스 수명 주기를 신중히 설계하여 복잡성을 최소화하는 것이 중요하다.

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#WPF에서 MVVM패턴으로 
프로그래밍 할때 ObservableCollection의
개념과 역할에 대해
알아보자.

 

반응형
내용 요약

 

ObservableCollection은 WPF에서 데이터 바인딩을 활용하는 데 매우 중요한 클래스이다. 이 클래스는 컬렉션의 변경 사항을 View(UI)에 자동으로 반영하도록 설계되었다. System.Collections.ObjectModel 네임스페이스에 포함되어 있으며, 컬렉션에 요소를 추가하거나 제거할 때 이를 알리는 기능을 제공한다.

 

목차
1.ObservableCollection이란 무엇인가?
2.ObservableCollection의 주요 특징
3.일반 컬렉션과의 차이점
4.ObservableCollection의 사용 사례
5.ObservableCollection의 구현 예제
   - 기본 사용법
   - 데이터 바인딩과의 통합
   - 컬렉션 변경 알림 처리
   - 필터링 및 정렬 적용
6.ObservableCollection과 INotifyCollectionChanged
7.ObservableCollection 사용 시 주의점
8.ObservableCollection의 확장과 활용

 

 

1.ObservableCollection이란 무엇인가?

 

ObservableCollection<T>는 컬렉션이 변경될 때(예: 요소 추가, 제거, 전체 초기화) 변경 알림을 발생시켜 UI와 데이터의 동기화를 유지하도록 돕는 클래스이다. WPF의 데이터 바인딩 시 컬렉션 변경 사항이 자동으로 UI에 반영되도록 하기 위해 주로 사용된다

namespace System.Collections.ObjectModel
{
    public class ObservableCollection<T> : Collection<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        // 생성자 및 메서드 정의
    }
}

 

2.ObservableCollection의 주요 특징

 

  1. 자동 UI 업데이트
    컬렉션의 데이터가 변경되면 변경 사항이 UI에 자동으로 반영된다.
  2. INotifyCollectionChanged 인터페이스 구현
    컬렉션의 변경 사항(추가, 삭제, 갱신)을 알리기 위해 CollectionChanged 이벤트를 제공한다.
  3. INotifyPropertyChanged 인터페이스 구현
    컬렉션의 속성 변경 사항을 알리기 위해 PropertyChanged 이벤트를 제공한다.
  4. 다양한 컬렉션 작업 지원
    기본적인 추가(Add), 제거(Remove), 삽입(Insert), 정리(Clear) 작업을 제공한다.

 

3.일반 컬렉션과의 차이점

4.ObservableCollection의 사용 사례

 

  1. 데이터 바인딩
    UI에서 실시간으로 변경 사항을 반영해야 할 때 유용하다.
  2. 동적 목록
    사용자 인터페이스에서 동적으로 항목을 추가하거나 제거할 때 사용된다.
  3. MVVM 패턴에서 컬렉션 관리
    ViewModel에서 데이터를 관리할 때 주요한 역할을 한다.
5.ObservableCollection의 구현 예제

 

기본 사용법

ObservableCollection은 컬렉션에 데이터를 추가하거나 제거하는 데 매우 직관적이다.

using System.Collections.ObjectModel;

public class MainViewModel
{
    public ObservableCollection<string> Names { get; set; }

    public MainViewModel()
    {
        Names = new ObservableCollection<string>
        {
            "Alice",
            "Bob",
            "Charlie"
        };
    }

    public void AddName(string name)
    {
        Names.Add(name);
    }

    public void RemoveName(string name)
    {
        Names.Remove(name);
    }
}

 

데이터 바인딩과의 통합

ObservableCollection은 데이터 바인딩을 활용할 때 매우 강력하다.

 

<XAML 코드>

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="ObservableCollection Demo" Height="300" Width="400">
    <Grid>
        <StackPanel>
            <ListBox ItemsSource="{Binding Names}" />
            <TextBox x:Name="NameInput" Width="200" Margin="10" />
            <Button Content="Add Name" Command="{Binding AddNameCommand}" CommandParameter="{Binding Text, ElementName=NameInput}" />
        </StackPanel>
    </Grid>
</Window>

 

<ViewModel 코드>

using System.Collections.ObjectModel;
using System.Windows.Input;

public class MainViewModel
{
    public ObservableCollection<string> Names { get; set; }
    public ICommand AddNameCommand { get; }

    public MainViewModel()
    {
        Names = new ObservableCollection<string> { "Alice", "Bob", "Charlie" };

        AddNameCommand = new RelayCommand(AddName);
    }

    private void AddName(object parameter)
    {
        if (parameter is string name && !string.IsNullOrWhiteSpace(name))
        {
            Names.Add(name);
        }
    }
}

 

컬렉션 변경 알림 처리

컬렉션 변경 시 이벤트를 수신하여 추가 작업을 수행할 수도 있다.

Names.CollectionChanged += (s, e) =>
{
    switch (e.Action)
    {
        case NotifyCollectionChangedAction.Add:
            Console.WriteLine("Item added");
            break;
        case NotifyCollectionChangedAction.Remove:
            Console.WriteLine("Item removed");
            break;
    }
};

 

필터링 및 정렬 적용

WPF에서는 기본적으로 ObservableCollection에 필터링과 정렬 기능이 없다. 이를 구현하려면 CollectionView를 사용해야 한다.

using System.ComponentModel;
using System.Windows.Data;

public ICollectionView FilteredNames { get; set; }

public MainViewModel()
{
    Names = new ObservableCollection<string> { "Alice", "Bob", "Charlie" };
    FilteredNames = CollectionViewSource.GetDefaultView(Names);

    FilteredNames.Filter = item => item.ToString().StartsWith("A");
}

 

6.ObservableCollection과 INotifyCollectionChanged

 

ObservableCollection은 INotifyCollectionChanged 인터페이스를 구현하여 컬렉션 변경 사항을 알린다.

public interface INotifyCollectionChanged
{
    event NotifyCollectionChangedEventHandler CollectionChanged;
}

이 이벤트는 Add, Remove, Replace 등의 작업에 반응하며, 변경 내용을 전달한다.

 

7.ObservableCollection 사용 시 주의점

 

스레드 안전성
ObservableCollection은 UI 스레드에서만 안전하게 사용할 수 있다. 백그라운드 스레드에서 작업하려면 Dispatcher를 사용해야 한다.

Application.Current.Dispatcher.Invoke(() => Names.Add("New Name"));

 

 

성능
많은 데이터 변경이 있을 경우 성능 문제가 발생할 수 있다. 이 경우 CollectionChanged 이벤트를 비활성화하거나 List<T>로 작업 후 한 번에 업데이트하는 방법을 고려할 수 있다.

 

필터링 및 정렬
기본적으로 필터링과 정렬을 지원하지 않으므로, CollectionView를 사용하는 것이 필요하다.

 

 

8.ObservableCollection의 확장과 활용

 

ObservableCollection을 상속받아 고유한 동작을 추가할 수 있다

public class CustomObservableCollection<T> : ObservableCollection<T>
{
    protected override void InsertItem(int index, T item)
    {
        base.InsertItem(index, item);
        Console.WriteLine($"{item} added at index {index}");
    }

    protected override void RemoveItem(int index)
    {
        Console.WriteLine($"{this[index]} removed from index {index}");
        base.RemoveItem(index);
    }
}

 


결론 및 개인적인 생각

 

ObservableCollection은 WPF MVVM 패턴에서 필수적인 도구로, 데이터와 UI를 동기화하는 데 강력한 기능을 제공한다. 특히, 실시간 업데이트와 바인딩을 결합하여 사용자 경험을 크게 향상시킬 수 있다.

 

개인적으로, WPF 프로젝트를 처음 시작할 때 List<T>로 바인딩 문제를 겪은 후 ObservableCollection을 알게 되었고, 이를 통해 데이터 변경 사항이 즉시 UI에 반영되는 과정을 경험하며 큰 만족감을 느꼈다. 그러나 성능 최적화와 스레드 안전성 같은 추가적인 요소도 항상 염두에 두어야 한다고 생각하며, WPF를 배우는 모든 개발자가 반드시 익혀야 할 개념 중 하나라고 확신한다.

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#WPF에서 MVVM패턴으로 
프로그래밍 할때 ICommand
인터페이스의 역할에 대해
알아보자.
반응형
내용 요약

 

MVVM 패턴에서 ICommand 인터페이스는 UI의 이벤트(예: 버튼 클릭)를 ViewModel로 전달하는 중요한 역할을 한다. 이는 MVVM 패턴의 핵심 원칙인 View와 ViewModel 간의 분리를 실현하는 데 필수적이다. 이번 글에서는 ICommand의 개념과 활용 방법, 그리고 실제 구현 사례를 통해 이를 자세히 알아보도록 하자.

 

목차

1.ICommand 인터페이스란 무엇인가?
2.ICommand의 주요 구성 요소
3.ICommand의 구현 방법
4.RelayCommand(DelegateCommand)의 활용
5.Command와 Data Binding의 연결
6.ICommand 사용 예제
   - 기본 구현
   - 파라미터 전달
   - CanExecute와 UI 상태 관리
7.ICommand와 MVVM의 관계
8.ICommand 구현의 장점과 한계

 

 

1.ICommand 인터페이스란 무엇인가?

 

ICommand는 WPF의 System.Windows.Input 네임스페이스에 정의된 인터페이스로, 버튼 클릭, 메뉴 항목 선택과 같은 사용자 입력을 처리하기 위해 사용된다. 이 인터페이스는 UI에서 발생한 이벤트를 ViewModel의 메서드로 연결할 수 있는 방법을 제공다.

 

public interface ICommand
{
    event EventHandler CanExecuteChanged;

    bool CanExecute(object parameter);
    void Execute(object parameter);
}

 

핵심 목적:

  • UI 이벤트를 처리하는 로직을 ViewModel에 두어 View와 ViewModel 간의 강한 결합을 방지함.
  • Binding을 통해 명령을 연결하여 코드 숨김(Code-Behind)을 최소화함.

 

2.ICommand의 주요 구성 요소

 

 

  • Execute 메서드
    • 명령 실행 시 호출되는 메서드
    • 예를 들어, 버튼이 클릭되었을 때의 동작을 정의함.
  • CanExecute 메서드
    • 명령 실행 가능 여부를 결정하는 메서드.
    • 반환값이 true일 때만 명령이 실행됨.
    • UI 요소(예: 버튼)의 활성화 상태를 제어함.
  • CanExecuteChanged 이벤트
    • CanExecute의 반환값이 변경될 때 UI 요소에 알림.
    • 일반적으로 ViewModel에서 상태 변화가 발생하면 호출됨.

 

 

3.ICommand의 구현 방법

 

ICommand 인터페이스를 직접 구현하려면 다음과 같이 작성한다.

using System;
using System.Windows.Input;

public class CustomCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged;

    public CustomCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

 

4.RelayCommand(DelegateCommand)의 활용

 

ICommand의 구현은 반복적이고 번거로울 수 있다. 따라서 이를 간소화하기 위해 RelayCommand 또는 DelegateCommand를 사용하는 것이 일반적이다. RelayCommand는 다음과 같이 정의된다.

public class RelayCommand : ICommand
{
    private readonly Action<object> _execute;
    private readonly Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged;

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

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }

    public void RaiseCanExecuteChanged()
    {
        CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    }
}

 

5.Command와 Data Binding의 연결

 

View에서 Command를 바인딩하여 버튼 클릭 등의 UI 동작을 ViewModel로 연결할 수 있다.

<Button Content="Click Me" Command="{Binding MyCommand}" />

 

public class MyViewModel
{
    public ICommand MyCommand { get; }

    public MyViewModel()
    {
        MyCommand = new RelayCommand(ExecuteCommand, CanExecuteCommand);
    }

    private void ExecuteCommand(object parameter)
    {
        // 버튼 클릭 시 실행할 로직
        MessageBox.Show("Button Clicked!");
    }

    private bool CanExecuteCommand(object parameter)
    {
        // 버튼 활성화 여부
        return true;
    }
}

 

6.ICommand 사용 예제

기본 구현

1.ViewModel에서 Command 정의

public ICommand SaveCommand { get; }

public MyViewModel()
{
    SaveCommand = new RelayCommand(Save, CanSave);
}

private void Save(object parameter)
{
    // 저장 로직
    Console.WriteLine("Data Saved!");
}

private bool CanSave(object parameter)
{
    // 조건: 데이터를 저장할 수 있는지 여부
    return !string.IsNullOrEmpty(SomeData);
}

 

2.XAML에서 바인딩

<Button Content="Save" Command="{Binding SaveCommand}" />

 

파라미터 전달

CommandParameter 속성을 사용하여 추가 데이터를 전달할 수 있습니다.

 

<XAML 코드>

<Button Content="Delete" Command="{Binding DeleteCommand}" CommandParameter="{Binding SelectedItem}" />

 

<ViewModel 코드>

public ICommand DeleteCommand { get; }

public MyViewModel()
{
    DeleteCommand = new RelayCommand(Delete, CanDelete);
}

private void Delete(object parameter)
{
    var item = parameter as MyItem;
    if (item != null)
    {
        Items.Remove(item);
    }
}

private bool CanDelete(object parameter)
{
    return parameter is MyItem;
}

 

7.ICommand와 MVVM의 관계

 

 

ICommand는 MVVM의 원칙인 관심사의 분리를 실현한다.

모든 로직(ViewModel에 위치)은 테스트 가능하며, UI 이벤트(View에 위치)는 ViewModel에 종속되지 않는다.

 

 

8.ICommand 구현의 장점과 한계

 

장점

View와 ViewModel의 강한 결합 제거: 로직이 ViewModel에 있어 재사용성과 유지보수성이 향상됩니다.

테스트 가능성: 명령 로직을 단위 테스트할 수 있습니다.

UI 상태 관리: CanExecute를 통해 UI의 활성화 상태를 동적으로 제어할 수 있습니다.

한계

구현 시 코드가 장황해질 수 있음. 이를 완화하기 위해 RelayCommand를 활용합니다.

복잡한 명령 체계에서는 추가적인 관리 코드가 필요합니다.

 


결론 및 개인적인 생각

 

ICommand는 WPF MVVM 패턴에서 사용자 입력을 처리하는 강력한 도구입니다. 이를 올바르게 구현하면 View와 ViewModel 간의 결합을 줄이고, 재사용 가능한 구조를 설계할 수 있습니다.

 

개인적으로, 처음에는 ICommand의 복잡함에 좌절했지만, RelayCommand 같은 도구를 활용하면서 효율적으로 코드를 작성할 수 있게 되었다. 특히, UI 상태를 동적으로 관리할 수 있는 기능은 사용자 경험을 향상시키는 데 매우 유용하다고 느껴졌다. ICommand를 이해하고 활용한다면 MVVM 패턴의 강점을 더욱 잘 느낄 수 있을 것이다.

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#WPF에서 MVVM패턴으로 
프로그래밍 할때 InotifyPropertyChanged
인터페이스의 역할에 대해
알아보자.

 

반응형
내용 요약

 

C# WPF에서 MVVM 패턴을 사용할 때, 

INotifyPropertyChanged 인터페이스는

데이터 바인딩을 지원하는 중요한 역할을 한다.

이 인터페이스는 속성 값이 변경될 때 알림을 제공하여

UI가 자동으로 업데이트되도록 한다.

이제 INotifyPropertyChanged가 작동하는 방식과

이벤트 발생에서 실제 함수가 호출되기까지의 흐름에 대해 자세히 알아보자.

 

설명 및 예시

INotifyPropertyChanged 인터페이스 개요

 

INotifyPropertyChanged 인터페이스는 단순히 하나의 이벤트를 정의한다.

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}

 

이 인터페이스를 구현하는 클래스는 PropertyChanged 이벤트를 발생시켜야 한다.

이 이벤트는 속성 값이 변경될 때마다 호출되며,

변경된 속성의 이름을 이벤트 인수로 전달한다.

 


PropertyChangedEventHandler 대리자

 

PropertyChanged 이벤트는 PropertyChangedEventHandler 대리자를 사용하며,

이 대리자는 다음과 같이 정의된다.

public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);

 

 

PropertyChangedEventArgs 클래스는 변경된 속성의 이름을 포함한다.

public class PropertyChangedEventArgs : EventArgs
{
    public PropertyChangedEventArgs([CallerMemberName] string propertyName = null)
    {
        PropertyName = propertyName;
    }

    public virtual string PropertyName { get; }
}

 


 

INotifyPropertyChanged 구현

 

INotifyPropertyChanged 인터페이스를 구현하는 클래스는 

PropertyChanged 이벤트를 발생시키는 메서드를 포함해야 한다.

일반적으로 OnPropertyChanged라는 메서드를 정의하여 이를 처리한다.

 

다음은 INotifyPropertyChanged를 구현한 예제이다.

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class Person : INotifyPropertyChanged
{
    private string _name;
    private int _age;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get => _name;
        set
        {
            if (_name != value)
            {
                _name = value;
                OnPropertyChanged();
            }
        }
    }

    public int Age
    {
        get => _age;
        set
        {
            if (_age != value)
            {
                _age = value;
                OnPropertyChanged();
            }
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

 


이벤트 발생에서 함수 호출까지의 흐름

 

1.속성 값 변경
Name 또는 Age 속성의 값이 변경되면, set 접근자에서 OnPropertyChanged 메서드가 호출된다.

 

2.OnPropertyChanged 메서드 호출

OnPropertyChanged 메서드는 PropertyChanged 이벤트를 발생시킨다.

이 메서드는 CallerMemberName 특성을 사용하여 호출된 속성의 이름을 자동으로 가져온다.


3.PropertyChanged 이벤트 발생

PropertyChanged 이벤트가 발생하면, 이 이벤트에 등록된 모든 이벤트 핸들러가 호출된다.

이벤트 핸들러는 PropertyChangedEventArgs 객체를 통해 변경된 속성의 이름을 전달받는다.


4.UI 업데이트

WPF 데이터 바인딩 시스템은 PropertyChanged 이벤트를 수신하고, 
변경된 속성의 이름을 확인하여 해당 속성에 바인딩된 UI 요소를 업데이트한다.

 


예제 코드

다음은 Person 클래스의 인스턴스를 ViewModel로 사용하여 UI를 업데이트하는 예제이다.

 

# ViewModel
using System.ComponentModel;

public class PersonViewModel : INotifyPropertyChanged
{
    private Person _person;

    public event PropertyChangedEventHandler PropertyChanged;

    public PersonViewModel()
    {
        _person = new Person { Name = "John Doe", Age = 30 };
    }

    public string Name
    {
        get => _person.Name;
        set
        {
            if (_person.Name != value)
            {
                _person.Name = value;
                OnPropertyChanged();
            }
        }
    }

    public int Age
    {
        get => _person.Age;
        set
        {
            if (_person.Age != value)
            {
                _person.Age = value;
                OnPropertyChanged();
            }
        }
    }

    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
# View (XAML)
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="400">
    <Grid>
        <StackPanel>
            <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Width="200" Margin="10"/>
            <TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}" Width="200" Margin="10"/>
        </StackPanel>
    </Grid>
</Window>
# View Code-Behind
using System.Windows;

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        DataContext = new PersonViewModel();
    }
}

 


설명
1.ViewModel

PersonViewModel 클래스는 Person 객체를 포함하고 있으며,

Name과 Age 속성을 통해 Person 객체의 속성에 접근한다.

속성 값이 변경될 때마다 OnPropertyChanged 메서드를 호출하여 PropertyChanged 이벤트를 발생시킨다.


2.View

XAML 파일에서 TextBox의 Text 속성을 Name과 Age 속성에 바인딩한다.

UpdateSourceTrigger=PropertyChanged를 사용하여 속성 값이 변경될 때마다 즉시 업데이트되도록 한다.


3.View Code-Behind

MainWindow 클래스의 생성자에서 DataContext를 PersonViewModel 인스턴스로 설정한다.


이 예제는 INotifyPropertyChanged 인터페이스를 사용하여 

속성 값이 변경될 때 UI가 자동으로 업데이트되는 과정을 보여준다.

이를 통해 MVVM 패턴에서 데이터 바인딩을 효율적으로 구현할 수 있다.

반응형
반응형
해당 포스팅은 개인적으로 C# WPF를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C#WPF 프로그래밍에서 
Thread와 비동기
프로그래밍에 대해
알아보자.

 

반응형
내용 요약

 

C# WPF (Windows Presentation Foundation)에서 MVVM (Model-View-ViewModel) 패턴을 사용할 때, 스레드(Thread)와 비동기 프로그래밍은 매우 중요한 개념이다. WPF는 UI 스레드와 백그라운드 스레드를 구분하여 UI의 응답성을 유지하면서도 긴 작업을 수행할 수 있도록 한다. 이 글에서는 MVVM 패턴에서 스레드를 사용하는 기본적인 내용과 예시를 통해 알아보도록 하자.

 

목차
1.MVVM 패턴 개요
2.Thread의 필요성
3.Thread 사용 예시
4.비동기 프로그래밍
5.UI 업데이트
6.결론
1.MVVM 패턴 개요

MVVM 패턴은 WPF 애플리케이션에서

UI와 비즈니스 로직을 분리하는 데 도움을 준다.

이 패턴은 다음과 같은 세 가지 구성 요소로 나뉘어 진다.

 

1.Model: 데이터와 비즈니스 로직을 포함한다.

2.View: 사용자 인터페이스(UI)를 정의한다.

3.ViewModel: View와 Model 간의 상호작용을 관리한다.

 

ViewModel은 데이터 바인딩을 통해 View와 연결된다.

 

2.Thread의 필요성

 

WPF 애플리케이션은 UI 스레드에서 실행된다.

긴 작업(예: 데이터베이스 쿼리, 파일 I/O 등)을 UI 스레드에서 실행하면 UI가 응답하지 않게 된다.

따라서 이러한 작업은 백그라운드 스레드에서 실행해야 한다.

이를 통해 UI는 사용자 입력에 즉시 반응할 수 있게 된다.

 

3.Thread 사용 예시

 

다음은 WPF MVVM 패턴에서 스레드를 사용하는 간단한 예시이다.

이 예시는 버튼 클릭 시 백그라운드에서 긴 작업을 수행하고,

작업이 완료되면 UI를 업데이트하는 방법을 보여준다.

 

# Model

public class DataModel
{
    public string Data { get; set; }
}

 

# ViewModel
using System.ComponentModel;
using System.Threading;
using System.Threading.Tasks;

public class MainViewModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set
        {
            _status = value;
            OnPropertyChanged(nameof(Status));
        }
    }

    public void LongRunningTask()
    {
        Status = "작업 시작...";
        Task.Run(() =>
        {
            // 긴 작업 시뮬레이션
            Thread.Sleep(5000); // 5초 대기
            Status = "작업 완료!";
        });
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

 

# View(XAML)
<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="200" Width="300">
    <Grid>
        <Button Content="작업 시작" Command="{Binding LongRunningTask}" Width="100" Height="30" />
        <TextBlock Text="{Binding Status}" Margin="10,50,10,10" />
    </Grid>
</Window>

 

4.비동기 프로그래밍

 

위의 예시에서는 Task.Run을 사용하여 비동기적으로 작업을 수행하였다.

비동기 프로그래밍은 UI 스레드가 차단되지 않도록 하여 사용자 경험을 향상시킨다.

C#에서는 async await 키워드를 사용하여 비동기 메서드를 쉽게 작성할 수 있다.

 

public async void LongRunningTaskAsync()
{
    Status = "작업 시작...";
    await Task.Run(() =>
    {
        // 긴 작업 시뮬레이션
        Thread.Sleep(5000); // 5초 대기
    });
    Status = "작업 완료!";
}

 

5.UI 업데이트

 

WPF에서는 UI 요소를 업데이트할 때 UI 스레드에서만 수행해야 한다.

위의 예시에서 Status 속성을 업데이트할 때, 

INotifyPropertyChanged 인터페이스를 사용하여 UI에 변경 사항을 알린다. 

Task.Run 내에서 UI를 직접 업데이트하면 예외가 발생하므로,

UI 업데이트는 항상 UI 스레드에서 수행해야 한다.

 

6.결론

 

WPF MVVM 패턴에서 스레드를 사용하는 것은

UI의 응답성을 유지하는 데 필수적이다.

비동기 프로그래밍을 통해 긴 작업을 백그라운드에서 실행하고,

UI는 사용자와의 상호작용을 계속할 수 있게 된다.

 

이 글에서는 기본적인 스레드 사용법과 비동기 프로그래밍을 통해 MVVM 패턴에서의 스레드 활용을 설명하였다.

이러한 개념을 바탕으로 더 복잡한 애플리케이션을 개발할 수 있을 것으로 생각된다.

 

 

반응형
반응형

 

우리 아이들의 수학에 대한
홈 스쿨링(Home-schooling)을 하는
모습/과정을 남기고픈 마음에
작성하게 된
포스팅입니다.

 

내용 요약

 

오늘은 아이들에게 수학을 가르치면서 집에서 해야하는 수학 숙제의 

양은 어떻게 정해서 하고 있는지에 대해 살펴보자.

 

수학 숙제 양 정하기

 

초등학교 학기중에 수학 숙제를 매일같이 시키는데,

항상 양이 문제다.

 

저학년일때는 영어 학원에서 그렇게 많은 시간을

빼앗기지 않았는데,

학년이 올라갈 수록 영어 학원에서 보내는 

시간도 길어지고

또 학원에서 내주는 숙제의 양과

난이도 또한 올라가기 때문에

그로 인해 수학 숙제를 하는 시간이

부족해지게 된다.

 

그리고 수학 숙제도 문제집의 난이도가 올라가면

난이도가 쉬운 문제집을 풀때보다 시간이 더 

걸리기 때문에 아이가 힘들어 했다.

어쩌면 좋을까...

 

그래서 

난이도가 쉬울때는 숙제의 양을 많게 했다가

난이도가 올라가면 양을 적게 줄여서

아이가 충분히 소화할 수 있도록

조절하였다.

 

그런데

이 부분도 항상 고민이었다

왜냐하면, 좀 더 열심히 집중해서 수학 문제를 풀면

금방 할 수 있을 것 같았고, 또 그렇게

해주기를 바라는 부모의 욕심(?) 때문일까

숙제의 양을 줄이기 위해서는 아이와의 타협도 중요하지만

나 자신과의 타협도 어느 정도 필요했다.

 

그리고 아이의 성향에 따라

힘들어도 참고 묵묵히 하는 아이가 있는가 하면,

하기 싫은 데 억지로 시키면 

더 하기 싫어하는 아이가 있다.

그리고 그런 상황에서 솔직하게 말을 해주면

좋은데, 그렇게 얘기 못하는 아이도 있기 때문에

아이와의 대화를 자주 하는 것이 

중요했던 거 같다.

 

항상 틀린 문제를 체크하면서 

왜 틀린 건지...

몰라서 틀린건지 실수인지...

힘들어 하는지...

등등을

확인 하면서 아이를

다독이기도 하고 때로는 모질게 혼내기도 하고

아, 이래서 부모가 자식 가르치는게

힘들다고 그랬던 건가

싶기도 했다.

 

 

숙제의 양을 그렇게 아이에게 맞춰서

조절하되 최소한 어느 정도 이상은 해야 한다는

기준은 있었기에 그 이하는

안된다는 단호함으로 아이와 같이 조절하였고,

 

다음으로

숙제를 하는 방식은

매일 매일 이었다. 

몇 문제씩 매일 풀던가 몇 장씩 매일 푸는 식으로...

 

만약 그날의 숙제를 다 하지 못하는 경우

그 주의 숙제는 주말까지 이어서 

무조건 끝내는 식이었다.

그래서 아이들은 처음에 많이 

투정 부리기도 하고 

또 때로는 

주말에 뭔가 꼭 하고 싶은 게 있을 때는

아침 일찍 일어나서 숙제를 하기도 

했던 기억이 있다.

물론 요즘도 그렇게 하고 있다.

 

 

잘 따라와 주는 아이가 항상 감사하고 기특하다.

언제까지 잘 따라와 줄지 모르겠지만 할 수 있는데까지 최선을 다해보려 한다.

 

 

 

 

 

 

 

 

반응형
반응형
해당 포스팅은 개인적으로 MCU를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

C언어로 MCU 프로그래밍을 할때
함수를 설명해주는 주석 블록을 Doxygen이라고 하는데,
어떤 기능이 있고 어떻게
사용하는지
알아보자.

 

 

반응형

 

Doxygen의 구성 요소


Doxygen 주석 블록은 함수, 변수, 클래스 등의 

코드 요소에 대한 설명을 제공하는 데 사용된다. 

주석 블록은 /** ... */로 감싸여 있으며

내부에 다양한 태그를 사용하여 설명을 추가한다. 

주요 태그와 그 의미는 다음과 같다

@brief: 함수나 클래스 등의 간단한 설명을 제공한다.
@param: 함수의 매개변수를 설명한다. [in], [out], [in,out] 등의 옵션을 사용하여 매개변수의 방향을 나타낼 수 있다.
@retval: 함수의 반환 값을 설명한다.
@return: 함수의 반환 값을 설명한다. @retval과 유사하지만, 주로 단일 반환 값에 사용된다.
@details: 함수나 클래스 등의 상세 설명을 제공한다.
@note: 주의 사항을 설명한다.
@warning: 경고 사항을 설명한다.
@code ... @endcode: 코드 블록을 포함한다.

 

예시를 통한 설명

 

다음은 Doxygen 주석 블록을 사용하여 함수의 문서를 작성하는 예시이다.

이 예시는 Metrology 주변 장치 레지스터를 초기화하는 함수를 설명한다.

 

/**
 * @brief  Initialize the Metrology peripheral registers to their default reset values.
 * @param[in]   None
 * @retval None
 * @details This function resets all the Metrology peripheral registers to their default values.
 *          It should be called during the system initialization to ensure that the Metrology
 *          peripheral is in a known state.
 * @note    This function does not return any value.
 * @warning Make sure that the Metrology peripheral is not in use before calling this function.
 */
void InitializeMetrology(void);

 

이 주석 블록은 InitializeMetrology 함수에 대한 설명을 제공한다.

 

각 태그의 의미는 다음과 같다.

@brief: 함수의 간단한 설명을 제공다. 

여기서는 "Initialize the Metrology peripheral registers to their default reset values."라고 설명하고 있다.
@param[in]: 함수의 입력 매개변수를 설명한다. 이 함수는 입력 매개변수가 없으므로 None으로 표시되어 있다.
@retval: 함수의 반환 값을 설명한다. 이 함수는 반환 값이 없으므로 None으로 표시되어 있다.
@details: 함수의 상세 설명을 제공합니다. 이 함수는 Metrology 주변 장치 레지스터를 기본 초기화 값으로 설정하는 역할을 하며, 시스템 초기화 중에 호출되어야 한다고 설명하고 있다.
@note: 주의 사항을 설명한다. 이 함수는 반환 값이 없다는 점을 강조하고 있다.
@warning: 경고 사항을 설명한다. 이 함수를 호출하기 전에 Metrology 주변 장치가 사용 중이지 않은지 확인해야 한다고 경고하고 있다.

 

함수 구현

 

이제 함수의 구현을 살펴보자. 주석 블록은 함수의 선언부에 위치하며, 함수의 구현부는 주석 블록 아래에 위치한다

#include <stdint.h>

// Metrology 주변 장치 레지스터의 가상 주소
#define METROLOGY_REG_BASE 0x40000000
#define METROLOGY_REG_COUNT 10

// Metrology 주변 장치 레지스터를 나타내는 구조체
typedef struct {
    volatile uint32_t REG[METROLOGY_REG_COUNT];
} Metrology_TypeDef;

// Metrology 주변 장치 레지스터의 포인터
#define METROLOGY ((Metrology_TypeDef *) METROLOGY_REG_BASE)

/**
 * @brief  Initialize the Metrology peripheral registers to their default reset values.
 * @param[in]   None
 * @retval None
 * @details This function resets all the Metrology peripheral registers to their default values.
 *          It should be called during the system initialization to ensure that the Metrology
 *          peripheral is in a known state.
 * @note    This function does not return any value.
 * @warning Make sure that the Metrology peripheral is not in use before calling this function.
 */
void InitializeMetrology(void) {
    // Metrology 주변 장치 레지스터를 기본 초기화 값으로 설정
    for (int i = 0; i < METROLOGY_REG_COUNT; i++) {
        METROLOGY->REG[i] = 0x00000000; // 기본 초기화 값으로 설정
    }
}

 

이 함수는 Metrology 주변 장치 레지스터를 기본 초기화 값으로 설정한다. METROLOGY_REG_BASE는 Metrology 주변 장치 레지스터의 가상 주소를 나타내며, METROLOGY_REG_COUNT는 레지스터의 개수를 나타낸다.  Metrology_TypeDef 구조체는 Metrology 주변 장치 레지스터를 나타내며, METROLOGY는 이 구조체의 포인터이다.

함수 내부에서는 for 루프를 사용하여 모든 레지스터를 기본 초기화 값인 0x00000000으로 설정한다.

 

Doxygen을 사용한 문서 생성

 

Doxygen을 사용하여 문서를 생성하려면, 먼저 Doxygen 설정 파일인 Doxyfile을 생성해야 한다. Doxyfile은 Doxygen의 다양한 옵션을 설정할 수 있는 파일이다. Doxyfile을 생성하는 방법은 다음과 같다.

터미널을 열고 Doxygen이 설치된 디렉토리로 이동한 다음 명령어를 실행하여 Doxyfile을 생성한다.

 

doxygen -g

 

이 명령어는 기본 설정을 포함한 Doxyfile을 생성한다. 생성된 Doxyfile을 열어 필요한 옵션을 수정할 수 있다. 예를 들어, 소스 코드 파일의 경로를 설정하려면 INPUT 옵션을 수정한다.

 

INPUT = /path/to/your/source/code

 

Doxyfile을 수정한 후, 다음 명령어를 실행하여 문서를 생성한다.

 

doxygen Doxyfile

 

 

이 명령어는 Doxyfile에 설정된 옵션을 기반으로 문서를 생성한다. 생성된 문서는 HTML, LaTeX, RTF 등의 형식으로 저장된다. HTML 형식의 문서는 웹 브라우저를 사용하여 쉽게 열어볼 수 있다.

생성된 문서 예시
Doxygen을 사용하여 생성된 문서는 함수, 변수, 클래스 등의 설명을 포함한다. 예를 들어, InitializeMetrology 함수에 대한 문서는 다음과 같은 형식으로 생성될 수 있다.

InitializeMetrology 함수 문서

InitializeMetrology

@brief
Initialize the Metrology peripheral registers to their default reset values.

@param[in]
None

@retval
None

@details
This function resets all the Metrology peripheral registers to their default values.
It should be called during the system initialization to ensure that the Metrology
peripheral is in a known state.

@note
This function does not return any value.

@warning
Make sure that the Metrology peripheral is not in use before calling this function.



이 문서는 함수의 간단한 설명, 매개변수, 반환 값, 상세 설명, 주의 사항, 경고 사항 등을 포함하며, 이를 통해 함수의 사용법을 쉽게 이해할 수 있다.

 

결론

Doxygen 주석 블록을 사용하면 코드의 가독성을 높이고, 자동으로 문서를 생성하여 코드의 유지보수성을 향상시킬 수 있다. 주석 블록은 함수, 변수, 클래스 등의 코드 요소에 대한 설명을 제공하며, Doxygen을 사용하여 다양한 형식의 문서를 생성할 수 있다. 이 예시를 통해 Doxygen 주석 블록의 사용법과 문서 생성 과정을 이해할 수 있었기를 바란다.

 

반응형
반응형
해당 포스팅은 개인적으로 MCU를 공부하면서 익힌 내용을 기억에 남기기 위한 작업의 일환으로 작성된 글로서
일부 내용에 오류가 있을 수도 있으니 참고하시기 바랍니다.

 

 

MCU에서의
중요한 구성요소 중에 하나인
Vector Table에 대하여
어떤 역할을 하는지
알아보자.

 

반응형

 

내용 요약

Renesas RX66T 마이크로컨트롤러(MCU)는 고성능의 32비트 마이크로컨트롤러로,

주로 산업용 제어 시스템, 로봇, 가전제품 등에서 사용된다.

이 MCU는 다양한 기능과 고속 연산 능력을 제공하며,

특히 모터 제어와 같은 실시간 응용 프로그램에 적합하다.

RX66T의 중요한 구성 요소 중 하나는 벡터 테이블(Vector Table)인데,

이 벡터 테이블은 인터럽트와 예외 처리를 관리하는 데 중요한 역할을 한다.

 

Vector와 Table의 의미

 

"벡터 테이블"이라는 이름은 주로 컴퓨터 과학과 전자공학에서 사용되는 용어로, 

특정 이벤트나 조건에 따라 실행할 코드의 주소를 저장하는 

데이터 구조를 의미합니다. 

이 용어는 다음과 같은 이유로 사용됩니다

 

1. 벡터(Vector)의 의미
"벡터"라는 단어는 여러 가지 의미를 가질 수 있지만, 컴퓨터 과학에서 벡터는 주로 방향과 크기를 가진 수학적 개념에서 유래한 용어로, 특정 방향으로의 이동이나 참조를 나타낸다. 벡터 테이블에서 "벡터"는 특정 이벤트(예: 인터럽트나 예외)가 발생했을 때 참조할 주소를 가리키는 포인터를 의미한다. 즉, 벡터는 특정 이벤트가 발생했을 때 실행할 코드의 시작 주소를 가리키는 역할을 한다.

2. 테이블(Table)의 의미
"테이블"은 데이터를 구조화하여 저장하는 방식 중 하나로, 행과 열로 구성된 데이터 집합을 의미한다. 벡터 테이블은 여러 개의 벡터(주소)를 행렬 형태로 정리한 데이터 구조로, 각 벡터는 특정 이벤트에 대응하는 주소를 저장한다. 이 테이블은 메모리 내의 고정된 위치에 저장되며, 각 엔트리는 특정 이벤트에 대응하는 ISR(Interrupt Service Routine) 또는 예외 처리 루틴의 주소를 포함한다.

 

Vector Table의 정의

RX66T의 내부에는 2가지 타입의 Vector Table이 있다.

하나는 Exception Vector Table이고, 

다른 하나는 Interrupt Vector Table로서 아래와 같다.

 

Exception Vector Table

 

Interrupt Vector Table

 

벡터 테이블은 MCU의 메모리 내에 위치한 데이터 구조로,

각각의 Vector는 4bytes로 구성되어있으며,

각 인터럽트와 예외에 대한 시작 주소를 저장하고 있다.

이 테이블은 인터럽트가 발생했을 때

해당 인터럽트 서비스 루틴(ISR, Interrupt Service Routine)으로

MCU가 점프할 수 있도록 한다.

벡터 테이블은 일반적으로 고정된 메모리 주소에 위치하며,

각 엔트리는 특정 인터럽트 또는 예외에 대응하는 ISR의 주소를 포함한다.

 

Vector Table의 내부 구성

Vector Table의 구성

1.리셋 벡터(Reset Vector)

MCU가 리셋될 때 실행을 시작할 주소를 포함합니다. 이 주소는 보통 초기화 코드의 시작 주소입니다.


2.예외 벡터(Exception Vectors)

CPU 예외(예: 불법 명령어 실행, 디버그 이벤트 등)에 대한 ISR 주소를 포함합니다.


3.인터럽트 벡터(Interrupt Vectors)

외부 및 내부 인터럽트 소스에 대한 ISR 주소를 포함합니다.

 


벡터 테이블의 역할

벡터 테이블의 주요 역할은 다음과 같습니다:


1.인터럽트 처리

인터럽트가 발생하면, MCU는 현재 실행 중인 작업을 중단하고

벡터 테이블을 참조하여 해당 인터럽트에 대한 ISR로 점프한다.

ISR이 실행된 후, MCU는 원래 작업으로 복귀한다.


2.예외 처리

예외가 발생하면, 벡터 테이블을 참조하여 

해당 예외에 대한 처리 루틴으로 점프한다.

예외 처리 루틴은 시스템 상태를 점검하고,

필요한 경우 시스템을 복구하거나 재시작한다.


3.시스템 초기화

리셋 벡터는 시스템이 리셋될 때 실행을 시작할 주소를 제공하여,

초기화 코드가 실행되도록 한다.

 


예제
다음은 RX66T에서 벡터 테이블을 설정하고 사용하는 예제이다.

이 예제는 간단한 타이머 인터럽트를 설정하고,

인터럽트가 발생했을 때 LED를 토글하는 코드이다.

#include <stdint.h>
#include "iodefine.h"

// 인터럽트 서비스 루틴 선언
void Timer_ISR(void);

// 벡터 테이블 설정
__attribute__((section(".vectors"))) void (* const VectorTable[])(void) = {
    (void (*)(void))0xFFFFFFFF,  // 초기 스택 포인터
    Reset_Handler,               // 리셋 벡터
    NMI_Handler,                 // NMI 벡터
    HardFault_Handler,           // 하드 폴트 벡터
    // ... (다른 예외 벡터)
    Timer_ISR,                   // 타이머 인터럽트 벡터
    // ... (다른 인터럽트 벡터)
};

// 타이머 인터럽트 서비스 루틴
void Timer_ISR(void) {
    // 인터럽트 플래그 클리어
    CMT0.CMCSR.BIT.CMF = 0;
    
    // LED 토글
    PORTA.PODR.BIT.B0 ^= 1;
}

// 메인 함수
int main(void) {
    // 시스템 초기화 코드
    SystemInit();
    
    // LED 포트 설정
    PORTA.PDR.BIT.B0 = 1;  // 출력 설정
    
    // 타이머 설정
    MSTP(CMT0) = 0;        // 타이머 모듈 활성화
    CMT0.CMCR.BIT.CKS = 0; // 분주비 설정
    CMT0.CMCOR = 31250;    // 비교 매치 값 설정
    CMT0.CMCSR.BIT.CMIE = 1; // 인터럽트 활성화
    IEN(CMT0, CMI0) = 1;   // 인터럽트 허용
    IR(CMT0, CMI0) = 0;    // 인터럽트 플래그 클리어
    CMT.CMSTR0.BIT.STR0 = 1; // 타이머 시작
    
    // 메인 루프
    while (1) {
        // 메인 작업 수행
    }
    
    return 0;
}

 

벡터 테이블 설정

__attribute__((section(".vectors")))를 사용하여 

벡터 테이블을 특정 메모리 섹션에 배치한다.

이 테이블은 리셋 벡터, NMI 벡터, 하드 폴트 벡터 등과 함께

타이머 인터럽트 벡터를 포함한다.


인터럽트 서비스 루틴

Timer_ISR 함수는 타이머 인터럽트가 발생했을 때 호출된다.

이 함수는 인터럽트 플래그를 클리어하고, LED를 토글한다.


메인 함수

시스템 초기화 후, LED 포트를 출력으로 설정하고, 

타이머를 설정하여 인터럽트를 활성화한다. 메인 루프에서는 주 작업을 수행한다.

 

결론

 

벡터 테이블은 RX66T MCU에서 인터럽트와 예외 처리를 관리하는 

중요한 데이터 구조이다. 

벡터 테이블을 통해 각 인터럽트와 예외에 대한 ISR 주소를 저장하고, 

인터럽트가 발생했을 때 해당 ISR로 점프할 수 있도록 한다.

이를 통해 실시간 응용 프로그램에서 신속하고 효율적인 인터럽트 처리가 가능하다.

반응형

+ Recent posts