in

SharePoint Blogs

The Best Place for SharePoint-related Blogs

This Blog

Syndication

News

Ein guter Blog lebt auch vom Feedback der Leser. Aus diesem Grund möchte ich alle Leser bitten und auffordern, Feedback und Bewertung für einzelne Posts abzugeben. Ich freue mich natürlich auch, wenn mein Blog oder auch einzelne Posts verlinkt werden. Dies hilft anderen Leser und ist zugleich auch Ansporn für mich!

Meine SharePoint-Notizen

SharePoint-Notizen aus meiner täglichen Projektarbeit mit dem Microsoft Office SharePoint Server 2007

August 2008 - Posts

  • Übersicht aller SharePoint-Versionen

    Im Blog von Aaron Saikovski habe ich eine interessante Aufstellung über die aktuellen SharePoint-Versionen gefunden. 

    Service Pack/Hotfix Version WSS V3.0 MOSS 2007
    Infrastructure Update (KB951695 & KB951297) 12.0.0.6318 12.0.0.6318
    Post-SP1 hotfix (KB953137 & KB953138) 12.0.0.6316.500 12.0.0.6316.500
    Post-SP1 hotfix (KB952698 & KB952704) 12.0.0.6315 12.0.0.6315
    Post-SP1 hotfix (KB948945) 12.0.0.6303 12.0.0.6303
    Post-SP1 hotfix (KB941274) 12.0.0.6301 12.0.0.6301
    Post-SP1 hotfix (KB941422) 12.0.0.6300 12.0.0.6300
    Service Pack 1 12.0.0.6219 12.0.0.6219
    Release To Manufacturing (RTM) 12.0.0.4518 12.0.0.4518

     

    Hier der Link auf den Artikel im Blog von Aaron Saikovski.

     

    Add to Technorati Favorites

  • How to: Professionelle Datenanzeige mit dem Control SPGridView

    Angeregt durch ein WebPart von René Hézser habe ich mir mal das Control SPGridView genauer angesehen. Im Gegensatz zum 'normalen' GridView wird SPGridView von SharePoint selbst verwendet, um z.B. den Inhalt von SharePoint-Listen anzuzeigen. Der große Vorteil des SPGridView: dieses Control bietet Unterstützung für Sortierung, Filterung und Paginierung - und passt sich ohne weitere Programmierung an das Layout von SharePoint an.

    Im Internet finden sich einige interessante Artikel darüber, wie man eine Datenanzeige mit einem SPGridView realisieren kann. Aber jeder dieser Artikel behandelt leider immer nur einen Teilaspekt - ein umfassendes Beispiel zum Thema SPGridView habe ich nicht gefunden. Aus diesem Grund habe ich selbst das folgende Beispiel geschrieben. Es zeigt beispielhaft die Datenanzeige mit einem SPGridView mit Sortierung, Paginierung und Filterung. Leider zeigt das SPGridView beim Filtern von Spalten kein Filter-Icon an, wie man es von SharePoint-Listen kennt. Hier musste ich selbst ein wenig recherchieren - aber mit Hilfe von Greg Galipeau konnte ich auch dafür eine Lösung finden. Der Trick besteht darin, sich an das Event RowDataBound zu hängen, hier die gefilterte Spalte auszulesen und mit diesen Informationen das Filter-Icon selbst in der Kopfzeile des SPGridView in der entsprechenden Spalte anzuzeigen.

    Die folgenden beiden Screenshots zeigen das SPGridView in Aktion. Im zweiten Screenshot sieht man, dass das SPGridView auch die Sortier- und Filtericons verwenden und anzeigen kann.

    Bevor wir uns jetzt auch noch den Sourcecode dazu ansehen, möchte ich noch darauf hinweisen, dass der folgende Code ausschließlich als Beispiel bzw. Anschauungsobjekt gedacht ist. Er ist in der vorliegenden Form nicht dafür geeignet, unverändert in eine produktive Umgebung übernommen zu werden.

    using System.Data;
    using System.Reflection;
    using System.Web.UI.WebControls;
    using Microsoft.SharePoint.WebControls;
    using System.Web;

    namespace Technidata.ITS.MOSS.Webparts
    {
        public class SPGridViewDemo : System.Web.UI.WebControls.WebParts.WebPart
        {
            private SPGridView m_oGridView;
            private ObjectDataSource oDataSource;

            public SPGridViewDemo()
            {
            }

            protected override void CreateChildControls()
            {
                m_oGridView = new SPGridView();

                // Set basic gridview properties
                m_oGridView.ID = "MyGridView";
                m_oGridView.AutoGenerateColumns = false;
                m_oGridView.AlternatingRowStyle.BackColor = System.Drawing.Color.WhiteSmoke;

                // Set properties for sorting
                m_oGridView.AllowSorting = true;

                // Set properties for paging
                m_oGridView.PageSize = 3;
                m_oGridView.AllowPaging = true;
                m_oGridView.PagerStyle.HorizontalAlign = HorizontalAlign.Center;

                // Set properties for filtering
                m_oGridView.AllowFiltering = true;
                m_oGridView.FilterDataFields = "Vorname,Nachname";
                m_oGridView.FilteredDataSourcePropertyName = "FilterExpression";
                m_oGridView.FilteredDataSourcePropertyFormat = "{1} LIKE '{0}'";

                // Set EventHandler for setting the filter icon
                m_oGridView.RowDataBound += new GridViewRowEventHandler(m_oGridView_RowDataBound);
               
                // Set columns of the SPGridView
                CreateGridViewColumns("Vorname");
                CreateGridViewColumns("Nachname");

                // Create the datasource object
                oDataSource = new ObjectDataSource();

                string strTypeName = "Technidata.ITS.MOSS.Webparts.SPGridViewDemo,";
                strTypeName += Assembly.GetExecutingAssembly().FullName;

                oDataSource.TypeName = strTypeName;
                oDataSource.SelectMethod = "FillDataTable";
                oDataSource.ID = "MyDataSource";

                HttpRequest oRequest = HttpContext.Current.Request;
                if ((oRequest.Form["__CALLBACKID"] == null)    ||
                    (oRequest.Form["__CALLBACKPARAM"] == null) ||
                    (!oRequest.Form["__CALLBACKID"].EndsWith("MyGridView")))
                {
                    if (null != ViewState["FilterExpression"])
                    {
                        oDataSource.FilterExpression = (string)ViewState"FilterExpression"];
                    }
                }

                Controls.Add(oDataSource);

                // Bind SPGridView to datasource object
                m_oGridView.DataSourceID = oDataSource.ID;

                Controls.Add(m_oGridView);

                // IMPORTANT! Call this line after Controls.Add()
                m_oGridView.PagerTemplate = null
            }

            // --------------------

            protected override void OnPreRender(System.EventArgs e)
            {
                ViewState["FilterExpression"] = oDataSource.FilterExpression;
                base.OnPreRender(e);
            }

            // --------------------

            protected override void Render(System.Web.UI.HtmlTextWriter writer)
            {
                m_oGridView.DataBind();
                base.Render(writer);
            }

            // --------------------

            void m_oGridView_RowDataBound(object sender, GridViewRowEventArgs e)
            {
                if ((null != sender) && (e.Row.RowType == DataControlRowType.Header))
                {
                    string strFilteredColumn = ((SPGridView)sender).FilterFieldName;
                    SetGridViewFilterIcon(m_oGridView, strFilteredColumn, e.Row);
                }
            }

            // --------------------

            public void SetGridViewFilterIcon(SPGridView oGridView, string strColumn, GridViewRow oRow)
            {
                if ((false == string.IsNullOrEmpty(strColumn)) && (null != oRow))
                {
                    // Show icon on filtered column
                    for (int iIndex = 0; iIndex < oGridView.Columns.Count; iIndex++)
                    {
                        DataControlField oField = oGridView.Columns[iIndex];

                        if (oField.HeaderText == strColumn)
                        {
                            Image oFilterIcon = new Image();
                            oFilterIcon.ImageUrl = "/_layouts/images/ewr093.gif";
                            oFilterIcon.ImageAlign = ImageAlign.Left;
                            oFilterIcon.Style[System.Web.UI.HtmlTextWriterStyle.MarginTop] = "2px";
                            oFilterIcon.ID = "FilterIcon";

                            Panel oPanel = new Panel();
                            oPanel.Controls.Add(oFilterIcon);

                            oRow.Cells[iIndex].Controls.Add(oPanel);

                            break;
                        }
                    }
                }
            }

            // --------------------

            public static DataTable FillDataTable()
            {
                DataTable oDataTable = new DataTable("Bond Actors");
                DataRow row = null;

                // Create the columns
                oDataTable.Columns.Add("Vorname", typeof(string));
                oDataTable.Columns.Add("Nachname", typeof(string));

                // Fill data table
                row = oDataTable.NewRow();
                row["Vorname"] = "Sean";
                row["Nachname"] = "Connery";
                oDataTable.Rows.Add(row);

                row = oDataTable.NewRow();
                row["Vorname"] = "George";
                row["Nachname"] = "Lazenby";
                oDataTable.Rows.Add(row);

                row = oDataTable.NewRow();
                row["Vorname"] = "Roger";
                row["Nachname"] = "Moore";
                oDataTable.Rows.Add(row);

                row = oDataTable.NewRow();
                row["Vorname"] = "Timothy";
                row["Nachname"] = "Dalton";
                oDataTable.Rows.Add(row);

                row = oDataTable.NewRow();
                row["Vorname"] = "Pierce";
                row["Nachname"] = "Brosnan";
                oDataTable.Rows.Add(row);

                row = oDataTable.NewRow();
                row["Vorname"] = "Daniel";
                row["Nachname"] = "Craig";
                oDataTable.Rows.Add(row);

                return oDataTable;
            }

            // --------------------

            private void CreateGridViewColumns(string strColumn)
            {
                BoundField oGridColumn = new BoundField();
                oGridColumn.DataField = strColumn;
                oGridColumn.HeaderText = strColumn;

                // IMPORTANT! This line is needed to display the arrow icons
                oGridColumn.SortExpression = strColumn;
                oGridColumn.Visible = true;

                m_oGridView.Columns.Add(oGridColumn);
            }
        }
    }

     

    Nachtrag: ich bin noch auf ein kleines Problem gestossen: wenn gleichzeitig die Sortierung und die Filterung aktiviert ist und man dann die Sortierreihenfolge ändert, funktioniert das Filtern nicht mehr. Aber auch dafür findet sich eine Lösung - in diesem Fall habe ich mir ein paar Zeilen Code bei Bob's SharePoint Bonanza ausgeliehen und gleich in mein obiges Beispiel integriert. Das Prinzip ist: man sichert in den ViewStates den aktuellen Filter und setzt den Filter aus den ViewStates beim Erzeugen bzw. Parametrieren des Objekts wieder.


     

    Add to Technorati Favorites

  • SharePoint-Listen und die Namen der Spalten

    SharePoint nutzt u.a. zur Speicherung von Daten Listen und Dokumentbibliotheken. Neben einigen vordefinierten Listen und Bibliotheken kann man auch benutzerdefinierte Listen erstellen und an die eigenen Bedürfnisse anpassen. Bei diesen benutzerdefinierten Listen kann man selbst festlegen, welche Spalten verwendet werden sollen und welchen Datentyp diese Spalten aufnehmen sollen. Und genau bei diesen Spalten bzw. den Namen der Spalten gibt es einiges zu beachten.

    Wenn man eine neue Spalte in einer benutzerdefinierten Liste anlegen möchte, wechselt man in die Listen-Einstellungen und klickt dort auf 'Spalte erstellen'. Jetzt gibt man der neuen Spalte einen Namen und wählt einen Datentyp aus einer Liste vordefinierter Datentypen aus. Das sieht dann z.B. so aus, wie in diesem Screenshot dargestellt:

     

    Auch wenn man der Spalte nur einen einzigen Spaltennamen zuweisen kann (was auf den ersten Blick auch logisch und völlig selbstverständlich erscheint), existieren neben dem Spaltennamen, den man beim Erstellen einer neuen Spalte vergibt noch zwei weitere Spaltennamen. Diese beiden zusätzlichen Spaltennamen bekommt ein SharePoint-User nur sehr selten zu sehen, ein Programmierer, der ein WebPart, einen Eventhandler, einen Workflow oder einen Timerjob programmiert, sollte aber schon sehr genau wissen, was es mit diesen zusätzlichen Spaltennamen auf sich hat.

    Der Spaltenname, der beim Erstellen einer neuen Spalte angegeben wird, wird als Display Name bezeichnet. Wie dieser Name schon vermuten läßt, ist der Display Name der Anzeigename einer Spalte. Dies bedeutet: überall in der Oberfläche wo es um das Anzeigen von Daten und Informationen geht, wird der Display Name (Anzeigename) verwendet.

    Beim Erstellen einer neuen Spalte erzeugt SharePoint (oder besser gesagt das unterlagerte Objekt-Modell) zwei weitere Spaltennamen: den Internal Name und den Static Name. Von diesen beiden Spaltennamen bekommt ein SharePoint-User kaum etwas zu sehen und auch ein SharePoint-Administrator oder ein Designer werden diese beiden zusätzlichen Spaltennamen nur selten zu Gesicht bekommen. Dennoch sind beide sehr wichtig und sollten besonders von Entwicklern beachtet werden.

    • der Display-Name wird auch als Anzeigename bezeichnet und wird vom SharePoint-Benutzer beim Anlegen einer Spalte angegeben.
    • der Static-Name wird von SharePoint beim Erzeugen einer neuen Spalte automatisch erzeugt.
    • der Internal-Name wird ebenfalls von SharePoint beim Erzeugen einer neuen Spalte erzeugt.

    Jetzt wissen wir, dass es nicht nur einen Spalten-Namen gibt, sondern drei - aber wozu braucht man denn drei Spaltennamen?

    In erster Linie sind diese drei Spalten-Namen für Programmierer interessant. Da der Display-Name (also der Anzeigename) nachträglich verändert werden kann, besteht gerade für die Programmierung die Notwendigkeit, nicht-veränderliche Spaltennamen zu haben, um Spalten auch nach der Änderung des Display-Name noch identifizieren zu können. Beim Verändern des Display-Names werden weder Internal-Name noch Static-Name verändert, dennoch ist der Static-Name nicht ganz so unveränderlich, wie der Internal-Name. Im Gegensatz zum Internal-Name kann der Static-Name nachträglich über Aufrufe aus dem SharePoint Objekt-Modell verändert werden. Static-Name und Internal-Name müssen nicht zwingend gleich lauten.

    • der Display-Name ist vom Benutzer direkt über die SharePoint-Oberfläche veränderbar und kann auch nachträglich beliebig oft umbenannt werden.
    • der Static-Name bleibt beim Umbenennen einer Spalte unverändert, ist aber nicht wirklich unveränderbar. Über das Objekt-Modell kann der Static-Name verändert werden.
    • der Internal-Name bleibt ebenfalls beim Umbenennen unverändert und kann auch über Objekt-Modell Aufrufe nicht mehr verändert werden.

    Um den Static-Name zu ändern, reichen im Prinzip die folgenden Code-Zeilen: 

    SPList oList = ...
    string strColumnName = ...

    oList.Fields.GetFieldByInternalName(strColumnName).StaticName = "NeuerName";
    oList.Fields.GetFieldByInternalName(strColumnName).Update(true);


    Der Internal-Name scheint der wirklich eindeutige und unveränderbare Namen einer Spalte zu sein. Der Static-Name ist für die Programmierung interessant - ein Beispiel dafür ist die Webpart-Programmierung. Bei uns haben wir Richtlinien (oder neu-deutsch: Best Practices) erarbeitet, wie wir welchen Spaltennamen bei der Programmierung einsetzen. Wenn bei einem Webpart über die Webpart-Properties z.B. ein Spaltenname einer SharePoint-Liste angegeben werden soll, verwenden wir dafür grundsätzlich den Display-Name, um dem Designer die Arbeit der WebPart-Parametrierung zu erleichtern. Der Designer kann bei der Parametrierung eines WebParts die gleichen Spaltennamen eingeben, die er auch beim Anlegen der Liste verwendet hat.

    Allerdings verwendet dann das eigentliche WebPart natürlich nicht mehr den in die Properties eingegebenen Display-Name, sondern wandelt diesen mit Hilfe der zugehörigen Liste in den Internal-Name. Intern arbeiten unsere WebParts grundsätzlich mit dem Internal-Name. Es hat für uns aber manchmal einen Vorteil, statt des unveränderlichen Internal-Name den nur bedingt unveränderlichen Static-Name zu verwenden. Dieser Vorteil wird deutlich, wenn wir das Thema Mehrsprachigkeit bzw. Variations betrachten. Angenommen wir haben zwei gleiche Listen - eine Liste mit deutschen Anzeigenamen und eine zweite Liste mit englischen Anzeigenamen. Mit beide Listen soll ein selbstprogrammierter Eventhandler verbunden werden (ein Beispiel dazu findet sich hier), der beim Beschreiben einer bestimmten Spalte getriggert werden soll. In diesem Fall würde der Eventhandler mit dem Static-Name der Triggerspalte arbeiten, denn der Display-Name wird sich aufgrund der unterschiedlichen Sprachen unterscheiden. Da der Static-Name zum Glück nicht vollständig unveränderlich ist, kann man -trotz unterschiedlichem Display-Name- den Static-Name nachträglich angleichen. Auf diese Weise kommt man mit einem Eventhandler für zwei sprachlich unterschiedliche Listen aus.

    Wie ändert man denn nun den Static-Name einer SharePoint-Spalte, wenn dies nicht über die Oberfläche, sondern nur über das Objekt-Modell geht? Um dies zu zeigen, habe ich ein kleines Tool geschrieben, welches auf unserer Homepage kostenlos zum Download (gegen eine kleine Registrierung) bereitsteht. Dieses Tool erlaubt die Auswahl eines Webs und einer Liste und zeigt die in der Liste vorhandenen Spalten mit ihrem Internal-Name an. Durch Anklicken einer Spalte kann man diese auswählen und dann ggf. Display-Name und Static-Name ändern. Man kann sich so einen schnellen Überblick über alle Spalten einer Liste verschaffen. Bei diesem Tool handelt es sich um ein Entwickler-Tool, welches wir intern bei unseren Entwicklungen nutzen. Für diesen Beitrag habe ich es ein wenig angepasst und aufgeräumt - dennoch bleibt es ein Entwickler-Tool und der Einsatz erfolgt grundsätzlich auf eigenes Risiko!

    Bei der Programmierung bzw. bei der Verwendung des SharePoint Objekt-Modells muss man aber sehr aufpassen, welchen Spaltennamen man mit welcher Methode verwenden kann bzw. soll. Leider ist dies im SDK nicht einheitlich geregelt. Es gibt Methoden, bei denen schon aufgrund des Methodennamens klar ist, mit welchem Spaltennamen sie arbeiten: SPFieldCollection.GetFieldByInternalName(name) arbeitet wie der Name bereits andeutet, mit dem Internal-Name einer Spalte, während SPFieldCollection[name] den Display-Name verwendet. Bei SPListItem[name] wird die Sache noch etwas undurchsichtiger: hier sind alle drei Spaltennamen als Parameter erlaubt. Eine erste Übersicht von Methoden und deren Spalten-Parametern findet sich in diesem Blog.

    Um zu zeigen, dass es nicht schwierig ist, bei der Programmierung die einzelnen Spaltennamen sauber zu trennen, hier ein kleines Snippet, wie man mit dem Display-Name den zugehörigen Internal-Name ermitteln kann.

    private string GetInternalColumnName(SPList oList, string strColumnDisplayName)
    {
        string strRetParam = string.Empty;

        try
        {
            strRetParam = oList.Fields.GetField(strColumnDisplayName).InternalName;
        }
        catch
        {
            strRetParam = strColumnDisplayName;
        }

        return (strRetParam);
    }
     

    Ich hoffe, dass ich mit diesem Beitrag zeigen konnte, wie wichtig es ist, gerade bei der Programmierung darauf zu achten, die unterschiedlichen Spaltennamen-Typen sauber zu trennen und sich vor Beginn der Programmierung zu überlegen, mit welchem Typ von Spaltennamen gearbeitet werden soll. Aus eigener Erfahrung kann ich sagen, dass es sehr aufwändig werden kann, Sourcecode zu debuggen, bei dem dauernd und scheinbar wahllos zwischen allen Spaltennamen-Typen gewechselt wird. Auch hier gilt: ein klar strukturiertes und durchdachtes Design macht das Programmieren und das Debuggen wesentlich einfacher!

    Bisher habe ich erstaunlich wenige Blog-Posts zu diesem Thema gefunden - Grund genug für mich, meine eigenen Erfahrungen zu diesem Thema in meinem Blog zusammenzufassen.

    Nachtrag: der Link zum kostenlosen Download unserer SharePoint-Tool geht im Text dieses Beitrags etwas unter. Deswegen hier nochmals der Link zu unserer SharePoint-Seite.

     

    Add to Technorati Favorites
  • How To: Wie behebt man das Problem mit der Add-On Meldung bzgl. der NAME.DLL?

    Vielleicht hat der eine oder andere Leser meines Blogs dieses Problem selbst schon einmal gehabt: man surft im Internet und möchte sich eine neue auf SharePoint-basierende Site ansehen und bekommt von seinem Browser folgende Meldung angezeigt:

     

    Gerade bei Internet-Auftritten von Unternehmen ist diese Meldung nicht nur lästig, sondern wirkt auf Kunden oft auch befremdlich. In Zeiten eines zum Glück deutlich gestiegen Sicherheitsbewußtseins beim Surfen im Internet hinterläßt eine solche Meldung bei vielen Surfern ein ungutes Gefühl.

    Bei Intranet-Auftritten ist die NAME.DLL, die sich letztendlich hinter dieser Meldung verbirgt, nützlich und hilfreich. Dahinter verbirgt sich u.a. die Smart-Tag bzw. Presence-Funktionalität. Bei Internet-Auftritten hat diese NAME.DLL meist aber keine sinvolle Funktion, sondern stört eher dadurch, dass es Besucher einer Site verwirrt oder sogar abschreckt.

    Microsoft beschreibt in einem Artikel zu KB931509, wie man dieses Problem lösen kann. Hier werden mehrere Lösungsmöglichkeiten beschrieben, denn man kann dieses Problem auch einfach dadurch lösen, dass man die betreffende Site zur Liste der vertrauenswürdigen Sites im IE7 hinzufügt. Für Intranet-Sites mag das eine Lösung sein, bei Internet-Auftritten wird man seinen Besuchern kaum zumuten wollen, den neuen Firmenauftritt als vertrauenswürdig einzustufen! Im KB-Artikel von Microsoft wird aber auch eine serverseitige Lösungsmöglichkeit beschrieben, die ein paar Änderungen an der Masterpage voraussetzt.

    Übrigens: im Zusammenhang mit einem SharePoint Add-On gibt es noch ein weiteres bekanntes Problem, welches sogar den IE7 zum Abstürzen bringen kann! Darüber und über eine Lösungsmöglichkeit habe ich hier bereits vor einiger Zeit berichtet.

     

    Add to Technorati Favorites

Need SharePoint Training? Attend a SharePoint Bootcamp!

Posts (c) their respective authors. Everything else (c) 2007 SharePoint Experts