|
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Documents;
namespace Test_SurfaceImageSourceManager.Common
{
/// <summary>
/// Wrapper for <see cref="RichTextBlock"/> that creates as many additional overflow
/// columns as needed to fit the available content.
/// </summary>
/// <example>
/// The following creates a collection of 400-pixel wide columns spaced 50 pixels apart
/// to contain arbitrary data-bound content:
/// <code>
/// <RichTextColumns>
/// <RichTextColumns.ColumnTemplate>
/// <DataTemplate>
/// <RichTextBlockOverflow Width="400" Margin="50,0,0,0"/>
/// </DataTemplate>
/// </RichTextColumns.ColumnTemplate>
///
/// <RichTextBlock Width="400">
/// <Paragraph>
/// <Run Text="{Binding Content}"/>
/// </Paragraph>
/// </RichTextBlock>
/// </RichTextColumns>
/// </code>
/// </example>
/// <remarks>Typically used in a horizontally scrolling region where an unbounded amount of
/// space allows for all needed columns to be created. When used in a vertically scrolling
/// space there will never be any additional columns.</remarks>
[Windows.UI.Xaml.Markup.ContentProperty(Name = "RichTextContent")]
public sealed class RichTextColumns : Panel
{
/// <summary>
/// Identifies the <see cref="RichTextContent"/> dependency property.
/// </summary>
public static readonly DependencyProperty RichTextContentProperty =
DependencyProperty.Register("RichTextContent", typeof(RichTextBlock),
typeof(RichTextColumns), new PropertyMetadata(null, ResetOverflowLayout));
/// <summary>
/// Identifies the <see cref="ColumnTemplate"/> dependency property.
/// </summary>
public static readonly DependencyProperty ColumnTemplateProperty =
DependencyProperty.Register("ColumnTemplate", typeof(DataTemplate),
typeof(RichTextColumns), new PropertyMetadata(null, ResetOverflowLayout));
/// <summary>
/// Initializes a new instance of the <see cref="RichTextColumns"/> class.
/// </summary>
public RichTextColumns()
{
this.HorizontalAlignment = HorizontalAlignment.Left;
}
/// <summary>
/// Gets or sets the initial rich text content to be used as the first column.
/// </summary>
public RichTextBlock RichTextContent
{
get { return (RichTextBlock)GetValue(RichTextContentProperty); }
set { SetValue(RichTextContentProperty, value); }
}
/// <summary>
/// Gets or sets the template used to create additional
/// <see cref="RichTextBlockOverflow"/> instances.
/// </summary>
public DataTemplate ColumnTemplate
{
get { return (DataTemplate)GetValue(ColumnTemplateProperty); }
set { SetValue(ColumnTemplateProperty, value); }
}
/// <summary>
/// Invoked when the content or overflow template is changed to recreate the column layout.
/// </summary>
/// <param name="d">Instance of <see cref="RichTextColumns"/> where the change
/// occurred.</param>
/// <param name="e">Event data describing the specific change.</param>
private static void ResetOverflowLayout(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// When dramatic changes occur, rebuild the column layout from scratch
var target = d as RichTextColumns;
if (target != null)
{
target._overflowColumns = null;
target.Children.Clear();
target.InvalidateMeasure();
}
}
/// <summary>
/// Lists overflow columns already created. Must maintain a 1:1 relationship with
/// instances in the <see cref="Chidren"/> collection following the initial RichTextBlock
/// child.
/// </summary>
private List<RichTextBlockOverflow> _overflowColumns = null;
/// <summary>
/// Determines whether additional overflow columns are needed and if existing columns can
/// be removed.
/// </summary>
/// <param name="availableSize">The size of the space available, used to constrain the
/// number of additional columns that can be created.</param>
/// <returns>The resulting size of the original content plus any extra columns.</returns>
protected override Size MeasureOverride(Size availableSize)
{
if (this.RichTextContent == null) return new Size(0, 0);
// Make sure the RichTextBlock is a child, using the lack of
// a list of additional columns as a sign that this hasn't been
// done yet
if (this._overflowColumns == null)
{
Children.Add(this.RichTextContent);
this._overflowColumns = new List<RichTextBlockOverflow>();
}
// Start by measuring the original RichTextBlock content
this.RichTextContent.Measure(availableSize);
var maxWidth = this.RichTextContent.DesiredSize.Width;
var maxHeight = this.RichTextContent.DesiredSize.Height;
var hasOverflow = this.RichTextContent.HasOverflowContent;
// Make sure there are enough overflow columns
int overflowIndex = 0;
while (hasOverflow && maxWidth < availableSize.Width && this.ColumnTemplate != null)
{
// Use existing overflow columns until we run out, then create
// more from the supplied template
RichTextBlockOverflow overflow;
if (this._overflowColumns.Count > overflowIndex)
{
overflow = this._overflowColumns[overflowIndex];
}
else
{
overflow = (RichTextBlockOverflow)this.ColumnTemplate.LoadContent();
this._overflowColumns.Add(overflow);
this.Children.Add(overflow);
if (overflowIndex == 0)
{
this.RichTextContent.OverflowContentTarget = overflow;
}
else
{
this._overflowColumns[overflowIndex - 1].OverflowContentTarget = overflow;
}
}
// Measure the new column and prepare to repeat as necessary
overflow.Measure(new Size(availableSize.Width - maxWidth, availableSize.Height));
maxWidth += overflow.DesiredSize.Width;
maxHeight = Math.Max(maxHeight, overflow.DesiredSize.Height);
hasOverflow = overflow.HasOverflowContent;
overflowIndex++;
}
// Disconnect extra columns from the overflow chain, remove them from our private list
// of columns, and remove them as children
if (this._overflowColumns.Count > overflowIndex)
{
if (overflowIndex == 0)
{
this.RichTextContent.OverflowContentTarget = null;
}
else
{
this._overflowColumns[overflowIndex - 1].OverflowContentTarget = null;
}
while (this._overflowColumns.Count > overflowIndex)
{
this._overflowColumns.RemoveAt(overflowIndex);
this.Children.RemoveAt(overflowIndex + 1);
}
}
// Report final determined size
return new Size(maxWidth, maxHeight);
}
/// <summary>
/// Arranges the original content and all extra columns.
/// </summary>
/// <param name="finalSize">Defines the size of the area the children must be arranged
/// within.</param>
/// <returns>The size of the area the children actually required.</returns>
protected override Size ArrangeOverride(Size finalSize)
{
double maxWidth = 0;
double maxHeight = 0;
foreach (var child in Children)
{
child.Arrange(new Rect(maxWidth, 0, child.DesiredSize.Width, finalSize.Height));
maxWidth += child.DesiredSize.Width;
maxHeight = Math.Max(maxHeight, child.DesiredSize.Height);
}
return new Size(maxWidth, maxHeight);
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.