//-----------------------------------------------------------------------
//
// Copyright (c) 2010 Edward McLeod-Jones
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// Read more about the MIT License for software at it's wikipedia
// page here: http://en.wikipedia.org/wiki/MIT_License
//
// If you happen to use this code in a project, please email me
// edward.mcleodjones@gmail.com and let me know how and where you use
// it. That is, after all, the fun part of sharing code: knowing
// that it gets used.
//
// Find out more about me and view my portfolio at http://edventuro.us/.
//
//-----------------------------------------------------------------------
namespace AutoScalingControls
{
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
///
/// A simple text control that shrinks or expands the font of the text to
/// display all of the text in the preferred size of the textblock.
/// Dependency Properties:
/// - MinFontSize: This is the smallest size the font will be reduced to. Defaults to 8pt.
/// - MaxFontSize: This is the largest size the font will be increased to. Defaults to 20.
/// - ScalingMode: This controls if the font size should be scaled only up, or down, or both ways
/// to fit the text within the boundaries of the textbox. Defaults to BothWays.
/// - StepSize: This is the point size the font will be increased or decreased
/// by each iteration until the text fits the desired size. Higher amounts will require fewer iterations,
/// so will be faster, but the changes will be more abrupt. Defaults to 0.5.
///
public class AutoScalingTextBlock : ContentControl
{
#region Text (DependencyProperty)
///
/// Gets or sets the Text DependencyProperty. This is the text that will be displayed.
///
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(AutoScalingTextBlock),
new PropertyMetadata(null, new PropertyChangedCallback(OnTextChanged)));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((AutoScalingTextBlock)d).OnTextChanged(e);
}
protected virtual void OnTextChanged(DependencyPropertyChangedEventArgs e)
{
this.InvalidateMeasure();
}
#endregion
#region MinFontSize (DependencyProperty)
private double _minFontSize = 8d;
///
/// Gets or sets the MinFontSize property. This is the smallest size the font will be reduced to. Defaults to 8pt.
///
public double MinFontSize
{
get { return (double)GetValue(MinFontSizeProperty); }
set { SetValue(MinFontSizeProperty, value); }
}
public static readonly DependencyProperty MinFontSizeProperty =
DependencyProperty.Register("MinFontSize", typeof(double), typeof(AutoScalingTextBlock),
new PropertyMetadata(8d, new PropertyChangedCallback(OnMinFontSizeChanged)));
private static void OnMinFontSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((AutoScalingTextBlock)d).OnMinFontSizeChanged(e);
}
protected virtual void OnMinFontSizeChanged(DependencyPropertyChangedEventArgs e)
{
_minFontSize = (double)e.NewValue;
this.InvalidateMeasure();
}
#endregion
#region MaxFontSize (DependencyProperty)
private double _maxFontSize = 20d;
///
/// Gets or sets the MaxFontSize property. This is the largest size the font will be increased to. Defaults to 20.
///
public double MaxFontSize
{
get { return (double)GetValue(MaxFontSizeProperty); }
set { SetValue(MaxFontSizeProperty, value); }
}
public static readonly DependencyProperty MaxFontSizeProperty =
DependencyProperty.Register("MaxFontSize", typeof(double), typeof(AutoScalingTextBlock),
new PropertyMetadata(20d, new PropertyChangedCallback(OnMaxFontSizeChanged)));
private static void OnMaxFontSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((AutoScalingTextBlock)d).OnMaxFontSizeChanged(e);
}
protected virtual void OnMaxFontSizeChanged(DependencyPropertyChangedEventArgs e)
{
_maxFontSize = (double)e.NewValue;
this.InvalidateMeasure();
}
#endregion
#region ScalingMode (DependencyProperty)
public enum ScalingModeOptions { BothWays, UpOnly, DownOnly };
private ScalingModeOptions _scalingMode = ScalingModeOptions.BothWays;
///
/// Gets or sets the ScalingMode property. This controls if the font size should be scaled only up, or down, or both ways
/// to fit the text within the boundaries of the textbox. Defaults to BothWays.
///
public ScalingModeOptions ScalingMode
{
get { return (ScalingModeOptions)GetValue(ScalingModeProperty); }
set { SetValue(ScalingModeProperty, value); }
}
public static readonly DependencyProperty ScalingModeProperty =
DependencyProperty.Register("ScalingMode", typeof(ScalingModeOptions), typeof(AutoScalingTextBlock),
new PropertyMetadata(ScalingModeOptions.BothWays, new PropertyChangedCallback(OnScalingModeChanged)));
private static void OnScalingModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((AutoScalingTextBlock)d).OnScalingModeChanged(e);
}
protected virtual void OnScalingModeChanged(DependencyPropertyChangedEventArgs e)
{
// _scalingMode = (ScalingModeOptions) Enum.Parse(typeof(ScalingModeOptions), (string) e.NewValue, true);
_scalingMode = (ScalingModeOptions)e.NewValue;
this.InvalidateMeasure();
}
#endregion
#region StepSize (DependencyProperty)
private double _stepSize = 0.5d;
///
/// Gets or sets the StepSize property. This is the point size the font will be increased or decreased
/// by each iteration until the text fits the desired size. Higher amounts will require fewer iterations,
/// so will be faster, but the changes will be more abrupt. Defaults to 0.5.
///
public double StepSize
{
get { return (double)GetValue(StepSizeProperty); }
set { SetValue(StepSizeProperty, value); }
}
public static readonly DependencyProperty StepSizeProperty =
DependencyProperty.Register("StepSize", typeof(double), typeof(AutoScalingTextBlock),
new PropertyMetadata(0.5d, new PropertyChangedCallback(OnStepSizeChanged)));
private static void OnStepSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((AutoScalingTextBlock)d).OnStepSizeChanged(e);
}
protected virtual void OnStepSizeChanged(DependencyPropertyChangedEventArgs e)
{
_stepSize = (double)e.NewValue;
this.InvalidateMeasure();
}
#endregion
///
/// A TextBlock that is set as the control's content and is ultimately the control
/// that displays our text
///
private TextBlock textBlock;
///
/// Initializes a new instance of the DynamicTextBlock class
///
public AutoScalingTextBlock()
{
// Create our TextBlock and initialize
this.textBlock = new TextBlock();
this.Content = this.textBlock;
// Force TextWrapping on
this.textBlock.TextWrapping = TextWrapping.Wrap;
}
///
/// Handles the measure part of the measure and arrange layout process. During this process
/// we measure the textBlock that we've created as content with increasingly bigger/smaller font sizes
/// until we find the font size that fits.
///
/// The available size
/// The base implementation of Measure
protected override Size MeasureOverride(Size availableSize)
{
Size unboundSize = new Size(availableSize.Width, double.PositiveInfinity);
// Set the text and measure it to see if it fits without alteration
this.textBlock.Text = this.Text;
Size textSize = base.MeasureOverride(unboundSize);
// Scale up first if necessary
while (textSize.Height < availableSize.Height)
{
// Increase the font size
this.textBlock.FontSize = this.textBlock.FontSize + _stepSize;
textSize = base.MeasureOverride(unboundSize);
if (_scalingMode == ScalingModeOptions.DownOnly)
{
if (this.textBlock.FontSize > this.FontSize)
{
this.textBlock.FontSize = this.FontSize;
break;
}
}
if (this.textBlock.FontSize >= _maxFontSize)
{
this.textBlock.FontSize = _maxFontSize;
break;
}
}
// Then scale down if neccessary
while (textSize.Height > availableSize.Height)
{
// Reduce the font size
this.textBlock.FontSize = this.textBlock.FontSize - _stepSize;
textSize = base.MeasureOverride(unboundSize);
if (_scalingMode == ScalingModeOptions.UpOnly)
{
if (this.textBlock.FontSize < this.FontSize)
{
this.textBlock.FontSize = this.FontSize;
break;
}
}
if (this.textBlock.FontSize <= _minFontSize)
{
this.textBlock.FontSize = _minFontSize;
break;
}
}
return base.MeasureOverride(availableSize);
}
}
}