WPF中GDI+图形图像的绘制:(一)绘制文本——动态设置字体、大小、颜色

GDI+(Graphics Device Interface Plus图形设备接口加)是.NET框架的重要组成部分,负责在屏幕和打印机上绘制图形图像和显示信息。GDI+不但在功能上比GDI 要强大很多,而且在代码编写方面也更简单,因此很快成为了Windows图形图像程序开发的首选。

从程序设计的角度看,GDI包括两部分:GDI对象和GDI函数。GDI对象定义了GDI函数使用的工具和环境变量;而GDI函数使用GDI对象绘制各种图形。

GDI+主要提供了以下三种功能:

1)二维矢量图形;2)图像处理;3)文字显示版式。

图形类Graphics是GDI+的核心,它提供绘制图形、图像和文本的各种方法(操作/函数),还可以存储显示设备和被画项目的属性(到图元文件)。所有的画图方法都被包括在Graphics类中,在绘制任何对象时,我们首先要创建一个Ggraphics实例,这个实例相当于创建了一块画布,有了画布才可以使用各种画图方法绘图。

下面主要通过创建实例并不断丰富完善的方式来简单介绍GDI+图形图像绘制中的一些概念与技巧,首先是绘制文本,实现效果如图:

WPF中GDI+图形图像的绘制:(一)绘制文本——动态设置字体、大小、颜色

1、新建WPF应用程序DrawDemo。

添加System.Drawing的引用,System.Drawing命名空间提供了对 GDI+ 基本图形功能的访问权限。

在主窗体添加一个Canvas控件。Canvas是WPF里基本的面板,它是一个存储控件的容器,主要用途是用来画图。通过设置ClipToBounds=”True”来裁剪超过自身范围的内容。设置HorizontalAlignment="Left",VerticalAlignment="Top"来设置以左上角为中心。

2、分别添加其他控件用来设置字体类型、大小、颜色。由于WPF中没有NumericUpDown控件,这里采用添加Winform控件的方式。

窗体xaml如下:

<Window x:Class="DrawDemo.MainWindow"
        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:local="clr-namespace:DrawDemo"
        xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
        Title="GDI+文本绘制" Height="768" Width="1280" Loaded="Window_Loaded">
    <Grid Background="#f0f0f0">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"></ColumnDefinition>
            <ColumnDefinition ></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Canvas x:Name="mainCanvas" Grid.Column="0" Background="White" Width="960" Height="720" Margin="10,5,0,0"
                HorizontalAlignment="Left"
                VerticalAlignment="Top"
                ClipToBounds="True">
            <Image x:Name="imgFont"></Image>
        </Canvas>
        <Grid Grid.Column="1" Margin="10">
            <Grid.RowDefinitions>
                <RowDefinition Height="50"></RowDefinition>
                <RowDefinition Height="50"></RowDefinition>
                <RowDefinition Height="50"></RowDefinition>
                <RowDefinition Height="50"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="80"></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Label Grid.Row="0" Grid.Column="0" Content="文本" VerticalAlignment="Center" HorizontalAlignment="Left"></Label>
            <TextBox x:Name="tbFontText" Grid.Row="0" Grid.Column="1" Height="26" Width="180" VerticalAlignment="Center" HorizontalAlignment="Left" TextChanged="tbFontText_TextChanged"></TextBox>
            <Label Grid.Row="1" Grid.Column="0" Content="选择字体" VerticalAlignment="Center" HorizontalAlignment="Left"></Label>
            <ComboBox x:Name="cbFontName" Grid.Row="1" Grid.Column="1" Height="26" Width="180" VerticalAlignment="Center" HorizontalAlignment="Left" SelectionChanged="cbFontName_SelectionChanged"></ComboBox>
            <Label Grid.Row="2" Grid.Column="0" Content="字体大小" VerticalAlignment="Center" HorizontalAlignment="Left"></Label>
            <WindowsFormsHost Grid.Row="2" Grid.Column="1" Height="26" Width="180" VerticalAlignment="Center" HorizontalAlignment="Left">
                <wf:NumericUpDown x:Name="numFontSize" Value="50" Maximum="1000" Minimum="1" ValueChanged="numFontSize_ValueChanged"/>
            </WindowsFormsHost>
            <Label Grid.Row="3" Grid.Column="0" Content="字体颜色" VerticalAlignment="Center" HorizontalAlignment="Left"></Label>
            <Rectangle x:Name="rFontColor" Grid.Row="3" Grid.Column="1" Fill="Black" Width="30" Height="30" VerticalAlignment="Center" HorizontalAlignment="Left" MouseLeftButtonDown="rFontColor_MouseLeftButtonDown"></Rectangle>
        </Grid>
    </Grid>
</Window>

3、为了更方便的绘制,抽象出FontItem类,来控制字体的绘制。代码如下:

using System.Windows.Media;

namespace DrawDemo.Root
{
    public class FontItem
    {
        private string text;                // 字体内容
        private string fontName;            // 字体名称
        private int fontSize;               // 字体大小
        private Color fontColor;            // 字体颜色

        public string Text
        {
            get
            {
                return text;
            }

            set
            {
                text = value;
            }
        }

        public string FontName
        {
            get
            {
                return fontName;
            }

            set
            {
                fontName = value;
            }
        }

        public int FontSize
        {
            get
            {
                return fontSize;
            }

            set
            {
                fontSize = value;
            }
        }

        public Color FontColor
        {
            get
            {
                return fontColor;
            }

            set
            {
                fontColor = value;
            }
        }
    }
}

4、主窗体交互逻辑:

(1)设置全局字体变量;

private FontItem fontItem;

(2)在窗体Loaded事件里加载本地字体;

/// <summary>
/// 加载本地字体
/// </summary>
private void InitFontName()
{
    try
    {
        InstalledFontCollection insFont = new InstalledFontCollection();
        System.Drawing.FontFamily[] families = insFont.Families;
        foreach (System.Drawing.FontFamily family in families)
        {
            this.cbFontName.Items.Add(family.Name);
        }
        this.cbFontName.SelectedItem = "宋体";
    }
    catch (Exception)
    {
                
    }
}

(3)在TextChanged事件中设置需要绘制的字体,并实现文本绘制,其中TextRenderingHint设置为AntiAliasGridFit,可查看博客WPF字体模糊解决方案;ImageHelper.cs类可查看WPF封装一个常用图片加载、保存、类型转换的类。

/// <summary>
/// 文本改变
/// </summary>
private void tbFontText_TextChanged(object sender, TextChangedEventArgs e)
{
    GetFontItem(this.tbFontText.Text.Trim());
    DrawText();
}
/// <summary>
/// 设置需要绘制的字体
/// </summary>
private void GetFontItem(string text)
{
    if (text.Length <= 0)
    {
	fontItem = null;
	return;
    }
    fontItem = new FontItem();
    fontItem.FontColor = ((SolidColorBrush)this.rStrokeColor.Fill).Color;
    fontItem.FontName = this.cbFontName.SelectedValue.ToString();
    fontItem.FontSize = (int)this.numFontSize.Value;
    fontItem.Text = text;
}
/// <summary>
/// 绘制字体
/// </summary>
private void DrawText()
{
    try
    {
	if (fontItem == null)
        {
            this.imgFont.Source = null;
            return;
        }

	System.Drawing.Font fontText = new System.Drawing.Font(fontItem.FontName, fontItem.FontSize);
	System.Drawing.Size sizeText = System.Windows.Forms.TextRenderer.MeasureText(fontItem.Text, fontText, new System.Drawing.Size(0, 0), System.Windows.Forms.TextFormatFlags.NoPadding);
	Rect viewport = new Rect(0, 0, sizeText.Width, sizeText.Height);

	if ((int)viewport.Width == 0 || (int)viewport.Height == 0)
	    return;

	System.Drawing.Bitmap tempMap = new System.Drawing.Bitmap((int)viewport.Width, (int)viewport.Height);
	System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(tempMap);
	g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
	g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
	g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
	System.Drawing.RectangleF rect = new System.Drawing.RectangleF(0, 0, sizeText.Width, sizeText.Height);
	System.Drawing.Drawing2D.GraphicsPath path = new System.Drawing.Drawing2D.GraphicsPath();
	path.AddString(fontItem.Text, fontText.FontFamily, (int)fontText.Style, fontText.Size, rect, System.Drawing.StringFormat.GenericDefault);
	g.FillPath(new System.Drawing.SolidBrush(System.Drawing.Color.FromArgb(fontItem.FontColor.A, fontItem.FontColor.R, fontItem.FontColor.G, fontItem.FontColor.B)), path);
	path.Dispose();

	BitmapImage tempImage = ImageHelper.BitmapToBitmapImage(tempMap,System.Drawing.Imaging.ImageFormat.Png);
	g.Dispose();
	tempMap.Dispose();

	if (tempImage != null)
	{
	    this.imgFont.Source = tempImage;
	    this.imgFont.Width = tempImage.Width;
	    this.imgFont.Height = tempImage.Height;
	    Canvas.SetLeft(this.imgFont, (this.mainCanvas.ActualWidth - tempImage.Width) / 2);
	    Canvas.SetTop(this.imgFont, (this.mainCanvas.ActualHeight - tempImage.Height) / 2);
	}
    }
    catch (Exception ex)
    {
	return;
    }
}

(4)分别实现字体、大小、颜色设置,其中颜色选择窗体参见WPF实现简单的颜色调色板功能

/// <summary>
/// 字体选择
/// </summary>
private void cbFontName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if(fontItem != null)
    {
        fontItem.FontName = this.cbFontName.SelectedValue.ToString();
        DrawText();
    }
}
/// <summary>
/// 字体大小
/// </summary>
private void numFontSize_ValueChanged(object sender, EventArgs e)
{
    if (fontItem != null)
    {
        fontItem.FontSize = (int)this.numFontSize.Value;
        DrawText();
    }
}
/// <summary>
/// 字体颜色
/// </summary>
private void rFontColor_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    ColorSelectorWindow csw = new ColorSelectorWindow();
    csw.ShowDialog();
    if (fontItem != null)
    {
        fontItem.FontColor = csw.returnSelectColor;
        this.rFontColor.Fill = new SolidColorBrush(csw.returnSelectColor);
        DrawText();
    }
}