2015-01-13 3 views
0

У меня есть ListBox, который ContentPresenter на вещи меняется на MultiDataTrigger в зависимости от некоторого ISTool и IsAlerting логических свойств:Установка ContentPresenter с MultiDataTrigger производит утечку памяти

<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) 
............. 

ИН EAS?

+0

Что вы пытаетесь сделать? Почему вы меняете IsAlerting 50 раз в секунду? Как пользователь может работать с дисплеем, изменяющимся 50 раз в секунду? – Paparazzi

+0

Я обновляю свойство IsAlerting 50 раз в секунду только для тестирования (в реальном мире IsAlerting меняется один раз в час). – Dmitriano

+0

Тогда в чем проблема? 50 раз в секунду - искусственное нереалистичное тестовое условие. – Paparazzi

ответ

0

Не уверен в утечке памяти, но я бы не изменил шаблон contenttemplate, как этот. Если вы установите ContentTemplate в ContentPresenter, свойство ContentSource ContentPresenter (которое по умолчанию является «Контент») будет отменено/проигнорировано, свойство Content будет пустым, а ваш ContentTemplate больше не будет иметь datacontext.

Лучше установить ContentTemplate в устройстве настройки стиля и использовать триггеры стиля для его изменения. По умолчанию ContentSource ContentPresenter сделает все остальное.

Я подозреваю, что это может решить вашу утечку памяти.

0

Утечка памяти может быть вызвана ситуацией, описанной в Can bindings create memory leaks in WPF?, возможно, усугубленной виртуализацией в окне списка (или, возможно, отсутствием ее, если у вас много элементов, и она распределяет их все сразу). Тот факт, что у вас есть System.Reflection.TargetInvocationException, заставляет меня думать, что вы привязываетесь к чему-то, что не является INotifyPropertyChanged, которое затем должно использовать отражение, чтобы выяснить, изменяются ли значения.

В новых версиях Visual Studio (2013, возможно, 2012) есть профилировщик памяти, который может показать, какие объекты протекают, что может помочь вам сузить ваш поиск.

+0

Это хорошая идея использовать профайлер памяти ... – Dmitriano

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>