2009-07-05 15 views
11

Puedo estar imaginando esto completamente, pero podría haber jurado que había una manera de hacer que los elementos Run individuales (o Parapgraph) en un RichTextBox sean de solo lectura. También podría haber jurado que intentado un método para hacer esto por mí mismo hace unas semanas y me quedé satisfecho con los resultados - Recuerdo vagamente que parecía algo como esto:Elementos de solo lectura ¿Ejecutar elementos en un WPF RichTextBox?

<RichTextBox x:Name="richTextBox" 
      AcceptsTab="True" 
      AcceptsReturn="True" 
      FontFamily="Courier New" 
      FontSize="14"> 
    <FlowDocument> 
     <Paragraph> 
      <Run IsReadOnly="True">I wish this was read-only!</Run> 
     </Paragraph> 
    </FlowDocument> 
</RichTextBox> 

Ahora, unas semanas más tarde, voy para tratar de hacer que los elementos de ejecución solo sean leídos en un RichTextBox solo para encontrar que no parece posible.

This post on the MSDN forums parece confirmar eso.

¿Me lo imaginaba por completo? ¿O hay una manera de hacer lo que quiero hacer?

Respuesta

7

Muy bien, he encontrado una solución que funciona para mi caso, pero puede no funcionar para todos los que quieran algo como esto. Es desordenado, pero cumple su función.

No voy a aceptar mi propia respuesta por unos días, solo en caso de que alguien más tenga una mejor manera de lograr esto.

Aquí vamos, en primer lugar, el XAML:

<Window x:Class="WpfApplication1.Window1" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="Window1" 
     Height="500" 
     Width="600"> 
    <DockPanel LastChildFill="True"> 
     <RichTextBox x:Name="rtb" 
        FontFamily="Courier New" 
        FontSize="14" 
        PreviewKeyDown="rtb_PreviewKeyDown"> 
      <FlowDocument> 
       <Paragraph> 
        <InlineUIContainer Unloaded="InlineUIContainer_Unloaded"> 
         <TextBlock FontFamily="Courier New" FontSize="14">This line of text is not editable.</TextBlock> 
        </InlineUIContainer> 
        <Run Foreground="Blue">But this is editable.</Run> 
       </Paragraph> 
      </FlowDocument> 
     </RichTextBox> 
    </DockPanel> 
</Window> 

Y el código subyacente:

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

namespace WpfApplication1 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 
     } 

     private void InlineUIContainer_Unloaded(object sender, RoutedEventArgs e) 
     { 
      (sender as InlineUIContainer).Unloaded -= new RoutedEventHandler(InlineUIContainer_Unloaded); 

      TextBlock tb = new TextBlock(); 
      tb.FontFamily = new FontFamily("Courier New"); 
      tb.FontSize = 14; 
      tb.Text = "This line of text is not editable."; 

      TextPointer tp = rtb.CaretPosition.GetInsertionPosition(LogicalDirection.Forward); 
      InlineUIContainer iuic = new InlineUIContainer(tb, tp); 
      iuic.Unloaded += new RoutedEventHandler(InlineUIContainer_Unloaded); 
     } 

     private void rtb_PreviewKeyDown(object sender, KeyEventArgs e) 
     { 
      if (e.Key == Key.Enter) 
      { 
       var newPointer = rtb.Selection.Start.InsertLineBreak(); 
       rtb.Selection.Select(newPointer, newPointer); 

       e.Handled = true; 
      } 
     } 
    } 
} 

Mi solución se basa en el hecho de que cuando un InlineUIContainer se retira de la interfaz de usuario, es Unloaded() método se llama. En ese punto, simplemente reinserto el InlineUIContainer borrado en la posición actual de intercalación.

Como con cualquier hack, hay un montón de desventajas. Las desventajas que estoy encontrando son los siguientes:

  • El texto que quiere ser de sólo lectura debe ser envuelto en una InlineUIContainer. Eso es un poco limitante para esta solución.
  • Tengo que capturar la tecla 'Intro' e insertar saltos de línea manualmente, de lo contrario, InlineUIContainer.Unloaded() sigue disparando cada vez que se presiona la tecla Enter. No es divertido, pero funciona para mi caso.

No es una gran solución, pero creo que funcionará para mí. Como dije, no voy a marcar esto como una respuesta a mi propia pregunta todavía; con suerte, alguien más tendrá una mejor manera de hacerlo.

+2

muy interesante que me ayudará a añadir "widgets" si ingresas al editor de texto. Estoy creando una ventana muy agradable como el editor Wiki. Entonces, para agregar imágenes, categorías y palabras clave, estoy usando pequeños objetos que decoran el objeto de texto enriquecido. Ojalá el editor fuera un poco más como el editor de estudio visual. De esta forma podría agregar "Adornos" pero ... ¡esto definitivamente me ayudó! Gracias. – JustinKaz

+0

Me alegro de que pueda ayudar, pero tenga cuidado, recuerdo vagamente que es posible eliminar el texto de solo lectura con la tecla de retroceso.No estoy seguro después de todo este tiempo, pero lo probaría a fondo para garantizar que los widgets permanezcan como de solo lectura. –

1

Esto puede lograrse mediante la manipulación de dos eventos: 1) OnMouseDown 2) OnPreviewKeyDown

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Windows.Controls; 
using System.Windows; 
using System.Windows.Input; 

namespace WpfApplication2 
{ 
public class MultiPartTextBox : TextBox 
{ 
    private string _prefix; 
    private string _content; 

    public string Prefix 
    { 
     get { return _prefix; } 
     set { _prefix = value; 
     Text = _prefix; 
     } 
    } 

    public string Content 
    { 
     get { return _content; } 
     set { 
      _content = value; 
      Text = _prefix + _content; 
     } 
    } 

    public MultiPartTextBox() { _prefix = string.Empty; } 

    protected override void OnMouseDown(MouseButtonEventArgs e) 
    { 

     base.OnMouseDown(e); 
     this.SelectionStart = _prefix.Length; 
     this.SelectionLength = 0; 
    } 

    //tab In 
    protected override void OnGotFocus(RoutedEventArgs e) 
    { 
     this.SelectionStart = _prefix.Length; 
     this.SelectionLength = 0; 
     base.OnGotFocus(e); 
    } 

    protected override void OnPreviewKeyDown(KeyEventArgs e) 
    { 
     if (e.Key == Key.Back 
      || e.Key == Key.Delete 
      || e.Key==Key.Left) 
     { 
      if (CaretIndex <= _prefix.Length) 
      { 
       e.Handled = true; 
       return; 
      } 
     } 
     base.OnPreviewKeyDown(e); 
    } 
    } 
    } 

en XAML tenemos que manejarlo de forma siguiente:

xmlns:uc="clr-namespace:WpfApplication2" 

     <uc:MultiPartTextBox Height="30" HorizontalAlignment="Left" 
      Margin="80,94,0,0" x:Name="multiPartTxt1" VerticalAlignment="Top" 
      Width="224" Prefix="NON-EDITABLE" CaretIndex="4" >    
     </uc:MultiPartTextBox> 
+0

Olvidaste los toques de dedos. – McKay

Cuestiones relacionadas