WPF MVVM패턴으로 뷰 전환시 View instance 계속 생성되는 문제

안녕하세요. 아래에서 문의를 드렸었는데 코드까지 송부하여 아래와 같이 다시 문의드립니다.

제가 구현하고자 하는 것은 버튼 클릭 시, View가 전환되는 것입니다.
.
.
.
App.xaml의 경우 ↓

<Application x:Class="NavigationView.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:NavigationView"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Mapping.xaml"/>
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
        
    </Application.Resources>
</Application>

.
.
.
Mapping.xaml의 경우 ↓

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:v="clr-namespace:NavigationView.View"
                    xmlns:vm="clr-namespace:NavigationView.ViewModel">

    <DataTemplate DataType="{x:Type vm:RedViewModel}">
        <v:RedUserControl/>
    </DataTemplate>

    <DataTemplate DataType="{x:Type vm:BlueViewModel}">
        <v:BlueUserControl/>
    </DataTemplate>
</ResourceDictionary>

.
.
.
MainWindow.xaml의 경우 ↓

<Window x:Class="NavigationView.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:NavigationView.View"
        xmlns:vm="clr-namespace:NavigationView.ViewModel"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <vm:MainWindowViewModel/>
    </Window.DataContext>
    
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <StackPanel Grid.Row="0" Orientation="Horizontal">
            <Button Content="Red UserControl" Command="{Binding RedCommand}"/>
            <Button Content="Blue UserControl" Command="{Binding BlueCommand}"/>
        </StackPanel>

        <ContentControl Grid.Row="1" Content="{Binding ContentView}"/>

    </Grid>
</Window>

.
.
.
MainWindowViewModel.cs의 경우 ↓

using NavigationView.Core;
using NavigationView.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace NavigationView.ViewModel
{
    public class MainWindowViewModel : ViewModelBase
    {
        private ViewModelManager _viewModelManager;

        private object _contentView;
        public object ContentView
        {
            get { return _contentView; }
            set { _contentView = value; OnPropertyChanged(); }
        }

        public ICommand RedCommand { get; set; }
        public void Red(object obj)
        {
            ContentView = _viewModelManager._redViewModel;
        }
        public ICommand BlueCommand { get; set; }
        public void Blue(object obj)
        {
            ContentView = _viewModelManager._blueViewModel;
        }

        public MainWindowViewModel()
        {
            _viewModelManager = new ViewModelManager();

            RedCommand = new RelayCommand<object>(Red);
            BlueCommand = new RelayCommand<object>(Blue);

            ContentView = _viewModelManager._redViewModel;
        }

    }
}

.
.
.
RedUserControl.xaml의 경우 ↓

<UserControl x:Class="NavigationView.View.RedUserControl"
             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:NavigationView.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             Background="Red">
    <Grid>
            
    </Grid>
</UserControl>

.
.
.
RedViewModel.cs의 경우 ↓

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NavigationView.ViewModel
{
    public class RedViewModel
    {
        public RedViewModel()
        {

        }
    }
}

.
.
.
BlueUserControl.xaml의 경우 ↓

<UserControl x:Class="NavigationView.View.BlueUserControl"
             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:NavigationView.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800"
             Background="Blue">
    <Grid>
            
    </Grid>
</UserControl>

.
.
.
BlueViewModel.cs의 경우 ↓

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NavigationView.ViewModel
{
    public class BlueViewModel
    {
        public BlueViewModel()
        {

        }
    }
}

.
.
.
ViewModelManager.cs의 경우 ↓

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace NavigationView.ViewModel
{
    public class ViewModelManager
    {
        public RedViewModel _redViewModel;
        public BlueViewModel _blueViewModel;

        public ViewModelManager()
        {
            _redViewModel = new RedViewModel();
            _blueViewModel = new BlueViewModel();
        }
    }
}

여기까지 작성을 했고 ViewModel을 전환 시 View가 재생성이 되는 현상이 발생되어 일전에 말씀하신 블로그를 참고해봤는데 제가 지식이 좀 부족하여 계속 막혀 진행이 되지 않고 있습니다ㅠ

이부분만 뚫리지 않아 고민하던차에 문의드리게 되었습니다.
감사합니다.

2개의 좋아요

@ryanoh1991 음 ContentView에 뷰를 스위칭 하고 있는 것이 아니라 각각의 ViewModel을 통해 전환하고 계시잖아요? 이 때문에 ContentControl의 ContentView, 즉 Content의 ContentTemplate이 전환되는 (ViewModel) 데이터 타입에 맞게 (Mapping.xaml에 정의된 시나리오 대로) 계속 인스턴스를 생성하고 있는 것입니다.

이것은 Window, ListBox, ListBoxItem, Button 등 ContentControl을 상속 받는 모든 컨트롤이 ContentPresenter과 DataTemplate을 활용할 때 모두 동일합니다.

아침이라 머리가 잘 안 돌아가서… 설명이 복잡하네요. GPT로 한번 정리해보시면 좋을 것 같습니다. :smile:

1개의 좋아요

위 게시물을 참고해 보세요.

참고로 구현에 정답은 없으며, 상황이나 여건에 따라 알맞는 방법이 있기 때문에 결국 적절히 판단하여 구현하셔야 합니다.

일전 게시물에서 다른 분께서 답변하신 것처럼 cache가 반드시 필요한 것도 아니며, 오히려 필요 이상의 리소스를 투입하는 모양이 될 수도 있습니다.

단지 DataTemplate을 이용해 특정 객체와 View를 매핑해 렌더링하는 방식은 View의 인스턴스가 계속 재생성된다는 문제점이 있다는 것이 핵심이고, 그것에 적용할 만한 해결책 중 하나로 위와 같은 방법이 있으며, 본인이 마주한 시나리오에서 그런 문제점이 영향을 미치는지 아닌지를 적절히 판단하는 것이 중요하다고 봅니다.

4개의 좋아요