|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// Description: ItemsPresenter object - site of the panel for layout of groups or items.
//
// Specs: Data Styling.mht
//
using System;
using System.Collections;
using System.Diagnostics;
using System.Windows.Media;
using MS.Internal;
using MS.Internal.Data; // WeakRefKey
namespace System.Windows.Controls
{
/// <summary>
/// An ItemsPresenter marks the site (in a style) of the panel that controls
/// layout of groups or items.
/// </summary>
[Localizability(LocalizationCategory.NeverLocalize)]
public class ItemsPresenter : FrameworkElement
{
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
/// <summary>
/// Called when the Template's tree is about to be generated
/// </summary>
internal override void OnPreApplyTemplate()
{
base.OnPreApplyTemplate();
AttachToOwner();
}
/// <summary>
/// This is the virtual that sub-classes must override if they wish to get
/// notified that the template tree has been created.
/// </summary>
public override void OnApplyTemplate()
{
// verify that the template produced a panel with no children
Panel panel = GetVisualChild(0) as Panel;
if (panel == null || VisualTreeHelper.GetChildrenCount(panel) > 0)
throw new InvalidOperationException(SR.ItemsPanelNotSingleNode);
OnPanelChanged(this, EventArgs.Empty);
base.OnApplyTemplate();
}
//------------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
/// <summary>
/// Override of <seealso cref="FrameworkElement.MeasureOverride" />.
/// </summary>
/// <param name="constraint">Constraint size is an "upper limit" that the return value should not exceed.</param>
/// <returns>The ItemsPresenter's desired size.</returns>
protected override Size MeasureOverride(Size constraint)
{
return Helper.MeasureElementWithSingleChild(this, constraint);
}
/// <summary>
/// Override of <seealso cref="FrameworkElement.ArrangeOverride" />.
/// </summary>
/// <param name="arrangeSize">Size the ItemsPresenter will assume.</param>
protected override Size ArrangeOverride(Size arrangeSize)
{
return Helper.ArrangeElementWithSingleChild(this, arrangeSize);
}
//------------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
internal ItemsControl Owner
{
get { return _owner; }
}
internal ItemContainerGenerator Generator
{
get { return _generator; }
}
// Internal Helper so the FrameworkElement could see this property
internal override FrameworkTemplate TemplateInternal
{
get { return Template; }
}
// Internal Helper so the FrameworkElement could see the template cache
internal override FrameworkTemplate TemplateCache
{
get { return _templateCache; }
set { _templateCache = (ItemsPanelTemplate)value; }
}
/// <summary>
/// TemplateProperty
/// </summary>
internal static readonly DependencyProperty TemplateProperty =
DependencyProperty.Register(
"Template",
typeof(ItemsPanelTemplate),
typeof(ItemsPresenter),
new FrameworkPropertyMetadata(
(ItemsPanelTemplate) null, // default value
FrameworkPropertyMetadataOptions.AffectsMeasure,
new PropertyChangedCallback(OnTemplateChanged)));
/// <summary>
/// Template Property
/// </summary>
private ItemsPanelTemplate Template
{
get { return _templateCache; }
set { SetValue(TemplateProperty, value); }
}
// Internal helper so FrameworkElement could see call the template changed virtual
internal override void OnTemplateChangedInternal(FrameworkTemplate oldTemplate, FrameworkTemplate newTemplate)
{
OnTemplateChanged((ItemsPanelTemplate)oldTemplate, (ItemsPanelTemplate)newTemplate);
}
// Property invalidation callback invoked when TemplateProperty is invalidated
private static void OnTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ItemsPresenter ip = (ItemsPresenter) d;
StyleHelper.UpdateTemplateCache(ip, (FrameworkTemplate) e.OldValue, (FrameworkTemplate) e.NewValue, TemplateProperty);
}
/// <summary>
/// Template has changed
/// </summary>
/// <remarks>
/// When a Template changes, the VisualTree is removed. The new Template's
/// VisualTree will be created when ApplyTemplate is called
/// </remarks>
/// <param name="oldTemplate">The old Template</param>
/// <param name="newTemplate">The new Template</param>
protected virtual void OnTemplateChanged(ItemsPanelTemplate oldTemplate, ItemsPanelTemplate newTemplate)
{
}
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
internal static ItemsPresenter FromPanel(Panel panel)
{
if (panel == null)
return null;
return panel.TemplatedParent as ItemsPresenter;
}
internal static ItemsPresenter FromGroupItem(GroupItem groupItem)
{
if (groupItem == null)
return null;
Visual parent = VisualTreeHelper.GetParent(groupItem) as Visual;
if (parent == null)
return null;
return VisualTreeHelper.GetParent(parent) as ItemsPresenter;
}
internal override void OnAncestorChanged()
{
if (TemplatedParent == null)
{
UseGenerator(null);
ClearPanel();
}
base.OnAncestorChanged();
}
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
// initialize (called during measure, from ApplyTemplate)
void AttachToOwner()
{
DependencyObject templatedParent = this.TemplatedParent;
ItemsControl owner = templatedParent as ItemsControl;
ItemContainerGenerator generator;
if (owner != null)
{
// top-level presenter - get information from ItemsControl
generator = owner.ItemContainerGenerator;
}
else
{
// subgroup presenter - get information from GroupItem
GroupItem parentGI = templatedParent as GroupItem;
ItemsPresenter parentIP = FromGroupItem(parentGI);
if (parentIP != null)
owner = parentIP.Owner;
generator = (parentGI != null) ? parentGI.Generator : null;
}
_owner = owner;
UseGenerator(generator);
// create the panel, based either on ItemsControl.ItemsPanel or GroupStyle.Panel
ItemsPanelTemplate template = null;
GroupStyle groupStyle = (_generator != null) ? _generator.GroupStyle : null;
if (groupStyle != null)
{
// If GroupStyle.Panel is set then we dont honor ItemsControl.IsVirtualizing
template = groupStyle.Panel;
if (template == null)
{
// create default Panels
if (VirtualizingPanel.GetIsVirtualizingWhenGrouping(owner))
{
template = GroupStyle.DefaultVirtualizingStackPanel;
}
else
{
template = GroupStyle.DefaultStackPanel;
}
}
}
else
{
// Its a leaf-level ItemsPresenter, therefore pick ItemsControl.ItemsPanel
template = (_owner != null) ? _owner.ItemsPanel : null;
}
Template = template;
}
void UseGenerator(ItemContainerGenerator generator)
{
if (generator == _generator)
return;
if (_generator != null)
_generator.PanelChanged -= new EventHandler(OnPanelChanged);
_generator = generator;
if (_generator != null)
_generator.PanelChanged += new EventHandler(OnPanelChanged);
}
private void OnPanelChanged(object sender, EventArgs e)
{
// something has changed that affects the ItemsPresenter.
// Re-measure. This will recalculate everything from scratch.
InvalidateMeasure();
//
// If we're under a ScrollViewer then its ScrollContentPresenter needs to
// be updated to work with the new panel.
//
ScrollViewer parent = Parent as ScrollViewer;
if (parent != null)
{
// If our logical parent is a ScrollViewer then the visual parent is a ScrollContentPresenter.
ScrollContentPresenter scp = VisualTreeHelper.GetParent(this) as ScrollContentPresenter;
if (scp != null)
{
scp.HookupScrollingComponents();
}
}
}
// workaround, pending bug 953483. The panel is
// being removed from the tree, so it should release
// its resources (chiefly - stop listening for generator's
// ItemsChanged event). Until there's a mechanism for
// this, just mark the panel as a non-ItemsHost, so
// that the next time it gets ItemsChanged it will
// stop listening. (See also bug 942265)
private void ClearPanel()
{
Panel oldPanel = (this.VisualChildrenCount > 0) ? this.GetVisualChild(0) as Panel : null;
Type type = null;
if( Template != null )
{
// Get the type of the template content's root
// Is this a FEF-based template?
if( Template.VisualTree != null )
{
type = Template.VisualTree.Type;
}
// Or, is it a (non-empty) Baml-based template?
else if (Template.HasXamlNodeContent)
{
System.Xaml.XamlType xType = (Template.Template as TemplateContent).RootType;
type = xType.UnderlyingType;
}
}
if (oldPanel != null && oldPanel.GetType() == type)
{
oldPanel.IsItemsHost = false;
}
}
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
ItemsControl _owner;
ItemContainerGenerator _generator;
ItemsPanelTemplate _templateCache;
}
}
|