SQL Server & ASP .NET Blog

Interessantes und Wissenswertes

Export nach Excel aus eigenen Anwendungen

Für den Export von Excel-Dateien aus eigenen Anwendungen gibt es eine schöne OpenSource-Lösung unter http://epplus.codeplex.com/=> EPPlus

Mit dieser Bibliothek (unter Referenzen einbinden) lassen sich recht einfach – auch komplexe – Excel Sheets erstellen und exportieren.

Ein kleines Beispiel, welches nur ein paar Zellen mit Text füllt und eine Zeile auf unsichtbar schaltet ist unten zu sehen.

using OfficeOpenXml;
void ExportToExcel()
{
    using (var p = new ExcelPackage(new FileInfo("Data\\Template.xlsx"), true))
    {
        //ExcelWorksheet
        ExcelWorksheet ws = p.Workbook.Worksheets[1];
        
        //Fill
        ws.Cells["A4"].Value = "Dies";
        ws.Cells["A5"].Value = "ist";
        ws.Cells["A6"].Value = "eine";
        ws.Cells["A8"].Value = "Demo";

        //set row 10 invisible
        ExcelRow row = ws.Row(10);
        row.Hidden = true;

        //Save to Disk
        Byte[] bin = p.GetAsByteArray();

        const string file = "Data\\Export.xlsx";
        File.WriteAllBytes(file, bin);

        //Open
        System.Diagnostics.Process.Start("Data\\Export.xlsx");
}

Auf jeden Fall wesentlich einfacher zu bedienen als die “hauseigene” Integration mit Office.

Visual Studio – Quelltext während Debuggen bearbeiten

Um den Quelltext einer Anwendung während des Debuggens zu bearbeiten, muss “Edit and Continue” aktiviert werden. Dies geht für Webanwendungen an zwei (!!) Stellen. Einerseits bei den Optionen und andererseits in den Projekteigenschaften unter “Web”. (Die Bearbeitung funktioniert allerdings erst, sobald der Code an einem Haltepunkt angekommen ist).

image

image

ClientIDs in Repeater ändern sich…

Ich hatte das Problem, dass sich plötzlich die ClientIDs, die automatisch vom Framework vergeben werden – geändert haben. Eine ID hieß nun nicht mehr myRepeater__ctl1_textBoxMyText sondern plötzlich myRepeater_ctl01_textBoxMyText. Ein kleiner Unterschied (ctl1 und ctl01 und der zusätzliche Unterstrich)– aber wenn man per JavaScript auf die Elemente zugreifen möchte mehr als ärgerlich – denn das funktioniert nicht mehr.

Grund war ein Eintrag in der web.config.

<xhtmlConformance mode="Legacy"/>

Dieser Eintrag sorgt dafür, dass die Elemente einer Seite anders gerendert werden. Mehr Info dazu hier: [hier] und [hier]

RavenDB – NoSQL Datenbank - Tutorial

Ich habe in den letzten Tagen ein Wenig mit der NoSQL (übersetzt mit “not only sql”) Datenbank “RavenDB” herumgespielt. RavenDB ist eine Dokument-Datenbank. Das bedeutet in diesem Fall, dass die Daten nicht in Form von relationalen Tabellen, sondern in Form von JSON-Dokumenten gespeichert werden. In dieser Datenbank können Objekte “einfach so” ohne kompliziertes Mapping gespeichert werden.

Einige Vorteile bzw. Eigenarten von Dokument-basierten Datenbanken sind:

  • die DB muss kein festgelegtes Schema haben, da alles in Dokumenten gespeichert wird – die Dokumente an sich bilden das Schema
  • es muss kein Mapping von Klassen auf Datenbankobjekte vorgenommen werden
  • Performance-Vorteile beim schreibenden Zugriff, da die Konsistenz innerhalb der Datenbank nicht immer gewährleistet sein muss
  • große Datenbestände können auf mehrere kleine Server verteilt werden

Zu finden ist RavenDB unter http://ravendb.net/. Lizenziert wird die Datenbank “dual”, also frei für OpenSource-Projekte und kostenpflichtig für kommerzielle closed-source Software.

Einführung

Nun aber zur Datenbank an sich: Die Datenbank kann grundsätzlich in zwei verschiedenen Modi betrieben werden – embedded in der eigenen Anwendung oder als Server. Unterschied sind die einzubindenden Referenzen und die Einrichtung der Verbindung zur Datenbank. Wichtig zu wissen ist, dass die Embedded-Variante nur mit Anwendungen, die für das .NET Framework 4 kompiliert werden, funktioniert. Die Client-Server Variante funktioniert auch mit .NET 3.5. Die entpackte RavenDB hat diese Verzeichnisstruktur:

image

Um nun den Server zu starten genügt das Starten der Raven.Server.Exe im Verzeichnis “Server”. Der Server ist dann unter http://localhost:8080 zu erreichen und wartet mit einer kleinen Administrationsoberfläche unter der man sich die gespeicherten Dokumente anschauen kann, Statistiken einsehen kann und Map/Reduce Indizes anlegen kann – aber dazu später mehr.

image

RavenDB – Hello World Skiverleih – ein kleines Tutorial

Nun erst einmal zu einem kleinen “Hello-World” mit RavenDB. Nehmen wir an, wir betreiben einen Skiverleih (diesmal keine Autovermietung :-)) und wollen unsere Ski in der Datenbank ablegen und dazu noch Bewertungen der Ski unserer Kunden…

Also zunächst ein neues Visual Studio Projekt geöffnet, ich nehme hier eine Konsolenanwendung und den RavenDB-Client aus dem Verzeichnis Client eingebunden.

image

Die Ski werden durch die Klasse Ski repräsentiert.

public class Ski
{
    public string Id { get; set; }
    public decimal VerleihpreisTag { get; set; }
    public decimal Anschaffungspreis { get; set; }
    public List<Bewertung> Bewertungen { get; set; }
    public int LaengeInCm { get; set; }
    public bool Gewachst { get; set; }

    public Ski()
    {
        Bewertungen = new List<Bewertung>();
    }

    public double Durchschnittswertung
    {
        get
        {
            if (Bewertungen.Count > 0)
                return Bewertungen.Average(x => x.Wertung);
            else
                return 0;
        }
    }

}

Für die Datenbank und das “Wiederfinden” bestimmter Ski ist es notwendig, dass die Klasse ein Property mit dem Namen “Id” besitzt. Für die Erstellung von IDs in RavenDB gibt es drei Möglichkeiten:

  • explizites Festlegen – also bspw. durch mySki.Id = “1”
  • automatische Generierung – wird der Klasse kein Id-Property “spendiert”, dann generiert RavenDB beim Einfügen automatisch eine ID (GUID), wenn es also nicht wichtig ist konkrete Ski anhand ihrer ID wiederzufinden, kann auf die Id verzichtet werden
  • teilautomatische Generierung – die Id kann bei allen Instanzen der Ski-Klasse auf bspw. “ski/” gesetzt werden. Das veranlasst RavenDB dazu die Ids aufsteigend zu generieren, also “ski/1” für den ersten Ski, “ski/2” für den zweiten, etc.

Im Beispiel werde ich die Ids manuell vergeben.

Nun gibt es noch die Klasse Bewertung, die eine Wertung und einen Kommentar des Skifahrers enthält.

public class Bewertung
{
    public double Wertung { get; set; }
    public string Kommentar { get; set; }
}

Innerhalb der Ski-Klasse werden alle Bewertungen zu einem Ski als List<Bewertung> gespeichert und ein Property Durchschnittswertung errechnet den Durchschnitt aller Bewertungen des Ski.

Um nun Daten in RavenDB zu speichern, muss zuerst ein DocumentStore angelegt, instanziert und initialisiert werden:

//Initialisierung
var ds = new DocumentStore() { Url = "http://localhost:8080" };
ds.Initialize();

Hier genügt es die URL, unter der die Datenbank läuft anzugeben. Möchte man den Embedded Client verwenden, kann hier auch das Datadirectory angegeben werden, unter dem die Daten gespeichert werden sollen.

Nun erstelle ich ein paar Ski mit Bewertungen:

//ein Ski mit zwei Bewertungen
var mySki = new Ski();
mySki.Id = "Ski/1";
mySki.VerleihpreisTag = 11.0m;
mySki.Anschaffungspreis = 399.99m;
mySki.LaengeInCm = 170;
mySki.Gewachst = true;            
mySki.Bewertungen.Add(new Bewertung() { Wertung = 5, Kommentar = "Super Ski!" });
mySki.Bewertungen.Add(new Bewertung() { Wertung = 2, Kommentar = "Genial am Hang!" });

//noch ein Ski mit einer Bewertung ungewachst
var mySki2 = new Ski();
mySki2.Id = "Ski/2";
mySki2.VerleihpreisTag = 9.0m;
mySki2.Anschaffungspreis = 299.99m;
mySki2.LaengeInCm = 120;
mySki2.Gewachst = false;
mySki2.Bewertungen.Add(new Bewertung() { Wertung = 1, Kommentar = "Doof!" });

//noch ein Ski mit einer Bewertung ungewachst
var mySki3 = new Ski();
mySki3.Id = "Ski/3";
mySki3.VerleihpreisTag = 9.0m;
mySki3.Anschaffungspreis = 299.99m;
mySki3.LaengeInCm = 120;
mySki3.Gewachst = false;
mySki3.Bewertungen.Add(new Bewertung() { Wertung = 2, Kommentar = "Blöd!" });

Wie angekündigt habe ich die IDs manuell vergeben. Würde ich nun einen weiteren Ski mit der Id “Ski/” anlegen wäre seine Id automatisch “Ski/4”.

Speichern in der Datenbank

Zum Speichern der Ski in der Datenbank genügt folgender Code:

//Ski speichern
using (var session = ds.OpenSession())
{
    session.Store(mySki);
    session.Store(mySki2);
    session.Store(mySki3);
    session.SaveChanges();                
}

Hier wird eine Session mit der Datenbank geöffnet, die Ski gespeichert und SaveChanges() aufgerufen. SaveChanges() entspricht in etwa einem COMMIT in einer SQL-Datenbank.

Die gespeicherten Ski können nun auch direkt in der Datenbank angezeigt werden. Also http://localhost:8080 und Klick auf “Documents” ergibt folgendes Bild:

image

Die drei Ski sind also gespeichert – soweit so gut :-) Schauen wir uns einmal den Ski mit der ID “Ski/1” genauer an, um den Aufbau innerhalb der Datenbank zu sehen:

image

Der Ski wird also in einem Dokument inkl. aller Bewertungen gespeichert. Unter Document Metadata sind noch die von RavenDB vergebenen Metadaten enthalten. Interessant hier ist der “Raven-Entitiy-Name”, der als Plural der Klasse benannt ist.

image

Ändern in der Datenbank

Um nun an einzelnen Ski, wieder im Beispiel “Ski/1” heranzukommen, um mit diesem weiter arbeiten zu können bzw. um die Daten zu verändern ist folgender Code notwendig:

//einen Ski lesen und ändern
using (var session = ds.OpenSession())
{
    //lesen
    var myDbSki = session.Load<Ski>("Ski/1");
    
    //ändern
    myDbSki.VerleihpreisTag = 10.0m;

    //speichern
    session.Store(myDbSki);
    session.SaveChanges();
}

Also wieder eine Session öffnen, den Ski per Load<Ski>("<Id_des_Ski>”) laden, verändern und per Store() und SaveChanges() wieder in der Datenbank speichern.

Löschen eines Ski

Soll nun ein Ski wieder aus der Datenbank gelöscht werden, muss dieser zunächst gelesen werden, um eine Verknüpfung zu einer existierenden Instanz herzustellen.

//einen Ski löschen
using (var session = ds.OpenSession())
{
    session.Delete<Ski>(session.Load<Ski>("Ski/2"));
    session.SaveChanges();
}

Soweit erst einmal zu den Grundfunktionen von RavenDB. Im nächsten Post werde ich die Indexierung via Map/Reduce und die Abfrage mehrerer Instanzen vorstellen.

ASP .NET - Bild aus Datenbank abrufen und anzeigen

In einem anderem Beitrag [hier]habe ich bereits beschrieben, wie einfach es ist, ein Bild auf einer Webseite hochzuladen und mit wenigen Zeilen C#-Code in einer SQL Server Datenbank zu speichern. Interessant ist natürlich auch der Rückweg: Also wie kommt das Bild wieder aus der Datenbank heraus und kann im Browser angezeigt werden?

Auch das geht wieder in einfachen Schritten.

Schritt 1 – Eine ASPX-Seite zur Ausgabe eines Bildes

Um ein Bild aus der Datenbank zu lesen und im Browser anzuzeigen, erstelle ich eine neue Seite “GetPicture.aspx”. Diese Seite hat keinen eigentlichen Inhalt, sondern nur den Zweck ein Bild anhand des Namens (oder eines beliebigen anderen Parameters, z. B. einer ID) aus der Datenbank zu holen und an den Browser zu senden.

protected void Page_Load(object sender, EventArgs e)
{
//Variable für die Bilddaten
Byte[] myFile;
//der Dateiname aus dem QueryString
string myFileName = Request.QueryString["FileName"];
if (myFileName != null)
{
//Verbindung zum Server
SqlConnection con = 
new SqlConnection("Data Source=192.168.1.13;Initial Catalog=BildDB;User ID=xxx;Password=xxx");
//Insert Statement mit den entsprechenden Parametern
SqlCommand cmd = new SqlCommand("SELECT picture FROM pictures WHERE name = @name", con);
//Parameter für den Bildnamen
SqlParameter paramName = new SqlParameter("@name", System.Data.SqlDbType.VarChar);
paramName.Value = myFileName;
cmd.Parameters.Add(paramName);
//Daten lesen
con.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read() ) //im Beispiel interessiert nur der erste Satz
{
myFile = (Byte[])reader["picture"];
Response.ContentType = "image/jpeg"; //ContentType setzen
Response.BinaryWrite(myFile);        //Bild ausgeben
}
con.Close();
}      

Ruft man nun diese Seite mit einem in der Datenbank vorhandenen Bildnamen auf, dann wird das Bild direkt im Browser angezeigt.

image

Schritt 2 – Bilder in einem Gridview anzeigen

Um nun mehrere Bilder aus der Datenbank anzuzeigen kann bspw. ein GridView verwendet werden. Auf einer zusätzlichen Seite “Anzeigen.aspx” habe ich eine neue SQLDatasource und ein GridView erstellt.

        <!-- Die Datenquelle -->
<asp:SqlDataSource ID="SqlDataSource1" runat="server" 
ConnectionString="<%$ ConnectionStrings:BildDBConnectionString %>" 
SelectCommand="SELECT picture, name, addedat FROM [pictures]">
</asp:SqlDataSource>
<!-- Anzeige im Gridview -->
<asp:GridView ID="GridView1" runat="server" 
DataSourceID="SqlDataSource1" 
AutoGenerateColumns="false">
<Columns>
<asp:BoundField DataField="name" HeaderText="name" SortExpression="name" />
<asp:BoundField DataField="addedat" HeaderText="addedat" 
SortExpression="addedat" />
<asp:ImageField DataImageUrlField="name" 
DataImageUrlFormatString="~/GetPicture.aspx?Filename={0}">
</asp:ImageField>
</Columns>
</asp:GridView>

Das “Besondere” an diesem GridView ist nun, dass es ein ImageField hat, welches direkt die in Schritt 1 erstellte ASPX-Seite aufruft und den aus der Datenbank gelieferten Dateinamen übergibt.

Das Endergebnis sieht dann so aus:

image 

Das Projekt (Visual Studio 2010) hängt zum Anschauen direkt hier am Beitrag. Fragen sind natürlich immer gern gesehen ;)

Bildverwaltung.zip (32,13 kb)

Fehler beim Erstellen des Steuerelements ...

Beim Öffnen einer Website in Visual Studio kann es vorkommen, dass einige Steuerelemente nicht korrekt angezeigt werden können.

Die Fehlermeldung (als Beispiel): "Fehler beim Erstellen des Steuerelements: <Elementname> asp:UpdatePanel konnte nicht für die ContentTemplate-Eigenschaft festgelegt werden." erscheint.

Woher diese Fehlermeldung kommt, weiß ich leider (noch) nicht. Auf jeden Fall kann Sie behoben werden: Einfach das Projekt schließen und alle Dateien in "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files" löschen und danach das Projekt wieder öffnen.

Eine weitere Möglichkeit soll sein diese Verzeichnis zu leeren: C:\Dokumente und Einstellungen\<USERNAME>\Lokale Einstellungen\Anwendungsdaten\Microsoft\VisualStudio\9.0\ProjectAssemblies (hat bei mir nicht funktioniert)

Reimers Google Maps .NET Control

Ich spiele gerade ein wenig mit Google Maps herum – genauer gesagt mit dem Google Maps .NET Control von Reimers. Mit diesem Control ist es sehr einfach eine Google Map auf die eigene Seite zu bringen.

Kleines Beispiel:

<Reimers:GoogleMap ID="myGMap" width="100%" Height="100%" runat="server">
</Reimers:GoogleMap>

…dieses Markup genügt um eine Map auf der Seite anzuzeigen. Einen Center-Punkt und Zoom im Markup festzulegen ist auch kein Problem:

<Reimers:GoogleMap ID="myGMap" width="100%" Height="100%" runat="server" Zoom="7">
<Center Latitude="50" Longitude="10" />
</Reimers:GoogleMap>

Hinzufügen von Markern im Code-Behind funktioniert folgendermaßen. Der Code fügt einen Marker an Position 50, 10 ein. Außerdem wird ein InfoWindow definiert, welches sich beim Klick auf den Marker öffnet und das eingefügte HTML anzeigt.

GoogleMarker gm1 = new GoogleMarker("mrkClick", 50, 10);
gm1.ClientSideHandlers.OnClick = gm1.OpenInfoWindowHTML(myGMap, "<b>Hallo vom SQL-ASP-Blog!!!</b>");
myGMap.Overlays.Add(gm1);

…das sieht dann so aus:

marker

Das Control beherrscht auch eigene Callbacks, so dass man auf bestimmte Events reagieren kann. So können zum Beispiel Marker nur hinzugefügt werden, wenn eine bestimmte Zoomstufe erreicht ist etc. – alles ohne Neu laden der Seite.

Wie gesagt – ich spiele erst einmal ein wenig damit herum. Aufgefallen ist mir bisher, dass es anscheinend keine einfache Möglichkeit gibt das Control und gleichzeitig UpdatePanels auf einer Seite zu verwenden. Finde ich etwas schade…

Trotzdem ist das Control auf jeden Fall einen Blick wert, wenn man mal einfach eine Google Map auf eine Seite bringen möchte.

Zu finden gibt es das Google Maps .NET Control hier: http://www.reimers.dk/. Für den Download ist eine Anmeldung notwendig.

ASP .NET Bild Upload in SQL Server speichern

Hier noch einmal ein Beitrag, wie es möglich ist Bilder (oder auch andere Dateien) von einer ASP .NET Seite aus im SQL zu speichern.

Eigentlich habe ich das mit diesem (hier) Beitrag schon beschrieben, aber hier nochmal konzentriert nur auf das Thema “Bild hochladen und im SQL Server speichern”. Ob es nun Sinn macht die Datei als solches zu speichern oder nur einen Verweis (als Pfad) soll hier nicht diskutiert werden – die Antwort darauf ist sowieso meist: “Es kommt drauf an…” (außerdem gibt es für solche Zwecke zumindest ab SQL Server 2008 auch den FILESTREAM Datentyp)

Also los. Wie kann ein hochgeladenes Bild im SQL Server gespeichert und wieder abgerufen werden? Ganz einfach! In nur drei einfachen Schritten :-)

1. Webseite

Wir brauchen die entsprechende Webseite. Also erstelle ich eine neue im Visual Studio und fülle die Seite mit einem FileUpload Control und einem Button zum Upload (FileUpload1 und Button1).

file_upload_1

2. Datenbank

In der Datenbank brauchen wir eine Tabelle zum Speichern der Bilder

CREATE TABLE pictures
(
picture VARBINARY(MAX)
,Name VARCHAR(255) 
,AddedAt DATETIME
)

Die Spalte “picture” wird das eigentliche Bild enthalten, daher der Datentyp VARBINARY(MAX).

3. Datei speichern

Nun kommt die eigentliche “Arbeit”: Beim Klick auf “Upload” soll das Bild mit dem Dateinamen und dem Datum in der Datenbank gespeichert werden. Im Event Button1.Click füge ich folgenden Code hinzu:

protected void Button1_Click(object sender, EventArgs e)
{
//die Datei aus dem FileUpload Control 
Byte[] myFile;
myFile = FileUpload1.FileBytes;
//der Dateiname ohne Pfad
string myFileName;            
myFileName = System.IO.Path.GetFileName(FileUpload1.FileName);
//connection to sql server
SqlConnection con = new SqlConnection("Data Source=192.168.1.13;Initial Catalog=TestDB;User ID=sa;Password=blah");
//Insert Statement mit den entsprechenden Parametern
SqlCommand cmd = new SqlCommand("INSERT INTO pictures VALUES (@picture, @name, GETDATE())", con);
//Parameter für das Bild an sich
SqlParameter paramPicture = new SqlParameter("@picture", System.Data.SqlDbType.VarBinary);
paramPicture.Value = myFile;
cmd.Parameters.Add(paramPicture);
//Parameter für den Dateinamen
SqlParameter paramFilename = new SqlParameter("@name", System.Data.SqlDbType.VarChar);
paramFilename.Value = myFileName;
cmd.Parameters.Add(paramFilename);
//Verbindung öffnen und INSERT ausführen (Achtung, kein Exception-Handling)
con.Open();
cmd.ExecuteNonQuery();
con.Close();     
}

Ich denke der Code ist so verständlich, dass ich diesen hier nicht näher erläutern muss. Fragen natürlich immer gern :-)

Ruft man nun die Webseite auf, wählt eine Datei und klickt auf Upload wird diese im SQL Server gespeichert, was dann so aussieht:

picture_2

Nun ist die Datei erst einmal im SQL Server. Wie man diese dann auch wieder da heraus bringt um bspw. das Bild anzuzeigen schreibe ich später nochmal. (das habe ich inzwischen  [hier] getan.)

Windows Usercontrol in ASP .NET Seite verwenden

Manchmal ist es nützlich Windows-Controls auf ASP .NET Seiten zu verwenden, beispielsweise, weil eine bestimmte Funktionalität gebraucht wird, die mit ASP oder JavaScript nicht oder nur sehr schwer umzusetzen ist.

Schonmal versucht einen Screenshot aus der Zwischenablage zu lesen um diesen dann in der Datenbank zu speichern? Mit ASP und JavaScript kenne ich keinen Weg, wie das zu machen ist, daher hier ein kurzes Tutorial, wie es durch das Einbinden eines Windows Usercontrol funktioniert. Selber ausgedacht habe ich mir das natürlich nicht, allerdings einige Male geschwitzt beim Implementieren :-) Daher hier noch einmal ausführlich. (Infos von [hier]und [hier] ).

Vorteile

  • es können Funktionen verwendet werden, die sonst via ASP .NET oder JavaScript nicht zur Verfügung stehen (Dateisystem auf dem Clientcomputer/Zwischenablage etc.)

Nachteile

  • funktioniert nur mit dem Internet-Explorer
  • für bestimmte Funktionen müssen der erstellten Assembly die entsprechenden Rechte vergeben werden 

Screenshot Usercontrol erstellen 

Zuerst öffne ich ein neues Projekt in Visual Studio, und zwar eine Windows Forms Control Library. 



Da ich einen Screenshot direkt im Browser machen möchte, nenne ich das Projekt der Einfachheit halber "Screenshot".

Das automatisch dem Projekt hinzugefügte Usercontrol nenne ich ebenso Screenshot. Dies wird das Control sein, mit dem später der Screenshot "aufgenommen" und gespeichert wird.


Um den Screenshot auf dem Usercontrol anzeigen zu können füge ich eine Picturebox (pbScreenshot), und als Hinweis noch ein Label (lblHinweis) zur Beschriftung hinzu. Das Ganze sieht dann so aus (also noch nicht wirklich spektakulär :-)): 


Die Form soll also heißen: "Wenn du mich anklickst und ein Bild in der Zwischenablage hast, dann zeig ich dir das an!". Durch Doppelklick auf die PictureBox lege ich den EventHandler an.

private void pbScreenshot_Click(object sender, EventArgs e)
{
if (System.Windows.Forms.Clipboard.ContainsImage()) //wenn im clipBoard ein Bild ist
{                
pbScreenshot.Image = System.Windows.Forms.Clipboard.GetImage(); //Bild anzeigen
lblHinweis.Visible = false;  //Hinweis unsichtbar
} 
else
{
MessageBox.Show("Kein Bild in der Zwischenablage!");   //Kein Bild => Hinweis
}
}

Ich denke der Quelltext ist selbstsprechend :-) Auf jeden Fall kann man nun bereits einen Screenshot auf das UserControl bringen. (Druck auf F5 und ausprobieren) Mehr...