Archive

Archive for November, 2011

Silverlight for Windows Phone 7: How to use ZXing 7.1 in Silverlight

November 22nd, 2011 Admin 16 comments

    ZXing is an open source library for processing of multi-format 1D/2D barcode images. Using a built-in camera on your mobile phone you can scan a barcode (i.e. make a picture containing the barcode) and decode it by exploiting ZXing library. As the result you may have a recognized and decoded string value of barcode. The library supports such formats as traditional UPC-A and UPC-E, EAN-8 and EAN-13, trendy QR Code and etc. The library initially is implemented in Java, but there is a port to C#. However, if you decide to use this port in Silverlight Windows Phone 7 project, you’ll run into some problems with compatibility as Silverlight provides a “limited” C#. To be more specific, the C# port of ZXing actively utilizes such non-generic collections as Hashtable and ArrayList from System.Collections namespace, but almost everything under this namespace was completely removed from Silverlight. For details, please read the following article – Non-Generic Collections to be Removed from Silverlight. Hashtable and ArrayList can be replaced with System.Collections.Generic.Dictionary<Object, Object> and System.Collections.Generic.List<Object> respectively. Instances are received as System.Collections.ArrayList.Synchronized() can be smoothly turned into usual instances, that are not thread safe, because multithreading isn’t used in the library. If it’s necessary to provide thread safety, it can be done outside the library at the stage of instantiating of and accessing to the barcode readers contained in ZXing library. Just follow the rule – each thread must allocate its own object.

Furthermore, the ZXing C# port uses Color, Rectangle and Bitmap classes from System.Drawing, which is unavailable in Silverlight as well. As the replacement we can consider System.Windows.Media.Color, System.Windows.Rect and System.Windows.Media.Imaging.WriteableBitmap respectively, but they aren’t absolutely compatible with the preceding ones and some kind of adjustment code is required.

System.SerializableAttribute isn’t accessible too. We can just get rid of it. A number of other small changes are required as well.

As the result, I made a Silverlight port of ZXing 7.1 from C# port. You can download it here or here. Where the original code was replaced, I left commentaries kind of “here was .net follower” :)

A few words of how to use it. In my application I try reading and decoding barcodes of UPC-A and UPC-E formats. For my needs I implemented a very simple wrapper around ZXing libraryBarcodeHelper, probably, it may be useful for somebody else (the helper is in downloadable solution). The wrapper is shown below. In the method TryToRecognizeBarcode you can see how interaction with ZXing is usually being put into practice.
Update: other methods, TryToRecognizeQRcode and TryToRecognizeDataMatrix were added to the BarcodeHelper. They allow to recognize QR and Data Matrix codes, respectively.

using System;
using System.Collections.Generic;
using System.Windows.Media.Imaging;

using com.google.zxing;

namespace Helpers
{
    public static class BarcodeHelper
    {
        public static bool TryToRecognizeBarcode(WriteableBitmap wb, out string barCode)
        {
            // set some recognition settings
            var zxhints = new Dictionary<object, object>()
                {
                    { DecodeHintType.TRY_HARDER, true },
                    { DecodeHintType.POSSIBLE_FORMATS, new List<Object>() { BarcodeFormat.UPC_A, BarcodeFormat.UPC_E } }
                };

            // create reader instance
            var reader = new com.google.zxing.oned.MultiFormatUPCEANReader(zxhints);
            return TryToRecognize(wb, reader, zxhints, out barCode);
        }

        public static bool TryToRecognizeQRcode(WriteableBitmap wb, out string qrCode)
        {
            // create reader instance
            var reader = new com.google.zxing.qrcode.QRCodeReader();
            return TryToRecognize(wb, reader, null, out qrCode);
        }

        public static bool TryToRecognizeDataMatrix(WriteableBitmap wb, out string dtm)
        {
            // create reader instance
            var reader = new com.google.zxing.datamatrix.DataMatrixReader();
            return TryToRecognize(wb, reader, null, out dtm);
        }

        private static bool TryToRecognize(WriteableBitmap wb, Reader reader, Dictionary<object, object> zxhints, out string output)
        {
            bool res = false;
            output = null;
            try
            {
                var luminiance = new RGBLuminanceSource(wb, wb.PixelWidth, wb.PixelHeight);
                var binarizer  = new com.google.zxing.common.HybridBinarizer(luminiance);
                var binBitmap  = new com.google.zxing.BinaryBitmap(binarizer);

                // recognize
                var results = reader.decode(binBitmap, zxhints); // exception will be thrown if reader cannot decode image.

                output = results.Text;
                res = true;
            }
            catch (Exception ex)
            {
            }
            return res;
        }
    }
}

Below is the sample code of usage taken from TestApplication (see downloadable solution):

WriteableBitmap wbmp = new WriteableBitmap(barcodeSample.Source as BitmapImage);
string recognizedOutput = string.Empty;
if (BarcodeHelper.TryToRecognizeBarcode(wbmp, out recognizedOutput))
// if (BarcodeHelper.TryToRecognizeQRcode(wbmp, out recognizedOutput))
// if (BarcodeHelper.TryToRecognizeDataMatrix(wbmp, out recognizedOutput))
    resultTexBlock.Text = recognizedOutput;
else
    resultTexBlock.Text = "Unrecognizable barcode!";

Hint: if the recognition failed on an image with a high resolution and, apparently, a big size, try reducing the image size in two times step by step until the image is recognized.

The following part of another simple test application demonstrates how to use camera to make picture containing barcode and recognize it then. You can find this small application in downloadable solution as well.

using System;
using System.Windows;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Tasks;
using System.Windows.Media.Imaging;
using System.IO;
using Helpers;

namespace WindowsPhoneApplication2
{
    public partial class MainPage : PhoneApplicationPage
    {
        private CameraCaptureTask camera = new CameraCaptureTask();

        // Constructor
        public MainPage()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            button1.IsEnabled = false;            

            camera.Completed += new EventHandler<PhotoResult>(camera_Completed);
            camera.Show();
        }

        void camera_Completed(object sender, PhotoResult e)
        {
            camera.Completed -= new EventHandler<PhotoResult>(camera_Completed);

            if (e.TaskResult == TaskResult.OK)
            {
                BitmapImage bmp = new BitmapImage();
                bmp.CreateOptions = BitmapCreateOptions.None; //Don't delay creation
                bmp.SetSource(e.ChosenPhoto);

                resultTexBlock.Text = string.Empty;

                WriteableBitmap wbmp = new WriteableBitmap(bmp);
                string recognizedBarcode = string.Empty;
                if (BarcodeHelper.TryToRecognizeBarcode(wbmp, out recognizedBarcode))
                    resultTexBlock.Text = recognizedBarcode;
                else
                    resultTexBlock.Text = "Unrecognizable barcode!";
            }            

            button1.IsEnabled = true;
        }
    }
}

If you find a bug, please, leave an appropriate comment here and the issue will be fixed in a timely manner.

Download ZXing7_1_Port.zip (Visual Studio 2010 Solution)

ps There is an alternative – Windows Phone 7 Silverlight ZXing Barcode Scanning Library, but, as I see, the version of ZXing they use isn’t up-to-date.

Related posts:

SharePoint: Brief introduction to Business Data Catalog (BDC)

November 4th, 2011 Admin No comments

    Business Data Catalog (BDC) allows to integrate business data from external business applications into SharePoint application. BDC has built-in support for displaying data from such data sources as databases and web services. In other words, if the business application is a database or comprises a web service to emit data, its data can be easily incorporated into SharePoint application. If some business application isn’t database and doesn’t have any proper web services, you always can develop your own web service to provide access to the business application’s data.

Once the external business application has been registered within Business Data Catalog, its data can be used in Business Data Web Parts, Business Data Column, Search, User profile and other custom solutions.

BDC provides access to the underlying data sources with a declarative Metadata Model. The Metadata Model allows to describe a simplified and consistent client object model over any business application. What is Metadata? Metadata describes the business applications’ APIs. For each business application, Metadata defines the business entities that the business application interacts with and the methods available in the business application. The Business Data Catalog stores the metadata in the metadata repository. So, using the Metadata Model, developer describes the API of the business application in a xml-file, so called Application Definition File. Then SharePoint administrator imports the Application Definition into the Business Data Catalog to register Metadata the file contains. After that the data from business application becomes available. The schematic structure of Application Definition File is shown below:

<LobSystem Type="..." Name="...">
  <LobSystemInstances>
    <LobSystemInstance Name="...">
      <Properties>
        ...
      </Properties>
    </LobSystemInstance>
  </LobSystemInstances>
  <Entities>
    <Entity Name="...">
      <Properties>
        ...
      </Properties>
      <Identifiers>
        ...
      </Identifiers>
      <Methods>
        ...
      </Methods>
    </Entity>
  </Entities>
</LobSystem>

*Note: some attributes are skipped

The LobSystem is a container for all of the other objects in the Metadata object model, and the root node in the Application Definition File. Each LobSystem object has an unique name and is of a certain type: either Database or Web Service. LobSystem object contains LobSystemInstances and Entities. The LobSystemInstance contains properties that define the authentication of the connection and the provider, which is used to connect to the external data source. Entity defines a type of returned business data object, it contains identifiers, methods and actions. Also Entity can have other related entities associated with them.

In terms of SharePoint Central Administration, the LobSystem is a Business Data Catalog Application or BDC Application, while LobSystemInstance can be named Business Data Catalog Application Instance or BDC Application Instance. Entity in SharePoint Central Administration and in Metadata Model means the same.

BDC Metadata Model Schema

All BDC Applications registered in Business Data Catalog can be viewed through the Central Administration: open SharePoint 3.0 Central Administration, click the name of Shared Service Provider (in my case it’s SharedServices1), then click the View applications link in the Business Data Catalog section. To create Application Definition File and import it to the Business Data Catalog, read the very good article written by Tobias Zimmergren.

To work with BDC programmatically you should use types from the following namespaces:

using Microsoft.Office.Server;
using Microsoft.Office.Server.ApplicationRegistry.MetadataModel;
using Microsoft.Office.Server.ApplicationRegistry.Infrastructure;
using Microsoft.Office.Server.ApplicationRegistry.Runtime;

You mainly will use the next interfaces and objects:

namespace Microsoft.Office.Server.ApplicationRegistry.MetadataModel
{
    // Provides access to all of the LOB systems and LOB system instances registered in the Business Data Catalog
    public sealed class ApplicationRegistry { ... }

    // Represents a business application registered in the Business Data Catalog
    public class LobSystem : MetadataObject { ... }

    // Represents an instance of a business application registered in the Business Data Catalog
    public class LobSystemInstance : MetadataObject { ... }

    // Represents a type of returned business data object, contains identifiers, methods and actions
    public class Entity : DataClass { ... }
}

namespace Microsoft.Office.Server.ApplicationRegistry.Runtime
{
    // Represents a filter that limits the instances returned to those that meet the comparison operator condition
    public class ComparisonFilter : UserInputFilter { ... }

    // Represents a filter that limits the instances returned to those where field like value, where value may contain the asterisk (*) wildcard character
    public class WildcardFilter : ComparisonFilter { ... }

    // Represents instances of business objects
    public interface IEntityInstance : IInstance { ... }

    // Provides a single iteration over the entity instance collection
    public interface IEntityInstanceEnumerator : IEnumerator<IEntityInstance> { ... }
}

namespace Microsoft.Office.Server.ApplicationRegistry.Infrastructure
{
    // Provides encoding and decoding of entity instance identifiers
    public static class EntityInstanceIdEncoder { ... }

    // Represents the SQL session provider to connect to the Shared Services Provider database
    public sealed class SqlSessionProvider { ... }
}

The Business Data Catalog is implemented as a Microsoft Office SharePoint Server 2007 Shared Service. If you are going to deal with BDC inside a standalone window/console-based application or inside SharePoint Jobs, you have to prepare Shared Resource Provider to use in the Object Model. For that, you need to invoke the method SqlSessionProvider.Instance().SetSharedResourceProviderToUse:

namespace Microsoft.Office.Server.ApplicationRegistry.Infrastructure
{
    public sealed class SqlSessionProvider
    {
        // some methods are omitted
        public void SetSharedResourceProviderToUse(string sharedResourceProviderName);
        public void SetSharedResourceProviderToUse(ServerContext serverContext);
    }
}

The first variant of the method accepts a name of Shared Resource Provider. In terms of SharePoint Central Administration, the method requires the name of Shared Service. You can see all deployed Shared Services through the Central Administration: open SharePoint 3.0 Central Administration, find the Shared Services Administration section inside the left-side navigation bar and look through the available Shared Services. One of them is a default Shared Service. Another way is to get programmatically the name of Shared Service, which serves your web application. To learn more, please read the following article – How to get Shared Service name.

The second variant of the method use an instance of ServerContext. Below is two auxiliary methods I usually use as wrappers to SetSharedResourceProviderToUse:

public static void PrepareSharedServices(SPSite spSite)
{
    ServerContext sc = ServerContext.GetContext(spSite);
    SqlSessionProvider.Instance().SetSharedResourceProviderToUse(sc);
}

public static void PrepareSharedServices(string sharedServicesName)
{
    SqlSessionProvider.Instance().SetSharedResourceProviderToUse(sharedServicesName);
}

Do not use this method inside your SharePoint web application, otherwise an exception will be thrown. Because in web context the Business Data Catalog uses the default Shared Services Provider automatically. As I said above, use SetSharedResourceProviderToUse only inside standalone non-web applications or SP Jobs.