Christian Wagnsonner

Published: 79 articles

WP8 – Erstellen von statischen Resourcen – Auslagern von Styles in eigenes Stylesheet

Hallo Leute,

XAML kann ziemlich schnell sehr unüberschaubar werden. Wie im HTML kann man Elementen/Objekten Styles mitgeben. Im HTML gibt es dafür die nützlichen CSS-Stylesheets. In Windows Phone 8 funktioniert dies etwas anders. Und zwar über die Möglichkeit von „Static Resources“.

Statische Resourcen verwendet man am einfachsten wie folgt:


<TextBlock Text="Avatar" Style="{StaticResource WIT_ControlLabel}"></TextBlock>

Nachdem man „StaticResource“ eingegeben hat, bekommt man von Visual Studio out-of-the-box ein Set an Vorlagen angeboten. Diese Liste lässt sich aber auch erweitern (Im Beispiel verwende ich einen eigenen Style, mehr dazu später), womit wir zum eigenen Stylesheet kommen. Dieses Stylesheet wird als normale XAML-Page erstellt und über die App.xaml eingebunden.

Änderungen App.xaml

<!--Application Resources-->
    <Application.Resources>
        
        <ResourceDictionary>
<em>            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="Resources/Styles.xaml"/>
                <ResourceDictionary Source="Resources/StaticContent.xaml"/>
            </ResourceDictionary.MergedDictionaries></em>
            <app:LocalizedStrings x:Key="LocalizedStrings"/>
        </ResourceDictionary>
    </Application.Resources>

Wie man im Codebeispiel sehen kann, binde ich zwei XAML-Dateien ein. Die beiden XAML-Dateien sind nahezu ident (nur die Styles unterscheiden sich), deshalb stelle ich euch nur eine der beiden vor.

Styles.xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone">

<Style x:Key="WIT_ControlLabel" TargetType="TextBlock">
        <Setter Property="Style" Value="{StaticResource PhoneTextSmallStyle}"></Setter>
        <Setter Property="VerticalAlignment" Value="Center"></Setter>
        <Setter Property="FontSize" Value="20"></Setter>
        <Setter Property="Margin" Value="12,0,0,0"></Setter>
        <Setter Property="Foreground" Value="DarkGray"></Setter>
    </Style>
</ResourceDictionary>

Das Codebeispiel zeigt die Definition des Styles „WIT_ControlLabel“, welches ich bereits oben verwendet habe.

Cheers,
Christian

WP8 – Layouting – Wie baue ich eine Tabelle?

Hallo Leute,

um mit XAML eine Tabelle zu bauen, kann man ähnlich wie in HTML vorgehen. Der große Unterschied ist, dass in XAML die Tabelle vorab definiert wird und man dann den Objekten eine Column und eine Row zuweist.

Beispiel HTML:

<table>
<tr>
<td rowspan="2">Inhalt über zwei Zeilen</td>
<td>Inhalt 1. Reihe, 2. Spalte</td>
</tr>
<tr>
<td>Inhalt 2. Reihe, 2. Spalte</td>
</tr>
</table>

Beispiel XAML:

<Grid>

<!-- DEFINITION -->
<Grid.RowDefinitions>
                <RowDefinition Height="Auto"></RowDefinition>
                <RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"></ColumnDefinition>
        <ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>

<!-- ZUWEISEN VON OBJEKTEN -->
<TextBlock Text="Inhalt über zwei Zeilen" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" />
<TextBlock Text="Inhalt 1. Reihe, 2. Spalte" Grid.Row="0" Grid.Column="1" Grid.RowSpan="2" />
<TextBlock Text="Inhalt 2. Reihe, 2. Spalte" Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" />

</Grid>

Cheers,
Christian

WP8 – ScrollViewer – scrollender Bereich

Hallo Leute,

um bei einer WP8-Page einen Bereich/Sektion „scrollbar“ zumachen, muss man einen „ScrollViewer“ verwenden.
Dieser wird einfach als Wrapper um ein Control gespannt, wie z.B.:

<ScrollViewer xmlns:src="clr-namespace:at.wienit.vacation.app.Controls">
<StackPanel Margin="12,0,0,0">
                        <TextBlock Text="{ StaticResource WIT_WelcomeTitle}" Style="{ StaticResource WIT_PanoramaTitle1stLine}" />
                        <TextBlock x:Name="pnrTitle"  Text="Maximilian Mustermann" Style="{StaticResource WIT_PanoramaTitle2ndLine}"></TextBlock>
                    </StackPanel>
                </ScrollViewer>

Cheers,
Christian

Sharepoint – DateTimeControl – Localization – LocaleId

Hallo,

und wieder was nützliches. Verwendet man (z.B. in einem WebPart) das Microsoft.SharePoint.WebControls.DateTimeControl Control, so wird dieses nicht automatisch den Spracheinstellungen des Benutzers angepasst – es bleibt immer im englischen Format (also MM/dd/yyyy).

Es gibt dafür eine einfache Lösung:

dateControl.LocaleId = (int)SPContext.Current.RegionalSettings.LocaleId;

Cheers,
Chris

Sharepoint – Dokument einer Dokumentenbibliothek hinzufügen

Hallo Leute,

heute möchte ich euch kurz zeigen, wie Ihr ein Dokument bzw. einen Stream in eine Dokumentenbibliothek hinzufügt.
Meine Methode fügt zudem eine fortlaufende Nummer hinzu, falls ein Dokument mit dem angegebenen Namen bereits existiert (Parameter „overwrite“).

        /// <summary>
        /// Checks if file exists.
        /// </summary>
        /// <param name="listName">Name of the list.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static bool CheckIfFileExists(string listName, string fileName)
        {
            var web = SPContext.Current.Web;
            var docUrl = Path.Combine(web.Url, listName, fileName);

            SPFile file = web.GetFile(docUrl);
            return file.Exists;
        }



        /// <summary>
        /// Adds the file to list.
        /// </summary>
        /// <param name="listName">Name of the list.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="streamToSave">The stream to save.</param>
        /// <param name="overwrite">if set to <c>true</c> [overwrite].</param>
        /// <exception cref="System.Exception">Cannot add file stream to list. Following error occured:  + ex.Message</exception>
        public static void AddFileToList(string listName, string fileName, Stream streamToSave, bool overwrite = false)
        {
            try
            {
                var fileNameToUse = string.Format(fileName, "");
                var web = SPContext.Current.Web;

                if (!overwrite)
                {
                    for(int i = 1; i <= 100; i++)
                    {
                        if (CheckIfFileExists(listName, fileNameToUse))
                        {
                            fileNameToUse = string.Format(fileName, " (" + i + ")");
                        }
                        else
                            break;
                    }
                }

                var docUrl = Path.Combine(web.Url, listName, fileNameToUse);
                web.Files.Add(docUrl, streamToSave);
            }
            catch (Exception ex)
            {
                throw new Exception("Cannot add file stream to list. Following error occured: " + ex.Message, ex);
            }
            
        }

Beispiel:

AddFileToList(this.DocLibrary, "Protokoll {0}.docx", stream, false);
// or
AddFileToList(this.DocLibrary, "Protokoll {0}.docx", stream);

Cheers,
Chris

Sharepoint – Quick Check – Enthält SPList Daten?

Hallo,

wieder ein kurzes Code-Snippet zur Prüfung, ob eine bestimmte Liste Daten enthält.
Dabei kann auch ein SPQuery mitgegeben werden.


        /// <summary>
        /// Lists the contains data.
        /// </summary>
        /// <param name="list">The list.</param>
        /// <param name="query">The query.</param>
        /// <returns></returns>
        public static bool ListContainsData(SPList list, SPQuery query = null)
        {
            SPListItemCollection items = null;
            if (query == null)
                items = list.Items;
            else
                items = list.GetItems(query);

            return items.Count > 0;
        }

Beispiel:

return ListContainsData(list, query);
// or
return ListContainsData(list);

Cheers,
Chris

Sharepoint – Url zu SPListItem – Get ListItem Url

Hi Leute,

heute habe ich ein kurzes Snippet für euch.

/// <summary>
        /// Gets the list item URL.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="type">The type.</param>
        /// <returns></returns>
        public static string GetListItemUrl(SPListItem item, PAGETYPE type)
        {
            var formUrl = item.ParentList.Forms[type].Url;
            return string.Format("{0}/{1}?ID={2}", item.Web.Url, formUrl, item.ID);
        }

Mit diesem Snippet kann man sich ganz einfach die vollständige Url zu einem ListItem geben lassen. PAGETYPE gibt dabei an, zu welchem Formular/Ansicht der Benutzer weitergeleitet werden soll (z.B. direkt zum Bearbeitungsformular)…

Beispiel:

GetListItemUrl(item, PAGETYPE.PAGE_DISPLAYFORM);

Ergebnis: http://server:port/sites/dev/yourspace/sites/dev/subsite/Lists/listname/DispForm.aspx?ID=3

Cheers,
Chris

Sharepoint 2010 | Infopath Listform von TEST auf PROD deployen

Hallo Leute,

mittlerweile habe ich auch mit den ersten Infopath-Sharepoint Formularen zu tun.

Vorweg: Geht man einfach im Sharepoint unter Listentools -> Liste -> Formular anpassen, so hat man einige Einschneidungen, z.B.:
* Entwicklertools im InfoPath stehen nicht zur Verfügung
* Deployment gestaltet sich schwierig

Ich möchte hier insbesondere auf den zweiten Punkt eingehen:
Denn Wenn man auf diese Art das Formular anpasst, kann man im InfoPath selbst die primäre Datenquelle nicht mehr verändern, ein Deployment für eine andere Liste bzw. auf einem anderen Server ist also nicht möglich …

2013-05-10 09_59_29-Datenverbindungen

Nach ein wenig Recherche habe ich „herausgefunden“, dass sich hinter der Extension „xsn“ eigentlich nur ein CAB-File versteckt, welches man einfach (umbenennen und) extrahieren kann. Man findet dann eine Datei Namens „manifest.xsn“, welche man am besten im Notepad (oder einem anderen Text-Editor) öffnet.

Nun sucht man nach „ListId“ und findet:
sharePointListID="{32D6310F-88CF-4C32-BAFC-B974A717D7BD}" contentTypeID="0x0100ABD98D05E8DE754086E57FD6330C5060"

Diese beiden Werte muss man durch jene des Zielsystems ersetzen.

Anschliessend sucht man nach „baseUrl“ und findet:

Auch hier ersetzt man die URL durch die neue/PROD-Sharepoint URL.

Wichtig: Die Liste muss bereits am Zielsystem existieren. 100%ig ident muss sie nicht sein – aber dazu später.

Nun muss man die Dateien wieder in ein XSN-Dokument verpacken. Ich habe hierfür folgendes gefunden:


;************************************************************

; MSDN Sample Source Code MakeCAB Directive file example

;************************************************************

.OPTION EXPLICIT

;*****************************************************************

; change the value of the caninet name for example myInfoPath.xsn

;******************************************************************

.Set CabinetNameTemplate=20131005_test.XSN

;**********************************************************************************************

; change the value of the Disk Directory Template value to the directory you want to store the xsn file into,,

;**********************************************************************************************

.set DiskDirectoryTemplate="C:UsersuserDesktop"

.Set Cabinet=on

.Set Compress=on

;*******************************************************

; Just List All the files to be added in the xsn file

;*******************************************************

"C:UsersuserDesktoptempChoices Data Connection39.xsd"
"C:UsersuserDesktoptempchoices.xml"
"C:UsersuserDesktoptempErgnzungen-E.xsl"
"C:UsersuserDesktoptempErgnzungenIT.xsl"
"C:UsersuserDesktoptempITPSE.xsl"
"C:UsersuserDesktoptempmanifest.xsf"
"C:UsersuserDesktoptempPlanung.xsl"
"C:UsersuserDesktoptempsampledata.xml"
"C:UsersuserDesktoptempschema.xsd"
"C:UsersuserDesktoptempschema1.xsd"
"C:UsersuserDesktoptempschema2.xsd"
"C:UsersuserDesktoptempschema3.xsd"
"C:UsersuserDesktoptempschema4.xsd"
"C:UsersuserDesktoptemptemplate.xml"
"C:UsersuserDesktoptempupgrade.xsl"
"C:UsersuserDesktoptempview1.xsl"
"C:UsersuserDesktoptempVorhaben.xsl"
"C:UsersuserDesktoptempWEintern.xsl"

;*********************

; End of the File

;*********************

Wichtig ist, dass alle Dateien in der Auflistung am Ende vorkommen. Das fertige XSN-File muss dann wieder in der Entwurfsansicht geöffnet werden.
InfoPath wirft dann sofort folgende Meldung auf:

2013-05-10 10_07_05-(Entwurf) Formular1 [Schreibgeschützt] - Microsoft InfoPath

Ich habe mit [JA] geantwortet, was die Felder korrekt aktualisiert hat. Was bei [Nein] genau passiert, kann ich nicht sagen.
Anschließend kann man die primäre Datenquelle überprüfen und wird die neue URL sehen. Auch das Deployment funktioniert dann gegen das Zielsystem.

Cheers,
Christian

CRM 2011 – Doubleclick im Subgrid deaktivieren/verhinden

Nach langer Zeit mal wieder was zum Thema CRM:

Nachdem wir in einem Projekt leider sehr große Hierarchien haben und die Subgrids bekanntermassen ja nur zwei Ebenen anzeigen, haben wir uns eine Art Schattenentität als Lösung einfallen lassen. Diese fungiert quasi als Linker zu den einzelnen Abhängigkeiten. Wie auch immer. Wir zeigen nun auf ein paar Formularen eine Art „Überblick“ an, die Daten in den Subgrids sind vom Typ „cust_linkentity“.

Das hat die negative Folgeerscheinung, dass bei Doppelklick das Standard-Formular der Entität „cust_linkentity“ geöffnet wird, obwohl diese Entität rein aus funktionellen Gründen existiert und für den Kunden eigentlich unsichtbar sein sollte.

Leider gibt es keinen von Microsoft unterstützten Weg, das zu verhindern. Meine Recherche hat mich zur Datei „AppGrid_DefaultData.htc“ gebracht, welche unter anderem die Funktion „handleDblClick()“ enthält.

ACHTUNG: Die nachfolgende Lösung ist von Microsoft explizit nicht supported!! Bitte nur machen, wenn Ihr euch über die Folgen im Klaren seid!

Original

function handleDblClick()
{
o = getSrcRow();
if (!IsNull(o))
if ((event.ctrlKey || event.srcElement.tagName == "INPUT" && event.srcElement.type == "checkbox") && o.selected)
unselectRow(o, true, true);
else
handleSelectRow(o);

var oSelectedRecords = getSelectedRecords();

if (oSelectedRecords.length == 0)
return;

var oSelRec = oSelectedRecords[0], oTr = oSelRec[3];

openRecord(oTr);

}
[/pre]

Wie Ihr seht, wird als Letztes mit "openRecord" das Formular der Entität geöffnet.

Angepasst

[sourcecode language="javascript"]
function handleDblClick()
{
o = getSrcRow();
if (!IsNull(o))
if ((event.ctrlKey || event.srcElement.tagName == "INPUT" && event.srcElement.type == "checkbox") && o.selected)
unselectRow(o, true, true);
else
handleSelectRow(o);

var oSelectedRecords = getSelectedRecords();

if (oSelectedRecords.length == 0)
return;

var oSelRec = oSelectedRecords[0], oTr = oSelRec[3];

var typename = o.getAttribute("otypename");
if (typename != "cust_linkentity") {

openRecord(oTr);
}
}
[/sourcecode]

Ich habe hier eine einfache Condition eingebaut, die das Formular nur öffnet, wenn es sich nicht um diesen einen bestimmten Typ handelt.

Cheers,
Chris

Scrolling in Subgrid

Sind heute vor folgende Herausforderung gestellt worden:

Ein Subgrid zeigt auf einer Seite 5 Records. Der Kunde möchte jedoch 10 Records auswählen (verteilt auf mehrere Seiten). Das Grid verliert jedoch nach dem Umblättern die Auswahl von der vorhergehenden Seite, was zu fachlichen Problemen führen könnte.

Sind dabei auf MSCRM-Kings-Blog gestossen. Der Kollege stellt einfach ein, dass 250 Records auf einer Seite dargestellt werden sollen und quätscht das Grid dann einfach mittels CSS auf die Höhe von 5 bzw. X Records zusammen.

Funktioniert in unserem Fall super. Hat jedoch die Limitierung, dass der Kunde auf diesem Wege maximal 250 Records auswählen kann (Limitierung seitens Microsoft für die Darstellung auf einer Seite), bevor das Problem von vorne los geht. Wird in unserem Fall sicher nicht passieren.

Hier der Link zum Blog: http://www.mscrmking.com/2011/07/crm-2011-adding-scrollbars-to-a-sub-grid/#comment-213

Und nun der Code:
[sourcecode language=“javascript“]
//Warning: This code below is not supported by Microsoft

//Place this code at the start of your script

function setSubgridHeight(grdID, noRecordsDisplay, noRecordsPerPage, forceRefresh) {
/// <summary>
/// Causes sub-grid to scroll by setting the number of records to be displayed and
// </summary>
/// <param name="grdID" type="string">
/// ID of sub-grid *required
/// </param>
/// <param name="noRecordsDisplay" type="int">
/// Controls height of sub-grid and how many recors are displayed
/// Before applying scrolling
/// </param>
/// <param name="noRecordsPerPage" type="int">
/// Determines number of records to display per page. If not provided
/// The value defined in the form editor will be used
/// </param>
/// <returns type="nothing" />
if (!IsNull(grdID)) {
var tbl = $("#" + grdID);

//Adjust the sub-grid page size if desired, otherwise this is controlled by the form editor
if (!IsNull(noRecordsPerPage)) {
//Get reference to divGridProps element of desired grid.
//Microsoft stores property infomrmaiton about the sub-grid within this element
var divProps = $("#divGridProps", tbl);
$("#recsPerPage", divProps).attr("value", noRecordsPerPage);
//Controls number of rows returned by data provider and sets the page size for the sub-grid
}

//Get reference to divGridParams element of desired grid.
//Microsoft stores parameter infomrmaiton about the sub-grid within this element
var divParams = $("#divGridParams", tbl);
noRecordsDisplay = (IsNull(noRecordsDisplay) ? "5" : noRecordsDisplay);

//Default to 5 visible records at a time
$("#RecordsPerPage", divParams).attr("value", noRecordsDisplay);

//Controls number of rows displayed in output table
$("#maxrowsbeforescroll", divParams).attr("value", noRecordsDisplay);
//Force scroll bar to display if more records included than desired to display

//Clear all of the empty TR that are added to fill the table height
var tb = $("#" + grdID + "_d").parent().parent();
$("tr", tb).filter(function(index) { return $(this).html() == ""; }).remove();

//Now resize the data area of the grid
$("#" + grdID + "_divDataArea").css("height", ((noRecordsDisplay – 1) * 25) + "px");

//Refresh the grid if desired
if(forceRefresh) {
document.getElementById(grdID).control.refresh();
}
} else {
throw Error.argument("grdID", "grdID is null or undefined");
}

//To use you would write something like
setSubgridHeight(#SUB-GRID ID#, 10, null, false);
[/sourcecode]

Have Fun!

Cheers,
Chris