2009-11-30 20 views
6

Tengo un richtextbox, que planeo guardar en una base de datos, que se puede cargar de nuevo en el mismo richtextbox. Lo tengo funcionando para poder guardar el documento de flujo como DataFormats.XamlPackage, que guarda las imágenes, pero el problema es que el texto no se puede buscar. Con DataFormats.Xaml, tengo el texto, por supuesto, pero no hay imágenes. Las imágenes serán pegadas por el usuario final, no las imágenes incluidas con la aplicación.C# WPF convertir BitmapImage pegado en richtextbox en binario

Intenté usar XamlWriter para obtener el texto en XML, y luego tomar las imágenes del documento por separado e insertarlas como binarias en el XML, pero parece que no puedo encontrar una manera de obtener las imágenes en formato binario. ..

¿Alguien tiene ideas sobre cómo hacer que las imágenes se vuelvan binarias, separadas del texto?

¡Gracias de antemano!

GetImageByteArray() es donde está el problema.

Código:

private void SaveXML() 
{ 
      TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd); 
      FlowDocument flowDocument = richTextBox.Document; 
using (StringWriter stringwriter = new StringWriter()) 
       { 
        using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter)) 
        { 
         XamlWriter.Save(flowDocument, writer); 
        } 

        testRTF t = new testRTF(); 
        t.RtfText = new byte[0]; 
        t.RtfXML = GetImagesXML(flowDocument); 
        t.RtfFullText = stringwriter.ToString(); 
        //save t to database 
       } 
       richTextBox.Document.Blocks.Clear(); 
} 


private string GetImagesXML(FlowDocument flowDocument) 
     { 

      using (StringWriter stringwriter = new StringWriter()) 
      { 
       using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter)) 
       { 

        Type inlineType; 
        InlineUIContainer uic; 
        System.Windows.Controls.Image replacementImage; 
        byte[] bytes; 
        System.Text.ASCIIEncoding enc; 

        //loop through replacing images in the flowdoc with the byte versions 
        foreach (Block b in flowDocument.Blocks) 
        { 
         foreach (Inline i in ((Paragraph)b).Inlines) 
         { 
          inlineType = i.GetType(); 

          if (inlineType == typeof(Run)) 
          { 
           //The inline is TEXT!!! 
          } 
          else if (inlineType == typeof(InlineUIContainer)) 
          { 
           //The inline has an object, likely an IMAGE!!! 
           uic = ((InlineUIContainer)i); 

           //if it is an image 
           if (uic.Child.GetType() == typeof(System.Windows.Controls.Image)) 
           { 
            //grab the image 
            replacementImage = (System.Windows.Controls.Image)uic.Child; 

            //get its byte array 
            bytes = GetImageByteArray((BitmapImage)replacementImage.Source); 
            //write the element 
            writer.WriteStartElement("Image"); 
            //put the bytes into the tag 
            enc = new System.Text.ASCIIEncoding(); 
            writer.WriteString(enc.GetString(bytes)); 
            //close the element 
            writer.WriteEndElement(); 
           } 
          } 
         } 
        } 
       } 

       return stringwriter.ToString(); 
      } 
     } 


//This function is where the problem is, i need a way to get the byte array 
     private byte[] GetImageByteArray(BitmapImage bi) 
     { 
      byte[] result = new byte[0]; 
        using (MemoryStream ms = new MemoryStream()) 
        { 
         XamlWriter.Save(bi, ms); 
         //result = new byte[ms.Length]; 
         result = ms.ToArray(); 
        } 
      return result; 
} 

ACTUALIZACIÓN

Creo que puede haber encontrado finalmente una solución, que voy a publicar a continuación. Utiliza BmpBitmapEncoder y BmpBitmapDecoder. Esto me permite obtener un archivo binario a partir de la imagen de mapa de bits, almacenarlo en la base de datos, cargarlo y mostrarlo de nuevo en FlowDocument. Las pruebas iniciales han demostrado ser exitosas. Para fines de prueba, estoy eludiendo el paso de mi base de datos y, básicamente, la duplicación de la imagen creando binarios, luego tomo el binario y lo convierto en una nueva imagen y lo agrego al FlowDocument. El único problema es que cuando intento tomar el FlowDocument modificado y uso la función XamlWriter.Save, se produce un error en la imagen recién creada con "No se puede serializar un tipo no público 'System.Windows.Media.Imaging.BitmapFrameDecode". Esto llevará más investigación. Sin embargo, tendré que dejarlo solo por el momento.

private void SaveXML() 
     { 
      TextRange documentTextRange = new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd); 
      FlowDocument flowDocument = richTextBox.Document; 

      string s = GetImagesXML(flowDocument);//temp 
      LoadImagesIntoXML(s); 

       using (StringWriter stringwriter = new StringWriter()) 
       { 
        using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter)) 
        { 
         XamlWriter.Save(flowDocument, writer);//Throws error here 
        } 

       } 
} 

private string GetImagesXML(FlowDocument flowDocument) 
     { 
      string s= ""; 

      using (StringWriter stringwriter = new StringWriter()) 
      { 


        Type inlineType; 
        InlineUIContainer uic; 
        System.Windows.Controls.Image replacementImage; 
        byte[] bytes; 
        BitmapImage bi; 

        //loop through replacing images in the flowdoc with the byte versions 
        foreach (Block b in flowDocument.Blocks) 
        { 
         foreach (Inline i in ((Paragraph)b).Inlines) 
         { 
          inlineType = i.GetType(); 

          if (inlineType == typeof(Run)) 
          { 
           //The inline is TEXT!!! 
          } 
          else if (inlineType == typeof(InlineUIContainer)) 
          { 
           //The inline has an object, likely an IMAGE!!! 
           uic = ((InlineUIContainer)i); 

           //if it is an image 
           if (uic.Child.GetType() == typeof(System.Windows.Controls.Image)) 
           { 
            //grab the image 
            replacementImage = (System.Windows.Controls.Image)uic.Child; 
            bi = (BitmapImage)replacementImage.Source; 

            //get its byte array 
            bytes = GetImageByteArray(bi); 

            s = Convert.ToBase64String(bytes);//temp 
           } 
          } 
         } 
        } 

       return s; 
      } 
     } 

private byte[] GetImageByteArray(BitmapImage src) 
     { 
       MemoryStream stream = new MemoryStream(); 
       BmpBitmapEncoder encoder = new BmpBitmapEncoder(); 
       encoder.Frames.Add(BitmapFrame.Create((BitmapSource)src)); 
       encoder.Save(stream); 
       stream.Flush(); 
      return stream.ToArray(); 
     } 


private void LoadImagesIntoXML(string xml) 
     { 


      byte[] imageArr = Convert.FromBase64String(xml); 
System.Windows.Controls.Image img = new System.Windows.Controls.Image() 

MemoryStream stream = new MemoryStream(imageArr); 
      BmpBitmapDecoder decoder = new BmpBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.Default); 
      img.Source = decoder.Frames[0]; 
      img.Stretch = Stretch.None; 

Paragraph p = new Paragraph(); 
      p.Inlines.Add(img); 
      richTextBox.Document.Blocks.Add(p); 
     } 
+0

posible que desee cambiar el título de este post a algo más apropiado ... parece que realmente está preguntando cómo tomar los bytes sin formato de un objeto de imagen y almacenarla en un documento XML ... –

+0

gracias , suena bien, estaba tratando de ser lo más específico posible en caso de que alguien tuviera otras ideas sobre cómo lograr el mismo efecto de guardar el texto en imágenes completas en binario, pero no creo que sea probable – JoeSharp

Respuesta

2

Buenas noticias. Tuve que trabajar en otra cosa por un tiempo, pero esto me permitió volver con un par de ojos frescos. Rápidamente me di cuenta de que podía combinar lo que sabía que estaba funcionando. Dudo que esta solución gane ningún premio, pero funciona. Sé que puedo envolver un FlowDocument como texto usando el XamlReader, manteniendo los elementos de la imagen pero perdiendo datos de imagen. También sabía que podía convertir un FlowDocument en binario usando XamlFormat. Así que tuve la idea de tomar FlowDocument y, usando una función que ya escribí para recorrer las imágenes, tomo cada imagen, básicamente la clono y coloco el clon en un nuevo FlowDocument. Tomo el nuevo FlowDocument que ahora contiene la imagen única, lo convierto en binario y luego tomo el binario resultante, lo convierto en cadena base64 y lo adhiero a la propiedad de etiqueta de la imagen en el FlowDocument original. Esto mantiene los datos de imagen en el FlowDocument original como texto. De esta manera puedo pasar el FlowDocument con datos de imagen (que yo llamo Formato SUBString) en el XamlReader para obtener texto con capacidad de búsqueda.Cuando sale de la base de datos, saco el FlowDocument del Xaml como es normal, pero luego recorro cada imagen, extraigo los datos de la propiedad de la etiqueta usando XamlFormat y luego creo otra imagen clonada para proporcionar la propiedad Source para mi real imagen. He proporcionado los pasos para obtener el formato SUBString a continuación.

/// <summary> 
    /// Returns a FlowDocument in SearchableText UI Binary (SUB)String format. 
    /// </summary> 
    /// <param name="flowDocument">The FlowDocument containing images/UI formats to be converted</param> 
    /// <returns>Returns a string representation of the FlowDocument with images in base64 string in image tag property</returns> 
    private string ConvertFlowDocumentToSUBStringFormat(FlowDocument flowDocument) 
    { 
     //take the flow document and change all of its images into a base64 string 
     FlowDocument fd = TransformImagesTo64(flowDocument); 

     //apply the XamlWriter to the newly transformed flowdocument 
     using (StringWriter stringwriter = new StringWriter()) 
     { 
      using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(stringwriter)) 
      { 
       XamlWriter.Save(flowDocument, writer); 
      } 
      return stringwriter.ToString(); 
     } 
    } 

    /// <summary> 
    /// Returns a FlowDocument with images in base64 stored in their own tag property 
    /// </summary> 
    /// <param name="flowDocument">The FlowDocument containing images/UI formats to be converted</param> 
    /// <returns>Returns a FlowDocument with images in base 64 string in image tag property</returns> 
    private FlowDocument TransformImagesTo64(FlowDocument flowDocument) 
    { 
     FlowDocument img_flowDocument; 
     Paragraph img_paragraph; 
     InlineUIContainer img_inline; 
     System.Windows.Controls.Image newImage; 
     Type inlineType; 
     InlineUIContainer uic; 
     System.Windows.Controls.Image replacementImage; 

     //loop through replacing images in the flowdoc with the base64 versions 
     foreach (Block b in flowDocument.Blocks) 
     { 
      //loop through inlines looking for images 
      foreach (Inline i in ((Paragraph)b).Inlines) 
      { 
       inlineType = i.GetType(); 

       /*if (inlineType == typeof(Run)) 
       { 
        //The inline is TEXT!!! $$$$$ Kept in case needed $$$$$ 
       } 
       else */if (inlineType == typeof(InlineUIContainer)) 
       { 
        //The inline has an object, likely an IMAGE!!! 
        uic = ((InlineUIContainer)i); 

        //if it is an image 
        if (uic.Child.GetType() == typeof(System.Windows.Controls.Image)) 
        { 
         //grab the image 
         replacementImage = (System.Windows.Controls.Image)uic.Child; 

         //create a new image to be used to get base64 
         newImage = new System.Windows.Controls.Image(); 
         //clone the image from the image in the flowdocument 
         newImage.Source = replacementImage.Source; 

         //create necessary objects to obtain a flowdocument in XamlFormat to get base 64 from 
         img_inline = new InlineUIContainer(newImage); 
         img_paragraph = new Paragraph(img_inline); 
         img_flowDocument = new FlowDocument(img_paragraph); 

         //Get the base 64 version of the XamlFormat binary 
         replacementImage.Tag = TransformImageTo64String(img_flowDocument); 
        } 
       } 
      } 
     } 
     return flowDocument; 
    } 

    /// <summary> 
    /// Takes a FlowDocument containing a SINGLE Image, and converts to base 64 using XamlFormat 
    /// </summary> 
    /// <param name="flowDocument">The FlowDocument containing a SINGLE Image</param> 
    /// <returns>Returns base 64 representation of image</returns> 
    private string TransformImageTo64String(FlowDocument flowDocument) 
    { 
     TextRange documentTextRange = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd); 
     using (MemoryStream ms = new MemoryStream()) 
     { 
      documentTextRange.Save(ms, DataFormats.XamlPackage); 
      ms.Position = 0; 
      return Convert.ToBase64String(ms.ToArray()); 
     } 
    } 
0

Guarde su imagen en un MemoryStream y escriba esa secuencia en su archivo XML.

El flujo de memoria lo convertirá en un Byte [].

+1

Gracias por la respuesta Tony . El problema es que la imagen es un objeto System.Windows.Controls.Image. Seguí descendiendo por la jerarquía pero no pude encontrar en qué objeto hijo se almacenan los bytes. Traté de guardar el objeto BitmapImage en la secuencia de la memoria, pero todo lo que recupero es la etiqueta xaml. usando (MemoryStream ms = new MemoryStream()) { XamlWriter.Save (bi, ms); resultado = ms.ToArray(); } – JoeSharp

+0

Resultado: JoeSharp

+0

puede publicar el código completo? –

0

Aquí está el código de ejemplo para mis dos sugerencias que he hecho ya, enfermo tiene que examinar la cuestión de carga útil si mis ejemplos no funcionan ...

 // get raw bytes from BitmapImage using BaseUri and SourceUri 
    private byte[] GetImageByteArray(BitmapImage bi) 
    { 
     byte[] result = new byte[0]; 
     string strImagePath = Path.Combine(Path.GetDirectoryName(bi.BaseUri.OriginalString), bi.UriSource.OriginalString); 
     byte[] fileBuffer; 
     using (FileStream fileStream = new FileStream(strImagePath, FileMode.Open)) 
     { 
      fileBuffer = new byte[fileStream.Length]; 
      fileStream.Write(fileBuffer, 0, (int)fileStream.Length); 
     } 
     using (MemoryStream ms = new MemoryStream(fileBuffer)) 
     { 
      XamlWriter.Save(bi, ms); 
      //result = new byte[ms.Length]; 
      result = ms.ToArray(); 
     } 
     return result; 
    } 
    // get raw bytes from BitmapImage using BitmapImage.CopyPixels 
    private byte[] GetImageByteArray(BitmapSource bi) 
    { 
     int rawStride = (bi.PixelWidth * bi.Format.BitsPerPixel + 7)/8; 
     byte[] result = new byte[rawStride * bi.PixelHeight]; 
     bi.CopyPixels(result, rawStride, 0); 
     return result; 
    } 
    private BitmapSource GetImageFromByteArray(byte[] pixelInfo, int height, int width) 
    { 
     PixelFormat pf = PixelFormats.Bgr32; 
     int stride = (width * pf.BitsPerPixel + 7)/8; 
     BitmapSource image = BitmapSource.Create(width, height, 96, 96, pf, null, pixelInfo, stride); 
     return image; 
    } 
+1

Parece que la primera no funciona. El BaseUri es "pack: // payload: ,, wpf2,/Xaml/Document.xaml" y el UriSource es "./Image1.bmp". Combinarlos resulta en "paquete: \ payload: ,, wpf2, \ Xaml \ ./ Image1.bmp". Probé una mezcla de eliminar el punto en la ruta Image1.bmp. Ni siquiera estoy seguro de hacia dónde apunta ese punto. Parece que la segunda solución funciona, pero cuando vuelve la matriz de bytes, cuando intento agregarla al XML, intento codificarla en ascii y aparece este error: '.', Valor hexadecimal 0x00 , es un personaje inválido (vea mi GetImagesXML() en la pregunta) – JoeSharp

+0

No estoy muy familiarizado con la propiedad zancada. Sé que es el ancho de la imagen en bytes, pero ¿puedes explicar brevemente las matemáticas que están sucediendo en esa línea, de dónde viene "+ 7"? Gracias – JoeSharp

+1

el error que está apareciendo es porque está utilizando la codificación ASCII para convertir la matriz de bytes en su forma ASCII, intente con Convert.ToBase64String (/ * byte [] * /) para convertir la matriz de bytes a una cadena base64 –