Wpf 使用MultiDataTrigger设置ContentPresenter会产生内存泄漏

Wpf 使用MultiDataTrigger设置ContentPresenter会产生内存泄漏,wpf,Wpf,我有一个列表框,其中项目的ContentPresenter由MultiDataTrigger更改,具体取决于某些IsTool和IsAlerting布尔属性: <Grid> <Grid.Resources> <DataTemplate x:Key="LayerTemplate"> <ContentControl x:Name="contentControl" Style="{DynamicResource P

我有一个列表框,其中项目的ContentPresenter由MultiDataTrigger更改,具体取决于某些IsTool和IsAlerting布尔属性:

<Grid>
    <Grid.Resources>
        <DataTemplate x:Key="LayerTemplate">
            <ContentControl x:Name="contentControl" Style="{DynamicResource PageHeaderContentControlStyle}">
                <layers:PageHeader/>
            </ContentControl>
        </DataTemplate>

        <DataTemplate x:Key="ToolTemplate">
            <ContentControl x:Name="contentControl" Style="{DynamicResource ToolHeaderContentControlStyle}">
                <layers:ToolHeader/>
            </ContentControl>
        </DataTemplate>

        <DataTemplate x:Key="LayerAlertTemplate">
            <ContentControl x:Name="contentControl" Style="{DynamicResource PageHeaderAlertContentControlStyle}">
                <layers:PageHeader/>
            </ContentControl>
        </DataTemplate>

        <DataTemplate x:Key="ToolAlertTemplate">
            <ContentControl x:Name="contentControl" Style="{DynamicResource ToolHeaderAlertContentControlStyle}">
                <layers:ToolHeader/>
            </ContentControl>
        </DataTemplate>

        <Style TargetType="{x:Type ListBoxItem}" x:Key="EmptyListViewSelection">
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Border BorderBrush="Transparent"
                                BorderThickness="0"
                                Background="{TemplateBinding Background}"
                                Margin="2"
                                FocusVisualStyle="{x:Null}">
                            <ContentPresenter DataContext="{Binding}" Name="contentPresenter"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding IsTool}" Value="False">
                                <Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource LayerTemplate}"/>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding IsTool}" Value="True">
                                <Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource ToolTemplate}"/>
                            </DataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsTool}" Value="False"/>
                                    <Condition Binding="{Binding IsAlerting}" Value="True"/>
                                </MultiDataTrigger.Conditions>
                                <MultiDataTrigger.Setters>
                                    <Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource LayerAlertTemplate}"/>
                                </MultiDataTrigger.Setters>
                            </MultiDataTrigger>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding IsTool}" Value="True"/>
                                    <Condition Binding="{Binding IsAlerting}" Value="True"/>
                                </MultiDataTrigger.Conditions>
                                <MultiDataTrigger.Setters>
                                    <Setter TargetName="contentPresenter" Property="ContentTemplate" Value="{StaticResource ToolAlertTemplate}"/>
                                </MultiDataTrigger.Setters>
                            </MultiDataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Grid.Resources>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <ListBox Name="listView" 
        BorderThickness="0"
        ItemsSource="{Binding Pages}"
        SelectedValue="{Binding SelectedPage}"
        ScrollViewer.HorizontalScrollBarVisibility="Hidden" 
        ScrollViewer.VerticalScrollBarVisibility="Hidden"
        ItemContainerStyle="{StaticResource EmptyListViewSelection}"
        IsTabStop="False"
        FocusVisualStyle="{x:Null}"
        Focusable="False" Background="{x:Null}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <WrapPanel Orientation="Horizontal" MaxWidth="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBox}}, Path=ActualWidth}" />
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
    <GroupBox Name="pageBox" IsEnabled="{Binding Visible}" Grid.Row="1" DataContext="{Binding Path=SelectedValue, ElementName=listView}">
        <GroupBox.Header>
            <Label Content="{Binding Name}" Padding="0"/>
        </GroupBox.Header>
        <ContentControl Content="{Binding}"/>
    </GroupBox>
</Grid>

如果IsAlerting属性更改通知(通过INotifyPropertyChange)大约每秒触发一次,则我的应用程序的内存使用量将从230MB增加到804MB,持续15分钟,并且应用程序最终会因以下调用堆栈而崩溃:

    Exception Info: System.Reflection.TargetInvocationException
Stack:
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(System.Xaml.XamlReader, System.Xaml.XamlObjectWriter)
   at System.Windows.FrameworkTemplate.LoadTemplateXaml(System.Xaml.XamlObjectWriter)
   at System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(System.Windows.DependencyObject, System.Windows.Markup.IComponentConnector, System.Windows.Markup.IStyleConnector, System.Collections.Generic.List`1<System.Windows.DependencyObject>, System.Windows.UncommonField`1<System.Collections.Hashtable>)
   at System.Windows.FrameworkTemplate.LoadContent(System.Windows.DependencyObject, System.Collections.Generic.List`1<System.Windows.DependencyObject>)
   at System.Windows.StyleHelper.ApplyTemplateContent(System.Windows.UncommonField`1<System.Collections.Specialized.HybridDictionary[]>, System.Windows.DependencyObject, System.Windows.FrameworkElementFactory, Int32, System.Collections.Specialized.HybridDictionary, System.Windows.FrameworkTemplate)
   at System.Windows.FrameworkTemplate.ApplyTemplateContent(System.Windows.UncommonField`1<System.Collections.Specialized.HybridDictionary[]>, System.Windows.FrameworkElement)
   at System.Windows.FrameworkElement.ApplyTemplate()
   at System.Windows.FrameworkElement.MeasureCore(System.Windows.Size)
   at System.Windows.UIElement.Measure(System.Windows.Size)
   at System.Windows.ContextLayoutManager.UpdateLayout()
   at System.Windows.ContextLayoutManager.UpdateLayoutCallback(System.Object)
   at System.Windows.Media.MediaContext+InvokeOnRenderCallback.DoWork()
   at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()
   at System.Windows.Media.MediaContext.RenderMessageHandlerCore(System.Object)
   at System.Windows.Media.MediaContext.RenderMessageHandler(System.Object)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate, System.Object, Int32)
.............
异常信息:System.Reflection.TargetInvocationException 堆栈: 位于System.Windows.FrameworkTemplate.LoadTemplateXaml(System.Xaml.XamlReader、System.Xaml.XamlObjectWriter) 位于System.Windows.FrameworkTemplate.LoadTemplateXaml(System.Xaml.XamlObjectWriter) 位于System.Windows.FrameworkTemplate.LoadOptimizedTemplateContent(System.Windows.DependencyObject、System.Windows.Markup.IComponentConnector、System.Windows.Markup.IStyleConnector、System.Collections.Generic.List`1、System.Windows.Unmonfield`1) 位于System.Windows.FrameworkTemplate.LoadContent(System.Windows.DependencyObject、System.Collections.Generic.List`1) 位于System.Windows.StyleHelper.ApplyTemplateContent(System.Windows.Unmonfield`1,System.Windows.DependencyObject,System.Windows.FrameworkElementFactory,Int32,System.Collections.Specialized.HybridDictionary,System.Windows.FrameworkTemplate) 位于System.Windows.FrameworkTemplate.ApplyTemplateContent(System.Windows.UncommonField`1,System.Windows.FrameworkElement) 在System.Windows.FrameworkElement.ApplyTemplate()中 位于System.Windows.FrameworkElement.MeasureCore(System.Windows.Size) 在System.Windows.UIElement.Measure处(System.Windows.Size) 位于System.Windows.ContextLayoutManager.UpdateLayout()处 位于System.Windows.ContextLayoutManager.UpdateLayoutCallback(System.Object) 在System.Windows.Media.MediaContext+InvokeRenderCallback.DoWork()中 在System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()中 位于System.Windows.Media.MediaContext.RenderMessageHandlerCore(System.Object) 位于System.Windows.Media.MediaContext.RenderMessageHandler(System.Object) 位于System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate、System.Object、Int32) .............
有什么想法吗?

不确定内存泄漏,但我不会像这样更改contenttemplate。如果在ContentPresenter上设置ContentTemplate,ContentPresenter的ContentSource属性(默认为“Content”)将被否决/忽略,Content属性将为空,并且ContentTemplate不再具有datacontext

最好在样式设置器中设置ContentTemplate,并使用样式触发器对其进行更改。ContentPresenter的默认ContentSource将执行其余操作


我怀疑这可能会解决您的内存泄漏问题。

您的内存泄漏可能是由中描述的情况造成的,可能是列表框中的虚拟化加剧了这种情况(或者如果您有很多项,并且它正在一次分配它们,则可能缺少这些项)。事实上,您有一个System.Reflection.TargetInvocationException,这让我觉得您绑定的不是INotifyPropertyChanged,它必须使用反射来确定值是否在更改


在较新版本的Visual Studio(2013,可能2012)中,有一个内存探查器,可以显示哪些对象正在泄漏,这可能有助于缩小搜索范围。

出于某种原因,将MultiDataTrigger移动到单独的样式解决了问题(不再存在内存泄漏):


....
<Style TargetType="{x:Type ContentControl}" x:Key="PageToolHeaderContentControlThemeStyle">
    <Setter Property="Foreground" Value="{StaticResource ForegroundBrush}"/>
    <Setter Property="Padding" Value="5,3"/>
         ....
                <ControlTemplate.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding Alert}" Value="Warning"/>
                            <Condition Binding="{Binding IsAcknowledged}" Value="True"/>
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.Setters>
                            <Setter TargetName="PART_BorderAlarmFrame" Property="Stroke" Value="Orange"/>
                            <Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger.Setters>
                    </MultiDataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding Alert}" Value="Warning"/>
                            <Condition Binding="{Binding IsAcknowledged}" Value="False"/>
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.Setters>
                            <Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger.Setters>
                        <MultiDataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundBrush"
                                                    Storyboard.TargetProperty="Color"
                                                    From="Transparent" To="Orange" Duration="0:0:0.5"
                                                    AutoReverse="True" 
                                                    RepeatBehavior="Forever" />
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiDataTrigger.EnterActions>
                    </MultiDataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding Alert}" Value="Alarm"/>
                            <Condition Binding="{Binding IsAcknowledged}" Value="True"/>
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.Setters>
                            <Setter TargetName="PART_BorderAlarmFrame" Property="Stroke" Value="Red"/>
                            <Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger.Setters>
                    </MultiDataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding Alert}" Value="Alarm"/>
                            <Condition Binding="{Binding IsAcknowledged}" Value="False"/>
                        </MultiDataTrigger.Conditions>
                        <MultiDataTrigger.Setters>
                            <Setter TargetName="PART_BorderAlarmFrame" Property="Visibility" Value="Visible"/>
                        </MultiDataTrigger.Setters>
                        <MultiDataTrigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation Storyboard.TargetName="BackgroundBrush"
                                                    Storyboard.TargetProperty="Color"
                                                    From="Transparent" To="Red" Duration="0:0:0.5"
                                                    AutoReverse="True" 
                                                    RepeatBehavior="Forever" />
                                </Storyboard>
                            </BeginStoryboard>
                        </MultiDataTrigger.EnterActions>
                    </MultiDataTrigger>
                    <Trigger Property="IsMouseOver" Value="True">
                        <Setter Property="Foreground" Value="{DynamicResource LightForegroundBrush}"/>
                        <Setter TargetName="PART_BorderDefaultFrame" Property="Stroke" Value="{StaticResource ButtonPressedBackgroundBrush}"/>
                        <Setter TargetName="PART_Border" Property="Background" Value="{StaticResource ButtonHoverBackgroundBrush}"/>
                    </Trigger>
                    <DataTrigger Binding="{Binding IsSelected}" Value="True">
                        <Setter Property="Foreground" Value="{DynamicResource LightForegroundBrush}"/>
                        <Setter TargetName="PART_BorderDefaultFrame" Property="Stroke" Value="{StaticResource ButtonPressedBackgroundBrush}"/>
                        <Setter TargetName="PART_Border" Property="Background" Value="{StaticResource ButtonPressedBackgroundBrush}"/>
                    </DataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>