el Guille, la Web del Visual Basic, C#, .NET y más...

Abrir y guardar archivos usando RichTextBox para WPF

 
Publicado el 12/Ene/2019
Actualizado el 15/Ene/2019
Autor: Guillermo 'guille' Som

Cómo implementar las opciones de abrir y guardar archivos usando un control RichTextBox para WPF. (publicado en mi blog)




 

Este contenido está obtenido de mi blog, la URL original es:
Abrir y guardar archivos usando RichTextBox para WPF

Pues eso… hoy te voy a explicar cómo implementar las opciones de abrir y guardar archivos usando un control RichTextBox para WPF.

Actualizado (o nota del 15/Ene/19)
En realidad actualizado no está, salvo este comentario del martes 15 de enero.

Donde está la actualización es en el post que he publicado hoy: Indicar el Encoding al guardar el contenido de un RichTextBox de WPF, y es que resulta que con el código tal como te lo muestro aquí, si decides usar el formato Xaml (ya sabes: lo guarda como párrafos, etc.) y tu texto tiene tildes (vocales acentuadas), pues… resulta que no lo hace bien.

Así que… te recomiendo que veas el post de hoy y si te has descargado el zip con los proyectos, modifiques el código. Gracias

Como ya vimos en el post anterior (Leer el contenido como cadena y asignar un valor nuevo) usaremos tres opciones de formatos admitidos por RichTextBox de WPF. ¿Los recuerdas? Vale, te los resumo de nuevo:

  • Rtf el contenido debe estar en formato RTF.
  • Xaml el contenido debe estar en formato Xaml pero el que se puede poner en un RichTextBox o FlowDocument, cuando lo guardas lo pone dentro de un elemento Section. Es decir, no vale cualquier código Xaml y menos el que define un objeto Window.
  • Text Formato de texto plano, sin ningún tipo de formato.

Contenido de la ventana principal (MainWindow)

En la aplicación he puesto un control RichTextBox (de eso se trata este ejemplo, ¿no?) y además de una etiqueta para mostrar la información del archivo activo, también hay un menú con un elemento (Archivo) con las opciones de Abrir, Guardar como y Salir.

En esos tres submenús he puesto imágenes, éstas están descargadas de las que pone Visual Studio 2017 a nuestra disposición, realmente las que utiliza el propio Visual Studio 2017. El enlace para descargar todas esas imágenes (son un montón, lo que yo te diga) es este: Biblioteca de imágenes de Visual Studio. En ese enlace te explica qué tipos de imágenes contiene y más cosillas, aparte, claro del enlace para descargarlas.

Este es el código XAMl de la ventana principal (recuerda que es el mismo diseño para Visual Basic que para C#, lo único que cambia es el espacio de nombres usado en cada proyecto, más abajo te pongo el ZIP con el código completo para Visual Basic y para C# en una solución de Visual Studio 2017.

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Wpf_Abrir_y_guardar_en_RichTextBox_vb"
        mc:Ignorable="d"
        Title="Abrir y guardar archivos en un RichTextBox (VB)" 
        WindowStartupLocation="CenterScreen"
        ResizeMode="CanResizeWithGrip" 
        WindowStyle="ThreeDBorderWindow"
        Loaded="Window_Loaded" Closing="Window_Closing"
        Height="450" Width="800"
        Icon="Images/RichTextBox_16x.png">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition Width="Auto" MinWidth="100" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" MinHeight="24"/>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition Height="Auto" MinHeight="24" />
        </Grid.RowDefinitions>
        <Menu Grid.Row="0" Grid.Column="0" 
              Grid.ColumnSpan="2" Background="AliceBlue">
            <MenuItem Header="_Archivo" ToolTip="Abrir, Guardar, Salir">
                <MenuItem x:Name="mnuAbrir" Header="_Abrir..."
                                  Click="MnuAbrir_Click">
                    <MenuItem.Icon>
                        <Image Source="Images\OpenFile_16x.png" />
                    </MenuItem.Icon>
                </MenuItem>
                <MenuItem x:Name="mnuGuardar" Header="_Guardar cómo..."
                                  Click="MnuGuardar_Click">
                    <MenuItem.Icon>
                        <Image Source="Images\Save_16x.png" />
                    </MenuItem.Icon>
                </MenuItem>
                <Separator />
                <MenuItem x:Name="mnuSalir" Header="_Salir" Click="MnuSalir_Click">
                    <MenuItem.Icon>
                        <Image Source="Images\Close_16x.png" />
                    </MenuItem.Icon>
                </MenuItem>
            </MenuItem>
        </Menu>
        <RichTextBox x:Name="rtb" 
                     BorderThickness="2"
                     AcceptsTab="True" AcceptsReturn="True"
                     Grid.Column="0" Grid.Row="1" 
                     Grid.ColumnSpan="2" Grid.RowSpan="2"
                     TextChanged="Rtb_TextChanged">
            <FlowDocument />
        </RichTextBox>
        <Label x:Name="lblStatus" Content="Información..."
               Padding="4,2,4,2"
               Grid.Column="0" Grid.Row="3" 
               Grid.ColumnSpan="2"
               Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" />
    </Grid>
</Window>

Las definiciones de las filas y columnas del Grid nos dan espacio (no demasiado ancho) para la primera fila (la de los menús) y la última (la de la etiqueta de estado).

La columna esa que tengo con MinWidth a 100 era para poder poner el típico botón Salir, pero como ya lo tengo en el menú de Archivo, ¿pa qué ponerlo? pero ya que estaba… la he dejado Smile

Las imágenes usadas como recurso están en una carpeta llamada Images y aparte de las mostradas en los menús, hay otra para usarla como icono de la ventana: RichTextBox_16x.png.

Nota:
Fíjate que en la definición de la ventana (Window) se asigna a la propiedad Icon, pero en realidad no es un icono. Te lo digo por si lo quieres usar como icono de la aplicación. En ese caso tendrás que crear un icono nuevo, añadir una nueva imagen (o tipo de imagen) de 16×16 con 24 bits, copiar la imagen con un programa, por ejemplo el Paint que se incluye con Windows y después pegarla en ese icono creado en Visual Studio.

Vale, te lo explico paso a paso.

Crear un icono con Visual Studio a partir de una imagen

Lo he puesto como post separado para no cargar más de la cuenta este: Crear un icono con Visual Studio a partir de una imagen.

Fíjate que en el código XAML no he indicado la visibilidad de las barras de desplazamiento del control RichTextBox, por tanto no se muestran (tampoco está incluido en un control ScrollViewer). Si quieres que se muestren tanto la horizontal como la vertical, tendrás que indicarlo expresamente.

HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto"

Si le asignas un valor Auto se mostrarán según sea necesario (al menos la vertical, ya que la horizontal siempre se mostrará). Si quieres que siempre sean visible las dos, en vez de Auto indica el valor Visible.

El código para abrir un archivo y asignar el contenido en el RichTextBox

A continuación te muestro las funciones (tanto para VB.NET como para C#) del método usado para abrir un archivo y asignarlo al contenido del control RichTextBox.

Nota:
El código mostrado en el método LoadRtfFormat lo he convertido a partir de un ejemplo mostrado en la documentación en línea de Visual Studio.

''' <summary>
''' Adaptado del ejemplo de la documentación de Microsoft
''' https://docs.microsoft.com/es-es/dotnet/framework/wpf/
'''     controls/richtextbox-overview
''' </summary>
Private Function LoadRtfFormat(_fileName As String,
                               richTB As RichTextBox,
                               formato As String) As Boolean
    Dim abierto = False

    If File.Exists(_fileName) Then
        Dim range = New TextRange(richTB.Document.ContentStart,
                                  richTB.Document.ContentEnd)
        Using sr As New StreamReader(_fileName, Encoding.Default, True)
            Try
                ' leer el contenido para admitir tildes, etc.   (09/Ene/19)
                Dim texto = sr.ReadToEnd
                Dim stream = New MemoryStream(Encoding.UTF8.GetBytes(texto))

                range.Load(stream, formato)
                abierto = True
            Catch ex As Exception
                MessageBox.Show("Error el formato no es válido" & vbCrLf &
                                ex.Message,
                                $"Abrir {formato}",
                                MessageBoxButton.OK,
                                MessageBoxImage.Asterisk)
            End Try
        End Using
    End If

    Return abierto
End Function

Private Function LoadRtf(ByVal _fileName As String,
                         richTB As RichTextBox) As Boolean
    Return LoadRtfFormat(_fileName, richTB, DataFormats.Rtf)
End Function

' Al cargar como Xaml da error el Visual Studio
' ya que lo trata como una clase
' y esto en realidad es para abrir con el XAML generado,
'   con los elementos Paragraph, Bold, Run, etc.
Private Function LoadRtfXaml(ByVal _fileName As String,
                             richTB As RichTextBox) As Boolean
    Return LoadRtfFormat(_fileName, richTB, DataFormats.Xaml)
End Function

''' <summary>
''' El formato texto abrirlo directamente
''' Aunque funciona igual que llamando a LoadRtfFormat
''' </summary>
Private Function LoadRtfText(ByVal _fileName As String,
                             richTB As RichTextBox) As Boolean
    'Return LoadRtfFormat(_fileName, richTB, DataFormats.Text)

    Dim abierto = False

    If File.Exists(_fileName) Then
        Dim range = New TextRange(richTB.Document.ContentStart,
                                  richTB.Document.ContentEnd)
        Using sr As New StreamReader(_fileName, Encoding.Default, True)
            Try
                ' leer el contenido para admitir tildes, etc.   (09/Ene/19)
                Dim texto = sr.ReadToEnd

                Dim textRange = New TextRange(richTB.Document.ContentStart,
                                              richTB.Document.ContentEnd)
                textRange.Text = texto

                abierto = True
            Catch ex As Exception
                MessageBox.Show("Error el formato no es válido" & vbCrLf &
                                ex.Message,
                                "Abrir Text",
                                MessageBoxButton.OK,
                                MessageBoxImage.Asterisk)
            End Try
        End Using
    End If

    Return abierto
End Function
/// <summary>
/// Adaptado del ejemplo de la documentación de Microsoft
/// https://docs.microsoft.com/es-es/dotnet/framework/wpf/
///     controls/richtextbox-overview
/// </summary>
private bool LoadRtfFormat(string _fileName, RichTextBox richTB, string formato)
{
    var abierto = false;

    if (File.Exists(_fileName))
    {
        var range = new TextRange(richTB.Document.ContentStart, 
                                  richTB.Document.ContentEnd);

        using (StreamReader sr = new StreamReader(_fileName, 
                                                  Encoding.Default, 
                                                  true))
        {
            try
            {
                // leer el contenido para admitir tildes, etc.   (09/Ene/19)
                var texto = sr.ReadToEnd();
                var stream = new MemoryStream(Encoding.UTF8.GetBytes(texto));

                range.Load(stream, formato);
                abierto = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error el formato no es válido\r\n"  + 
                                ex.Message, $"Abrir {formato}", 
                                MessageBoxButton.OK, 
                                MessageBoxImage.Asterisk);
            }
        }
    }

    return abierto;
}

private bool LoadRtf(string _fileName, RichTextBox richTB)
{
    return LoadRtfFormat(_fileName, richTB, DataFormats.Rtf);
}

// Al cargar como Xaml da error el Visual Studio
// ya que lo trata como una clase
// y esto en realidad es para abrir con el XAML generado,
// con los elementos Paragraph, Bold, Run, etc.
private bool LoadRtfXaml(string _fileName, RichTextBox richTB)
{
    return LoadRtfFormat(_fileName, richTB, DataFormats.Xaml);
}

/// <summary>
/// El formato texto abrirlo directamente
/// Aunque funciona igual que llamando a LoadRtfFormat
/// </summary>
private bool LoadRtfText(string _fileName, RichTextBox richTB)
{
    var abierto = false;

    if (File.Exists(_fileName))
    {
        var range = new TextRange(richTB.Document.ContentStart, 
                                  richTB.Document.ContentEnd);

        using (StreamReader sr = new StreamReader(_fileName, 
                                                  Encoding.Default, 
                                                  true))
        {
            try
            {
                // leer el contenido para admitir tildes, etc.   (09/Ene/19)
                var texto = sr.ReadToEnd();

                var textRange = new TextRange(richTB.Document.ContentStart, 
                                              richTB.Document.ContentEnd);
                textRange.Text = texto;

                abierto = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Error el formato no es válido\r\n" + 
                                ex.Message, 
                                "Abrir Text", 
                                MessageBoxButton.OK, 
                                MessageBoxImage.Asterisk);
            }
        }
    }

    return abierto;
}

Nota:
El código del método LoadRtfText se podría haber reducido haciendo una llamada al método LoadRtfFormat e indicando como último argumento DataFormats.Text. Pero… ese también vale, por si quieres verlo de la forma tradicional.

Te recuerdo que el tipo XAML no es un archivo de diseño normal XAML / WPF si no el formato XAML del contenido del RichTextBox.

Para llamar a ese método lo haremos desde el método de evento MnuAbrir_Click que es el que usará la aplicación cuando el usuario pulse en el menú Abrir.

Veamos el código para VB y C# y te explico un par de detalles.

Private Sub MnuAbrir_Click(sender As Object, e As RoutedEventArgs)
    If rtbModificado Then
        If MessageBox.Show("El texto está modificado," & vbCrLf &
                           "¿seguro que quieres abir?",
                           "Texto Modificado",
                           MessageBoxButton.YesNo,
                           MessageBoxImage.Question) = MessageBoxResult.No Then
            Exit Sub
        End If
    End If
    Dim oFD As New OpenFileDialog
    oFD.Filter = "Formato RTF|*.rtf|" &
                 "Formato Xaml del contenido RTF|*.xaml|" &
                 "Código C# y VB|*.cs;*.vb|" &
                 "Texto (*.txt)|*.txt|Todos (*.*)|*.*"

    oFD.Title = "Selecciona el archivo"
    oFD.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyDocument
    oFD.FileName = ""
    If oFD.ShowDialog() Then
        Dim ext = System.IO.Path.GetExtension(oFD.FileName).ToLower()
        Select Case ext
            Case ".rtf"
                LoadRtf(oFD.FileName, rtb)
            Case ".xaml"
                LoadRtfXaml(oFD.FileName, rtb)
            Case Else
                LoadRtfText(oFD.FileName, rtb)
        End Select

        rtbModificado = False
        lblStatus.Content = $"Texto cargado de: {oFD.FileName}"
    End If
End Sub
private void MnuAbrir_Click(object sender, RoutedEventArgs e)
{
    if (rtbModificado)
    {
        if (MessageBox.Show("El texto está modificado,\r\n" + 
                            "¿seguro que quieres abir?", 
                            "Texto Modificado", 
                            MessageBoxButton.YesNo, 
                            MessageBoxImage.Question) == MessageBoxResult.No)
            return;
    }
    OpenFileDialog oFD = new OpenFileDialog();
    oFD.Filter = "Formato RTF|*.rtf|" + "Formato Xaml del contenido RTF|*.xaml|" + 
                 "Código C# y VB|*.cs;*.vb|" + "Texto (*.txt)|*.txt|Todos (*.*)|*.*";

    oFD.Title = "Selecciona el archivo";
    // oFD.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyDocuments;
    oFD.InitialDirectory = Environment.GetFolderPath(
                                        Environment.SpecialFolder.MyDocuments);
    oFD.FileName = "";
    if (oFD.ShowDialog() == true)
    {
        var ext = System.IO.Path.GetExtension(oFD.FileName).ToLower();
        switch (ext)
        {
            case ".rtf":
                {
                    LoadRtf(oFD.FileName, rtb);
                    break;
                }

            case ".xaml":
                {
                    LoadRtfXaml(oFD.FileName, rtb);
                    break;
                }

            default:
                {
                    LoadRtfText(oFD.FileName, rtb);
                    break;
                }
        }

        rtbModificado = false;
        lblStatus.Content = $"Texto cargado de: {oFD.FileName}";
    }
}

En este método se comprueba si el texto está modificado, de ser así, da la oportunidad para guardarlo antes de abrir el nuevo.

El método OpenFileDialog está definido en el espacio de nombres Microsoft.Win32, si prefieres el clásico de Windows Forms, tendrás que agregar una referencia al proyecto a esa biblioteca de WinForms.
Para los de Visual Basic no hay gran diferencia, salvo que el método ShowDialog devuelve True o False según se haya aceptado o cancelado. En el caso de C# hay que comprobarlo con el signo de igualdad, ya que el valor devuelto por el método ShowDialog es de tipo bool?.

En cuanto al directorio de inicio (InitialDirectory) con Visual Basic he utilizado el objeto My, para C# tengo una clase que simula algunas de las características de My, concretamente My.Properties, My.Application.Info y My.Computer.FileSystem.SpelciaDirectories, pero he preferido usar aquí la llamada directa a las clases de .NET (que es lo que supongo que harán las definiciones correspondientes de Visual Basic). Concretamente la llamada al método Environment.GetFolderPath al que le pasamos el valor MyDocuments de la enumeración Environment.SpecialFolder y devuelve el valor como una cadena, que es lo que necesitamos aquí.

El tipo de archivo lo dará la extensión del mismo y eso hago, una comprobación según la extensión, diferenciando los tipos Rtf y Xaml del resto, que los considero como texto (Text).

Finalmente mostramos en la etiqueta de información el nombre del archivo e indicamos que el texto no se ha modificado.

El código para guardar en un archivo el contenido del RichTextBox

Ahora le toca la parte de guardar. El método principal lo he llamado SaveRtfFormat que también está en el ejemplo sobre RichTextBox que te he indicado antes.

Veamos el código para Visual Basic y C#.

''' <summary>
''' Adaptado del ejemplo de la documentación de Microsoft
''' https://docs.microsoft.com/es-es/dotnet/framework/wpf/controls/
'''     richtextbox-overview
''' </summary>
Private Function SaveRtfFormat(_fileName As String,
                               richTB As RichTextBox,
                               formato As String) As Boolean
    Dim range As TextRange
    Dim guardado As Boolean = False

    range = New TextRange(richTB.Document.ContentStart, richTB.Document.ContentEnd)
    Using fStream As New FileStream(_fileName, FileMode.Create)
        Try
            range.Save(fStream, formato)
            guardado = True
        Catch ex As Exception
            MessageBox.Show("Error el formato no es válido" & vbCrLf &
                            ex.Message,
                            $"Guardar {formato}",
                            MessageBoxButton.OK,
                            MessageBoxImage.Asterisk)
        End Try

        fStream.Close()
    End Using

    Return guardado
End Function

Private Function SaveRtf(ByVal _fileName As String,
                         richTB As RichTextBox) As Boolean
    Return SaveRtfFormat(_fileName, richTB, DataFormats.Rtf)
End Function
Private Function SaveRtfXaml(ByVal _fileName As String,
                             richTB As RichTextBox) As Boolean
    Return SaveRtfFormat(_fileName, richTB, DataFormats.Xaml)
End Function

''' <summary>
''' El formato Text lo guardo como texto normal
''' </summary>
Private Function SaveRtfText(ByVal _fileName As String,
                        richTB As RichTextBox) As Boolean
    'Return SaveRtfFormat(_fileName, richTB, DataFormats.Text)

    Dim guardado = False

    Try
        Dim texto = getRtbText(richTB)
        Using sw As New StreamWriter(_fileName, False, Encoding.Default)
            sw.WriteLine(texto)
        End Using

        guardado = True
    Catch ex As Exception
        MessageBox.Show("Error al guardar:" & vbCrLf &
                        ex.Message,
                        "Guardar Text",
                        MessageBoxButton.OK,
                        MessageBoxImage.Asterisk)
    End Try

    Return guardado
End Function
/// <summary>
/// Adaptado del ejemplo de la documentación de Microsoft
/// https://docs.microsoft.com/es-es/dotnet/framework/wpf/controls/
///     richtextbox-overview
/// </summary>
private bool SaveRtfFormat(string _fileName, RichTextBox richTB, string formato)
{
    TextRange range;
    bool guardado = false;

    range = new TextRange(richTB.Document.ContentStart, 
                          richTB.Document.ContentEnd);

    using (FileStream fStream = new FileStream(_fileName, 
                                               FileMode.Create))
    {
        try
        {
            range.Save(fStream, formato);
            guardado = true;
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error el formato no es válido\r\n"+ 
                            ex.Message, 
                            $"Guardar {formato}", 
                            MessageBoxButton.OK, 
                            MessageBoxImage.Asterisk);
        }

        fStream.Close();
    }

    return guardado;
}

private bool SaveRtf(string _fileName, RichTextBox richTB)
{
    return SaveRtfFormat(_fileName, richTB, DataFormats.Rtf);
}
private bool SaveRtfXaml(string _fileName, RichTextBox richTB)
{
    return SaveRtfFormat(_fileName, richTB, DataFormats.Xaml);
}

/// <summary>
/// El formato Text lo guardo como texto normal
/// </summary>
private bool SaveRtfText(string _fileName, RichTextBox richTB)
{
    var guardado = false;

    try
    {
        var texto = getRtbText(richTB);
        using (StreamWriter sw = new StreamWriter(_fileName, 
                                                  false, 
                                                  Encoding.Default))
        {
            sw.WriteLine(texto);
        }

        guardado = true;
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error al guardar:\r\n" + 
                        ex.Message, 
                        "Guardar Text", 
                        MessageBoxButton.OK, 
                        MessageBoxImage.Asterisk);
    }

    return guardado;
}

/// <summary>
/// Extrae el texto de un RichTextBox y lo devuelve como una cadena.
/// De un ejemplo en C# de:
/// https://docs.microsoft.com/es-es/dotnet/framework/wpf/controls/
///     how-to-extract-the-text-content-from-a-richtextbox
/// </summary>
private string getRtbText(RichTextBox rtb)
{
    var textRange = new TextRange(rtb.Document.ContentStart,
                                  rtb.Document.ContentEnd);
    return textRange.Text;
}

Como en el método SaveRtfText lo hago directamente, llamo al método getRtbText que ya vimos en el post anterior.

Estos métodos los llamaremos desde el método de evento relacionado con el evento Click del menú Guardar como…, tal como te muestro a continuación.

Private Sub MnuGuardar_Click(sender As Object, e As RoutedEventArgs)
    Dim oFD As New SaveFileDialog
    oFD.Filter = "Formato RTF|*.rtf|" &
                 "Formato Xaml del contenido RTF|*.xaml|" &
                 "Código C# y VB|*.cs;*.vb|" &
                 "Texto (*.txt)|*.txt|Todos (*.*)|*.*"

    oFD.Title = "Selecciona el archivo para guardar el contenido RTF"
    oFD.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyDocuments
    oFD.FileName = ""
    If oFD.ShowDialog() Then
        Dim ext = System.IO.Path.GetExtension(oFD.FileName).ToLower()
        Select Case ext
            Case ".rtf"
                SaveRtf(oFD.FileName, rtb)
            Case ".xaml"
                SaveRtfXaml(oFD.FileName, rtb)
            Case Else
                SaveRtfText(oFD.FileName, rtb)
        End Select

        rtbModificado = False
        lblStatus.Content = $"Texto guardado como: {oFD.FileName}"
    End If

End Sub
private void MnuGuardar_Click(object sender, RoutedEventArgs e)
{
    SaveFileDialog oFD = new SaveFileDialog();
    oFD.Filter = "Formato RTF|*.rtf|" + "Formato Xaml del contenido RTF|*.xaml|" + 
                 "Código C# y VB|*.cs;*.vb|" + "Texto (*.txt)|*.txt|Todos (*.*)|*.*";

    oFD.Title = "Selecciona el archivo para guardar el contenido RTF";
    // oFD.InitialDirectory = My.Computer.FileSystem.SpecialDirectories.MyDocuments;
    oFD.InitialDirectory = Environment.GetFolderPath(
                                        Environment.SpecialFolder.MyDocuments);
    oFD.FileName = "";
    if (oFD.ShowDialog() == true)
    {
        var ext = System.IO.Path.GetExtension(oFD.FileName).ToLower();
        switch (ext)
        {
            case ".rtf":
                {
                    SaveRtf(oFD.FileName, rtb);
                    break;
                }

            case ".xaml":
                {
                    SaveRtfXaml(oFD.FileName, rtb);
                    break;
                }

            default:
                {
                    SaveRtfText(oFD.FileName, rtb);
                    break;
                }
        }

        rtbModificado = false;
        lblStatus.Content = $"Texto guardado como: {oFD.FileName}";
    }
}

Aquí nada especial que contar, salvo que en vez de usar la clase OpenFileDialog usamos SaveFileDialog, el resto, es lo mismo que te expliqué antes.

Y este es el código principal de la aplicación, a falta de la comprobación al cerrar el formulario, perdón ventana, principal que se comprueba si el código se ha modificado y el evento TextChanged del control RichTextBox.

Private Sub Rtb_TextChanged(sender As Object, e As TextChangedEventArgs)
    If inicializando Then Return

    rtbModificado = True
End Sub



Private Sub Window_Closing(sender As Object, e As CancelEventArgs)
    ' Comprobar si están modificados
    If rtbModificado Then
        If MessageBox.Show("El texto está modificado," & vbCrLf &
                           "¿Quieres guardarlo?",
                           "Texto Modificado",
                           MessageBoxButton.YesNo,
                           MessageBoxImage.Question) = MessageBoxResult.Yes Then
            rtb.Focus()
            MnuGuardar_Click(mnuGuardar, Nothing)
        End If
    End If
End Sub
private void Rtb_TextChanged(object sender, TextChangedEventArgs e)
{
    if (inicializando)
        return;

    rtbModificado = true;
}

private void Window_Closing(object sender, CancelEventArgs e)
{
    // Comprobar si est\f2án modificados
    if (rtbModificado)
    {
        if (MessageBox.Show("El texto está modificado,\r\n" + 
                            "¿Quieres guardarlo?", 
                            "Texto Modificado", 
                            MessageBoxButton.YesNo, 
                            MessageBoxImage.Question) == MessageBoxResult.Yes)
        {
            rtb.Focus();
            MnuGuardar_Click(mnuGuardar, null);
        }
    }
}

Ah, sí, una cosilla que he puesto en el evento de carga de la ventana (evento Window_Loaded) en el que asigno un ancho grande al documento interno del RichTextBox con idea de que no se muestren las líneas cortadas (ver figuras 1 y 2).

Figura 1. Si no asignamos un valor grande a Document.PageWidth, las líneas largas se mostrarán partidas
Figura 1. Si no asignamos un valor grande a Document.PageWidth, las líneas largas se mostrarán partidas
Figura 2. Al asignar un valor alto a Document.PageWidth las líneas no se cortan
Figura 2. Al asignar un valor alto a Document.PageWidth las líneas no se cortan

Nota:
El archivo mostrado en las figuras 1 y 2 es uno en formato RTF coloreado por mi aplicación gsColorear.

Lo que hago es asignar un valor 2000 a la propiedad PageWidth del objeto Document del RichTextBox (si crees que tendrás líneas de más de 2000, pues ponle un valor mayor).

Ese ejemplo (o el truco para hacer eso) lo saqué (creo) de un foro de StackOverflow:
C#/WPF: Disable Text-Wrap of RichTextBox.

Private Sub Window_Loaded(sender As Object,
                          e As RoutedEventArgs)
    ' Para que no corte las líneas
    rtb.Document.PageWidth = 2000

    lblStatus.Content = My.Application.Info.Copyright & " - " &
                        My.Application.Info.Description

    inicializando = False

    rtb.Focus()

End Sub
private void Window_Loaded(object sender, RoutedEventArgs e)
{
    // Para mostrar la info del Copyright y Description
    FileVersionInfo fvi;
    System.Reflection.Assembly ensamblado;
    ensamblado = System.Reflection.Assembly.GetExecutingAssembly();
    fvi = FileVersionInfo.GetVersionInfo(ensamblado.Location);

    // Para que no corte las líneas
    rtb.Document.PageWidth = 2000;

    //lblStatus.Content = My.Application.Info.Copyright + " - " +
    //                    My.Application.Info.Description;

    lblStatus.Content = fvi.LegalCopyright + " - " +
                        fvi.Comments;

    inicializando = false;

    rtb.Focus();
}

Nota:
Esto ocurre (que se corten las líneas) porque el control RichTextBox no tiene una propiedad TextWrapping como ocurre con el control TextBox.

Eso mismo se puede hacer asignando el valor directamente en la definición del objeto FlowDocument del RichTextBox:

<FlowDocument PageWidth="2000" />

Resaltar en el código para C# que como no tiene el objeto My de Visual Basic, para acceder a la información del Copyright y Description uso llamadas directas a la información ofrecida por la clase FileVersionInfo a partir del ensamblado actual.

Pues ya está, esto es todo… para la próxima ocasión veremos cómo cambiar algunas propiedades del contenido del RichTextBox, como por ejemplo el tipo y tamaño de la letra y algunas cosillas más, ya que si pruebas a abrir un archivo de texto plano, pues… como que no se ve muy bien, en la figura 3 tienes un ejemplo de cómo se vería un archivo de C# (sin colorear).

Figura 3. Así se vería el contenido de un archivo abierto como texto
Figura 3. Así se vería el contenido de un archivo abierto como texto

Nos vemos.
Guillermo

Aquí tienes el código completo para poder usarla (una vez descomprimido) con la solución para Visual Studio 2017. Están los dos proyectos, el de Visual Basic y el de C# (como de costumbre).

El zip: Wpf_Abrir_guardar_RichTextBox_20190112_2355.zip (34.7 KB)

MD5 Checksum: 461518743ED0913B642C788342774057



 


La fecha/hora en el servidor es: 09/12/2024 22:11:41

La fecha actual GMT (UTC) es: 

©Guillermo 'guille' Som, 1996-2024