TOC

This article is currently in the process of being translated into Portuguese (~99% done).

Controles de Texto:

How-to: Creating a Rich Text Editor

Este é outro artigo de instruções, inspirado em quão legal é o controle RichTextBox e como é fácil criar um editor de texto rico pequeno, mas muito capaz - pense no Windows Wordpad! Embora o WPF torne isso realmente fácil para nós, haverá um pouco mais de código XAML e C # do que o normal, mas tudo bem. Vamos percorrer as seções interessantes uma de cada vez e, em seguida, no final, mostraremos toda a listagem de código .

Neste artigo, usaremos muitos controles e técnicas que usamos em outras partes do tutorial, para que as explicações não sejam detalhadas demais. No caso de você precisar se refrescar em partes, você pode sempre voltar para as descrições detalhadas.

Para começar, vamos dar uma olhada no que estamos procurando. Este deve ser o resultado final:

Interface

A interface consiste em um controle ToolBar com botões e caixas de combinação. Há botões para carregar e salvar um documento, botões para controlar várias propriedades de estilo e peso de fonte e duas caixas de combinação para controlar a família e o tamanho da fonte.

Abaixo da barra de ferramentas está o controle RichTextBox, onde toda a edição será feita.

Comandos

A primeira coisa que você pode notar é o uso dos Comandos do WPF, que já discutimos anteriormente neste artigo. Usamos os comandos Abrir e Salvar da classe ApplicationCommands para carregar e salvar o documento, e usamos os comandos ToggleBold, ToggleItalic e ToggleUnderline da classe EditingCommands para nosso estilo relacionado botões.

A vantagem de usar comandos é mais uma vez óbvia, pois o controle RichTextBox já implementa os comandos ToggleBold, ToggleItalic e ToggleUnderline. Isso significa que não precisamos escrever nenhum código para eles funcionarem. Basta conectá-los ao botão designado e ele funciona:

<ToggleButton Command="EditingCommands.ToggleBold" Name="btnBold">
    <Image Source="/WpfTutorialSamples;component/Images/text_bold.png" Width="16" Height="16" />
</ToggleButton>

Também obtemos atalhos de teclado gratuitamente - pressione Ctrl + B para ativar o ToggleBold, Ctrl + I para ativar o ToggleItalic e Ctrl + U para ativar o ToggleUnderline.

Observe que estou usando um ToggleButton em vez de um controle Button regular. Quero que o botão seja verificável, se a seleção estiver atualmente em negrito, e isso é suportado pela propriedade IsChecked do ToggleButton. Infelizmente, o WPF não tem como manipular essa parte para nós, então precisamos de um pouco de código para atualizar os vários estados de botão e caixa de combinação. Mais sobre isso depois.

Os comandos Abrir e Salvar não podem ser manipulados automaticamente, então teremos que fazer isso como de costume, com um CommandBinding para a Janela e depois um manipulador de eventos no Code-behind:

<Window.CommandBindings>
    <CommandBinding Command="ApplicationCommands.Open" Executed="Open_Executed" />
    <CommandBinding Command="ApplicationCommands.Save" Executed="Save_Executed" />
</Window.CommandBindings>

Mostrarei a implementação mais adiante neste artigo.

Família e tamanho da fonte

Para mostrar e alterar a família e o tamanho da fonte, temos algumas caixas de combinação. Eles são preenchidos com as famílias de fontes do sistema, bem como uma seleção de tamanhos possíveis no construtor para a janela, desta forma:

public RichTextEditorSample()
{
	InitializeComponent();
	cmbFontFamily.ItemsSource = Fonts.SystemFontFamilies.OrderBy(f => f.Source);
	cmbFontSize.ItemsSource = new List<double>() { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 };
}

Mais uma vez, o WPF torna mais fácil para nós obter uma lista de possíveis fontes, usando a propriedade SystemFontFamilies. Como a lista de tamanhos é mais uma sugestão, tornamos esse controle ComboBox editável, para que o usuário possa inserir um tamanho personalizado:

<ComboBox Name="cmbFontFamily" Width="150" SelectionChanged="cmbFontFamily_SelectionChanged" />
<ComboBox Name="cmbFontSize" Width="50" IsEditable="True" TextBoxBase.TextChanged="cmbFontSize_TextChanged" />

Isso também significa que estaremos lidando com mudanças de maneira diferente. Para a família de fontes ComboBox, podemos apenas manipular o evento SelectionChanged, enquanto nos conectamos ao evento TextBoxBase.TextChanged do tamanho ComboBox, para lidar com o fato de que o usuário pode selecionar e inserir manualmente um tamanho.

O WPF manipula a implementação dos comandos Negrito, Itálico e Sublinhado para nós, mas para a família e o tamanho da fonte, teremos que alterar manualmente esses valores. Felizmente, é bem fácil, usando o método ApplyPropertyValue (). Os manipuladores de eventos acima mencionados se parecem com isso.

private void cmbFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
	if(cmbFontFamily.SelectedItem != null)
		rtbEditor.Selection.ApplyPropertyValue(Inline.FontFamilyProperty, cmbFontFamily.SelectedItem);
}

private void cmbFontSize_TextChanged(object sender, TextChangedEventArgs e)
{
	rtbEditor.Selection.ApplyPropertyValue(Inline.FontSizeProperty, cmbFontSize.Text);
}

Nada muito chique aqui - simplesmente passamos o valor selecionado / inserido para o método ApplyPropertyValue (), juntamente com a propriedade que desejamos alterar.

Atualizando o estado

Como mencionado anteriormente, o WPF manipula os comandos Negrito, Itálico e Sublinhado para nós, mas temos que atualizar manualmente o estado de seus botões, uma vez que isso não faz parte da funcionalidade Comandos. No entanto, tudo bem, pois também temos que atualizar as duas caixas de combinação para refletir a família e o tamanho da fonte atual.

Queremos atualizar o estado assim que o cursor se mover e / ou a seleção mudar, e para isso, o evento SelectionChanged do RichTextBox é perfeito. Veja como lidamos com isso:

private void rtbEditor_SelectionChanged(object sender, RoutedEventArgs e)
{
	object temp = rtbEditor.Selection.GetPropertyValue(Inline.FontWeightProperty);
	btnBold.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontWeights.Bold));
	temp = rtbEditor.Selection.GetPropertyValue(Inline.FontStyleProperty);
	btnItalic.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontStyles.Italic));
	temp = rtbEditor.Selection.GetPropertyValue(Inline.TextDecorationsProperty);
	btnUnderline.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(TextDecorations.Underline));

	temp = rtbEditor.Selection.GetPropertyValue(Inline.FontFamilyProperty);
	cmbFontFamily.SelectedItem = temp;
	temp = rtbEditor.Selection.GetPropertyValue(Inline.FontSizeProperty);
	cmbFontSize.Text = temp.ToString();
}

Realmente um bocado de linhas, mas o trabalho real requer apenas algumas linhas - apenas as repetimos com uma pequena variação para atualizar cada um dos três botões, bem como as duas caixas de combinação.

O princípio é bem simples. Para os botões, usamos o método GetPropertyValue () para obter o valor atual de uma determinada propriedade de texto, por exemplo, o FontWeight e, em seguida, atualizamos a propriedade IsChecked dependendo se o valor retornado é o mesmo que estamos procurando ou não.

Para as caixas de combinação, fazemos a mesma coisa, mas em vez de definir uma propriedade IsChecked, definimos as propriedades SelectedItem ou Text diretamente com os valores retornados.

Carregando e salvando um arquivo

Ao manipular os comandos Abrir e Salvar, usamos um código muito semelhante:

private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
{
	OpenFileDialog dlg = new OpenFileDialog();
	dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*";
	if(dlg.ShowDialog() == true)
	{
		FileStream fileStream = new FileStream(dlg.FileName, FileMode.Open);
		TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd);
		range.Load(fileStream, DataFormats.Rtf);
	}
}

private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
{
	SaveFileDialog dlg = new SaveFileDialog();
	dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*";
	if(dlg.ShowDialog() == true)
	{
		FileStream fileStream = new FileStream(dlg.FileName, FileMode.Create);
		TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd);
		range.Save(fileStream, DataFormats.Rtf);
	}
}

Um OpenFileDialog ou SaveFileDialog é usado para especificar o local e nome do arquivo e, em seguida, o texto é carregado ou salvo usando um objeto TextRange, que obtemos diretamente do RichTextBox, em combinação com um FileStream, que fornece o acesso ao arquivo físico . O arquivo é carregado e salvo no formato RTF, mas você pode especificar um dos outros tipos de formato se quiser que o editor ofereça suporte a outros formatos, por exemplo, texto simples.

O exemplo completo

Aqui está o código para o aplicativo inteiro - primeiro o XAML e depois o C # Code-behind:

<Window x:Class="WpfTutorialSamples.Rich_text_controls.RichTextEditorSample"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="RichTextEditorSample" Height="300" Width="400">
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Open" Executed="Open_Executed" />
        <CommandBinding Command="ApplicationCommands.Save" Executed="Save_Executed" />
    </Window.CommandBindings>
    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Command="ApplicationCommands.Open">
                <Image Source="/WpfTutorialSamples;component/Images/folder.png" Width="16" Height="16" />
            </Button>
            <Button Command="ApplicationCommands.Save">
                <Image Source="/WpfTutorialSamples;component/Images/disk.png" Width="16" Height="16" />
            </Button>
            <Separator />
            <ToggleButton Command="EditingCommands.ToggleBold" Name="btnBold">
                <Image Source="/WpfTutorialSamples;component/Images/text_bold.png" Width="16" Height="16" />
            </ToggleButton>
            <ToggleButton Command="EditingCommands.ToggleItalic" Name="btnItalic">
                <Image Source="/WpfTutorialSamples;component/Images/text_italic.png" Width="16" Height="16" />
            </ToggleButton>
            <ToggleButton Command="EditingCommands.ToggleUnderline" Name="btnUnderline">
                <Image Source="/WpfTutorialSamples;component/Images/text_underline.png" Width="16" Height="16" />
            </ToggleButton>
            <Separator />
            <ComboBox Name="cmbFontFamily" Width="150" SelectionChanged="cmbFontFamily_SelectionChanged" />
            <ComboBox Name="cmbFontSize" Width="50" IsEditable="True" TextBoxBase.TextChanged="cmbFontSize_TextChanged" />
        </ToolBar>
        <RichTextBox Name="rtbEditor" SelectionChanged="rtbEditor_SelectionChanged" />
    </DockPanel>
</Window>
using System;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.Win32;
using System.Windows.Controls;

namespace WpfTutorialSamples.Rich_text_controls
{
	public partial class RichTextEditorSample : Window
	{
		public RichTextEditorSample()
		{
			InitializeComponent();
			cmbFontFamily.ItemsSource = Fonts.SystemFontFamilies.OrderBy(f => f.Source);
			cmbFontSize.ItemsSource = new List<double>() { 8, 9, 10, 11, 12, 14, 16, 18, 20, 22, 24, 26, 28, 36, 48, 72 };
		}

		private void rtbEditor_SelectionChanged(object sender, RoutedEventArgs e)
		{
			object temp = rtbEditor.Selection.GetPropertyValue(Inline.FontWeightProperty);
			btnBold.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontWeights.Bold));
			temp = rtbEditor.Selection.GetPropertyValue(Inline.FontStyleProperty);
			btnItalic.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(FontStyles.Italic));
			temp = rtbEditor.Selection.GetPropertyValue(Inline.TextDecorationsProperty);
			btnUnderline.IsChecked = (temp != DependencyProperty.UnsetValue) && (temp.Equals(TextDecorations.Underline));

			temp = rtbEditor.Selection.GetPropertyValue(Inline.FontFamilyProperty);
			cmbFontFamily.SelectedItem = temp;
			temp = rtbEditor.Selection.GetPropertyValue(Inline.FontSizeProperty);
			cmbFontSize.Text = temp.ToString();
		}

		private void Open_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			OpenFileDialog dlg = new OpenFileDialog();
			dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*";
			if(dlg.ShowDialog() == true)
			{
				FileStream fileStream = new FileStream(dlg.FileName, FileMode.Open);
				TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd);
				range.Load(fileStream, DataFormats.Rtf);
			}
		}

		private void Save_Executed(object sender, ExecutedRoutedEventArgs e)
		{
			SaveFileDialog dlg = new SaveFileDialog();
			dlg.Filter = "Rich Text Format (*.rtf)|*.rtf|All files (*.*)|*.*";
			if(dlg.ShowDialog() == true)
			{
				FileStream fileStream = new FileStream(dlg.FileName, FileMode.Create);
				TextRange range = new TextRange(rtbEditor.Document.ContentStart, rtbEditor.Document.ContentEnd);
				range.Save(fileStream, DataFormats.Rtf);
			}
		}

		private void cmbFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e)
		{
			if(cmbFontFamily.SelectedItem != null)
				rtbEditor.Selection.ApplyPropertyValue(Inline.FontFamilyProperty, cmbFontFamily.SelectedItem);
		}

		private void cmbFontSize_TextChanged(object sender, TextChangedEventArgs e)
		{
			rtbEditor.Selection.ApplyPropertyValue(Inline.FontSizeProperty, cmbFontSize.Text);
		}
	}
}

E aqui está outra imagem em que selecionamos algum texto. Observe como os controles da barra de ferramentas refletem o estado da seleção atual:

Resumo

Como você pode ver, implementar um editor de rich text no WPF é muito simples, especialmente devido ao excelente controle RichTextBox. Se você quiser, você pode facilmente estender este exemplo com coisas como alinhamento de texto, cores, listas e até mesmo tabelas.

Por favor, esteja ciente de que, embora o exemplo acima funcione perfeitamente, não há absolutamente nenhuma manipulação ou verificação de exceção, para manter a quantidade de código no mínimo. Existem vários locais que poderiam facilmente lançar uma exceção, como a caixa de combinação de tamanho de fonte, onde um poderia causar uma exceção inserindo um valor não numérico. Você deve, claro, verificar tudo isso e lidar com possíveis exceções se quiser expandir esse exemplo para o seu trabalho.


This article has been fully translated into the following languages: Is your preferred language not on the list? Click here to help us translate this article into your language!