✍ DependencyInjection 설치
✍ MainWindow.xaml 삭제 후
Models
ViewModels
Views
폴더 생성
Views 폴더에 MainView.xaml 생성
ViewModels에 MainViewModel 생성
App.xaml 에서 StartupUri 제거
💻 App.xaml.cs
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using WpfDINavigation.ViewModels;
using WpfDINavigation.Views;
namespace WpfDINavigation
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public new static App Current => (App)Application.Current;
private IServiceProvider ConfigureServies()
{
var services = new ServiceCollection();
// ViewModels
services.AddSingleton<MainViewModel>();
// Views
services.AddSingleton(s => new MainView()
{
DataContext = s.GetRequiredService<MainViewModel>()
});
return services.BuildServiceProvider();
}
public App()
{
Services = ConfigureServies();
var mainView = Services.GetRequiredService<MainView>();
mainView.Show();
}
public IServiceProvider Services { get; }
}
}
✔ Application.Current는 자기 자신을 호출하는 속성
Services.GetRequiredService<MainView>(); 이 구문이 실행되면
services.AddSingleton(s => new MainView()
{
DataContext = s.GetRequiredService<MainViewModel>()
});
MainView 생성자가 생성이 되고 뷰모델이 DataContext로 들어간다고 생각하면 이해하기 쉽다.
✍ MVVM 패턴을 위해 ViewModelBase 생성
✔ INotifyPropertyChanged 인터페이스는 MVVM 패턴의 필수 인터페이스!
✔ CallerMemberName을 사용하면 매개변수를 입력하지 않았을 때, 그 속성의 이름이 그대로 인자값으로 넘어온다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace WpfDINavigation.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
// 속성값이 변경되었을 때 이벤트에 알려주기 위해 메서드 생성
protected void OnPropertyChanged([CallerMemberName]string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
✍ RelayCommand 생성
RelayCommand는 Command 바인딩을 위해 ICommand를 상속받는다.
ICommand 안에는 canexecute와 execute가 있다.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfDINavigation.Commands
{
public class RelayCommand<T> : ICommand
{
private readonly Action<T>? _execete;
private readonly Predicate<T>? _canExecute;
public RelayCommand(Action<T>? execute, Predicate<T>? canExecute = null)
{
this._execete = execute;
this._canExecute = canExecute;
}
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object? parameter)
{
return _canExecute?.Invoke((T)parameter) ?? true;
}
public void Execute(object? parameter)
{
_execete?.Invoke((T)parameter);
}
}
}
✔ CanExecute
커맨드를 실행할지에 대한 제한조건을 건다
해당 콜백함수가 만약 false라면 커맨드를 실행할 수 없다.
✔ Execute
커맨드를 실행하는 구문
파라미터를 매개변수로 넘겨주고 콜백함수를 실행시킨다.
💻 MainView.xaml
xmlns:viewmodels="clr-namespace:WpfDINavigation.ViewModels"
추가하기
<Window.Resources>
<DataTemplate DataType="{x:Type viewmodels:LoginViewModel}">
<local:LoginView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:SignupViewModel}">
<local:LoginView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:TestViewModel}">
<local:LoginView/>
</DataTemplate>
</Window.Resources>
<Window.Resources>에 DataTemplate에 뷰모델을 추가해준다
<Window x:Class="WpfDINavigation.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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:local="clr-namespace:WpfDINavigation.Views"
xmlns:viewmodels="clr-namespace:WpfDINavigation.ViewModels"
mc:Ignorable="d"
Title="MainView" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type viewmodels:LoginViewModel}">
<local:LoginView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:SignupViewModel}">
<local:SignupView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:TestViewModel}">
<local:TestView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentViewModel}"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfDINavigation.ViewModels
{
public class MainViewModel : ViewModelBase
{
private INotifyPropertyChanged? _currentViewModel;
public MainViewModel()
{
CurrentViewModel = new LoginViewModel();
}
public INotifyPropertyChanged? CurrentViewModel
{
get { return _currentViewModel; }
set
{
if(_currentViewModel != value)
{
_currentViewModel = value;
OnPropertyChanged();
}
}
}
}
}
--> CurrentViewModel 추가
✔ LoginView, SignupView, TestView xaml 디자인 추가
<UserControl x:Class="WpfDINavigation.Views.LoginView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfDINavigation.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="LoginView"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="35"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Command="{Binding ToSignupCommand}" Content="ToSignup" Margin="5"/>
<Button Command="{Binding ToTestCommand}" Content="ToTest" Grid.Column="1"/>
</Grid>
</Grid>
</UserControl>
<UserControl x:Class="WpfDINavigation.Views.SignupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfDINavigation.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="Yellow">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="SignupView"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="35"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Command="{Binding ToLoginCommand}"
Content="ToLogin" Margin="5"/>
<Button Command="{Binding ToTestCommand}"
Grid.Column="1"
Content="ToTest"
Margin="5"/>
</Grid>
</Grid>
</UserControl>
<UserControl x:Class="WpfDINavigation.Views.TestView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfDINavigation.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="SkyBlue">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="TestView"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="35"/>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Command="{Binding ToSignupCommand}"
Content="ToSignup" Margin="5"/>
<Button Command="{Binding ToLoginCommand}"
Grid.Column="1"
Content="ToLogin"
Margin="5"/>
</Grid>
</Grid>
</UserControl>
✍ MainNavigationStore.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfDINavigation.ViewModels;
namespace WpfDINavigation.Stores
{
public class MainNavigationStore : ViewModelBase
{
private INotifyPropertyChanged? _currentViewmodel;
public INotifyPropertyChanged? CurrentViewModel
{
get { return _currentViewmodel; }
set
{
_currentViewmodel = value;
CurrentViewModelChanged?.Invoke();
_currentViewmodel = null;
}
}
public Action? CurrentViewModelChanged { get; set; }
}
}
✍ NavigationService 생성하기
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WpfDINavigation.Stores;
using WpfDINavigation.ViewModels;
namespace WpfDINavigation.Services
{
public class NavigationService : INavigationService
{
private readonly MainNavigationStore _mainNavigationStore;
private INotifyPropertyChanged CurrentViewModel
{
set => _mainNavigationStore.CurrentViewModel = value;
}
public NavigationService(MainNavigationStore mainNavigationStore)
{
this._mainNavigationStore = mainNavigationStore;
}
public void Navigate(NaviType naviType)
{
switch (naviType)
{
case NaviType.LoginView:
CurrentViewModel = (ViewModelBase)App.Current.Services.GetService(typeof(LoginViewModel))!;
break;
case NaviType.SignupView:
CurrentViewModel = (ViewModelBase)App.Current.Services.GetService(typeof(SignupViewModel))!;
break;
case NaviType.TestView:
CurrentViewModel = (ViewModelBase)App.Current.Services.GetService(typeof(TestViewModel))!;
break;
default:
return;
}
}
}
}
💻 INavigationService 인터페이스
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WpfDINavigation.Services
{
public interface INavigationService
{
void Navigate(NaviType naviType);
}
}
💻 RelayCommands
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WpfDINavigation.Commands
{
public class RelayCommand<T> : ICommand
{
private readonly Action<T>? _execete;
private readonly Predicate<T>? _canExecute;
public RelayCommand(Action<T>? execute, Predicate<T>? canExecute = null)
{
this._execete = execute;
this._canExecute = canExecute;
}
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object? parameter)
{
return _canExecute?.Invoke((T)parameter) ?? true;
}
public void Execute(object? parameter)
{
_execete?.Invoke((T)parameter);
}
}
}
💻 LoginViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using WpfDINavigation.Commands;
using WpfDINavigation.Services;
namespace WpfDINavigation.ViewModels
{
public class LoginViewModel : ViewModelBase
{
private readonly INavigationService _navigationService;
private void ToSignup(object _)
{
_navigationService.Navigate(NaviType.SignupView);
}
private void ToTest(object _)
{
_navigationService.Navigate(NaviType.TestView);
}
public LoginViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
ToSignupCommand = new RelayCommand<object>(ToSignup);
ToTestCommand = new RelayCommand<object>(ToTest);
}
public ICommand ToSignupCommand { get; set; }
public ICommand ToTestCommand { get; set; }
}
}
💻 SignupViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using WpfDINavigation.Commands;
using WpfDINavigation.Services;
namespace WpfDINavigation.ViewModels
{
public class SignupViewModel : ViewModelBase
{
private readonly INavigationService _navigationService;
private void ToLogin(object _)
{
_navigationService.Navigate(NaviType.LoginView);
}
private void ToTest(object _)
{
_navigationService.Navigate(NaviType.TestView);
}
public SignupViewModel(INavigationService navigationService)
{
_navigationService = navigationService;
ToLoginCommand = new RelayCommand<object>(ToLogin);
ToTestCommand = new RelayCommand<object>(ToTest);
}
public ICommand ToLoginCommand { get; set; }
public ICommand ToTestCommand { get; set; }
}
}
💻 ViewModelBase
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace WpfDINavigation.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
// 속성값이 변경되었을 때 이벤트에 알려주기 위해 메서드 생성
protected void OnPropertyChanged([CallerMemberName]string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
💻 App.xaml.cs
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using WpfDINavigation.Services;
using WpfDINavigation.Stores;
using WpfDINavigation.ViewModels;
using WpfDINavigation.Views;
namespace WpfDINavigation
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public new static App Current => (App)Application.Current;
private IServiceProvider ConfigureServies()
{
var services = new ServiceCollection();
// Stores
services.AddSingleton<MainNavigationStore>();
// Services
services.AddSingleton<INavigationService, NavigationService>();
// ViewModels
services.AddSingleton<MainViewModel>();
services.AddSingleton<LoginViewModel>();
services.AddSingleton<SignupViewModel>();
services.AddSingleton<TestViewModel>();
// Views
services.AddSingleton(s => new MainView()
{
DataContext = s.GetRequiredService<MainViewModel>()
});
return services.BuildServiceProvider();
}
public App()
{
Services = ConfigureServies();
var mainView = Services.GetRequiredService<MainView>();
mainView.Show();
}
public IServiceProvider Services { get; }
}
}
'C# > WPF' 카테고리의 다른 글
[WPF] 카카오톡 구현 - Dependency Injection(DI) IoC를 이용한 ViewModel간 데이터 전달 (0) | 2023.08.27 |
---|---|
[WPF] Dependency Injection(DI) IoC를 이용한 ViewModel간 데이터 전달 (0) | 2023.08.23 |
[WPF] 카카오톡 구현 7 - ComboBox UserControl 만들기 (0) | 2023.08.22 |
[WPF] 카카오톡 구현 6 - ComboBox BackGround 변경하기 (1) | 2023.08.22 |
[WPF] 카카오톡 구현 5 - PasswordBox WaterMarkText, Validating 만들어보기 (0) | 2023.08.22 |