WPF中RichTextBox的研究

  最近在写一个类似文本编辑器的东西,有选择字体样式和大小的功能,但在设计的时候遇到了一些问题。

  如果需要设计像QQ聊天时那样的改变字体大小及样式的话(即选择一个字体样式或大小而下面的字体将全部改变),只要把RichTextBox和Combobox绑定就可以了。这样比较简单。不过WPF中的默认字体( System.Windows.Media.FontFamily)是没有中文字体的,所以我用了安装的字体( System.Drawing.FontFamily)。因此就需要自己来写这个类,同时需要添加System.Drawing的这个引用。同样那字号的话为了看起来明显可以挑一些来写一个新类。代码如下:

View Code
<Window x:Class="RichTextBoxDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:RichTextBoxDemo"
        Title="MainWindow" Height="550" Width="900" WindowStartupLocation="CenterScreen">
    <Window.Resources>
        <local:FontFamilySource x:Key="fontFamilies" />
        <local:FontSizeSource x:Key="fontSizes" />
    </Window.Resources>
    <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="5, 10">
            <ComboBox x:Name="ComboBoxFontFamily" ItemsSource="{StaticResource fontFamilies}"  MinWidth="180" Margin="10,0" IsSynchronizedWithCurrentItem="True">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <Label Content="{Binding Name}" FontFamily="{Binding Name}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
            <ComboBox x:Name="ComboBoxFontSize" ItemsSource="{StaticResource fontSizes}" MinWidth="100" IsSynchronizedWithCurrentItem="True"/>
        </StackPanel>
        <UniformGrid Rows="1">
            <RichTextBox  Padding="5,10" x:Name="RichTextBox1" FontFamily="{Binding Source={StaticResource fontFamilies},Path=/}"
                          FontSize="{Binding Source={StaticResource fontSizes},Path=/}" />
        </UniformGrid>
    </DockPanel>
</Window>
View Code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;

namespace RichTextBoxDemo {
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
        }
    }
    public class FontFamilySource : List<System.Drawing.FontFamily> {
        public FontFamilySource() {
            this.AddRange(new System.Drawing.Text.InstalledFontCollection().Families.Select(p => p).ToList());
        }
    }
    public class FontSizeSource : List<double> {
        public FontSizeSource() {
            this.AddRange(Enumerable.Range(10, 62).Where(p => p % 4 == 0).Select(p => (double)p));
        }
    }
}

  如果需要设计像WORD那样当改变字体的大小及样式时(即原来已写的字体大小不变,二从光标处继续写的字体变为改变的字体。同时还可以通过划出一段字来改变其字体),这时WPF中的设置就很不人性化,不能直接通过Selection或是对Paragraph很简单的设置字体,这样会出现当鼠标点到中间时,改变字体会迫使其他字体全部改变。只能通过TextChanged时的offset来判断位置。同时还需考虑是不是文档的开头或中间。最后才通过ApplyPropertyValue()来设置字体。代码如下:

View Code
<Window x:Class="RtbDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:RtbDemo"
        Title="MainWindow" Height="550" Width="900" WindowStartupLocation="CenterScreen">
    <Window.Resources>
        <local:FontFamilySource x:Key="fontFamilies" />
        <local:FontSizeSource x:Key="fontSizes" />
    </Window.Resources>
    <DockPanel>
        <StackPanel Orientation="Horizontal" DockPanel.Dock="Top" Margin="5, 10">
            <ComboBox x:Name="ComboBoxFontFamily" ItemsSource="{StaticResource fontFamilies}"  MinWidth="180" Margin="10,0" SelectionChanged="ComboBoxFontFamily_SelectionChanged">
                <ComboBox.ItemTemplate>
                    <DataTemplate>
                        <Label Content="{Binding Name}" FontFamily="{Binding Name}" />
                    </DataTemplate>
                </ComboBox.ItemTemplate>
            </ComboBox>
            <ComboBox x:Name="ComboBoxFontSize" ItemsSource="{StaticResource fontSizes}" MinWidth="100" />
        </StackPanel>
        <UniformGrid Rows="1">
            <RichTextBox FontSize="24" Padding="5,10" x:Name="RichTextBox1" />
        </UniformGrid>
    </DockPanel>
</Window>
View Code
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Drawing;

namespace RtbDemo {
    public partial class MainWindow : Window {
        public MainWindow() {
            InitializeComponent();
            RichTextBox1.Focus();

            RichTextBox1.TextChanged += OnTextChanged;
            ComboBoxFontSize.SelectionChanged += ComboBoxFontSize_SelectionChanged;
            this.Loaded += OnLoaded;
        }

        void ComboBoxFontSize_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            if (!RichTextBox1.Selection.IsEmpty) {
                RichTextBox1.Selection.ApplyPropertyValue(RichTextBox.FontSizeProperty, ComboBoxFontSize.SelectedValue);
            }
            RichTextBox1.Focus();
        }

        private void ComboBoxFontFamily_SelectionChanged(object sender, SelectionChangedEventArgs e) {
            if (!RichTextBox1.Selection.IsEmpty) {
                TextRange range = new TextRange(RichTextBox1.Selection.Start, RichTextBox1.Selection.End);
                var v = ((System.Drawing.FontFamily)ComboBoxFontFamily.SelectedValue).Name.ToString();
                range.ApplyPropertyValue(RichTextBox.FontFamilyProperty, v);
            }
        }

        private void OnLoaded(object sender, RoutedEventArgs e) {
            this.ComboBoxFontFamily.SelectedValue = (this.ComboBoxFontFamily.ItemsSource as FontFamilySource).FirstOrDefault(p => p.Name == "宋体");
            this.ComboBoxFontSize.SelectedValue = (this.ComboBoxFontSize.ItemsSource as FontSizeSource).FirstOrDefault(p => p.ToString() == "24");
        }

        private void OnTextChanged(object sender, TextChangedEventArgs e) {
            if (RichTextBox1.Selection.IsEmpty) {
                foreach (var each in e.Changes) {
                    var start = RichTextBox1.Document.ContentStart;
                    for (int i = 0; i < each.AddedLength; ++i) {
                        var pos = start.GetPositionAtOffset(each.Offset + i + 1);
                        var ctx = pos.GetPointerContext(LogicalDirection.Backward);
                        if (ctx == TextPointerContext.ElementStart) {
                            var adjacent = pos.GetAdjacentElement(LogicalDirection.Backward);
                            if (adjacent.GetType() == typeof(Run)) {
                                var r = adjacent as Run;
                                r.FontSize = (double)ComboBoxFontSize.SelectedValue;
                                System.Windows.Media.FontFamily fontFamily = new System.Windows.Media.FontFamily((ComboBoxFontFamily.SelectedValue as System.Drawing.FontFamily).Name);
                                r.FontFamily = fontFamily;
                            }
                        } else if (ctx == TextPointerContext.Text) {
                            if (pos.Parent is Run) {
                                var r = pos.Parent as Run;
                                if (r.FontSize != (double)ComboBoxFontSize.SelectedValue) {
                                    RichTextBox1.Selection.Select(pos, pos.GetNextInsertionPosition(LogicalDirection.Backward));
                                    RichTextBox1.Selection.ApplyPropertyValue(RichTextBox.FontSizeProperty, ComboBoxFontSize.SelectedValue);
                                    RichTextBox1.Selection.Select(pos, pos);
                                }
                                System.Windows.Media.FontFamily fontFamily = new System.Windows.Media.FontFamily((ComboBoxFontFamily.SelectedValue as System.Drawing.FontFamily).Name);
                                if (r.FontFamily != fontFamily) {
                                    RichTextBox1.Selection.Select(pos, pos.GetNextInsertionPosition(LogicalDirection.Backward));
                                    RichTextBox1.Selection.ApplyPropertyValue(RichTextBox.FontFamilyProperty, fontFamily);
                                    RichTextBox1.Selection.Select(pos, pos);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public class FontFamilySource : List<System.Drawing.FontFamily> {
        public FontFamilySource() {
            this.AddRange(new System.Drawing.Text.InstalledFontCollection().Families.Select(p => p).ToList());
        }
    }
    public class FontSizeSource : List<double> {
        public FontSizeSource() {
            this.AddRange(Enumerable.Range(10, 62).Where(p => p % 4 == 0).Select(p => (double)p));
        }
    }
}

 

原文地址:https://www.cnblogs.com/socialdk/p/2580623.html