Dienstag, 23. September 2014

Datenabruf aus Micorsoft Dynamics AX2012 R3 für eine Windows 8.1 App

Der Microsoft Dynamics AX2012 R3 bietet uns von hause aus schon einige Web Service mit deren Hilfe wir Daten aus dem AX konsumieren können. Hier bei möchte ich den QueryService Web Service hervorheben. Mit diesem lassen sich sowohl statische Querys aus dem AX ausführen als auch eigen Querys die in der Applikation definiert werden.

Hierdurch ist es uns möglich schnell und recht unkomplizierter Daten aus dem Microsoft AX 2012 R3 in einer App für Windows 8.1 oder Windows Phone 8.1 zu präsentieren.

hier ein kleines Beispiel für eine AX Query Ausführung:














 
Hier muss das benutzte Query jedoch im AX angelegt werden, was entsprechende AX Kenntnisse voraussetzt.

Alternativ können auch eigene Query in der Applikation erstellt werden, dies setzt dann eine Kenntnis der  Datenstriktur voraus.
 
Hier das Beispiel für eine solches Query:
 























 

Dienstag, 22. Juli 2014

Barcode mit ZXing.Net Generieren

Um einen Barcode mit der ZXing.Net API zu erstellen braucht es nicht viel. Man erstellt sich eine Instanz der Klasse BarcodeWriter aus der ZXing.Net API. Diesem "writer" wird das gewünschte Format übergeben und wenn nötig noch weitere Parameter zur Größe.
Mit der Methode "write" kann dann der gewünschte Barcode erstellt werden.

Siehe Beispiel:











Als Rückgabe der Methode erhält man ein PixelData-Objekt welches sich in ein WriteableBitmap umwandeln lässt, welches wir dann in der App verwendeten können.

Ich hoffe ihr habt viel Spaß beim selber ausprobieren, bis zum nächsten mal.

Windows Phone 8.1 Barcode Scanner mit ZXing.Net

Im voran gegangen Post Windows 8.1 Barcodescanner App mit ZXing.Net von mir, habe ich darauf hingewiesen das unter Windows Phone 8.1, uns der CameraCatureUI Dialog nicht zur Verfügung steht und aus diesem Grund die Methoden der MediaCature Klassen verwendet werden müssten.

Hier nun das Beispiel unter Windows Phone 8.1. Dieses Vorgehen kann auch unter Windows 8.1 in einer Windows Store App verwendet werden.

Zu beginn müssen wir die das MediaCature Objekt initialisieren und ein paar Einstellung vornehmen.
 















Um die richtig Kamera Id zu erhalten, verwende ich die folgende Methode diese ermittelt anhand gewünschten Kamera Position die Id. Sollte es keine Rückwertige Kamera geben wird die erste gefunden verwendet.









Anschließend kann wie hier im Beispiel eine Vorschau gestartet werden und auf ein Event Reagiert werden mit dem wir das Bild dann an unsere Barcode Scanner Klasse (siehe Windows 8.1 Barcodescanner App mit ZXing.Net ) übergeben.













Es ist äußerst wichtig das wir das MediaCapture-Objekt und verwandte Objekte beim Anhalten der App korrekt beenden und löschen. Andernfalls können andere Apps, die auf die Kamera des Gerätes zugreifen, gestört werden.

Daher ist es am besten für diese zwecke das MediaCapture-Objekt und das verwendete Vorschau CaptureElement als öffentliche Eigenschaften in der app.xaml.cs zu deklarieren. Im OnSuspending Ereignishändler rufen wie dann die entsprechend CleanUpCatureResouces Methode auf.

























Ich wünsche euch viel Spaß beim selber ausprobieren und bis zum nächsten mal.

Freitag, 18. Juli 2014

Windows 8.1 Barcode Scanner App mit ZXing.Net API

Mit Hilfe der ZXing .Net API http://zxingnet.codeplex.com/ kann man recht einfach und unkompliziert einen Barcodescanner für eine Windows Store App erstellen.
Hier nun ein kleines Beispiel mit Hilfe des CameraCatureUI Dialoges, dieser Dialog steht uns unter Windows Phone 8.1 nicht zur Verfügung hier müssen wir die Methoden der MediaCature Klasse verwenden weiter Informationen unter Aufnehmen und Speichern von Fotos aus der Kamera

Zunächst erstellen wir mit dem CameraCaturUI Dialog eine StorageFile die unser aufgenommenes Foto hält.


Die Datei reichen wir an eine von mir erstellte BarcodeScanner klasse weiter die den Barcode mit Hilfe des ZXing.Net API den Barcode ausliest und mir über das Event BarcodeScanCompleted den ausgelesenen Barcode mitteilt.. Die geschieht in der Methode ScannBarcodeAsync.


Um in der jeweiligen Aufrufs Seite auf den Abschluss des Scans reagieren zu könne wurde von mir in der Klasse noch ein Event erstellt welches dem ausgelesen Barcode und ein Flag zurückgibt ob der Scan erfolgreich war.


Schon haben wir einen Barcode Scanner implementiert und könne die Barcode die über die ZXing.Net APIT erkannt werden können. Auslesen.

Ich wünsche euch viel Spaß und bis zum nächsten mal.

Mittwoch, 16. Juli 2014

Erstellen einens einfachen REST Service

Im Grunde bezeichnet REST die Idee, dass eine URL genau einen Seiteninhalt als Ergebnis einer serverseitigen Aktion (etwa das Anzeigen einer Trefferliste nach einer Suche) darstellt.

REST setzt auf ein zustandsloses Client-Server-Protokoll, wobei im Normalfall HTTP oder HTTPS eingesetzt wird. Jede REST-Nachricht enthält dabei alle Informationen, die für den Server bzw. Client notwendig sind, um die Nachricht zu verstehen. Zustandslosigkeit wirkt sich begünstigend auf die Skalierbarkeit eines Webservices aus. Das ist natürlich genau das was wir für unsere Apps brauchen, etwas was schnell und einfach unsere App mit Daten füttert.
Und da die Daten als XML Ausgelesen werden, können wir diese REST Services sogar Plattformunabhängig nutzen.

Hier folgt nun ein kleines Beispiel für das erstellen eines REST Service. Dieser Service wird uns entweder eine Liste von Produkten oder über Parameter ein gewünschtes Produkt zurück geben. Wir betrachten somit nur den Lesenden zugriff über "GET"

1. Schritt eins Erstellen eines WCF Service Projekt

  • Starten Sie Visual Studio (im meinem Beispiel Visual Studio 2013)
  • Erstellen Sie ein neues Projekt vom Typ WCF-Dienstanwendung
Erstellen WCF-Dienstanweisung

2. Datenquelle für die Rückgabe erstellen

  • Für diese Beispiel erstellen wir ein einfache Klasse mit dem Namen Product.cs in der wir die Eigenschaften des Produktes erfassen können.

Des weiteren fügen wir eine Singelton-Klasse Namens Products.cs hinzu, von der wir die Methoden implantieren mit den wir an die Daten kommen wollen. Hier im Beispiel wird die Liste in einer einfachen Dummy Methode erstellt. Dies kann man natürlich auch gegen einen Datenbank Operation austauschen.


3. Erstellen des Service Contract

Fügen Sie Ihrem Projekt eine neues WCF-Dienst Element Namens ProductRESTService.svc hinzu.


Sie können die nicht benötigten Klasse Service1.svc, IService1.cs und Service1.cs gerne löschen. Beim hinzufügen werden sowohl die Service Klasse ProductRESTService.svc,  als auch der Service Vertrags als IProductRESTService.cs erstellt.

Im Service Vertrags als IProductRESTService.cs implementieren wir die Methode die über den Service zur Verfügung stehen sollen. Hierbei sollte der Namespace System.ServiceModel.Web eingebunden werden.


Das wichtige hierbei ist es zu die Nutzung des WebInvoke Attributes und seiner Parameter zu verstehen.
  • Method = "GET", stellt den HTTP GET Request da.
  • ResponseFormat  = WebMessageFormat.Xml, hier können wir zwischen XML oder JSON als Rückgabe Format auswählen.
  • BodyStyle = WebMessageBodyStyle.Bare, siehe WebMessageBodyStyle-Enumeration
  • UriTemplate ="GetProductById/{id}", siehe UriTemplate und UriTemplateTable 

4. Erstellen der REST Service Klasse

In der Service Klassen ProductRESTService.svs.cs müssen wir nun noch die im Service Vertrags definierten Methoden des Interfaces IProductRESTService implementieren und mit den jeweiligen Aufrufen bestücken.



5. Konfigurieren der Service

Was jetzt noch fehlt ist die Konfiguration des Dienst in der Web.config für einen REST Dienst wird das webHTTPBinding verwendet



6. Testen des REST Dienstes

Um den Dienst zu Testen können wir ihn  über einen rechst Klick auf die ProductRESTService.svs > Im Browser anzeigen (Internet Explorer) ausführen.
Die wird uns auch die Standard Dienst Seite leiten in der die Handhabung des Dienstes für WCF Clients beschrieben wir.
Wir wollen aber REST, das einzige was wir machen müssen ist die URL zu änderten und zwar mit dem jeweiligen Befehl des Service Vertrages:

http://localhost:54964/ProductRESTService.svc/GetProductById/3
 oder

http://localhost:54964/ProductRESTService.svc/GetProductList/

Dies sollte uns das gewünschte XML zurückgeben.

Und schon ist unser einfacher REST Dienst fertig. Viel Spaß bei selbst probieren und bis zum nächsten mal.

Dienstag, 15. Juli 2014

Meine Blogs jetzt auch als App für Windows 8.1 und Windows Phone 8.1

Hallo zusammen,

hier jetzt auch mal ein Praktisches Beispiel für ein Universal-App für Windows 8.1 und Windows Phone 8.1 Apps. Ihr könnt nun meine Blogs auch über dies App folgen und seit so immer auf dem laufenden.

Ich könnt die App im Windows Store unter dem folgenden Link beziehen:

Windows 8.1

http://apps.microsoft.com/windows/app/ccs-blogreader/26d01b75-35e9-437f-8ac8-44eb8584538a

Windows Phone 8.1

http://www.windowsphone.com/s?appid=120d63f1-c3d4-4364-a1f6-0891cc93c8b6

Dienstag, 8. Juli 2014

XAML Formatieren von Bindings mit IValueConverter Interface


In vielen Fällen kommt es vor das, dass Format was man von einer Datenquelle erhält, nicht der passende Formatierung für den Anzeigezweck entspricht. Man kann natürlich die Datenquelle anpassen oder die ToString()Methoden Überschreibt. Aber die kann man auch eleganter über einen Konverter erreichen.

Um einen Konverter für eine Type zu erstellen kann man sich des Interfaces IValueConverter bedienen.

Diese Interface bedingt die Methode Convert und ConvertBack und können im XAML beim Binding der Datenquelle wie folgt verwendet werden.

< UserControl.Resources>
       <Converter:DateTimeConverter x:Key="DateTimeConverter" />
</UserControl.Resources>

<GridViewColumn
EntryDateMember ="{Binding Path=EntryDate, Converter={StaticResource DateTimeConverter}}" />
Die zugehörige C# Klasse kann mit folgendem Listing verwendet werden.
public class DateTimeConverter : IValueConverter
{
     public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null)
        {
            return ((DateTime)value).ToString("dd.MM.yyyy");
        }
        else
        {
            return String.Empty
        }

    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
      return DateTime.Parse(value.ToString());
   }
}



Im diesem Beispiel wird das Entrittsdatum im Format “dd.MM.yyyy” zurückgegen. Man kann diese Konverter Klassen auf Bedingungsprüfungen verwenden, um Beispielsweise die Darstellung des Textes zu verändern über Abhängigkeiten der Datenquelle.
Im Beispiel wird die Schriftdicke durch einen Booleschen Wert bestimmt.

public class BooleanToFontWieghtConverter
             : IValueConverter
{
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            string ret = "Normal";
            if((bool)value)
            {
                ret = "ExtraBold";
            }
            return ret;
         }

       public object ConvertBack(object value, Type targetType, object parameter, string language)
      {
            return value.ToString().Equals("ExtraBold");
      }
}

Für weiter Informationen siehe MSDN IValueConverter-Schnittstelle

Mittwoch, 25. Juni 2014

Speicher einer Generischen Liste in den Application Storage


Im letzten Post haben ich gezeigt wie wir eine Klasse in ein XML Serializieren und Deserializieren können, nun wollen wir unsere erzeugten XML Daten auch als StorageFile im Lokalen Applikation Verzeichnis Speicher.
 //Vergeben des Dateinamen
 string fileName = „TrackinData“;

 //Erzeugen des XML
 string localData = ObjectSerializer>.ToXml(list);
 if (!string.IsNullOrEmpty(localData))
 {
    //Erstellen der Datei und Speichern im Lokalen Verzeichnis
    StorageFile localFile = await ApplicationData.Current.LocalFolder.CreateFileAsync(string.Format("{0}.xml", fileName), CreationCollisionOption.ReplaceExisting);
    await FileIO.WriteTextAsync(localFile, localData);
 }

Um die Daten wieder zu laden muss die Entsprechende Datei aus dem Lokalen Applikation Verzeichnis geladen werden und das erhaltene XML wieder Deserializieren werden.

  StorageFile localFile;
  ObservableCollection itemCollection = new ObservableCollection();
  Try
  {
      localFile = await ApplicationData.Current.LocalFolder.GetFileAsync("localData.xml");
  }
  catch (FileNotFoundException)
  {
      localFile = null;
  }
  
  if (localFile != null)
  {
      string localData = await FileIO.ReadTextAsync(localFile);
      itemCollection = ObjectSerializer>.FromXml(localData);
  }

Serializing / Deserializing objects in und aus einem XML


Zum Speicher und Austausch von Daten und Einstellungen greife ich gerne auf XML-Dateien zurück. Um diese zu erstellen habe ich bei http://www.getcodesamples.com/ eine recht elegante Lösung gefunden.

Mit Hilfe der ObjectSerializer Klasse kann man ganz einfach eine Klasse oder eine generische Liste in ein XML umwandeln.

ObjectSerializer Klasse
    internal static class ObjectSerializer
    {
        // Serialize to xml 
        public static string ToXml(T value)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            StringBuilder stringBuilder = new StringBuilder();
            XmlWriterSettings settings = new XmlWriterSettings()
            {
                Indent = true,
                OmitXmlDeclaration = true,
            };
            using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings))
            {
                serializer.Serialize(xmlWriter, value);
            }
            return stringBuilder.ToString();
        }

        // Deserialize from xml 
        public static T FromXml(string xml)
        {
            T value;
            object empty = null;
            value = (T)empty;
            Try
            {
                XmlSerializer serializer = new XmlSerializer(typeof(T));
                using (StringReader stringReader = new StringReader(xml))
                {
                    object deserialized = serializer.Deserialize(stringReader);
                    value = (T)deserialized;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);               
            }

Hierfür müssen in den zu Serializierenden Klassen XMLRootElemet Attribute gesetzt werden. Damit diese als root gekennzeichnet wird.


    [XmlRoot(ElementName = " XMLExampleClass ")]
    public class XMLExampleClass
    {

Entsprechend kann dann die Klasse unter Verwendung der Methode ToXml in ein XML Serializiert werde und mit der Methode FromXml wieder Deserializiert.

    XMLExampleClass instance = new XMLExampleClass();
    instance.Id = 0;
    instance.Name = “Test”

    // Serialize to xml 
    string localXml = ObjectSerializer.ToXml(instance);

   // Deserialize from xml
   XMLExampleClass instanceFromXml = ObjectSerializer.FromXml(localXml);

Dienstag, 10. Juni 2014

Verwende von AX2012 Query Service aus Windows Store App


Mit Hilfe des AX2012 Web Service QueryService können direkt aus einer App das Ergebnis eines Query des AOT verwendet werden ( siehe hierzu: http://technet.microsoft.com/en-us/library/gg847959.aspx ) leider habe ich in den ganzen Beispielen die es im TechLab oder MSDN gibt keins gefunden was auch für eine Windows Store App und Windows 8.1 funktioniert. Jedoch konnte man Auch hier schnell die Funktionen umschrieben um es unter Windows 8.1 als Store App zu Verwendern.

Hier ein kleines Beispiel mit deren Hilfe man zu Beispiel Bestands Informationen ermittelt kann. Ich verwende hierfür eine Hilfsklasse DynamicAXConnector mit deren Hilfe ich auch dynamisch die Endpoint Adressen der Web Service steuern kann.

Listing DynamicAXConnector:

namespace DynamicAXConnectorExampel
{
    public class DynamicAXConnector
         : IDisposable
    {
        #region Fields

        private string username = string.Empty;
        private string domainName = string.Empty;
        private string password = string.Empty;
        private string aosServer = string.Empty;
        private int port = 0;
        private WCFAdapters adapter;
        private AXQueryService.QueryServiceClient queryClient;

        #endregion

        #region Constructor

        public DynamicAXConnector(string username, string password, string aosServer, int wcfPort, WCFAdapters adapter)
        {
            this.aosServer = aosServer;
            this.adapter = adapter;
            this.port = wcfPort;
            this.password = password;

            string[] dom = username.Split('\\');
            if (dom.Length > 0)
            {
                this.domainName = dom[0];
                this.username = dom[1];
            }
        }

        #endregion

        #region Methods

        public async Task> GetInventSummary()
        {
            ObservableCollection ret = new ObservableCollection();
            try
            {
                EndpointAddress adr = this.NewEndPointAddress("QueryService", "DynamicsAx/Services/QueryService");
                this.queryClient = new AXQueryService.QueryServiceClient(AXQueryService.QueryServiceClient.EndpointConfiguration.QueryServiceEndpoint, adr);
                this.queryClient.ClientCredentials.Windows.ClientCredential.Domain = this.domainName;
                this.queryClient.ClientCredentials.Windows.ClientCredential.UserName = this.username;
                this.queryClient.ClientCredentials.Windows.ClientCredential.Password = this.password;

                AXQueryService.ExecuteStaticQueryRequest req = new ExecuteStaticQueryRequest();
                req.queryName = "ANXWin8InventSummary";
                req.paging = null;
                AXQueryService.ExecuteStaticQueryResponse res = await this.queryClient.ExecuteStaticQueryAsync(req);
                List list = res.ExecuteStaticQueryResult.Nodes;
                ret = BoInventSummary.FetchDataByQueryService(list, "ANXWin8InventSummary");              
            }
            catch (Exception)
            {
                throw;
            }

            return ret;
        }     

        public void Dispose()
        {
            this.client = null;
            this.response = null;
        }

        private EndpointAddress NewEndPointAddress(string addressHeader, string serviceName)
        {
            string uri = string.Empty;
            if (this.port == 0)
            {
                this.port = 8101;
            }

            switch (adapter)
            {
                case WCFAdapters.NetTcp:
                    uri = string.Format("net.tcp://{0}:{1}/{2}", this.aosServer, (this.port + 100).ToString(), serviceName);
                    break;
                case WCFAdapters.Http:
                    uri = string.Format("http://{0}:{1}/{2}", this.aosServer, (this.port + 100).ToString(), serviceName);
                    break;
                default:
                    break;
            }

            EndpointIdentity id = new SpnEndpointIdentity("");
            AddressHeader addressHeader1 = AddressHeader.CreateAddressHeader(addressHeader, uri, 1);
            AddressHeader[] addressHeaders = new AddressHeader[1] { addressHeader1 };

            return new EndpointAddress(new Uri(uri), id, addressHeaders);
        }

        #endregion
    }
}

In der Methode BoInventSummary.FetchDataByQueryService(list, ANXWin8InventSummary") wird das Objekt für die spätere DataSource aufbereitet und mit Werten gefütterte.      


 public static ObservableCollection FetchDataByQueryService(List list, string queryName)
        {
            ObservableCollection result = new ObservableCollection();
            foreach (XElement item in list.Elements(queryName).Elements("InventTable"))
            {
                BoInventSummary bob = new BoInventSummary();
                bob.ItemId = (string)item.Element("ItemId").Value;
                bob.Name = (string)item.Element("NameAlias").Value;
                foreach (XElement unit in list.Elements(queryName).Elements("InventTableModule"))
                {
                    bob.UnitId = (string)unit.Element("UnitId").Value;
                    break;
                }
                
                decimal physicalInvent = 0;
                decimal reservPhysical = 0;
                
                foreach (XElement sum in list.Elements(queryName).Elements("InventSum"))
                {
                    if (((string)sum.Element("ItemId").Value).Equals((string)item.Element("ItemId").Value))
                    {
                        physicalInvent += Convert.ToDecimal(sum.Element("PhysicalInvent").Value);
                        reservPhysical += Convert.ToDecimal(sum.Element("ReservPhysical").Value);
                    }
                }

                bob.PhysicalInvent = physicalInvent;
                .
                .
                .
                bob.ReservPhysical = reservPhysical;              
                result.Add(bob);
            }

            return result;
        }

Donnerstag, 27. März 2014

Datenschutzrichtlinien zur Windows Settings Charm hinzufügen.

Wie es so schon in den App-Zertifizierungsanforderungen für den Windows Store heißt:

4.1.1 Ihre App muss über eine Datenschutzrichtlinie verfügen, sofern sie netzwerkfähig ist.

Sofern Ihre App über die technischen Möglichkeiten zur Datenübertragung verfügt, benötigen Sie eine Datenschutzrichtlinie. Sie müssen auf der Seite Beschreibung der App sowie in den App-Einstellungen (angezeigt über den Windows-Charm "Einstellungen") Zugriff auf Ihre Datenschutzrichtlinie ermöglichen.
http://msdn.microsoft.com/de-DE/library/windows/apps/hh694083.aspx

Hierfür sollte man seine Datenschutzrichtlinie als eigene Webseite bereitzustellen. Der Vorteil der eigenen Seite ist es das man so auch gleich eventuelle Lokalisierungen für mehrere Sprachen selbst hinterlegen kann.

Anschließend muss man in seiner App die App.xlam.cs um folgende Einträge ergänzen:

protected override void OnWindowCreated(WindowCreatedEventArgs args) 
        {

            SettingsPane.GetForCurrentView().CommandsRequested += (s, e) => e.Request.ApplicationCommands.Add(new SettingsCommand("privacypolicy", "Datenschutzrichtlinien“, ShowPrivacyPolicy));
         }

private async void ShowPrivacyPolicy(IUICommand c)
        {
            Uri uri = new Uri("http://www.MyPrivacyPolicy.org/privacyPolicyWin8App/“);
            await Windows.System.Launcher.LaunchUriAsync(uri);
        }
Sowie Ihr diese Zeilen in euer App integriert habt sollten Eure App-Einstellungen in der Windows Setting Charm den Punkt „Datenschutzrichtlinien“ enthalten.

Windows Phone 8 Gestengesteuerte Seitenwechsel

Im ersten Moment war es mir ein Rätzel wie ich einen simplen Seiten Aufruf, für Windows Phone 8 gestengesteuert implementieren sollte. Viel Beispiele und Implementieren Basieren auf dem Silverlight Toolkit für Windows Phone.
 Leider ist diese Toolkit für Windows Phone 8 Obsolet, ich habe lange gesucht bis ich auf folgenden Link gestoßen bin.


http://win8rants.wordpress.com/2012/11/14/navigation-with-swipe-on-windows-phone-8/


Wie immer sind die einfachsten Lösungen meist die effektivste, hier wir entsprechend auf das ManipulationCompleted Event des jeweiligen Controls reagiert. Entsprechend der Bewegung Richtung kann man seinen eigenen Seiten Aufruf platzieren.private void LayoutRoot_ManipulationCompleted(object sender, System.Windows.Input.ManipulationCompletedEventArgs e)
        {
            double dY = e.TotalManipulation.Translation.Y;
            double dX = e.TotalManipulation.Translation.X;

            if (Math.Abs(dY) > Math.Abs(dX))
            {
                // Vertical
            }
            else
            {
                // Horizontal
                if (dX < 0)
                {
                    NavigationService.Navigate(new Uri("/SecondPage.xaml", UriKind.Relative));
                }
            }
        }