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

时间:2015-01-13 15:05:19

标签: wpf

我有一个ListBox,项目的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)
.............

任何想法?

3 个答案:

答案 0 :(得分:0)

不确定内存泄漏,但我不会像这样更改内容模板。如果您在ContentPresenter上设置ContentTemplate,ContentPresenter的ContentSource属性(默认情况下为&#39; Content&#39;)将被取消/忽略,Content属性将为空,而您的ContentTemplate不会有一个datacontext了。

最好在样式设置器中设置ContentTemplate并使用样式触发器来更改它。 ContentPresenter的默认ContentSource将完成剩下的工作。

我怀疑这可能会解决你的内存泄漏。

答案 1 :(得分:0)

您的内存泄漏可能是由Can bindings create memory leaks in WPF?中描述的情况引起的,可能会因列表框中的虚拟化而加剧(或者如果您有很多项目并且一次性分配所有项目,则可能缺少内存泄漏)。你有一个System.Reflection.TargetInvocationException的事实让我觉得你绑定的东西不是INotifyPropertyChanged,然后必须使用反射来判断值是否在变化。

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

答案 2 :(得分:0)

出于某种原因,将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>