Как установить шрифт RichTextBox для следующего текста?

Мне нужно установить семейство шрифтов для следующего текста, который будет записан в RichTextBox. Я попытался установить это с помощью...

<RichTextBox x:Name="RichTextEditor" MaxWidth="1000" SpellCheck.IsEnabled="True"
             FontFamily="{Binding ElementName=TextFontComboBox, Path=SelectedItem}"
             FontSize="{Binding ElementName=TextSizeComboBox, Path=SelectedValue}"
             Width="Auto" Height="Auto" HorizontalScrollBarVisibility="Auto" 
VerticalScrollBarVisibility="Auto" />

... но он изменил весь текст. Я полагаю, что с свойством Selection я могу ограничить изменение, которое будет применяться только к выбранной области. Но как для следующего - еще не напечатанного текста?

Ответ 1

Чтобы установить FontFamily на основе позиции курсора, вам нужно определить настраиваемый элемент управления с свойством зависимости, который поможет вставить новый раздел Run, переопределив метод OnTextInput.

Я включил большую часть кода, вам нужно будет изменить пространства имен в соответствии с вашей средой разработки.

В коде используется ViewModel для управления доступными шрифтами и управления, если шрифт изменился. Этот код является только прототипом и не касается проблем фокусировки между двумя элементами управления.

Чтобы использовать этот код:
1- Введите текст в RichTectBox.
2- Измените шрифт в ComboBox.
3- Вкладка Назад к RichTextBox.
4- Напечатайте еще текст.

Вот настраиваемый элемент управления RichTextBox:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;

namespace RichTextboxFont.Views
{
  public class RichTextBoxCustom : RichTextBox
  {
    public static readonly DependencyProperty CurrentFontFamilyProperty =
            DependencyProperty.Register("CurrentFontFamily", 
            typeof(FontFamily), typeof  
            (RichTextBoxCustom), 
            new FrameworkPropertyMetadata(new FontFamily("Tahoma"), 
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
            new PropertyChangedCallback(OnCurrentFontChanged)));

    public FontFamily CurrentFontFamily
    {
       get
       {
         return (FontFamily)GetValue(CurrentFontFamilyProperty);
       }
       set
       {
         SetValue(CurrentFontFamilyProperty, value);
       }
    }

    private static void OnCurrentFontChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {}

    protected override void OnTextInput(TextCompositionEventArgs e)
    {
      ViewModels.MainViewModel mwvm = this.DataContext as ViewModels.MainViewModel;
      if ((mwvm != null) && (mwvm.FontChanged))
      {
        TextPointer textPointer = this.CaretPosition.GetInsertionPosition(LogicalDirection.Forward);
        Run run = new Run(e.Text, textPointer);
        run.FontFamily = this.CurrentFontFamily;
        this.CaretPosition = run.ElementEnd;
        mwvm.FontChanged = false;
      }
      else
      {
         base.OnTextInput(e);
      }
    }
  } 
}

Вот XAML:

<Window x:Class="RichTextboxFont.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:local="clr-namespace:RichTextboxFont.Views" 
  xmlns:ViewModels="clr-namespace:RichTextboxFont.ViewModels" 
  Title="Main Window" 
  Height="400" Width="800">
  <DockPanel>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <ComboBox ItemsSource="{Binding Path=Fonts}" 
                  SelectedItem="{Binding Path=SelectedFont, Mode=TwoWay}"/>
        <local:RichTextBoxCustom Grid.Row="1" 
                                 CurrentFontFamily="{Binding Path=SelectedFont, Mode=TwoWay}" 
                                 FontSize="30"/>
    </Grid>
  </DockPanel>
</Window>

Вот модель ViewModel: Если вы не используете модели просмотра, дайте мне знать, и я добавлю также код базового класса; в противном случае google/stackoverflow также может помочь вам.

using System.Collections.ObjectModel;
using System.Windows.Media;

namespace RichTextboxFont.ViewModels
{
  public class MainViewModel : ViewModelBase
  {
    #region Constructor

    public MainViewModel()
    {
       FontFamily f1 = new FontFamily("Georgia");
       _fonts.Add(f1);
       FontFamily f2 = new FontFamily("Tahoma");
       _fonts.Add(f2);
    }

    private ObservableCollection<FontFamily> _fonts = new ObservableCollection<FontFamily>();
    public ObservableCollection<FontFamily> Fonts
    {
      get
      {
         return _fonts;
      }
      set
      {
        _fonts = value;
        OnPropertyChanged("Fonts");
      }
    }

    private FontFamily _selectedFont = new FontFamily("Tahoma");
    public FontFamily SelectedFont
    {
      get
      {
        return _selectedFont;
      }
      set
      {
        _selectedFont = value;
        FontChanged = true;
        OnPropertyChanged("SelectedFont");
      }
    }

    private bool _fontChanged = false;
    public bool FontChanged
    {
      get
      {
         return _fontChanged;
      }
      set
      {
        _fontChanged = value;
        OnPropertyChanged("FontChanged");
      }
    }

  #endregion
  }
}

Вот код окна, где я инициализирую ViewModel:

using System.Windows;

namespace RichTextboxFont.Views
{
  public partial class MainView : Window
  {
    public MainView()
    {
      InitializeComponent();
      this.DataContext = new ViewModels.MainViewModel();
    }
  }
}

Ответ 2

Там намного проще сделать это: Внедрите панель инструментов для RichTextBox.

В отличие от WinForms, RichTextBox в WPF по умолчанию не имеет панели инструментов, но ее очень легко создать самостоятельно. RichTextBox автоматически обрабатывает многие EditingCommands, поэтому это просто вопрос создания панели инструментов и некоторых кнопок. Microsoft предоставила образец кода для этого в нижней части Обзор RichTextBox в MSDN.

К сожалению, эти команды редактирования не включают установку свойства FontFace для выбора, хотя вы можете создать ComboBox на панели инструментов, которая может инициировать изменение с помощью обработчика события в файле codebehind.

То, что подход, взятый в этой статье CodePlex от Gregor Pross: WPF RichTextEditor

Проект прокомментирован на немецком языке, но сам источник очень четко написан. Код, используемый для его селектора шрифтов ComboBox, выглядит следующим образом:

    private void Fonttype_DropDownClosed(object sender, EventArgs e)
    {            
        string fontName = (string)Fonttype.SelectedItem;

        if (fontName != null)
        {                
            RichTextControl.Selection.ApplyPropertyValue(System.Windows.Controls.RichTextBox.FontFamilyProperty, fontName);
            RichTextControl.Focus();
        }
    }

Основная причина, по которой люди борются с выбором FontFace, заключается в том, что после выбора шрифта вы должны вернуть фокус в RichTextBox. Если пользователь должен вручную нажать вкладку или щелкнуть мышью в RichTextBox, будет создан новый текстовый выбор, и вы потеряете параметры форматирования, которые вы выбрали.

Один из ответов на этот вопрос StackOverflow обсуждает эту проблему. WPF Richtextbox FontFace/FontSize

Ответ 3

Это не совсем тривиальный ответ.

Чтобы сделать встроенное форматирование текста в Rich TextBox, как вы хотите, вам придется изменить свойство документа RichTextBox. Очень просто, что-то вроде этого будет работать

<RichTextBox >
  <RichTextBox.Document>
    <FlowDocument>
      <Paragraph>
        <Run>Something</Run>
        <Run FontWeight="Bold">Something Else</Run>
      </Paragraph>
    </FlowDocument>
  </RichTextBox.Document>
</RichTextBox>  

Я думаю, вы могли бы создать настраиваемый элемент управления, который создает новый элемент блока и задает необходимые свойства шрифта на основе пользовательского ввода.

Например, если пользователь набирает что-то, то нажимает жирным шрифтом. Вы захотите обернуть предыдущий текст в прогоне и создать новый элемент выполнения, который будет выделен шрифтом FontWeight, затем последующий текст будет завернут в жирный пробег.

Опять же, это не тривиальное решение, но я не могу придумать другого способа выполнить то, что вам нужно.