Latest News

  • Super Tower Defense Game
    Mar 10, 2010

    New to the games section is the new Super Tower Defense game. Defend your base from the ever advancing army of tanks, buggies...

  • New Rich Text Editor User Control
    Feb 19, 2010

    By popular request, here we present a re-usable User Control containing the Liquid RichTextBox along with the most common formatting functions included.

  • Silverlight 3 Controls V5.2.7 Released
    Feb 19, 2010

    This release includes several fixes for issues raised in the forum. The main improvement is to the RichTextBox which now provides access and methods to the document elements allowing...

  • Super Shoot Em Up Game
    Feb 04, 2010

    Added to the games section is the new Super Shoot 'Em Up game. Take control of a tank with your aim being to blow up your opposing tanks and collect all the powerups.

  • Silverlight 3 Controls V5.2.6 Released
    Feb 04, 2010

    This release includes some minor fixes for several forum posts. Please see the notes on the download page for full details on what has changed.

Creating an Image Resizing Dialog

The RichTextBox by default does not have support for image resizing built in.  To achieve this you can make use of the ImageDoubleClick event that is present in the RichTextBox (and RichTextBlock) to present the user with a Dialog allowing the image to be resized.


A RichTextBox with Image

The Image Resizing User Control

We have a single User Control that contains the Image preview and scale controls.

<UserControl x:Class="RichTextBoxImageEdit.ImageViewer"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:liquid="clr-namespace:Liquid;assembly=Liquid"
    >
    <Grid x:Name="LayoutRoot" Margin="0 2 0 0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="360" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <ScrollViewer x:Name="ElementViewer" Grid.Column="0" Padding="0" HorizontalScrollBarVisibility="Auto" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" />
        <Grid Grid.Column="1">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="70" />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="4" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="32" />
                <RowDefinition Height="32" />
                <RowDefinition Height="32" />
                <RowDefinition Height="32" />
                <RowDefinition Height="32" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
            <TextBlock Grid.Column="0" Grid.Row="1" Margin="0 0 4 0" Text="Filename:" HorizontalAlignment="Right" />
            <TextBlock x:Name="ElementFilename" Grid.Column="1" Grid.Row="1" Text="" TextWrapping="Wrap" FontWeight="Bold" HorizontalAlignment="Left" />
            <TextBlock Grid.Column="0" Grid.Row="2" Margin="0 0 4 0" Text="Width:" HorizontalAlignment="Right" />
            <TextBlock x:Name="ElementWidth" Grid.Column="1" Grid.Row="2" Text="" FontWeight="Bold" HorizontalAlignment="Left" />
            <TextBlock Grid.Column="0" Grid.Row="3" Margin="0 0 4 0" Text="Height:" HorizontalAlignment="Right" />
            <TextBlock x:Name="ElementHeight" Grid.Column="1" Grid.Row="3" Text="" FontWeight="Bold" HorizontalAlignment="Left" />

            <RadioButton x:Name="ElementScaleRadio" Grid.Column="0" Grid.Row="6" Grid.ColumnSpan="2" VerticalAlignment="Bottom" Margin="8 0 0 4" GroupName="imageViewer" Content="Scale by %" Checked="ElementRadio_Click" />
            <Border Grid.Column="0" Grid.Row="7" Grid.ColumnSpan="2" Grid.RowSpan="3" Margin="6 0 4 0" BorderBrush="#aaaaaa" BorderThickness="1" CornerRadius="4" Background="#f0f0f0" />
            <TextBlock Grid.Column="0" Grid.Row="7" Margin="0 0 4 0" Text="Scale X:" VerticalAlignment="Center" HorizontalAlignment="Right" />
            <TextBlock x:Name="ElementScaleXLabel" Grid.Column="1" Grid.Row="7" Foreground="#444444" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
            <Slider x:Name="ElementScaleX" Grid.Column="1" Grid.Row="7" VerticalAlignment="Center" Margin="0 0 10 0" Minimum="1" Maximum="200" SmallChange="1" />
            <TextBlock Grid.Column="0" Grid.Row="8" Margin="0 0 4 0" Text="Scale Y:" VerticalAlignment="Center" HorizontalAlignment="Right" />
            <TextBlock x:Name="ElementScaleYLabel" Grid.Column="1" Grid.Row="8" Foreground="#444444" HorizontalAlignment="Center" VerticalAlignment="Bottom" />
            <Slider x:Name="ElementScaleY" Grid.Column="1" Grid.Row="8" VerticalAlignment="Center" Margin="0 0 10 0" Minimum="1" Maximum="200" SmallChange="1" />
            <CheckBox x:Name="ElementLock" Grid.Column="1" Grid.Row="9" VerticalAlignment="Bottom" Margin="0 0 0 8" Content="Lock Scale" IsChecked="True" />

            <RadioButton x:Name="ElementPixelSizeRadio" Grid.Column="0" Grid.Row="10" Grid.ColumnSpan="2" VerticalAlignment="Bottom" Margin="8 0 0 4" GroupName="imageViewer" Content="Pixel Size" Checked="ElementRadio_Click" />
            <Border Grid.Column="0" Grid.Row="11" Grid.ColumnSpan="2" Grid.RowSpan="2" Margin="6 0 4 0" BorderBrush="#aaaaaa" BorderThickness="1" CornerRadius="4" Background="#f0f0f0" />
            <TextBlock Grid.Column="0" Grid.Row="11" Margin="0 0 4 0" Text="Width:" VerticalAlignment="Center" HorizontalAlignment="Right" />
            <liquid:NumericUpDown x:Name="ElementScaledSizeX" Grid.Column="1" Grid.Row="11" Width="70" HorizontalAlignment="Left" Margin="0 8 8 4" Height="26" ValueChanged="ElementScaledSize_ValueChanged" />
            <TextBlock Grid.Column="0" Grid.Row="12" Margin="0 0 4 0" Text="Height:" VerticalAlignment="Center" HorizontalAlignment="Right" />
            <liquid:NumericUpDown x:Name="ElementScaledSizeY" Grid.Column="1" Grid.Row="12" Width="70" HorizontalAlignment="Left" Margin="0 4 8 8" Height="26" ValueChanged="ElementScaledSize_ValueChanged" />
        </Grid>
    </Grid>
</UserControl>


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using Liquid;

namespace RichTextBoxImageEdit
{
    public partial class ImageViewer : UserControl
    {
        private string _imageURL = string.Empty;
        private Image _image;
        private Size? _buildSize = null;
        private Size _originalSize = new Size();
        private bool _loaded = false;
        private string _targetPath = string.Empty;

        #region Public Properties

        public Dialog ParentDialog { get { return (Dialog)Parent; } }

        public string ImageURL
        {
            get { return _imageURL; }
        }

        public double ImageWidth
        {
            get { return Math.Round(_image.ActualWidth); }
        }

        public double ImageHeight
        {
            get { return Math.Round(_image.ActualHeight); }
        }

        public byte[] BitmapData { get; set; }

        public string Filename
        {
            get { return ElementFilename.Text; }
        }

        public bool ScaleByPercent
        {
            get { return ElementScaleRadio.IsChecked.Value; }
        }

        public double ScaleX
        {
            get { return ElementScaleX.Value; }
        }

        public double ScaleY
        {
            get { return ElementScaleY.Value; }
        }

        public bool ShowAlt { get; set; }

        #endregion

        public ImageViewer()
        {
            InitializeComponent();

            ElementScaleX.ValueChanged += new RoutedPropertyChangedEventHandler<double>(Scale_ValueChanged);
            ElementScaleY.ValueChanged += new RoutedPropertyChangedEventHandler<double>(Scale_ValueChanged);

            ShowAlt = true;
        }

        public void Build(string imageURL, string filename, Size? size)
        {
            SetEnabledState(false);

            _imageURL = imageURL;
            _buildSize = size;

            string[] split = _imageURL.Split('/');
            filename = split[split.Length - 1];
            string targetPath = imageURL.Replace(filename, "");

            _image = new Image();
            _image.Source = new BitmapImage(new Uri(_imageURL, UriKind.Relative));

            _image.Stretch = Stretch.Fill;
            _image.HorizontalAlignment = HorizontalAlignment.Center;
            _image.VerticalAlignment = VerticalAlignment.Center;
            _image.SizeChanged += new SizeChangedEventHandler(_image_SizeChanged);
            _image.CacheMode = new BitmapCache();

            ElementViewer.Content = _image;

            if (filename.Length > 0)
            {
                ElementFilename.Text = filename;
            }
            else if (imageURL.Length > 0)
            {
                split = _imageURL.Split('/');
                ElementFilename.Text = split[split.Length - 1];
            }

            _loaded = false;

            ElementScaleRadio.IsChecked = size == null;
            ElementPixelSizeRadio.IsChecked = size != null;
        }

        private void SetEnabledState(bool isEnabled)
        {
            if (!isEnabled)
            {
                ElementScaleRadio.IsEnabled = false;
                ElementScaleX.IsEnabled = false;
                ElementScaleY.IsEnabled = false;
                ElementLock.IsEnabled = false;

                ElementPixelSizeRadio.IsEnabled = false;
                ElementScaledSizeX.IsEnabled = false;
                ElementScaledSizeY.IsEnabled = false;
                ParentDialog.DisableAllButtons();
            }
            else
            {
                ElementScaleRadio.IsEnabled = true;
                ElementPixelSizeRadio.IsEnabled = true;

                ElementScaleX.IsEnabled = ElementScaleRadio.IsChecked.Value;
                ElementScaleY.IsEnabled = ElementScaleRadio.IsChecked.Value;
                ElementLock.IsEnabled = ElementScaleRadio.IsChecked.Value;

                ElementScaledSizeX.IsEnabled = ElementPixelSizeRadio.IsChecked.Value;
                ElementScaledSizeY.IsEnabled = ElementPixelSizeRadio.IsChecked.Value;

                ParentDialog.EnableAllButtons();
            }
        }

        #region Event Handling

        private void _image_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (!_loaded && e.NewSize.Height > 0 && e.NewSize.Width > 0)
            {
                SetEnabledState(true);
                _originalSize = new Size(_image.ActualWidth, _image.ActualHeight);

                ElementWidth.Text = _originalSize.Width.ToString() + "px";
                ElementHeight.Text = _originalSize.Height.ToString() + "px";

                if (_buildSize != null)
                {
                    ElementScaleX.Value = ((_buildSize.Value.Width / _image.ActualWidth) * 100);
                    ElementScaleY.Value = ((_buildSize.Value.Height / _image.ActualHeight) * 100);
                    _image.Width = Math.Round(_originalSize.Width * (ElementScaleX.Value * 0.01d));
                    _image.Height = Math.Round(_originalSize.Height * (ElementScaleY.Value * 0.01d));
                }
                else
                {
                    ElementScaleX.Value = 100;
                    ElementScaleY.Value = 100;

                    _image.Width = _originalSize.Width;
                    _image.Height = _originalSize.Height;
                }

                ElementScaledSizeX.Value = (int)Math.Round(_originalSize.Width * (ElementScaleX.Value * 0.01d));
                ElementScaledSizeY.Value = (int)Math.Round(_originalSize.Height * (ElementScaleY.Value * 0.01d));

                ParentDialog.GetButton("ok").IsEnabled = true;
                _loaded = true;
            }
        }

        private void Scale_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            ToolTipService.SetToolTip(ElementScaleX, Math.Round(ElementScaleX.Value));
            ToolTipService.SetToolTip(ElementScaleY, Math.Round(ElementScaleY.Value));

            ElementScaleXLabel.Text = Math.Round(ElementScaleX.Value).ToString() + "%";
            ElementScaleYLabel.Text = Math.Round(ElementScaleY.Value).ToString() + "%";
            ElementScaledSizeX.Value = (int)Math.Round(_originalSize.Width * (ElementScaleX.Value * 0.01d));
            ElementScaledSizeY.Value = (int)Math.Round(_originalSize.Height * (ElementScaleY.Value * 0.01d));

            if (ElementLock.IsChecked.Value)
            {
                if (sender == ElementScaleX)
                {
                    ElementScaleY.Value = ElementScaleX.Value;
                }
                if (sender == ElementScaleY)
                {
                    ElementScaleX.Value = ElementScaleY.Value;
                }
            }
        }

        private void ElementRadio_Click(object sender, RoutedEventArgs e)
        {
            if (_loaded)
            {
                ElementScaleX.IsEnabled = ElementScaleRadio.IsChecked.Value;
                ElementScaleY.IsEnabled = ElementScaleRadio.IsChecked.Value;
                ElementLock.IsEnabled = ElementScaleRadio.IsChecked.Value;

                ElementScaledSizeX.IsEnabled = ElementPixelSizeRadio.IsChecked.Value;
                ElementScaledSizeY.IsEnabled = ElementPixelSizeRadio.IsChecked.Value;
            }
        }

        private void ElementScaledSize_ValueChanged(object sender, EventArgs e)
        {
            _image.Width = ElementScaledSizeX.Value;
            _image.Height = ElementScaledSizeY.Value;
        }

        #endregion
    }
}


This is the dialog will be displayed when the image is double-clicked:

The Image Resize Dialog

Our Main Test Application

Here we have the main test application.  Note in the C# what we are doing in the ObjectDoubleClick event.  It is this event that we use to setup the Image Preview dialog.

<UserControl x:Class="RichTextBoxImageEdit.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:liquidRichText="clr-namespace:Liquid;assembly=Liquid.RichText"
    xmlns:liquidPopup="clr-namespace:Liquid;assembly=Liquid.Popup"
    xmlns:imageEdit="clr-namespace:RichTextBoxImageEdit"
    Width="640" Height="480">
  <Grid x:Name="LayoutRoot">
    <liquidRichText:RichTextBox x:Name="richTextBox" Width="380" Height="300" Margin="4" HorizontalAlignment="Left" VerticalAlignment="Top" EnableObjectSelection="True" />
    <liquidPopup:Dialog x:Name="resizeDialog" Width="610" Height="410" Title="Image Resize" Closed="resizeDialog_Closed">
        <imageEdit:ImageViewer x:Name="resizer" />
    </liquidPopup:Dialog>
  </Grid>
</UserControl>


using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

using Liquid;

namespace RichTextBoxImageEdit
{
    public partial class MainPage : UserControl
    {
        private Image _currentImage = null;

        public MainPage()
        {
            InitializeComponent();

            richTextBox.HTML = "<p>Double-click the image to resize!</p><img src=\"images/Lake.jpg\" />";
            richTextBox.ObjectDoubleClick += new RichTextBoxEventHandler(richTextBox_ObjectDoubleClick);
        }

        private void richTextBox_ObjectDoubleClick(object sender, RichTextBoxEventArgs e)
        {
            List<UIElement> selected = richTextBox.GetSelectedObjects();

            if (selected.Count > 0 && selected[0] is Image)
            {
                _currentImage = (Image)selected[0];
                BitmapImage imageSource = (BitmapImage)_currentImage.Source;

                resizer.Build(imageSource.UriSource.ToString(), imageSource.UriSource.ToString(), new Size(_currentImage.ActualWidth, _currentImage.ActualHeight));
                
                resizeDialog.ShowAsModal();
            }
        }

        private void resizeDialog_Closed(object sender, DialogEventArgs e)
        {
            if (resizeDialog.Result == DialogButtons.OK)
            {
                richTextBox.ResizeImage(_currentImage, new Size(resizer.ImageWidth, resizer.ImageHeight));
            }
        }

        private void save_Click(object sender, RoutedEventArgs e)
        {
            string html = richTextBox.Save(Format.HTML, RichTextSaveOptions.None);
        }
    }
}


There is plenty of scope for improvement here.  In our dialog we simply allow a % to be applied to the X/Y axis or an actual Pixel Size.

Silverlight Controls

  • Rich TextBox

    Create and edit rich content with this slick and expandable Rich TextBox...

  • TreeView

    This easy to use TreeView comes with drag and drop, sorting, searching and much more...

  • Context Menu

    You too can have cool popup context menus in your Silverlight applications...

  • Resizable Dialog

    Draggable and resizable popup dialogs are what serious Silverlight developers need...

  • Spell Checker

    Real-time spell checking in Silverlight? We did it first here...