WPF Listbox 메모리 증가 문제

안녕하세요. WPF Listbox 메모리 관리 방법 혹은 팁을 조언 받고싶어서 글을 작성하게 되었습니다.

현재 제가 만든 프로그램은 ListBox의 ItemSource가 클릭할때마다, 아주 자주 바뀌는 데이터입니다.
ListBox안에 DataTemplate이 새로운 View를 생성하는 구조로 알고있습니다.
이구조 때문에 계속해서 메모리가 늘어나고 있는것같은데
한번 생긴 View는 Listbox에서 캐싱 혹은 풀링 하는 방법이있는지 혹은 방법이 없다면 제가 접근해야하는 카테고리는 무엇인지 도움 받고싶습니다.

아래 가상화 방법도 적용해보았으나, 메모리 상승이 여전히 되었습니다.

[상황]

  • Recipe를 자주 번갈아가면서 클릭하면서 Algorism을 수정 작업을 하는 프로그램
  • LeftView에 ARecipe클릭시 AlogosimView와 ResultView를 로드
  • ContentView에서 AlgorismItem클릭시 ParamerView를 로드
  • Recipe를 클릭할때마다 평균 100개의 Alogorism이 생성됨
  • 이 과정에서 메모리가 증가가 높음 GC가 돌기전에 프로그램이 살짝 프리징될 정도
[예시 코드] 

[1] 메인 화면
// MainView
<ListBox x:Name="RecipeList" ItemSource="{Binding SelectedRecipeViewModel">
    <ListBox.Resources> 
        <DataTemplate DataType="{x:Type vm:RecipeViewModel }">
           <view:RecipeView></view:RecipeView>
       </DataTemplate>
      </ListBox.Resources> 
</ListBox>


// MainViewModel
Public RecipeViewModel SelectedRecipeViewModel = null; 


[2] Recipe 화면 
//RecipeViewModel 

<ListBox x:Name="AlgorismList" ItemSource={Binding AlgorismListCollection} >
    <ListBox.Resources> 
        <DataTemplate DataType="{x:Type vm:ATypeAlgorismModel}">
           <view:ATypeAlgorismView></view:ATypeAlgorismView>
       </DataTemplate>
        <DataTemplate DataType="{x:Type vm:BTypeAlgorismModel}">
           <view:BTypeAlgorismView></view:BTypeAlgorismView>
       </DataTemplate>
      </ListBox.Resources> 
</ListBox>

[3] Algorism 화면 

<ListBox x:Name="AlgorismList" ItemSource={Binding ParameterTypeCollection} >
    <ListBox.Resources> 

//AParamType.. 

//BParamType..

//CParamType..      


      </ListBox.Resources> 
</ListBox>

자주 바뀌는 리스트박스에 적용해보면 어떨까요?

적용해보았는데도 효과가없습니다.ㅠㅠ
모든 이벤트와 behavior를 전부 삭제하고 해봐도 클릭시 ListBox를 그릴때마다 메모리 증가정도가 심한데
이런 경우 어떻게 접근해야할까요

제가 생각하기에도 이게 원인인 것 같습니다.
MVVM 원칙을 지키시기 위해 View를 사전에 생성하지 않고 ViewModel의 타입에 따라 DataTemplate을 랜더링하게 해놓으신 듯한데, 그러면 View 객체들이 새로 만들어질 것은 당연하지요.

위 방법은…원글을 보니 알고 계실 듯한데, ViewModel이 미리 생성되어 있어도 View는 ViewModel이 갈아 끼워질때마다 계속해서 만들어지는 구조로, 종속되는 컨트롤들의 상호작용이 번번할수록 오버헤드가 될 것 같습니다.

말씀하신대로 View를 생성한게 있다면 code behind를 이용해서 저장하시는 건 어떤가요? 이런건 MVVM 원칙을 위배하지 않습니다.

사실 Zero Code Behind는 욕심이라고 생각하는 편이라…


물론 View를 Code Behind에서 C#을 이용해 생성하고 ViewModel을 DataContext에 할당했다가 다른 View로 교체되면 DataContext에 살아있는 ViewModel 객체가 물려있어서 View의 메모리 해제가 안되는…버그라면 버그? 인 문제가 있다고 예전에 들었던 것 같습니다. 이런 부분만 조심하면 되지 않을까 싶습니다.

2 Likes


혹시 최상위 ListBox 컨트롤의 하위 Visual 요소가 몇 개인지 한 번 확인 부탁드립니다.

하위 요소를 펼쳐서 캡쳐한 이미지를 같이 올려주셔도 도움이 될 것 같네요.

3 Likes

답변 감사합니다.
말씀하신대로 Code behind로 캐싱하는 방법도 생각해보아야겠습니다.

말씀하신 아래 부분이 맘에 걸리네요… 한번 찾아서 공부해보겠습니다.

물론 View를 Code Behind에서 C#을 이용해 생성하고 ViewModel을 DataContext에 할당했다가 다른 View로 교체되면 DataContext에 살아있는 ViewModel 객체가 물려있어서 View의 메모리 해제가 안되는…버그라면 버그? 인 문제가 있다고 예전에 들었던 것 같습니다. 이런 부분만 조심하면 되지 않을까 싶습니다.

1 Like

답변감사합니다.
1730개입니다…

[1] ListBox 트리

[2] ListBox Item의 트리

생각보다 view 를 cache 하는 게 번거롭고 체크해야할 게 많은 문제이긴하죠.

만약 한 ListBox 안에서 view의 template 이 바뀌지 않는 형태라고 하면
ListBox 를 직접 사용하지 말고 ItemsControl 로 교체해보는 건 어떨까 싶네요.

대신 template 에 대한 구현은 추가로 해야하긴 하지만
virtualizingStackPanel + itemsControl 조합이면 어느정도 view cache 를 커버할 수 있지 않을까… 하는 생각이 들긴하지만

view 자체가 변경되는 상황이라면 무용지물입니다.

그럴 땐 cache 를 위한 전용 panel 을 작성해서 처리해야할 수도 있어요…

그 상황이 안 오기를 바라며… =ㅁ=

2 Likes

1~2천개 정도라면 크게 복잡한 뷰는 아닌 것 같은데요.


툴 바 마지막에 [내 XAML만 표시] 체크를 해제한 후, 한 번 더 캡처해 올려주세요.
Visualization이 정상적으로 적용되었는지 확인하기 위함입니다.

가능하시다면 민감한 정보를 가린 상태에서 전체 뷰 캡처도 함께 부탁드립니다.

현재 View나 ViewModel의 레이아웃 자체가 메모리를 과도하게 사용하는 것은 아닌 것으로 보입니다.
Visualization이 제대로 적용되지 않은 상태에서 다수의 View가 생성되고 있으며,
View의 생성자나 Loaded 이벤트 핸들러에서 무거운 작업이 수행되거나,
ViewModel 생성 시점에 많은 데이터를 추가 생성하고 있는 것이 아닌가 추측됩니다.

3 Likes

신경써주셔서 감사합니다. ㅠㅠ…

[내 XAML만 표시를 해제한 View]

View구조의 데이터 이름만 가리고 구조만 보내드립니다.

[상위 ListBox가 있는 View]
원본 글에서 RecipeView(ContentView)

... 생략  

      <ListBox ItemsSource="{Binding 선택된컨테이너.컬렉션}"
          SelectedItem="{Binding 선택된컨테이너.레시피뷰모델, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
          VerticalContentAlignment="Stretch"
          HorizontalContentAlignment="Stretch"
          SelectionMode="Extended"
          VirtualizingPanel.IsVirtualizing="True"
          VirtualizingPanel.VirtualizationMode="Recycling"
          HorizontalAlignment="Stretch"
          VerticalAlignment="Stretch">

     <behavior:Interaction.Behaviors>
         <compositeBehavior:CompositeKeyDownBehavior DeleteCommand="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"
                                                     ShowEditViewCommand="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"></compositeBehavior:CompositeKeyDownBehavior>
     </behavior:Interaction.Behaviors>

     <ListBox.Resources>
         <DataTemplate DataType="{x:Type 알고리즘타입1모델}">
             <view:CompositeContextListView Height="Auto"
                                            IsLabelVisible="{Binding DataContext.프로퍼티, RelativeSource={RelativeSource AncestorType=local:현재뷰}, Mode=TwoWay}"
                                            CloseCommand="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"
                                            ConnectVarHolderCommand="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"> 
                 <behavior:Interaction.Behaviors>
                     <compositeBehavior:DropCompositeBehavior Command="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"></compositeBehavior:DropCompositeBehavior>
                 </behavior:Interaction.Behaviors>
             </view:CompositeContextListView>
         </DataTemplate>
         <DataTemplate DataType="{x:Type 알고리즘타입2모델}">
             <view:CompositeListView Height="Auto"
                                     IsLabelVisible="{Binding DataContext.프로퍼티, RelativeSource={RelativeSource AncestorType=local:현재뷰}, Mode=TwoWay}"
                                     CloseCommand="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"
                                     ConnectVarHolderCommand="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}">
                 <behavior:Interaction.Behaviors>
                     <compositeBehavior:DropCompositeBehavior  Command="{Binding RelativeSource={RelativeSource AncestorType=local:현재뷰}, Path=DataContext.커멘드}"></compositeBehavior:DropCompositeBehavior>
                 </behavior:Interaction.Behaviors>
             </view:CompositeListView>
         </DataTemplate>
     </ListBox.Resources>

     
     <ListBox.ItemContainerStyle>
         <Style TargetType="ListBoxItem">
             <Style.Triggers>
                 <Trigger Property="IsSelected" Value="True" >
                     <Setter Property="Background" Value="Transparent" />
                 </Trigger>
             </Style.Triggers>
             <Setter Property="Template">
                 <Setter.Value>
                     <ControlTemplate TargetType="ListBoxItem">
                         <Border>
                             <Border.Style>
                                 <Style TargetType="Border">
                                     <Setter Property="BorderThickness" Value="3" />
                                     <Setter Property="BorderBrush" Value="#1c1c1c" />
                                     <Style.Triggers>
                                         <DataTrigger Binding="{Binding 프로퍼티, RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
                                                      Value="True">
                                             <DataTrigger.EnterActions>
                                                 <BeginStoryboard Name="SelectionEffect">
                                                     <Storyboard>
                                                         <ColorAnimation AutoReverse="True" RepeatBehavior="Forever" Duration="0:0:1" To="#135f93" Storyboard.TargetProperty="BorderBrush.Color" />
                                                     </Storyboard>
                                                 </BeginStoryboard>
                                             </DataTrigger.EnterActions>
                                             <DataTrigger.ExitActions>
                                                 <StopStoryboard BeginStoryboardName="SelectionEffect"></StopStoryboard>
                                             </DataTrigger.ExitActions>
                                         </DataTrigger>
                                     </Style.Triggers>
                                 </Style>
                             </Border.Style>
                             <ContentPresenter></ContentPresenter>
                         </Border>
                     </ControlTemplate>
                 </Setter.Value>
             </Setter>
             <Style.Resources>
                 <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black"/>
                 <!-- Background of selected item when focussed -->
                 <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightCyan"/>
                 <!-- Background of selected item when not focussed -->
                 <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightGray" />
             </Style.Resources>
         </Style>
     </ListBox.ItemContainerStyle>
     
 </ListBox>
... 생략   

[ListBox가 가지고있는 아이템 뷰 (문제의 뷰)]
원본 글에서 AlgorismView

<UserControl >
    <UserControl.Resources>
        <converter:알고리즘이름으로이미지를리턴하는컨버터 x:Key="알고리즘이름으로이미지를리턴하는컨버터"></converter:알고리즘이름으로이미지를리턴하는컨버터>
        <dx:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"></dx:BoolToVisibilityConverter>
        <dx:BoolInverseConverter x:Key="BoolInverseConverter"></dx:BoolInverseConverter>
    </UserControl.Resources>

    <Grid>

        <Border BorderThickness="3">
            <Border.Style>
                <Style TargetType="{x:Type Border}">
                    <Setter Property="BorderBrush" Value="#252526"></Setter>
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding 프로퍼티1, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
                            <Setter Property="BorderBrush" Value="Orange"></Setter>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding 프로퍼티2, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
                            <Setter Property="BorderBrush" Value="Red"></Setter>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding 프로퍼티3, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="True">
                            <Setter Property="BorderBrush" Value="Green"></Setter>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Border.Style>
        </Border>

        <DockPanel Margin="3">
            <Grid DockPanel.Dock="Top"
                  Height="25">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition MinWidth="25"></ColumnDefinition>
                    <ColumnDefinition MinWidth="25"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <Border Grid.Column="0"
                        Grid.ColumnSpan="5">
                    <Border.Style>
                        <Style TargetType="Border">
                            <Setter Property="Background" Value="#1f1f1f"></Setter>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding 타입1}" Value="0">
                                    <Setter Property="Background" Value="#1f1f1f"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding 타입2}" Value="1">
                                    <Setter Property="Background" Value="#cc8d10"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding 타입3}" Value="2">
                                    <Setter Property="Background" Value="#cf372c"></Setter>
                                </DataTrigger>
                                <DataTrigger Binding="{Binding 타입4}" Value="3">
                                    <Setter Property="Background" Value="#005e9f"></Setter>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Border.Style>
                </Border>
                <Image Grid.Column="0"
                       Source="{Binding 프로퍼티, Converter={StaticResource 알고리즘이름으로이미지를리턴하는컨버터}}"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"></Image>

                <TextBlock Text="{Binding 프로퍼티}"
                           HorizontalAlignment="Center"
                           VerticalAlignment="Center"
                           FontSize="15"
                           Grid.Column="1">
                </TextBlock>

                <TextBlock VerticalAlignment="Center"
                           Width="250"
                           TextAlignment="Center"
                           TextTrimming="CharacterEllipsis"
                           TextWrapping="NoWrap"
                           FontWeight="Bold"
                           FontSize="15"
                           ToolTipService.BetweenShowDelay="0"
                           ToolTipService.InitialShowDelay="0"
                           ToolTip="{Binding 프로퍼티, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
                           Grid.Column="2">
                    <TextBlock.Style>
                        <Style TargetType="TextBlock">
                            <Setter Property="Text" Value="{Binding 프로퍼티, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding ElementName=view, Path=IsLabelVisible}" Value="True">
                                    <Setter Property="Text" Value="{Binding 프로퍼티, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </TextBlock.Style>
                </TextBlock>

                <UniformGrid Grid.Column="3"
                             Rows="1">
                    <Button Margin="0"
                            Padding="0"
                            ToolTip="커스텀 변수 추가"
                            ToolTipService.BetweenShowDelay="0"
                            ToolTipService.InitialShowDelay="0"
                            Command="{Binding 커멘드}">
                        <Image Source="{dx:DXImage 'SvgImages/Icon Builder/Actions_Add.svg'}"></Image>
                        <Button.Style>
                            <Style TargetType="Button">
                                <Style.Triggers>
                                    <DataTrigger Binding="{convMVVM:EnumStateExtensions 프러퍼티1}" Value="TRUE">
                                        <Setter Property="IsEnabled" Value="False"></Setter>
                                    </DataTrigger>
                                    <DataTrigger Binding="{convMVVM:EnumStateExtensions 프러퍼티2}" Value="TRUE">
                                        <Setter Property="IsEnabled" Value="False"></Setter>
                                    </DataTrigger>
                                    <DataTrigger Binding="{convMVVM:EnumStateExtensions 프러퍼티3}" Value="TRUE">
                                        <Setter Property="IsEnabled" Value="False"></Setter>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                    </Button>
                    <Button Margin="0"
                            Padding="0"
                            ToolTip="커스텀 변수 삭제"
                            ToolTipService.BetweenShowDelay="0"
                            ToolTipService.InitialShowDelay="0"
                            Command="{Binding 커멘드}">
                        <Image Source="{dx:DXImage 'SvgImages/Icon Builder/Actions_Remove.svg'}"></Image>
                        <Button.Style>
                            <Style TargetType="Button">
                                <Style.Triggers>
                                    <DataTrigger Binding="{convMVVM:EnumStateExtensions 프러퍼티1}" Value="TRUE">
                                        <Setter Property="IsEnabled" Value="False"></Setter>
                                    </DataTrigger>
                                    <DataTrigger Binding="{convMVVM:EnumStateExtensions 프러퍼티2}" Value="TRUE">
                                        <Setter Property="IsEnabled" Value="False"></Setter>
                                    </DataTrigger>
                                    <DataTrigger Binding="{convMVVM:EnumStateExtensions 프러퍼티3}" Value="TRUE">
                                        <Setter Property="IsEnabled" Value="False"></Setter>
                                    </DataTrigger>
                                </Style.Triggers>
                            </Style>
                        </Button.Style>
                    </Button>

                    <dx:SimpleButton Margin="0"
                                     Padding="0"
                                     ButtonKind="Toggle"
                                     IsChecked="{Binding 프로퍼티}">
                        <Image Source="{dx:DXImage 'SvgImages/XAF/Action_Workflow_Deactivate.svg'}"></Image>
                    </dx:SimpleButton>

                    <Button Margin="0"
                            Padding="0"
                            Command="{Binding ElementName=view, Path=커멘드}"
                            CommandParameter="{Binding ElementName=view, Path=DataContext}">
                        <Image Source="{dx:DXImage 'SvgImages/RichEdit/CloseHeaderAndFooter.svg'}"></Image>
                    </Button>

                </UniformGrid>
                
            </Grid>


            <Grid Visibility="{Binding 프로퍼티, Converter={StaticResource BoolToVisibilityConverter}}"
                  DockPanel.Dock="Top">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="150"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                    <ColumnDefinition Width="150"></ColumnDefinition>
                </Grid.ColumnDefinitions>

                <Image Grid.Column="1"
                       Grid.Row="0"
                       Source="{Binding Name, Converter={StaticResource 알고리즘이름으로이미지를리턴하는컨버터}}"
                       HorizontalAlignment="Center"
                       VerticalAlignment="Center"
                       Height="40">
                </Image>

                <Grid Grid.Column="0">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>
                    <ItemsControl ItemsSource="{Binding 입력파라메터컬렉션}">
                        <ItemsControl.Resources>
                            <DataTemplate DataType="{x:Type 파라메터 타입1}">
                                <varView:파라메터뷰 FontColor="Orange">
                                    <behavior:Interaction.Behaviors>
                                        <varBehavior:DropVarHolderBehavior Command="{Binding 커멘드}"></varBehavior:DropVarHolderBehavior>
                                        <varBehavior:CtrlMouseDoubleClickBehavior Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=ConnectVarHolderCommand}"
                                                                                  CompositeViewModel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext}"
                                                                                  VarHolderViewModel="{Binding }"
                                                                                  HolderType="Input"></varBehavior:CtrlMouseDoubleClickBehavior>
                                    </behavior:Interaction.Behaviors>
                                </varView:파라메터뷰>
                            </DataTemplate>
                        </ItemsControl.Resources>
                    </ItemsControl>

                    <ItemsControl ItemsSource="{Binding 출력파라메터컬렉션}"
                                  Grid.Row="1">
                        <ItemsControl.Resources>
                            <DataTemplate DataType="{x:Type 파라메터 타입2}">
                                <varView:파라메터뷰 FontColor="Green">
                                    <behavior:Interaction.Behaviors>
                                        <varBehavior:DragVarHolderBehavior ParentLabel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext.프로퍼티}"></varBehavior:DragVarHolderBehavior>
                                        <varBehavior:CtrlMouseDoubleClickBehavior Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=커멘드}"
                                                                                  CompositeViewModel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext}"
                                                                                  VarHolderViewModel="{Binding }"
                                                                                  HolderType="Output"></varBehavior:CtrlMouseDoubleClickBehavior>
                                    </behavior:Interaction.Behaviors>
                                </varView:파라메터뷰>
                            </DataTemplate>
                        </ItemsControl.Resources>
                    </ItemsControl>
                    <ItemsControl ItemsSource="{Binding 결과아이템컬렉션}"
                                  Grid.Row="2">
                        <ItemsControl.Resources>
                            <DataTemplate DataType="{x:Type 결과타입}">
                                <varView:형식이다른파라메터뷰 FontColor="IndianRed">
                                    <behavior:Interaction.Behaviors>
                                        <varBehavior:DragVarHolderBehavior ParentLabel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext.프로퍼티}"></varBehavior:DragVarHolderBehavior>
                                    </behavior:Interaction.Behaviors>
                                </varView:형식이다른파라메터뷰>
                            </DataTemplate>
                        </ItemsControl.Resources>
                    </ItemsControl>
                </Grid>

                <Grid Grid.Column="2">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="*"></RowDefinition>
                    </Grid.RowDefinitions>

                    <ItemsControl ItemsSource="{Binding InputVarHolderCollection}"
                                  Grid.Row="0">
                        <ItemsControl.Resources>
                            <DataTemplate DataType="{x:Type varViewModel:VarHolderViewModel}">
                                <varView:형식이다른파라메터뷰 FontColor="Orange">
                                    <behavior:Interaction.Behaviors>
                                        <varBehavior:DropVarHolderBehavior Command="{Binding DropConnectCommand}"></varBehavior:DropVarHolderBehavior>
                                        <varBehavior:CtrlMouseDoubleClickBehavior Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=ConnectVarHolderCommand}"
                                                                                  CompositeViewModel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext}"
                                                                                  VarHolderViewModel="{Binding }"
                                                                                  HolderType="Input"></varBehavior:CtrlMouseDoubleClickBehavior>
                                    </behavior:Interaction.Behaviors>
                                </varView:형식이다른파라메터뷰>
                            </DataTemplate>
                        </ItemsControl.Resources>
                    </ItemsControl>
                    <ItemsControl ItemsSource="{Binding 파라메터 컬렉션}"
                                  Grid.Row="1">
                        <ItemsControl.Resources>
                            <DataTemplate DataType="{x:Type 파라메터}">
                                <varView:형식이다른파라메터뷰 FontColor="Green">
                                    <behavior:Interaction.Behaviors>
                                        <varBehavior:DragVarHolderBehavior ParentLabel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext.프로퍼티}"></varBehavior:DragVarHolderBehavior>
                                        <varBehavior:CtrlMouseDoubleClickBehavior Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=커멘드}"
                                                                                  CompositeViewModel="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:현재뷰}}, Path=DataContext}"
                                                                                  VarHolderViewModel="{Binding }"
                                                                                  HolderType="Output"></varBehavior:CtrlMouseDoubleClickBehavior>
                                    </behavior:Interaction.Behaviors>
                                </varView:형식이다른파라메터뷰>
                            </DataTemplate>
                        </ItemsControl.Resources>
                    </ItemsControl>
                </Grid>


            </Grid>

            <Grid Visibility="{Binding FieldVisibility, Converter={StaticResource BoolToVisibilityConverter}}"
                  DockPanel.Dock="Top">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBox Text="Execution Time(ms)"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Stretch"
                         HorizontalContentAlignment="Center"
                         VerticalContentAlignment="Center"
                         Cursor="Arrow"
                         IsReadOnly="True"
                         Focusable="False"  
                         Grid.Column="0">
                </TextBox>
                <TextBox Text="{Binding 프로퍼티}"
                         TextWrapping="Wrap"
                         AcceptsReturn="True"
                         IsReadOnly="True"
                         Grid.Column="1">
                </TextBox>
            </Grid>

            <Grid Visibility="{Binding 프로퍼티, Converter={StaticResource BoolToVisibilityConverter}}"
                  DockPanel.Dock="Top">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBox Text="Label"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Stretch"
                         HorizontalContentAlignment="Center"
                         VerticalContentAlignment="Center"
                         Cursor="Arrow"
                         IsReadOnly="True"
                         Focusable="False"  
                         Grid.Column="0">
                </TextBox>
                <TextBox Text="{Binding 프로퍼티}"
                         TextWrapping="Wrap"
                         AcceptsReturn="True"
                         Grid.Column="1">
                </TextBox>
            </Grid>

            <Grid Visibility="{Binding 프로퍼티, Converter={StaticResource BoolToVisibilityConverter}}"
                  DockPanel.Dock="Top">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="120"></ColumnDefinition>
                    <ColumnDefinition Width="*"></ColumnDefinition>
                </Grid.ColumnDefinitions>
                <TextBox Text="Comment"
                         HorizontalAlignment="Stretch"
                         VerticalAlignment="Stretch"
                         HorizontalContentAlignment="Center"
                         VerticalContentAlignment="Center"
                         Cursor="Arrow"
                         IsReadOnly="True"
                         Focusable="False"  
                         Grid.Column="0"></TextBox>
                <TextBox Text="{Binding 프로퍼티, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                         TextWrapping="Wrap"
                         AcceptsReturn="True"
                         Grid.Column="1"></TextBox>
            </Grid>
        </DockPanel>
    </Grid>
</UserControl>

답변해주셔서 감사합니다.

너무 슬프게도 view의 template이 바껴야합니다…ㅠㅠ
그래도 조언해주셔서 감사합니다.
WPF를 자세히 모르니 어렵네요 ㅠㅠ

요소가 5천개 정도라 하더라도 모든 항목이 다 보여지는 것이 아니기 때문에 멈춤 현항이나 메모리 증가는 크지 않을것 같습니다.

Visual Tree를 보니 Dev Express 컴포넌트가 적용된 것 같은데요, 기본 ListBox 컨트롤이나 Dev Express 컴포넌트의 테마가 적용된 ListBox를 재정의해서 사용하지 않았다면 기본적으로 Virtualization이 적용되어 있을것으로 보입니다.

일단 의심되든 부분인 알고리즘이름으로이미지를리턴하는컨버터를 사용하는 Image 요소 부분만 주석처리하고 메모리가 늘어나는지 한번 확인해주세요.
알고리즘이름으로이미지를리턴하는컨버터 클래스 소스도 올려주시면 좋을 것 같습니다.

3 Likes

일단 의심되든 부분인 알고리즘이름으로이미지를리턴하는컨버터 를 사용하는 Image 요소 부분만 주석처리하고 메모리가 늘어나는지 한번 확인해주세요.

를 주석해도, 랜더링될때마다 메모리는 계속 높게 증가하고 있습니다.
저도 맨 처음에 이미지랑 각종 이벤트 및 behavior부터 주석을 해보았지만 메모리 증가는 계속 증가했습니다.

view에 무거울만한 listbox나 여러 trigger등을 주석을 치면 메모리 증가 현상이
감소 하긴합니다. 말씀하신 것처럼 view의 레이아웃 자체가 메모리를 과도하게 사용하는 듯하네요…

[알고리즘이름으로이미지를리턴하는컨버터]

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {

                var compositeName = (string)value;
                return CompositeSymbol.GetImages(compositeName);

            }
            catch (Exception ex)
            {
                return CompositeSymbol.GetImages("");
            }
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
1 Like