2009-08-17 18 views
10

Chcę konwertować plik excel na obraz (każdy format jest ok) programowo (C#). Obecnie używam bibliotek Microsoft Interop & Office 2007, ale domyślnie nie obsługuje on zapisywania obrazu.Programowo (C#) przekonwertować Excel do obrazu

Więc moja obecna praca wokół jest następująca:

  • Otwórz plik programu Excel przy użyciu programu Microsoft Interop;
  • Znajdź maksymalny zakres (zawierający dane);
  • Użyj CopyPicture() w tym zakresie, który skopiuje dane do Schowka.

Teraz część trudne (i moje problemy):

Problem 1:

Korzystanie z klasy .NET schowku, nie jestem w stanie uzyskać dokładne dane skopiowane ze schowka : dane są takie same, ale w jakiś sposób formatowanie jest zniekształcone (czcionka całego dokumentu wydaje się być odważna i nieco bardziej nieczytelna, podczas gdy nie były); Jeśli wkleję ze schowka za pomocą programu mspaint.exe, wklejony obraz jest poprawny (i dokładnie tak, jak powinienem być).

Zdemontowałem mspaint.exe i znalazłem funkcję, z której korzysta (OleGetClipboard), aby pobrać dane ze schowka, ale nie mogę sprawić, żeby działało w C#/.NET.

Inne rzeczy, których próbowałem, to schowek WINAPI (OpenClipboard, GetClipboardData, CF_ENHMETAFILE), ale wyniki były takie same, jak przy użyciu wersji .NET.

Problem 2:

Korzystanie zakres i CopyPicture, czy są jakieś obrazy w arkuszu Excel, te obrazy nie są kopiowane wraz z otaczającymi danych do schowka.

Niektóre kodu źródłowego

Excel.Application app = new Excel.Application(); 
app.Visible = app.ScreenUpdating = app.DisplayAlerts = false; 
app.CopyObjectsWithCells = true; 
app.CutCopyMode = Excel.XlCutCopyMode.xlCopy; 
app.DisplayClipboardWindow = false; 

try { 
    Excel.Workbooks workbooks = null; 
    Excel.Workbook book = null; 
    Excel.Sheets sheets = null; 

    try { 
     workbooks = app.Workbooks; 
     book = workbooks.Open(inputFile, false, false, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
           Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, 
           Type.Missing, Type.Missing); 
     sheets = book.Worksheets; 
    } catch { 
     Cleanup(workbooks, book, sheets); //Cleanup function calls Marshal.ReleaseComObject for all passed objects 
     throw; 
    } 

    for (int i = 0; i < sheets.Count; i++) { 
     Excel.Worksheet sheet = (Excel.Worksheet)sheets.get_Item(i + 1); 

     Excel.Range myrange = sheet.UsedRange; 
     Excel.Range rowRange = myrange.Rows; 
     Excel.Range colRange = myrange.Columns; 

     int rows = rowRange.Count; 
     int cols = colRange.Count; 

     //Following is used to find range with data 
     string startRange = "A1"; 
     string endRange = ExcelColumnFromNumber(cols) + rows.ToString(); 

     //Skip "empty" excel sheets 
     if (startRange == endRange) { 
      Excel.Range firstRange = sheet.get_Range(startRange, endRange); 
      Excel.Range cellRange = firstRange.Cells; 
      object text = cellRange.Text; 
      string strText = text.ToString(); 
      string trimmed = strText.Trim(); 

      if (trimmed == "") { 
       Cleanup(trimmed, strText, text, cellRange, firstRange, myrange, rowRange, colRange, sheet); 
       continue; 
      } 
      Cleanup(trimmed, strText, text, cellRange, firstRange); 
     } 

     Excel.Range range = sheet.get_Range(startRange, endRange); 
     try { 
      range.CopyPicture(Excel.XlPictureAppearance.xlScreen, Excel.XlCopyPictureFormat.xlPicture); 

      //Problem here <------------- 
      //Every attempt to get data from Clipboard fails 
     } finally { 
      Cleanup(range); 
      Cleanup(myrange, rowRange, colRange, sheet); 
     } 
    } //end for loop 

    book.Close(false, Type.Missing, Type.Missing); 
    workbooks.Close(); 

    Cleanup(book, sheets, workbooks); 
} finally { 
    app.Quit(); 
    Cleanup(app); 
    GC.Collect(); 
} 

Pobieranie danych ze schowka wykorzystaniem WinAPI powiedzie, ale ze złej jakości. Źródło:

protected virtual void ClipboardToPNG(string filename) { 
    if (OpenClipboard(IntPtr.Zero)) { 
     if (IsClipboardFormatAvailable((int)CLIPFORMAT.CF_ENHMETAFILE)) { 
      int hEmfClp = GetClipboardDataA((int)CLIPFORMAT.CF_ENHMETAFILE); 

      if (hEmfClp != 0) { 
       int hEmfCopy = CopyEnhMetaFileA(hEmfClp, null); 

       if (hEmfCopy != 0) { 
        Metafile metafile = new Metafile(new IntPtr(hEmfCopy), true); 

        metafile.Save(filename, ImageFormat.Png); 
       } 
      } 
     } 

     CloseClipboard(); 
    } 
} 

Ktoś ma rozwiązanie? (Używam .NET 2.0 btw)

+0

mógłbyś udostępnić kod źródłowy? W jakim formacie chcesz uzyskać skopiowane dane? W bitmapie? –

Odpowiedz

3

zrobi to.

Można zobaczyć nasze ASP.NET (C# i VB) „ wykres Excel i próbek Zakres Imaging” samples here i pobrać bezpłatną wersję próbną here jeśli chcesz go wypróbować.

SpreadsheetGear działa również z Windows Forms, aplikacjami konsolowymi itp. (Nie określiłeś typu tworzonej aplikacji). Istnieje również formant Windows Forms do wyświetlania skoroszytu w twojej aplikacji, jeśli tak naprawdę jesteś po.

Zastrzeżenie: Jestem właścicielem SpreadsheetGear LLC

3

Z tego, co rozumiem na podstawie twojego pytania, nie jestem w stanie odtworzyć problemu.

Wybrałem gamę ręcznie w programie Excel, wybrał Copy As Picture z opcjami jak pokazano na ekranie i Bitmap wybranym, potem używany następujący kod, aby zapisać dane Schowek:

using System; 
using System.IO; 
using System.Windows; 
using System.Windows.Media.Imaging; 
using System.Drawing.Imaging; 
using Excel = Microsoft.Office.Interop.Excel; 

public class Program 
{ 
    [STAThread] 
    static void Main(string[] args) 
    { 
     Excel.Application excel = new Excel.Application(); 
     Excel.Workbook wkb = excel.Workbooks.Add(Type.Missing); 
     Excel.Worksheet sheet = wkb.Worksheets[1] as Excel.Worksheet; 
     Excel.Range range = sheet.Cells[1, 1] as Excel.Range; 
     range.Formula = "Hello World"; 

     // copy as seen when printed 
     range.CopyPicture(Excel.XlPictureAppearance.xlPrinter, Excel.XlCopyPictureFormat.xlPicture); 

     // uncomment to copy as seen on screen 
     //range.CopyPicture(Excel.XlPictureAppearance.xlScreen, Excel.XlCopyPictureFormat.xlBitmap); 

     Console.WriteLine("Please enter a full file name to save the image from the Clipboard:"); 
     string fileName = Console.ReadLine(); 
     using (FileStream fileStream = new FileStream(fileName, FileMode.Create)) 
     { 
      if (Clipboard.ContainsData(System.Windows.DataFormats.EnhancedMetafile)) 
      { 
       Metafile metafile = Clipboard.GetData(System.Windows.DataFormats.EnhancedMetafile) as Metafile; 
       metafile.Save(fileName); 
      } 
      else if (Clipboard.ContainsData(System.Windows.DataFormats.Bitmap)) 
      { 
       BitmapSource bitmapSource = Clipboard.GetData(System.Windows.DataFormats.Bitmap) as BitmapSource; 

       JpegBitmapEncoder encoder = new JpegBitmapEncoder(); 
       encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); 
       encoder.QualityLevel = 100; 
       encoder.Save(fileStream); 
      } 
     } 
     object objFalse = false; 
     wkb.Close(objFalse, Type.Missing, Type.Missing); 
     excel.Quit(); 
    } 
} 

Odnośnie drugiego problemu: Z tego co wiem, w programie Excel nie jest możliwe wybranie jednocześnie zakresu komórek i obrazu. Jeśli chcesz uzyskać oba obrazy jednocześnie, możesz wydrukować arkusz Excela do pliku obrazu/PDF/XPS.

+0

Dziękujemy za aktualizację. W moim przypadku, Clipboard.ContainsData (DataFormats.EnhancedMetafile) zwraca true, ale następnie Clipboard.GetData (DataFormats.EnhancedMetafile) zawsze zwraca wartość null. mspaint.exe (jak zawsze) jest w stanie wkleić dane skopiowane do schowka przez CopyPicture() – Zurb

+0

@Zurb: Czy otrzymasz 'null' z' Clipboard.GetData' również po uruchomieniu kodu przykładowego powyżej ? Zauważ, że używa klasy schowka WPF w 'System.Windows', a nie tej z formularzy Windows. Czy możesz sprawdzić, czy żadna inna aplikacja nie blokuje schowka? –

+0

@divo: Zapomniałem wspomnieć, że używam .NET 2.0, więc nie mogę uruchomić twojego kodu całkowicie jako wysłany. Dodałem część mojego kodu źródłowego do pytania. – Zurb

1

Ponieważ wątek asp.net nie posiada właściwej ApartmentState dostęp do Schowek Class, więc trzeba napisać kod dostępu do schowka w nowym wątku. Na przykład:

private void AccessClipboardThread() 
{ 
    // access clipboard here normaly 
} 

w głównym wątku:

.... 
Excel.Range range = sheet.get_Range(startRange, endRange); //Save range image to clipboard 
Thread thread = new Thread(new ThreadStart(AccessClipboardThread)); 
thread.ApartmentState = ApartmentState.STA; 
thread.Start(); 
thread.Join(); //main thread will wait until AccessClipboardThread finish. 
.... 
+0

Opublikowany przeze mnie kod został zaimplementowany do pracy w standardowej aplikacji formularzy C# Windows (nadal nie działa btw), chociaż planowałem użyć go w Internecie. W każdym razie dzięki. – Zurb

+0

Mogę potwierdzić, że używanie tego działa dla rozwiązania internetowego. – Dave

0

ciekawe robiłem to w przedziale STA przez jakiś czas z sukcesem. Napisałem aplikację, która działa co tydzień i przesyła raporty o stanie projektu, w tym niektóre wykresy generowane programowo za pomocą programu Excel.

Ostatniej nocy to się nie powiodło, wszystkie wykresy zwróciły wartość zerową. Dzisiaj debuguję i nie znajduję żadnego wyjaśnienia, że ​​metoda Clipboard.GetImage() zwraca nagle zerową wartość, której nie uzyskała. Ustalając punkt przerwania podczas tego połączenia, mogę skutecznie wykazać (naciskając CTRL + V w MS-Word), że obraz JEST rzeczywiście w schowku. Niestety kontynuowanie w Clipboard.GetImage() zwraca null (niezależnie od tego, czy tak szpieguję, czy nie).

Mój kod działa jako aplikacja konsolowa, a metoda główna ma atrybut [STAThread]. Debuguję go jako aplikację formularzy Windows (cały mój kod znajduje się w bibliotece i mam po prostu dwa przednie).

Obie dzisiaj zwracają wartość null.

Z zainteresowań wyodrębniłem moduł pobierający wykres w wątek zgodnie z opisem (i zauważ, że wątek.ApartmentState jest przestarzały) i działa poprawnie, ale mimo to zwracane są wartości zerowe.

Cóż, zrezygnowałem i zrobiłem to, co każdy informatyk zrobiłby, zrestartował.

Voila ... wszystko było dobrze. Idź na figurę ... to dlatego wszyscy nienawidzimy komputerów, Microsoft Windows i Microsoft Office? Hmmmm ... Jest coś, coś całkowicie przejściowego, co może ci się przydarzyć PC, który sprawia, że ​​Clipboard.GetImage() nie działa!

1

To jest błąd z GDI +, jeśli chodzi o konwersję metaplików do formatu mapy bitowej.
Zdarza się dla wielu pól elektromagnetycznych wyświetlających wykresy z tekstem. Aby ponownie utworzyć, wystarczy utworzyć wykres w programie Excel, który wyświetla dane dla osi X i Y. Skopiuj wykres jako obraz i wklej słowo jako metaplik. Otwórz dokumentację, a zobaczysz pole elektromagnetyczne w folderze multimediów. Jeśli teraz otworzysz tę EMF w dowolnym programie do malowania opartym na systemie Windows, który przekształci go w mapę bitową, zauważysz zniekształcenia, w szczególności tekst i linie staną się większe i zniekształcone. Niestety jest to jeden z tych problemów, na które Microsoft prawdopodobnie nie przyzna się lub nie robi nic. Miejmy nadzieję, że wkrótce również Google i Apple przejmą świat biur/tekstów.

0

Jeśli nie masz nic przeciwko Linux (styl), możesz użyć OpenOffice (lub LibreOffice), aby przekonwertować xls najpierw do pdf, a następnie użyć ImageMagic do konwersji pdf na obraz. Podstawowy przewodnik można znaleźć pod adresem http://www.novell.com/communities/node/5744/c-linux-thumbnail-generation-pdfdocpptxlsimages.

Ponadto wydaje się, że istnieją interfejsy API .Net dla obu wyżej wymienionych programów. Zobacz: http://www.opendocument4all.com/download/OpenOffice.net.pdf i http://imagemagick.net/script/api.php#dot-net

Powiązane problemy