TOC

This article has been localized into Danish by the community.

Kontroller til tekstformattering:

Hvordan: Opret en RTF-editor

Dette er endnu en hvordan-artikel inspireret af hvor sej, RichTextBox kontrollen er, og hvor let, den gør det at lave en lille, men meget funktionsrig RTF-editor - tænk Windows Wordpad! Selv om WPF gør tingene meget nemt for os, vil der stadig være en del mere XAML og C# kode end normalt, men det er helt okay. Vi vil gennemgå de interessant sektioner en ad gangen, og til slut vil jeg vise dig hele kodelistningen

I denne artikel vil vi bruge en masse kontroller og teknikker, som vi har brugt i andre dele af selvstudiet, så forklaringerne vil ikke være for detaljerede. Hvis du har brug for at opfriske dele af det, kan du altid gå tilbage til de fuldt detaljerede beskrivelser.

Som en start vil vi se, hvad vi går efter. Dette skulle være det endelige resultat:

Interface

Interfacet består af en ToolBar kontrol med knapper og kombinationsbokse. Der er knapper til at indlæse og gemme et dokument, knapper til kontrol af diverse fontvægt- og typografiegenskaber, og så to kombinationsbokse til kontrol af fontfamilie og størrelse.

Under værktøjslinjen er RichTextBox kontrollen, hvor al redigeringen vil foregå.

Kommandoer

Det første, du vil bemærke, er brugen af WPF kommandoer, som vi allerede har diskutteret tidligere. Vi bruger Open og Save kommandoer fra ApplicationCommands klassen til at indlæse og gemme dokumentet, og vi bruger ToggleBold, ToggleItalic og ToggleUnderline kommandoer fra EditingCommands klassen til vores typografi-relaterede knapper.

Fordelen ved at bruge Commands er endnu en gang indlysende, eftersom RichTextBox kontrollen allerede implementerer ToggleBold, ToggleItalic og ToggleUnderline kommandoerne. Dette betyder, at vi ikke er nødt til at skrive noget kode for, at fo dem til at virke. Bare kæd dem sammen med de designerede knapper, og det virker:

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

Vi får også tastaturgenveje gratid - tryk Ctrl+B for at aktivere ToggleBold, Ctro+I for at aktivere ToggleItalic og Ctrl+U for at aktivere ToggleUnderline.

Bemærk, at jeg bruger en ToggleButton i stedet for en almindelig Button kontrol. Jeg ønsker, at knappen skal være markerbar, hvis markeringen er fed, og det er understøttet via IsChecked egenskaben på ToggleButton. Desværre har WPF ikke nogen måde at håndtere denne del for os, så vi bar brug for noget kode til at opdatere de forskellige knappers og kombinationsbokses status. Mere om det senere.

Open og Save kommandoerne kan ikke håndteres automatisk, så vi er nødt til at gøre som normalt med en CommandBinding til Window og en event handler i code-behind.

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

Jeg viser dig implementationen senere i artiklen.

Fontfamilie og -størrelse

For at vise og ændre fontfamilie og -størrelse, har vi et par kombinationsbokse. De udfyldes med fontfamilier, der er i Windows såvel som et udvalg af mulige størrelser i vinduets konstruktør, som dette:

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 };
}

Endnu en gang gør WPF det nemt for os at få en liste over mulige fonte ved at bruge SystemFontFamilies egenskaben. Siden listen over størrelser mere er et forslag, gør vi den ComboBox redigerbar, så brugeren kan indtaste en anden størrelse:

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

Dette betyder også, at vi vil håndtere ændringer anderledes. Til fontfamilie ComboBox, kan vi bare håndtere SelectionChanged hændelsen, mens vi tager fat i TextBoxBase.TextChanged hændelsen på størrelses ComboBox for at håndtere, at brugeren kan både vælge og manuelt indtaste en størrelse.

WPF håndterer implementationen af Bold, Italic og Underline kommandoerne for os, men for fontfamilie og -størrelse er vi nødt til manuelt at ændre disse værdier. Heldigvis er det ret enkelt at gøre ved hjælp af ApplyPropertyValue() metoden. Event handlerne nævnt ovenfor ser således ud:

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);
}

Ikke noget fancy her - vi videregiver ganske enkelt den valgte/indtastede værdi til ApplyPropertyValue() metoden sammen med den egenskab, vi ønsker at ændre.

Opdatering af status

Som tidligere nævnt, håndterer WPF Bold, Italic og Underline kommandoerne for os, men vi er nødt til manuelt af opdatere knappernes status, eftersom dette ikke er en del af kommandoernes funktionalitet. Men det er okay, da vi også skal opdatere de to kombinationsbokse for at afspejle den aktuelle fontfamilie og -størrelse.

Vi ønsker at opdatere status så snart, markøren flyttes og/eller markeringen ændres, og til det er SelectionChanged hændelsen på RichTextBox perfekt. Her er hvordan, vi håndterer det:

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();
}

En hel del linjer, men den egentlige opgave kræver kun et par linjer. Vi kopierer dem bare med en smule variation for at opdatere hver af de tre knapper såvel som de to kombinationsbokse.

Princippet er ret enkelt. Til knapperne bruger vi GetPropertyValue() metoden til at hente den aktuelle værdi for en givet tekstegenskab, f.eks. FontWeight,og derefter opdaterer vi IsChecked egneskaben afhængig af, om den returnerede værdi er den samme som den, vi kigger efter eller ej.

For kombinationsboksene gør vi det samme, men i stedet for at sætte en IsChecked egenskab, sætter vi SelectedItem eller Text egenskaberne direkte med de returnerede værdier.

Indlæse og gemme en fil

Til håndtering af Open og Save kommandoerne, bruger vi meget ens kode:

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);
	}
}

En OpenFileDialog eller SaveFileDialog bruges til at angive lokation og filnavn, og derefter bliver teksten enten indlæst eller gemt ved hjælp af et TextRange objekt, som vi får direkte fra RichTextBox, i kombination med en FileStream, som giver adgang til den fysiske fil. Filen indlæses og gemmes i RTF formatet, men du kan angive en af de andre formattyper, hvis du ønsker, din editor skal understøtte andre formater, f.eks. ren tekst.

Det fulde eksempel

Her er koden til hele applikationen - først XAML og derefter 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);
		}
	}
}

Og her er endnu et skærmbillede, hvor vi har valgt noget tekst. Bemærk hvordan kontrollerne på værktøjslinjen afspejler status for den aktuelle markering:

Resume

Som du kan se, er implementeringen af en RTF-editor i WPF ret simpel, specielt på grund af den fremragende RichTextBox kontrol. Hvis du ønsker det, kan du nemt udvide dette eksempel med ting som tekstjustering, farver, lister og endda tabeller.

Vær opmærksom på, at mens det ovenstående eksempel burde fungere ganske udmærket, er der absolut ingen fejlhåndtering og kontrol overhovedet for at holde mængden af kode minimal. Der er adskillige steder, hvor undtagelser kan opstå, som kombinationsboksen til fontstørrelse, hvor indtastning af en ikke-numerisk værdi kunne forårsage en undtagelse. Du bør selvfølgelig kontrollere alt dette og håndtere mulige undtagelser, hvis du ønsker at udvide dette eksempel til dit projekt.


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!