Prism에서의 전역 Command 세팅방법 - 모든 View의 Command를 한 커맨드로 실행시키고 싶을 때
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/12-UsingCompositeCommands
코드는 이쪽에서 참조
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/12-UsingCompositeCommands
내가 만든 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { moduleCatalog.AddModule<ModuleA.ModuleAModule>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterSingleton<IApplicationCommands, ApplicationCommands>(); } } | cs |
기본 시작 윈도우를 MainWindow로 설정했고.
모듈을 사용하니 ConfigureModuleCatalog를 이용하여 모듈 카타로그에 ModuleA의 ModuleAModule을 추가했네요
RegisterTypes으로 컨테이너를 등록하였는데
containerRegistry에 ApplicationCommands를 등록했습니다. 이 부분은 잘 모르겠으니 나중에 자세히 알아보겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <Window x:Class="UsingCompositeCommands.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" Title="{Binding Title}" Height="350" Width="525"> <Window.Resources> <Style TargetType="TabItem"> <Setter Property="Header" Value="{Binding DataContext.Title}" /> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Button Content="Save" Margin="10" Command="{Binding ApplicationCommands.SaveCommand}"/> <TabControl Grid.Row="1" Margin="10" prism:RegionManager.RegionName="ContentRegion" /> </Grid> </Window> | cs |
일단 MainWindow의 XAML을 확인해보니 ViewModelLocator를 이용하여 ViewModel을 이용 하였음을 알 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public class MainWindowViewModel : BindableBase { private string _title = "Prism Unity Application"; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } private IApplicationCommands _applicationCommands; public IApplicationCommands ApplicationCommands { get { return _applicationCommands; } set { SetProperty(ref _applicationCommands, value); } } public MainWindowViewModel(IApplicationCommands applicationCommands) { ApplicationCommands = applicationCommands; } } } | cs |
MainWindow의 ViewModel을 확인하니 title 프로퍼티가 있고,
IApplicationCommands 라는 프로퍼티가 생겨있습니다.
MainWindowViewModel이 생성될때 applicationCommands를 받아와서 저장 하는 용도로 사용하려는 프로퍼티로 보입니다.
====================
다시 Mainwindow의 XMAL로 돌아가서
<TabControl Grid.Row="1" Margin="10" prism:RegionManager.RegionName="ContentRegion" />
탭 컨트롤에 리전이 걸려있는데.
ContentRegion은 MainWindow관련 코드에서는 찾아보질 못했습니다. 그럼 어디에 있는지 찾아보면 App에서 붙인 모듈 ModuleAModule에 정의되어 있을 겁니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public class ModuleAModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<IRegionManager>(); IRegion region = regionManager.Regions["ContentRegion"]; var tabA = containerProvider.Resolve<TabView>(); SetTitle(tabA, "Tab A"); region.Add(tabA); var tabB = containerProvider.Resolve<TabView>(); SetTitle(tabB, "Tab B"); region.Add(tabB); var tabC = containerProvider.Resolve<TabView>(); SetTitle(tabC, "Tab C"); region.Add(tabC); } public void RegisterTypes(IContainerRegistry containerRegistry) { } void SetTitle(TabView tab, string title) { (tab.DataContext as TabViewModel).Title = title; } } | cs |
OnInitialized , 즉 모듈이 생성될 당시에 컨테이너 제공자로부터 regionManager를 받아옵니다.
해당 region에 ContentRegion이라는 등록명을 넣고
해당 View로 tabA, tabB, tabC 를 넣어놓습니다.
이 tab view들은 하나의 클래스 인데 TabView라는 유저컨트롤로 만들어져있습니다.
SetTitle 이라는 메소드는 Tabview형식을 받아 title을 변경하는 메소드입니다.
내용을 보면
TabView.DataContext는 TabViewModel 형식 일텐데. 즉 ViewLocator로 연결되어 있는 ViewModel이라는 것입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public class TabViewModel : BindableBase { IApplicationCommands _applicationCommands; private string _title; public string Title { get { return _title; } set { SetProperty(ref _title, value); } } private bool _canUpdate = true; public bool CanUpdate { get { return _canUpdate; } set { SetProperty(ref _canUpdate, value); } } private string _updatedText; public string UpdateText { get { return _updatedText; } set { SetProperty(ref _updatedText, value); } } public DelegateCommand UpdateCommand { get; private set; } public TabViewModel(IApplicationCommands applicationCommands) { _applicationCommands = applicationCommands; UpdateCommand = new DelegateCommand(Update).ObservesCanExecute(() => CanUpdate); _applicationCommands.SaveCommand.RegisterCommand(UpdateCommand); } private void Update() { UpdateText = $"Updated: {DateTime.Now}"; } } | cs |
TabViewModel 을 열어보면 내부에 Title이라는 프로퍼티가 만들어져있는 것을 알 수 있습니다.
===========================
그런데, 이 내부 안에 DelegateCommand가 있는게 보입니다.
public DelegateCommand UpdateCommand { get; private set; }
UpdateCommand 가 어디에 연결되어 있는지 확인하니 Save Button에 연결되어 있음을 확인할 수 있습니다.
이제 TabViewModel로 만든 모든 tab은 버튼 클릭시 이 updataCommand가 실행되게 됩니다.
생성자를 다시 보면 applicationCommadns에 SaveCommand.Registercommand를 통해서 런타임으로 만들어진 DelegateCommand를 등록 할 수 있습니다.
이렇게 등록하게 될 경우 어떤 일이 일어나는 걸까요.
바로 전역으로 ApplicationCommand를 불러서 모든 버튼에 Command를 발생시킬 수 있게 됩니다.
그 코드는
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <Window x:Class="UsingCompositeCommands.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" Title="{Binding Title}" Height="350" Width="525"> <Window.Resources> <Style TargetType="TabItem"> <Setter Property="Header" Value="{Binding DataContext.Title}" /> </Style> </Window.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*" /> </Grid.RowDefinitions> <Button Content="Save" Margin="10" Command="{Binding ApplicationCommands.SaveCommand}"/> <TabControl Grid.Row="1" Margin="10" prism:RegionManager.RegionName="ContentRegion" /> </Grid> </Window> | cs |
MainWindow XAML에서 확인 할 수 있습니다.
최상단에 button이 있는데 Command로 ApplicationComands.SaveCommand를 호출하는 것을 볼 수 있습니다.
즉 응용프로그램 생성 당시 만들어지는 applicationCommand를 MainWindow에 저장하여 해당 윈도우에서 전역 applicationCommand에 접근하여 저장된 모든 Command를 실행시키는 것입니다.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism에서 DelegateCommand를 하는 방법 (0) | 2019.02.20 |
---|---|
Prism에서 CustomViewModel을 view에 붙이는 방법 (0) | 2019.02.19 |
Prism으로 어떻게 다른 모듈의 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism에서 DelegateCommand를 하는 방법
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/11-UsingDelegateCommands
참고 코드
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/11-UsingDelegateCommands
내가 만든 코드
기본적인 코드는 비슷하니 패스
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | using System; using Prism.Commands; using Prism.Mvvm; namespace UsingDelegateCommands.ViewModels { public class MainWindowViewModel : BindableBase { private bool _isEnabled; public bool IsEnabled { get { return _isEnabled; } set { SetProperty(ref _isEnabled, value); ExecuteDelegateCommand.RaiseCanExecuteChanged(); } } private string _updateText; public string UpdateText { get { return _updateText; } set { SetProperty(ref _updateText, value); } } public DelegateCommand ExecuteDelegateCommand { get; private set; } public DelegateCommand<string> ExecuteGenericDelegateCommand { get; private set; } public DelegateCommand DelegateCommandObservesProperty { get; private set; } public DelegateCommand DelegateCommandObservesCanExecute { get; private set; } public MainWindowViewModel() { Console.WriteLine("JJH: MainWindowViewModel MainWindowViewModel"); ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute); DelegateCommandObservesProperty = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => IsEnabled); DelegateCommandObservesCanExecute = new DelegateCommand(Execute).ObservesCanExecute(() => IsEnabled); ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric).ObservesCanExecute(() => IsEnabled); } private void Execute() { Console.WriteLine("JJH: MainWindowViewModel Execute"); UpdateText = $"Updated: {DateTime.Now}"; } private void ExecuteGeneric(string parameter) { Console.WriteLine("JJH: MainWindowViewModel ExecuteGeneric"); UpdateText = parameter; } private bool CanExecute() { Console.WriteLine("JJH: MainWindowViewModel CanExecute"); return IsEnabled; } } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <Window x:Class="UsingDelegateCommands.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" prism:ViewModelLocator.AutoWireViewModel="True" Title="Using DelegateCommand" Width="350" Height="275"> <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"> <CheckBox IsChecked="{Binding IsEnabled}" Content="Can Execute Command" Margin="10"/> <Button Command="{Binding ExecuteDelegateCommand}" Content="DelegateCommand" Margin="10"/> <Button Command="{Binding DelegateCommandObservesProperty}" Content="DelegateCommand ObservesProperty" Margin="10"/> <Button Command="{Binding DelegateCommandObservesCanExecute}" Content="DelegateCommand ObservesCanExecute" Margin="10"/> <Button Command="{Binding ExecuteGenericDelegateCommand}" CommandParameter="Passed Parameter" Content="DelegateCommand Generic" Margin="10"/> <TextBlock Text="{Binding UpdateText}" Margin="10" FontSize="22"/> </StackPanel> </Window> | cs |
MainWindowViewModel 과 MainWindow가 XAML에서 ViewModelLocator로 연결되어 있는 상태이다.
메인윈도우 뷰모델을 확인하면.
IsChecked 컨트롤은 IsEnabled 프로퍼티와 연결되어 있다. 이게 two way인가보다 프로퍼티의 상태를 보면
get과 set이 있는데, get의 경우 단순히 _isEnabled를 반환하고
set의 경우 _isEnabled를 value값으로 초기화하고, ExecuteDelegateCommand.RaiseCanExecuteChanged();를 실행시킨다.
프로그램의 실행 순서로 보았을 때, 생성자가 제일먼저 실행되고 그 뒤에 CanExecute가 실행되는 것을 확인 할 수 있다.
생성자를 살펴보면
1 2 3 4 5 6 7 8 9 10 11 12 | public MainWindowViewModel() { Console.WriteLine("JJH: MainWindowViewModel MainWindowViewModel"); ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute); DelegateCommandObservesProperty = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => IsEnabled); DelegateCommandObservesCanExecute = new DelegateCommand(Execute).ObservesCanExecute(() => IsEnabled); ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric).ObservesCanExecute(() => IsEnabled); Console.WriteLine("JJH: MainWindowViewModel MainWindowViewModel end"); } | cs |
이렇게 구성되어 있는데,
ExecuteDelegateCommand는 버튼의 DelegateCommand에 붙어있다.
ExecuteDelegateCommand는 이렇게 public DelegateCommand ExecuteDelegateCommand { get; private set; } 프로퍼티로 선언되어 있다.
이 ExecuteDelegateCommand에 들어간 new DelegateCommand(Execute, CanExecute);를 살펴보면
DelegateCommand를 생성하여 담았는데, 첫번째 매개변수에 실행할 함수, 두번째 매개변수에 CanExecute 즉 실행 여부 함수를 담아야 한다.
따라서 DelegateCommand버튼을 누를 경우 실행 순서가 CanExecute ->Execute 순으로 일어나게 될 것이다.
CanExecute가 먼저 실행되고 그 뒤에 Execute가 실행되는 모습을 볼 수 있다.
따라서 DelegateCommand(실행할 메소드, 실행 여부 판단 메소드)로 구성되는 것을 알 수 있었다.
==================================================================
이제 DelegateCommand ObservesProperty에 대해 확인해보면
해당 버튼은 DelegateCommandObservesProperty라는 프로퍼티와 연결되어 있다.
생성자에서 DelegateCommandObservesProperty = new DelegateCommand(Execute, CanExecute).ObservesProperty(() => IsEnabled); 이렇게 DelegateCommand가 붙어있는데 기본적인 딜리게이트 커맨드의 생성자는 이해했지만 ObservesProperty가 뭔지 모르겠다.
선언문을 확인해보면
//
// 요약:
// Observes a property that implements INotifyPropertyChanged, and automatically
// calls DelegateCommandBase.RaiseCanExecuteChanged on property changed notifications.
//
// 매개 변수:
// propertyExpression:
// The property expression. Example: ObservesProperty(() => PropertyName).
//
// 형식 매개 변수:
// T:
// The object type containing the property specified in the expression.
//
// 반환 값:
// The current instance of DelegateCommand
public DelegateCommand ObservesProperty<T>(Expression<Func<T>> propertyExpression);
이렇게 되어 있는데
INotifyPropertyChanged 인터페이스를 구현한 property를 관찰합니다. 그리고 속성 변경 알림에서 자동적으로 DelegateCommandBase.RaiseCanExecutechanged를 호출합니다.
저 RaiseCanExecuteChanged는 뭘까요?
namespace Prism.Commands
{
//
// 요약:
// An System.Windows.Input.ICommand whose delegates can be attached for Prism.Commands.DelegateCommandBase.Execute(System.Object)
// and Prism.Commands.DelegateCommandBase.CanExecute(System.Object).
public abstract class DelegateCommandBase : ICommand, IActiveAware
{
//
// 요약:
// Creates a new instance of a Prism.Commands.DelegateCommandBase, specifying both
// the execute action and the can execute function.
protected DelegateCommandBase();
//
// 요약:
// Gets or sets a value indicating whether the object is active.
public bool IsActive { get; set; }
//
// 요약:
// Occurs when changes occur that affect whether or not the command should execute.
public event EventHandler CanExecuteChanged;
//
// 요약:
// Fired if the Prism.Commands.DelegateCommandBase.IsActive property changes.
public event EventHandler IsActiveChanged;
//
// 요약:
// Raises Prism.Commands.DelegateCommandBase.CanExecuteChanged so every command
// invoker can requery to check if the command can execute. Note that this will
// trigger the execution of Prism.Commands.DelegateCommandBase.CanExecuteChanged
// once for each invoker.
public void RaiseCanExecuteChanged();
DelegateCommand 가 상속한 DelegateCommandBase 안에 있는 RaiseCanExceuteChanged입니다.
요약을 읽어보면
command가 execute 할 수 있을 때, Prism.Commands.DelegateCommandBase.CanExcecutechanged를 올립니다 모든 command 호출자가 확인할 수 있도록 말입니다. 각 호출자에 대해서 한번씩 실행하게 됩니다.
어...음...번역이 매끄럽지 못한데. 이해를 해보자면
ObservesProperty(람다식)을 실행하게 되면 DelegateCommand를 반환합니다. 다만 이때 반환되는 DelegateCommand는 Property가 변경될때 어떠한 메소드가 trigger로 실행되게 됩니다. 그것이 람다식으로 넘긴 함수 ( )=>IsEnabled가 실행되게 됩니다.
즉 여기서의 IsEnabled는 get{return _isEnabled;}가 실행되게 되는 것입니다.
정확히 이해하자면.
ObservesProperty는 프로퍼티가 변경되어있는지 확인하는 메소드이며 변경되었다면 해당 Command를 실행 할 수 있게합니다.
매개변수로 넘어가는 람다 메소드 즉 프로퍼티 식은, 관찰할 프로퍼티이며 해당 프로퍼티가 바뀔 경우 반응하게 됩니다.
ObservesProperty(변경을 관찰할 프로퍼티)
new DelegateCommand(Execute).ObservesCanExecute(() => IsEnabled); 의 경우
CanExecute가 붙지 않았으므로 프로퍼티가 변경됨에 상관없이 항상 실행이 가능한 상태인데.
여기에 CanExecute를 관찰하는 ObservesCanExecute를 실행함으로써 다른 CanExecute를 이용 할 수 있게 만든 코드입니다.
즉 하나의 DelegateCommand가 굳이 하나의 CanExecute만을 사용하는 것이 아닌 런타임시에 변경 되게 할 수있는 코드입니다.
매개변수로 프로퍼티의 상태를 나타내는 get{}을 넘겼으므로 프로퍼티 상태에 따라서 해당 Command를 사용 할 수 있게 됩니다.
ExecuteGenericDelegateCommand = new DelegateCommand<string>(ExecuteGeneric).ObservesCanExecute(() => IsEnabled);
의 경우 제네릭을 사용하여 만들어진 Command인데.
이건 진짜 모르겠네.
대충 이해하자면 ExecuteGeneric를 실행시키는데 이 실행여부 함수로 ObservesCanExecute를 이용하여 IsEnabled가 실행 가능하게 true 일경우 이 command를 실행 가능하게 하는 것이고.
private void ExecuteGeneric(string parameter)
{
parameter = "what?";
Console.WriteLine("JJH: MainWindowViewModel ExecuteGeneric");
UpdateText = parameter;
}
이렇게 구현되어 있는데.
이 Execute가 실행되면 화면 하단의 시간이 출력되던 부분에서 waht? 이 출력되게 된다.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism에서의 전역 Command 세팅방법 - 모든 View의 Command를 한 커맨드로 실행시키고 싶을 때 (0) | 2019.02.20 |
---|---|
Prism에서 CustomViewModel을 view에 붙이는 방법 (0) | 2019.02.19 |
Prism으로 어떻게 다른 모듈의 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism에서 CustomViewModel을 view에 붙이는 방법
9번 안되서
10번으로 넘어감
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/10-CustomRegistrations
코드 참조
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | using Prism.Ioc; using Prism.Mvvm; using Prism.Unity; using System.Windows; using ViewModelLocator.ViewModels; using ViewModelLocator.Views; namespace ViewModelLocator { /// <summary> /// Interaction logic for App.xaml /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override void ConfigureViewModelLocator() { base.ConfigureViewModelLocator(); // type / type //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), typeof(CustomViewModel)); // type / factory //ViewModelLocationProvider.Register(typeof(MainWindow).ToString(), () => Container.Resolve<CustomViewModel>()); // generic factory //ViewModelLocationProvider.Register<MainWindow>(() => Container.Resolve<CustomViewModel>()); // generic type ViewModelLocationProvider.Register<MainWindow, CustomViewModel>(); } } } | cs |
기본적인 루틴은 같았고.
configureViewModelLocator만 달랐는데.
확인해보면.
스태틱하게 싱글톤으로 살아 있는 ViewModellocationProvider의 Register메소드를 이용하여 <View , ViewModel>() 방식으로 붙여놓았다.
잘된다..
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism에서의 전역 Command 세팅방법 - 모든 View의 Command를 한 커맨드로 실행시키고 싶을 때 (0) | 2019.02.20 |
---|---|
Prism에서 DelegateCommand를 하는 방법 (0) | 2019.02.20 |
Prism으로 어떻게 다른 모듈의 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism으로 어떻게 다른 모듈의 view를 호출하는가
지난번 모듈 예제를 그대로 설명하려한다.
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/HowToPrismModules
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Linq; using System.Threading.Tasks; using System.Windows; using Prism.Ioc; using Prism.Unity; using HowToPrismModules.Views; using Prism.Modularity; using JJHModule; namespace HowToPrismModules { /// <summary> /// App.xaml에 대한 상호 작용 논리 /// </summary> public partial class App : PrismApplication { protected override Window CreateShell() { Console.WriteLine("JJH: App CreateShell"); return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { // // } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { var moduleAType = typeof(Class1); moduleCatalog.AddModule(new ModuleInfo() { ModuleName = moduleAType.Name, ModuleType = moduleAType.AssemblyQualifiedName, InitializationMode = InitializationMode.OnDemand }); } } } | cs |
시작 윈도우로 Mainwindow를 설정하고
Bootstrapper에서 초기화를 시도하는 configureModuleCatalog메소드를 오버라이딩 한다.
1 2 3 4 5 6 7 8 9 10 11 | <prism:PrismApplication x:Class="HowToPrismModules.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:HowToPrismModules" xmlns:prism="http://prismlibrary.com/" > <Application.Resources> </Application.Resources> </prism:PrismApplication> | cs |
혹여나 실행이 안되면 xml부분을 잘 확인하자
<prism:PrismApplication 이라는 점과 xmlns:prism이라는 네임스페이스를 받아오는 것에 유의하면 된다.
configureModuleCatalog 메소드에는 다른 모듈의 class1을 참조하기 위하여
이렇게 참조 추가를 해주어야 한다.
특히 만들어지는 JJHModule, 즉 다른 모듈의 경우 클래스라이브러리 형태의 프로젝트로 만들어야지 참조하기 편하다. 원래 클래스 라이브러리가 맞기도 하고...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Shapes; using Prism.Modularity; namespace HowToPrismModules.Views { /// <summary> /// MainWindow.xaml에 대한 상호 작용 논리 /// </summary> public partial class MainWindow : Window { IModuleManager _moduleManager; public MainWindow(IModuleManager moduleManager) { InitializeComponent(); _moduleManager = moduleManager; } private void Button_Click(object sender, RoutedEventArgs e) { _moduleManager.LoadModule("Class1"); } } } | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <Window x:Class="HowToPrismModules.Views.MainWindow" 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:HowToPrismModules.Views" xmlns:prism="http://prismlibrary.com/" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Button Click="Button_Click" Content="Button" HorizontalAlignment="Left" Height="96" Margin="248,109,0,0" VerticalAlignment="Top" Width="157"/> <ContentControl prism:RegionManager.RegionName="ContentRegion"/> </Grid> </Window> | cs |
이제 메인윈도우에서 해당 View를 가져와야하니 클래스를 이렇게 선언해주고
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Prism.Ioc; using Prism.Modularity; using Prism.Regions; namespace JJHModule { public class Class1 : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<RegionManager>(); regionManager.RegisterViewWithRegion("ContentRegion",typeof(Views.ViewA)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } } | cs |
모듈 쪽의 클래스는 이렇게 만들어 놓아야 한다.
간단하게 OnInitialized라는 메소드가 하는 일은 단순히 RegionManager에 자신의 View를 추가하는 일밖에 하지 않는다.
이후 MainWindow에서는 이 RegionManger를 호출하여 등록명을 불르면 해당 View가 출현되는 방식이다.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism에서 DelegateCommand를 하는 방법 (0) | 2019.02.20 |
---|---|
Prism에서 CustomViewModel을 view에 붙이는 방법 (0) | 2019.02.19 |
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism은 어떻게 View상호작용 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 모듈을 관리하는가
코드는 여기를 참조
https://github.com/PrismLibrary/Prism-Samples-Wpf
여기서 07- Modules-LoadManual
나는 이 코드를 실행 하고 싶어도 실행이 안된다. 이유가 뭔지는 모름 ㅠㅠ
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/HowToPrismModules
일단 성공한 프로젝트
아무튼 분석을 하자면.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog) { var moduleAType = typeof(ModuleAModule); moduleCatalog.AddModule(new ModuleInfo() { ModuleName = moduleAType.Name, ModuleType = moduleAType.AssemblyQualifiedName, InitializationMode = InitializationMode.OnDemand }); } } | cs |
app에서 Mainwindow를 시작 윈도우로 설정하였다.
추가적으로 CinfigureModuleCatalog메소드가 보이는데 이 메소드는 응용프로그램 시작 후 Booststrapper에서 실행하게 되는 메소드이다.
내용을 보자면 자동적으로 만들어진 moduleCatalog를 매개변수로 받아
ModuleModule이라는 괴상한 이름의 클래스 타입을 모듈로 추가하게된다.
이때 추가하는 형태가 ModuleInfo라는 형태인데 ModuleInfo 안에는 ModuleName, Moduletype, InitializationMode 등을 설정할 수 있게 되어 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public partial class MainWindow : Window { IModuleManager _moduleManager; public MainWindow(IModuleManager moduleManager) { InitializeComponent(); _moduleManager = moduleManager; } private void Button_Click(object sender, RoutedEventArgs e) { _moduleManager.LoadModule("ModuleAModule"); } } | cs |
Mainwindow으로 가게되면 moduleManager를 받아오고
클릭할 경우 모듈 메니저에서 LoadModule이라는 메소드를 실행시킨다. 이때 넣는 매개변수가 모듈 이름이 된다
그럼 도대체 이 모듈모듈ModuleModule은 어디서 가저오는 것일까?
ModulA라는 네임스페이스 안에 ModuleModue이 존재하고있다.
이런 변태 같은.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using ModuleA.Views; using Prism.Ioc; using Prism.Modularity; using Prism.Regions; namespace ModuleA { public class ModuleAModule : IModule { public void OnInitialized(IContainerProvider containerProvider) { var regionManager = containerProvider.Resolve<IRegionManager>(); regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA)); } public void RegisterTypes(IContainerRegistry containerRegistry) { } } } | cs |
IModule을 상속받아 구현된 OnInitialized는 매개변수로 컨테이너 제공자라는 매개변수를 받아오는데, 이 컨테이너제공자는 해당 모듈을 실행시킬 때 모듈을 실행 시키는 주체 모듈로부터 받아와진다.
따라서 받은 컨테이너 제공자에서 IRegionManager를 호출하면 해당 RegionManager안에서 ContentRegion이라는 등록명에 ModuleModule의 ViewA를 등록하면
연결이 완료된다.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism에서 CustomViewModel을 view에 붙이는 방법 (0) | 2019.02.19 |
---|---|
Prism으로 어떻게 다른 모듈의 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism은 어떻게 View상호작용 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 View에서 View를 호출하는가 - ViewDiscovery (0) | 2019.02.19 |
Prism은 어떻게 View를 다른 View로 변경하는가
전 포스팅에서 다뤘듯이 RegionManager를 통해 등록명으로 접근 view를 출력 시킬 수 있다는 것을 알 수 있었다.
그러면 응용으로 런타임시에 view를 바꿀 수 있지 않을까?
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/06-ViewActivationDeactivation
코드 참조는 이곳에서 하면 된다
1 2 3 4 5 6 7 8 9 10 11 12 | public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } } | cs |
app에서 Mainwindow를 실행시키게 해놓았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | public partial class MainWindow : Window { IContainerExtension _container; IRegionManager _regionManager; IRegion _region; ViewA _viewA; ViewB _viewB; public MainWindow(IContainerExtension container, IRegionManager regionManager) { InitializeComponent(); _container = container; _regionManager = regionManager; this.Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { _viewA = _container.Resolve<ViewA>(); _viewB = _container.Resolve<ViewB>(); _region = _regionManager.Regions["ContentRegion"]; _region.Add(_viewA); _region.Add(_viewB); } private void Button_Click(object sender, RoutedEventArgs e) { //activate view a _region.Activate(_viewA); } private void Button_Click_1(object sender, RoutedEventArgs e) { //deactivate view a _region.Deactivate(_viewA); } private void Button_Click_2(object sender, RoutedEventArgs e) { //activate view b _region.Activate(_viewB); } private void Button_Click_3(object sender, RoutedEventArgs e) { //deactivate view b _region.Deactivate(_viewB); } } | cs |
Mainwindow를 확인하면
생성자로 받을 컨테이너와 리전매니저를 저장할 멤버변수가 보이고
Loaded 이벤트에 Mainwindow_Loaded 메소드가 붙어있는 것을 확인 할 수 있다.
Loaded 이벤트는 윈도우가 로드 될때 실행되는 이벤트로 그러한 의미에서
MainWindow_Loaded는 윈도우가 실행될때 실행되게 되는 메소드다. 왜 굳이 loaded에 놓았을까?...
Loaded 메소드는 생성자의 InitializeComponent에서 Loaded를 실행시킨다.. 아니면 누가 알주세요.
따라서 InitialzeComponent가 된 후에 Loaded를 한다는 것은....xaml이 실행되기 전에 load를 연결시키겠다는 의미로 보인다.
이후 Mainwindow_Loaded 메소드를 확인하면 배주에 regionManager에서 region을 호출하여 _region에 담는다.
그렇기에 구조가
_regionManager
-> region["ContentRegion"] = _region
-> _viewA
-> _viewB
이런 식으로 구조가 만들어지게 된다.
_region을 포인터용으로 사용하여 만들어진 region["ContentRegion"] 에 접근하여 view를 추가하는 행위이다.
두개 이상의 view가 하나의 region에 연결되어 있는 경우 기본적으로 제일먼저 붙은 view가 실행되겠지만
region.Activate로 출현할 view를 선택할 수 있다.
아무래도 구조가
_regionManger
->region["ContentRegion"] = _region
->_viewA , true
->_viewB , false
이런 형태로 되어 있는 것 같고,
_region.Activate(view)를 이용하여 해당 region["ContentRegion"] 에서 value값으로 view와 같은 녀석을 검색하고
찾았다면 해당 view의 bool 값을 true로 바꾸고 나머지를 false로 바꾸어 출력 가능 대상을 바꾸는 것 같다.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism으로 어떻게 다른 모듈의 view를 호출하는가 (0) | 2019.02.19 |
---|---|
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
Prism은 어떻게 View상호작용 view를 호출하는가 (0) | 2019.02.19 |
Prism은 어떻게 View에서 View를 호출하는가 - ViewDiscovery (0) | 2019.02.19 |
Region이란 무엇인가 (0) | 2019.02.19 |
Prism은 어떻게 View상호작용 view를 호출하는가
기본적인 WPF의 경우
window에서 다른 usercontrol을 출현시키기 위해서는 해당 usercontrol을 알고 있어야 했다.
그것을 prism에서 어떻게 분리하였는지 확인해본다.
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/05-ViewInjection
참고는 이곳에서 하면 된다
1 2 3 4 5 6 7 8 9 10 11 12 | public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } } | cs |
app에서 시작 윈도우로 MainWindow를 지정해 놓았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | public partial class MainWindow : Window { IContainerExtension _container; IRegionManager _regionManager; public MainWindow(IContainerExtension container, IRegionManager regionManager) { InitializeComponent(); _container = container; _regionManager = regionManager; } /* 어차피 regionmanager에 등록하는 과정이다. 바로 registerviewwithregion으로 하나 분리하여 등록 명 따로 붙일 view따로 하나 하는 행동은 같은 것이다. */ private void Button_Click(object sender, RoutedEventArgs e) { var view = _container.Resolve<ViewA>(); _regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA)); //IRegion region = _regionManager.Regions["ContentRegion"]; //region.Add(view); } } | cs |
Main윈도우를 확인하면 IContainerExtension 과 IregionManager가 멤버 변수로 되어 있는데 이는 생성자에서 받을 수 있는 현재 retion manager와 container를 저장하고 있기 위함이다.
이후 Button_Click메소드가
1 2 3 4 5 6 7 8 9 10 11 | <Window x:Class="ViewInjection.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" Title="Shell" Height="350" Width="525"> <DockPanel LastChildFill="True"> <Button DockPanel.Dock="Top" Click="Button_Click">Add View</Button> <ContentControl prism:RegionManager.RegionName="ContentRegion" /> </DockPanel> </Window> | cs |
mainwindow xaml에서 이렇게 붙어있게된다.
이후 컴파일시에는 ContentRegion이라는 것이 무엇인지 몰라 출력하지 않고 있다가
런타임에서 버튼 클릭시 해당 view를 regionmanager에 등록시키는 과정을 진행하여 런타임으로 붙을 수 있게 한다.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
---|---|
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism은 어떻게 View에서 View를 호출하는가 - ViewDiscovery (0) | 2019.02.19 |
Region이란 무엇인가 (0) | 2019.02.19 |
Prism의 구성 Bootstrapper 와 Shell (0) | 2019.02.19 |
Prism은 어떻게 View에서 View를 호출하는가 - ViewDiscovery
이 내용은 ViewDiscovery에 대한 내용이다.
https://github.com/2Bbear/WindowsProgrmaDevelop/tree/master/WPF/UsingMvvmPrismLibrary/04-ViewDiscovery
참조는 04번 항목을 참조하면 된다.
1 2 3 4 5 6 7 8 9 10 11 12 | public partial class App : PrismApplication { protected override Window CreateShell() { return Container.Resolve<MainWindow>(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { } } | cs |
app 부분을 보면 시작 화면으로 MainWindow로 해놓았다.
1 2 3 4 5 6 7 8 9 | public partial class MainWindow : Window { public MainWindow(IRegionManager regionManager) { InitializeComponent(); //view discovery regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA)); regionManager.RegisterViewWithRegion("ContentRegion_Main",typeof(MyView)); } } | cs |
MainWindow 생성자를 확인하면 매개변수로 IRegionManager를 받고있다.
이 RegionManager로 모든 View를 관리 할 수 있게 된다.
여기서 regionManger는 응용프로그램 생성 초기 단계에서 만들어진 객체이다.
이제 이 regionManger에 View를 등록해야한다. (등록 이름, 등록할 타입view)
이후
xaml에서는 이 등록 이름으로 각 view를 호출 할 수 있게 된다.
1 2 3 4 5 6 7 8 9 10 11 | <Window x:Class="ViewDiscovery.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prism="http://prismlibrary.com/" Title="Shell" Height="350" Width="525"> <Grid> <ContentControl prism:RegionManager.RegionName="ContentRegion" /> <ContentControl prism:RegionManager.RegionName="ContentRegion_Main" /> </Grid> </Window> | cs |
이런 식으로 만들어 View가 직접적으로 View를 참조하지 못하게만들어 느슨하게 개발 할 수 있게되는 것이다... 호오.
'중단한 프로젝트 > WPF_PrismLibrary(추후진행)' 카테고리의 다른 글
Prism은 어떻게 모듈을 관리하는가 (0) | 2019.02.19 |
---|---|
Prism은 어떻게 View를 다른 View로 변경하는가 (0) | 2019.02.19 |
Prism은 어떻게 View상호작용 view를 호출하는가 (0) | 2019.02.19 |
Region이란 무엇인가 (0) | 2019.02.19 |
Prism의 구성 Bootstrapper 와 Shell (0) | 2019.02.19 |