Zoek

Uitgebreid zoeken Artikelen per auteur

  

NAV+VSTO+WPF+LINQ=OBA

NAV+VSTO+WPF+LINQ=OBA

Een simpel sommetje voor een geavanceerde oplossing …

Er zijn meer dan 500 miljoen Office gebruikers. Dat maakt Office (Word, Excel, PowerPoint, etc.) tot een van de meeste gebruikte client applicaties die Microsoft te bieden heeft. Daarom is een Office Business Application (OBA) een logische oplossing om business data in de context van de “everyday-work-environment” te brengen. Een OBA bestaat uit vier componenten; de LOB applicatie (bijv. SAP, PeopleSoft, of, zoals in dit artikel, MS Dynamics NAV), een aangepaste Office client applicatie (hier gebruik ik MS Word 2007), Microsoft Office SharePoint Server 2007 (MOSS2007) en de services die deze drie componenten aan elkaar knopen tot een bruikbare oplossing.

Office Business Applications (OBA’s) zijn oplossingen die bestaan uit services, tools en servers die aangeboden worden in het Microsoft 2007 Office system en kenmerken zich door de integratie met line of business (LOB) applicaties

Microsoft Dynamics NAV

Navision (NAV) is een business management solution voor het MKB. Met functionaliteit voor de financiële verslaglegging, voorraad beheer, service management, human resource management, etc. is het een volledig ERP systeem dat de “standaard” bedrijfsprocessen ondersteunt. Vele ISV’s (Independent Software Vendors) bieden diverse add-in’s op NAV die branch-specifieke bedrijfsprocessen ondersteunen.

NAV kent een eigen ontwikkelomgeving, C/SIDE (client/server integrated development environment).  Het NAV team van Microsoft heeft hiervoor een eigen programmeertaal ontwikkeld. De NAV programmeertaal voor het schrijven van “eigen” business rules heet C/AL (client/application language). Hoewel deze taal geschreven is in C++, vind je daarvan maar weinig terug. NAV heeft standaard een integratie met Office Word en Excel 2003 op basis van WordML. Wanneer een gebruiker een Word document wil genereren met informatie uit NAV, zal hij van te voren moeten weten welke informatie hij wil gebruiken, en de daarbij behorende template in NAV moeten kiezen of maken. Om nieuwe templates aan te kunnen bieden zal er een ontwikkelaar met gedegen XML kennis aan te pas moeten komen.

Fig. 1: Microsoft Dynamics NAV

Waarschijnlijk komt eind dit jaar de nieuwe versie van MS Dynamics NAV uit (NAV 2009). Deze zal een 3-tier architectuur hebben met een webservice laag welke je kan gebruiken om Add-On’s op Navision te schrijven in .NET. Dit geeft ook veel mogelijkheden om OBA’s te schrijven voor NAV. Het weerhoudt ons echter niet om alvast te experimenteren met OBA voor NAV.

Het probleem

Stel dat een account manager een brief wil schrijven naar een van zijn klanten. Als hij een gebruiker van NAV is, kan hij kiezen voor een standaard brief-template uit NAV. De naam en adresgegevens van de klant staat netjes in de gegenereerde brief. De account manager begint met het schrijven van zijn brief en bedenkt dat hij een nieuw product onder de aandacht van zijn klant wil brengen.

Wil hij de juiste informatie in het document hebben, zal hij NAV moeten openen, het product op moeten zoeken en de informatie moeten knippen en plakken. Maar voor hetzelfde geld is de accountmanager geen gebruiker van NAV, en ook dan wil hij gebruik maken van de informatie die in NAV staat, zoals klantgegevens en productinformatie.

In dit artikel laat ik zien hoe je deze productinformatie in een document kunt plaatsen zonder dat je als gebruiker Word hoeft te verlaten. Hiervoor wordt een zoekfunctie gemaakt in de taskpane van Word met behulp van VSTO. In dit voorbeeld maken we er een document-level add-in van. Hierdoor is de functionaliteit alleen beschikbaar indien er voor deze template gekozen wordt. Je kunt er ook een application add-in van maken; dan is de functionaliteit beschikbaar wanneer je Word opstart. Vanuit een development point of view zitten daar ook verschillen in. Deze verschillen zullen ook de revue passeren.

De zoekfunctie zal met behulp van LINQ to SQL een resultaat geven. Om het zoekresultaat er aantrekkelijk uit te laten zien laten we de foto’s van de producten zien waarbij de naam en de prijs van het product wordt getoond. Door gebruik te maken van een fish-eye worden foto’s vergroot bij een mouse-over. Dit is mogelijk door gebruik te maken van Windows Presentation Foundation (WPF).

Fig. 2: Het eindresultaat: OBA in action

Hoe krijg je dit voor elkaar? In figuur 3 wordt de architectuur getoond van de document-level customization die in dit artikel verder wordt beschreven.

Fig. 3: Architectuur van document-level customization met WPF en LINQ

VSTO Word 2007 document

We beginnen met het creëren van een Word-template Dit hebben we vast allemaal al eens gedaan, dus dat heeft geen uitleg nodig! Open Word en creëer een document met de juiste opmaak.

In deze OBA wordt via het double-click event op een item de geselecteerde productinformatie op de plek waar de cursor staat geplaatst. Het is ook mogelijk om de informatie op een bepaalde, vaste plek in het document te plaatsen door gebruik te maken van Content Controls. Content Controls hebben het voordeel dat er logica voor kan worden geschreven, zoals het plaatsen van een lock zodat de content read-only wordt.

TIP: Gebruik Content Controls indien je de data op vaste plekken in het document wilt plaatsen en logica wilt schrijven op dat stuk data. De Content Controls vind je op de Developer Tab in de Ribbon. Mocht deze niet zichtbaar zijn klik dan op de Office Button (links bovenin), kies voor Word Options, en vink de “Show Developers tab in Ribbon” aan.

TIP: Content Controls kun je ook gebruiken om MOSS 2007 document library columns (metadata) te plaatsen en aan te passen binnen het document.

Nu begint het leuke werk! Om maatwerk op document niveau te schrijven maak je een Word 2007 document project aan in Visual studio. Klik op File – New – Project; in het Project scherm kies je voor Visual C# (of VB.NET) Office – 2007 – Word 2007 document.

Daarna krijg je een scherm waarin je een keuze krijgt om een nieuw document te creëren of een bestaand document te gebruiken. Omdat we de opmaak al in een document hebben gezet, kiezen we voor het demo document.

Wanneer het project is geladen zie je binnen de IDE van Visual Studio het document in een Word omgeving. Hierdoor werkt Word en het document als een designer binnen Visual Studio.

TaskPane vs ActionsPane

We beginnen met het toevoegen van een Windows.Forms.UserControl. Deze UserControl (met ID UserControl1) zal op de TaskPane geplaatst worden. Afhankelijk of het een document-level customization is of een application-level add-in is, creëer je resp. een ActionsPane of een TaskPane.

Een ActionsPane is specifiek voor een document en wordt “gehost” binnen de Office task pane samen met andere ActionsPanes zoals de wel bekende “Styles and Formatting”-action pane. ActionsPanes kun je dan ook alleen in document-level applicatie aanmaken.

Voor een Word AddIn (application-level) is de code als volgt:

Private void ThisAddIn_Startup(
  object sender, System.EventArgs e)
{
  Microsoft.Office.Tools.CustomTaskPane myTaskPane =
    CustomTaskPanes.Add(
      new UserControl1(), “Search products”);
  
   // set taskpane vertical at the bottom
   myTaskPane.DockPosition =
     MsoDockPosition.msoCTPDockPositionBottom;
   // don’t forget to set visibility, otherwise
   // you will not see it
   myTaskPane.Visible = true;
}

Listing 1: TaskPane in Application Level AddIn

Voor een document level customization ziet de code er als volgt uit:

Private void ThisDocument_Startup(
  object sender, System.EventArgs e)
{
  this.ActionsPane.Controls.Add(new UserControl1());
  
  // set taskpane vertical at the bottom
  this.CommandBars[“Task Pane”].Position =
    Microsoft.Office.Core.MsoBarPosition.msoBarBottom;
  
}

Listing 2: ActionsPane in Document-Level customization

Een gebruiker kan een TaskPane afsluiten. Daarom zul je functionaliteit moeten schrijven die het mogelijk maakt om de TaskPane weer te tonen. Deze functionaliteit kun je het beste in de Ribbon plaatsen, maar het synchroon houden van de Ribbon met de TaskPane is geen eenvoudige opgave.

TIP: In het artikel “Synchroniseren van de Ribbon met een TaskPane in Word 2007” van Maurice de Beijer wordt uitgelegd hoe je de interactie tussen de TaskPane en de Ribbon goed kunt laten werken (http://www.sdn.nl/Default.aspx?tabid=50&itemid=2525).

Windows Presentation Foundation

Het gebruik van WPF geeft een verbeterde user interface en biedt een geavanceerde wijze om informatie te tonen. Op het internet kun je veel WPF controls vinden. De fish-eye die ik hier gebruik is gedownload van Code Project (http://www.codeproject.com/KB/WPF/).

In dit voorbeeld zijn twee WPF UserControls gebruikt, een voor de functionaliteit (zoek textbox en button) en een voor het resultaat.

Een WPF UserControl bestaat uit een CodeBehind-file en een XAML-file. Het is aan te bevelen om Expression Blend te gebruiken voor het design van een WPF UserControl. In Expression Blend kun je het VS project laden en je controls opmaken. Op de achtergrond maakt hij de XAML-code voor je aan.

In de WPF UserControl, UserControl2, staat in het Grid een StackPanel met een tekstbox en een button om de zoekfunctie te starten, en een ListBox die op basis van de FishEye Panel de buttons, zoals ze in UserControl3, worden opgesteld toont. De FishEye is een class die overerft van de base class Panel. De ListBox gebruikt als ItemsPanelTemplate de FishEyePanel.

...

 
   

Listing 3: FishEye panel als template voor de listbox panel

De XAML van het grid ziet er als volgt uit:

...

 
 
           Relief="0.39" Smoothness="0.54" LightAngle="134"/>
 

 
 
          StartPoint="0.024,0.005">
      
      
   

 

 
 
          BorderBrush="#FF26394A" Background="#5FFFFFFF"/>
   
 

  
      HorizontalAlignment="Stretch"
    VerticalAlignment="Stretch" BorderThickness="2,2,2,2"
    BorderBrush="#FF23403A"
    HorizontalContentAlignment="Stretch">
    
              StartPoint="0.5,0">
        
        
      

    

    
      
                  AnimationMilliseconds="150" ScaleToFit="true"/>
    

    

   
     
        
     

   

  


...

Listing 4: XAML van WPF UserControl UserControl2

In de WPF Usercontrol UserControl3 worden de image buttons geplaats waar een overlay van de tekst wordt getoond. De plaatjes staan op een fileshare en hebben dezelfde naam als de naam van het produkt in de NAV database.

...
  BorderBrush="#4C000000" BorderThickness="1,1,1,1"
  Width="100" Height="50">
 
          "{Binding Description,Converter={StaticResource
       BinaryToBitmapConverter}}" Stretch="UniformToFill"/>
 

 
   
              Text="{Binding Path=Description}" Margin="5,5,5,5">
       
         
       

     

              Text="{Binding Path=Unit_Price,
          Converter={StaticResource DoubleToCurrency}}"
        Margin="5,2,5,5">
       
         
       

     

   

 


...

Listing 5: XAML Border van WPF UserControl UserControl3

LINQ en LambdaExpressions

De data komt uit NAV. In deze demo is gekozen om via LINQ to SQL een koppeling te maken met de NAV demo database Contoso. Het is niet aan te raden om direct de NAV database te benaderen. Hierdoor verlies je de business rules die wellicht op de data zitten. Voor deze demo is wel een directe koppeling gemaakt. Hiermee wil ik alleen aantonen hoe eenvoudig het gebruik van LINQ to SQL is.

De informatie die we willen gebruiken staat in de NAV Items tabel. Voeg een LINQ to SQL class toe aan het project (“DataContoso”). Daarna maak je een dataconnectie met de NAV demo database en sleep je de Contoso$Item tabel op de design view van het DBML-file. Na een build worden een layout en designer.cs file aangepast, en wordt van de tabellen en relaties een object model opgebouwd. 

De databinding in de WPF UserControl XAML is op basis van een LINQ query die in UserControl2 wordt opgebouwd.

private void LoadContosoItems(string SearchText)
{
  var DB = new DataContorsoDataContext();
  var foundItems =
    from item in DB.Contoso_Items
    where (item.Description.Contains(SearchText))
    select item;
  Items.ItemsSource = foundItems;
}

Listing 6: Lambda Expression op de DataContoso LINQ to SQL class

In de WPF UserControl XAML zie je ook twee StaticResources staan als Converter van de data. Voor beide Converters bestaan een dataconversie die tijdens de databinding aangeroepen wordt en een class, met een IValueConverter interface, die een object teruggeeft. Hierdoor kun je de data uit de database manipuleren.

class DoubleToCurrency : IValueConverter
{
  public object Convert(object value, Type targetType,
    object parameter,
    System.Globalization.CultureInfo culture)
  {
    return String.Format("{0:C}", value);
  }
  
  public object ConvertBack(object value, Type targetType,
    object parameter,  
    System.Globalization.CultureInfo culture)
  {
    throw new NotImplementedException();
  }
}

Listing 7: Converter voor de tariefinformatie

Nu hebben we de functionaliteit voor de zoekfunctie en de databinding van het resultaat.

De volgende stap is om op een double-click op een geselecteerd item de informatie van het product in het Word-document te plaatsen. Hiervoor maken we eerst een functie die controleert of het Item is geselecteerd en vervolgens geeft het de betreffende informatie door naar de functie die de tekst plaatst op de plek waar de cursor staat.

private void InsertSelectedItemInWord()
{
  if (Items.SelectedItem != null)
  {
    var item = (Contoso_Item)Items.SelectedItem;

    InsertTextAtSelection
    (
      String.Format("Product: {0} \r\n Price:{1}",
        item.Description, item.Unit_Price)
    );         
  }
}

Voor een document-level customization kan dit met de volgende code:

private void InsertTextAtSelection(string text)
{
  Word.Selection currentSelection =    
    Globals.ThisDocument.ThisApplication.Selection;
  currentSelection.InsertAfter(text);
}

Let op: Het kan zijn dat de gebruiker de “Insert” functie aan heeft staan. Hierdoor zou je tekst kunnen overschrijven, wat niet de bedoeling is. Door de Application.Options.Overtype op false te zetten wordt de tekst toegevoegd. Het is wel raadzaam om OverType daarna weer terug te zetten zoals de gebruiker dit had ingesteld.

Nu het laatste stukje … de functies moeten aangeroepen worden op het event. Wanneer op de search button wordt geklikt, dient de zoekfunctie LoadContosoItems aangeroepen te worden. En op het double-click event wordt de functie InsertSelectedItemInWord aangeroepen, zodat de informatie over het product toegevoegd wordt aan het document.

public UserControl2()
{
  InitializeComponent();
  SearchButton.Click += (sender,e) =>
  {
    LoadContosoItems(SearchBox.Text);
  };

  Items.MouseDoubleClick += (sender,e) =>
  {
    InsertSelectedItemInWord();
  };
}

Tenslotte …

Je zou dit nog verder uit kunnen breiden met een koppeling met een MOSS document library, maar dat is voor een volgend artikel. Ook heb ik niet stilgestaan bij de deployment van een VSTO-applicatie. Hiervoor verwijs ik je graag naar de Office Developer Center waar twee artikelen staan over de deployment van VSTO-applicaties met Windows Installer.

In dit artikel wilde ik laten zien dat je met VSTO, WPF en LINQ op basis van informatie uit Navision een eenvoudige maar zeer bruikbare OBA kan maken. De complete code is te downloaden vanaf de SDN site.

Met dank aan Robertjan Tuit, die me geholpen heeft met het WPF gedeelte.

Meer lezen

De sources die bij dit artikel horen kun je downloaden via Wanrooij_OBA_SRC.zip.

Geef feedback:
Verzend Commentaar