Using a custom tile provider with the XtraMap control

I saw this post a few weeks ago in the DevExpress Support Center inquiring about how to connect the XtraMap control to the Nokia HERE Maps system. Nokia’s HERE system provides geocoding, routing, and for our purposes, a map tile provider.

HERE Maps in an XtraMap control
HERE Maps in an XtraMap control

Out of the box, the XtraMap control can be paired with the OpenMaps and Bing Map providers. If these don’t suit your needs, you do have the ability to supply your own map provider by writing a little bit of code.

For our example, we’re going to connect to the HERE map provider. If you want to do your own testing–or use it in a production environment–you will want to sign up for a developer license. HERE maps allows for a limited free developer account, so go ahead and register for that for access to the mapping, geocoding and routing functionality.

As I’ve mentioned in a previous blog, the #1 request I see with regards to the XtraMap control is for the ability to connect it to Google Maps. The Google Maps terms of service does not allow for it to be used in a desktop environment. That being said, you could probably use a little bit of creativity and adapt the techniques shown here to achieve it…

Luckily for us, most of what we need to do really involves some boilerplate code. The best starting place is this example in the DevExpress Support Center: How to load image tiles from another source by creating custom data provider. This example illustrates using a local tile store but we can easily adapt it to fetch map tiles from HERE.

The first step is creating a class that derives from the MapDataProviderClass. We really don’t need to change anything from the example code except for some class/member names to suit our needs.

    sealed public class HereMapTileProvider : MapDataProviderBase
    {

        #region Private members

        /// <summary>
        /// Map project
        /// </summary>
        private readonly SphericalMercatorProjection _Projection = new SphericalMercatorProjection();

        #endregion

        #region Public accessors

        /// <summary>
        /// Gets the Projection for this tile provider
        /// </summary>
        public override ProjectionBase Projection
        {
            get { return _Projection; }
        }

        /// <summary>
        /// Returns a size struct representing the base size of this provider, in pixels
        /// </summary>
        protected override Size BaseSizeInPixels
        {
            get { return new Size(Convert.ToInt32(HereMapTileSource._TileSize * 2), Convert.ToInt32(HereMapTileSource._TileSize * 2)); }
        }

        #endregion


        /// <summary>
        /// Initializes a new instance of the HereMapTileProvider class
        /// </summary>
        public HereMapTileProvider()
        {
            TileSource = new HereMapTileSource(this);
        }


        /// <summary>
        /// Returns the pixel size of this map for the provided zoom level
        /// </summary>
        /// <param name="zoomLevel">Current zoom level</param>
        /// <returns></returns>
        public override MapSize GetMapSizeInPixels(double zoomLevel)
        {
            double imageSize;
            imageSize = HereMapTileSource.CalculateTotalImageSize(zoomLevel);
            
            return new MapSize(imageSize, imageSize);

        }   //End the GetMapSizeInPixels() method




    }   //End the HereMapTileProvider class

The important stuff is in our tile source class, which must derive from the MapTileSourceBase class. Again, we can keep most everything standard with regards to the DevExpress example. The only real work we have to do is in the GetTileByZoomLevel method. This method is responsible for taking the map’s current position and zoom level and translating that into a tile source location from the provider. Internally, the MapControl will make a request to to that tile source, get the tiles and render them on the map.

The HERE tile api gives us the following prototype for a tile request:

http://{1-4}.base.maps.cit.api.here.com/maptile/2.1/maptile/{map id}/{scheme}/{zoom}/{column}/{row}/{size}/{format}
?app_id=DemoAppId01082013GAL
&app_code=AJKnXv84fjrb0KIHawS0Tg
&{param}={value}

We’ll fill in some of those parameters with values from the XtraMap control and others will be static values from the api documentation. You’re free to play around with the different options for  the {scheme} or {format} parameters, or even modify the Url to show a satellite or terrain view. For our purposes, I’ve implemented the GetTileByZoomLevel in the following manner:

/// <summary>
/// Returns a Uri used to fetch tiles for the provided zoom level and co-ordinates
/// </summary>
/// <param name="zoomLevel">Current zoom level</param>
/// <param name="tilePositionX">Current tile x co-ordinate</param>
/// <param name="tilePositionY">Current tile y co-ordinate</param>
/// <returns></returns>
public override Uri GetTileByZoomLevel(int zoomLevel, int tilePositionX, int tilePositionY)
{

    string Url = String.Format(@"http://1.base.maps.api.here.com/maptile/2.1/maptile/newest/normal.day/{0}/{1}/{2}/256/png?app_id={3}&app_code={4}",
                zoomLevel,              //{0}                            
                tilePositionX,          //{1}
                tilePositionY,          //{2}
                _ApplicationID,         //{3}
                _ApplicationCode);      //{4}

    if (zoomLevel <= _MaxZoomLevel)
        return new Uri(Url);
    else
        return null;

}   //End the GetTileByZoomLevel() method

And really, that’s all there is to it! Like I said above, the example provided by the DevExpress Support Center really does contain all of the boilerplate code you need to get started. Feel free to adapt that, or sign up for a HERE developer account, download the project I’ve built and modify it suit your own needs: CustomTileProvider.zip

Using a custom tile provider with the XtraMap control

Previewing images in a SuperToolTip

In my previous post I mentioned the possibility of displaying a PDF page thumbnail in a tooltip to show a larger preview when the user hovers over it. Here’s what our end goal looks like:

Pdf Page SuperToolTip
The thumbnail displays a larger preview of itself when the mouse hovers above a page.

To accomplish this, we’ll add upon the PdfThumbnails solution from the last post to create this new functionality.

First, drop a ToolTipController onto the frmMain form. This component can be used to provide tooltips for DevExpress which implement the IToolTipControlClient interface necessary for displaying tooltips. Don’t worry about the nitty-gritty: DevExpress controls all seem to implement this interface allowing you to customize and create tooltips.

Once you drop the ToolTipController onto your form, make sure that you then set the grdThumbnails GridControl’s ToolTipController property to this new ToolTipController instance.

To provide our tooltips we’ll need to handle an event that is fired when the user hovers over a control (or event an element within a control, such as a single grid cell). This event comes in the form of the ToolTipController’s GetActiveObjectInfo event. Let’s create a handler for this event:


///

/// Get active object info event handler for the ToolTipController
/// &lt;/summary&gt;
/// &lt;param name=&quot;sender&quot;&gt;&lt;/param&gt;
/// &lt;param name=&quot;e&quot;&gt;&lt;/param&gt;
private void toolTipController1_GetActiveObjectInfo(object sender, DevExpress.Utils.ToolTipControllerGetActiveObjectInfoEventArgs e)
{
WinExplorerViewHitInfo hitInfo = wvThumbnails.CalcHitInfo(e.ControlMousePosition);

if (hitInfo.IsValid == false || hitInfo.InItem == false)
return;

ToolTipControlInfo toolTipInfo = null;
SuperToolTip toolTip = new SuperToolTip();

toolTipInfo = new ToolTipControlInfo(hitInfo.RowHandle.ToString(), &quot;Page Preview&quot;);
ToolTipItem item1 = new ToolTipItem();
item1.Image = PdfPage.ScaleThumbnailImage(hitInfo.ItemInfo.Image, 640, 480);
toolTip.Items.Add(item1);

toolTipInfo.SuperTip = toolTip;
e.Info = toolTipInfo;

} //End the toolTipController1_GetActiveObjectInfo() method

Our tooltip will be pretty simple–it’s just an image with no text, hyperlinks etc… We construct a SuperToolTip object, and more importantly, a ToolTipControlInfo object. The ToolTipControlInfo object is what is responsible for holding the information we’d like to show in a tooltip. That is, we can set a Title, Text or Image property, all of which are the basis of any tooltip, be it a standard tooltip or SuperToolTip.

One important thing to note is that the ToolTipControlInfo object itself has a property called “Object” (of datatype Object… a little confusing, I agree) which needs to be set to a unique value to identify what the tooltip is associated with. I do this by simply setting the property to the row handle of the WinExplorerView, since it will be unique.

The last bit of code isn’t really DevExpress-centric, but we need a way to scale the image to a manageable size because we don’t want to actually show the full size page image:

/// &amp;lt;summary&amp;gt;
/// Returns a thumbnail scaled to fit within the provided width and height ratio
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&amp;quot;ImageToScale&amp;quot;&amp;gt;Image to be scaled&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&amp;quot;MaxWidth&amp;quot;&amp;gt;New image maximum width&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&amp;quot;MaxHeight&amp;quot;&amp;gt;New image maximum height&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;Scaled image&amp;lt;/returns&amp;gt;
static internal Image ScaleThumbnailImage(Image ImageToScale, int MaxWidth, int MaxHeight)
{
    double ratioX = (double)MaxWidth / ImageToScale.Width;
    double ratioY = (double)MaxHeight / ImageToScale.Height;
    double ratio = Math.Min(ratioX, ratioY);

    int newWidth = (int)(ImageToScale.Width * ratio);
    int newHeight = (int)(ImageToScale.Height * ratio);

    Image newImage = new Bitmap(newWidth, newHeight);
    Graphics.FromImage(newImage).DrawImage(ImageToScale, 0, 0, newWidth, newHeight);

    return newImage;

}   //End the ScaleThumbnailImage() method

This method just takes the thumbnail image (which itself is actually a full-sized Bitmap of the page–the WinExplorerView scales it internally to a smaller graphic) and scales it to a new Bitmap of the dimensions requested in the MaxWidth & MaxHeight parameters.

In the future, we’ll get back to our Xpo Contact List project and use this image tooltip functionality to show preview images of our contacts in the program grids. This functionality can be helpful anywhere in your application where you display a small image and want to provide the user with a quick way to view a larger version of that image.

Download the PdfThumbnails_NEW source for our project and see it in action for yourself!

Previewing images in a SuperToolTip

Adding a thumbnail view & navigation to the PdfViewer control

If you’ve used the PdfViewer control you’ll know that it provides an easy way to display PDF documents within  your WinForms application. Currently, you can select text, manipulate a file (via the PdfDocumentProccessor non-visual component) and even fill out PDF forms. The 14.2.5 release even allows the ability to programmatically fill in PDF forms–pretty useful stuff for those of you processing electronic documents and working towards the dream of a paperless office.

One feature that I find lacking in the PdfViewer control is a good thumbnail navigation system. Adobe Reader offers this ability with a simple thumbnail navigation system; each page of the document is displayed as a small image which users can quickly scroll through. Double-clicking on a thumbnail opens the corresponding page and end users can even change the size of the thumbnail images.

Screenshot of Adobe Reader's thumbnail navigation
Screenshot of Adobe Reader’s thumbnail navigation

The DevExpress PdfViewer will display thumbnails for pages if you zoom out to about the 10-20% zoom level, but the functionality ends there. You can’t double-click one of the pages and have the viewer navigate to that particular page. How can we implement a system similar to Adobe Reader ourselves?

DevExpress's non-functional thumbnail mode.
DevExpress’s non-functional thumbnail mode.

To mimic Adobe Reader’s thumbnail function, I immediately though of a few controls that might make our job easier:

  • GalleryControl – This could easily organize and display our thumbnail images. We don’t really need any grouping functionality but that’s easy to ignore.
  • GridControl – This tends to be my default go-to for displaying a list of items/records. But that’s only the first half of the equation–after that, we’d have to decide which view type to use. The LayoutView in a single column mode would work well, but honestly, I hate the layout editor.

In the end, I decided to use the GridControl, but with the WinExplorerView instead of the LayoutView. The WinExplorerView mimics the various ways you might view a folder in Windows Explorer–details, large icons, small icons etc… This is all controlled via the OptionsViewStyles property, so we can even allow the user to toggle between styles at runtime.

At the end of this exercise, we’ll end up with something like this:

Our semi-finished product: a PdfViewer control with a sidebar of page thumbnails.
Our semi-finished product: a PdfViewer control with a sidebar of page thumbnails.

To being, we’ll create a form and place a LayoutControl onto the form. Dock the LayoutControl to fill the form, and then drop a GridControl onto the LayoutControl, followed by a PdfViewer control to the right. The GridControl can be placed in a LayoutControlGroup and the PdfViewer can just be contained within a standard LayoutControlItem. I also changed the view type of the GridControl from the default GridView to the WinExplorerView type.

The thumbnail logic is all going to be contained within a class I’ve called PdfPage. Let’s have a look at it:

namespace PdfThumbnails
{
    /// &amp;lt;summary&amp;gt;
    /// Encapsulates the properties and methods of a PdfPage
    /// &amp;lt;/summary&amp;gt;
    sealed public class PdfPage
    {

        #region Public properties

        /// &amp;lt;summary&amp;gt;
        /// Gets or sets the number of this Pdf Page
        /// &amp;lt;/summary&amp;gt;
        public int PageNumber
        {
            get;
            set;
        }

        /// &amp;lt;summary&amp;gt;
        /// Gets the thumbnail image for this Pdf Page
        /// &amp;lt;/summary&amp;gt;
        public Image Thumbnail
        {
            get;
            private set;
        }

        #endregion


        /// &amp;lt;summary&amp;gt;
        /// Initializes a new instance of the PdfPage class
        /// &amp;lt;/summary&amp;gt;
        public PdfPage()
        {

        }



        /// &amp;lt;summary&amp;gt;
        /// Sets the thumbnail image for the current PdfPage
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;DocumentViewer&amp;quot;&amp;gt;PdfViewer control responsible for displaying the PdfPage&amp;lt;/param&amp;gt;
        public void SetThumbnailImage(PdfViewer DocumentViewer)
        {
            int PageWidth = 0;
            int PageHeight = 0;
            int PagePixelWidth = 0;
            int PagePixelHeight = 0;

            using (Graphics graphics = DocumentViewer.CreateGraphics())
            {
                //Get the page dimensions
                PageWidth = (int)DocumentViewer.GetPageSize(PageNumber).Width;
                PageHeight = (int)DocumentViewer.GetPageSize(PageNumber).Height;

                //Convert the page dimensions into screen pixels
                PagePixelWidth = (PageWidth * (int)graphics.DpiX);
                PagePixelHeight = (PageHeight * (int)graphics.DpiY);

                Thumbnail = DocumentViewer.CreateBitmap(PageNumber, Math.Max(PagePixelWidth, PagePixelHeight));

            }   //End the using() statement

        }   //End the SetThumbnailImage class



    }   //End the PdfPage class
}   //End the PdfThumbnails namespace

The class is pretty sparse in and of itself–just two properties. One to keep track of the page number within the document and another to hold the thumbnail image. The important part is the SetThumbnailImage method, which is in charge of creating a thumbnail for this page.

The method requires an instance of the PdfViewer control because unfortunately, the control doesn’t offer a way to get an instance of a single visual page. Luckily, having an instance of the PdfViewer control allows to use its CreateGraphics method to get a reference to the drawing surface which will be useful for generating that thumbnail. And it is useful because the PdfViewer’s GetPageSize method returns the page’s dimensions in inches, not in pixels.

I think this was a poor decision on the part of DevExpress, because it’s not mentioned in the documentation that SizeF struct returned from the method is measured in inches and I have no clue how useful that information would be in metric-based countries. Our demonstration PDF will return 8 & 11 for the page’s width and height, respectively. Since we don’t want to create an 8px X 11px Bitmap, we need to convert these dimensions into pixels.

I’m not a graphics programmer, but I do know that DPI settings will influence the conversion between inches and pixels–after all, its name implies how many dots-per-inch there are on our display! Because of this, we can’t just multiply the page dimensions by some magic number. We have to take this DPI into account and multiply by that instead.

Once we have the page and screen dimensions, it’s a matter of using the PdfViewer’s CreateBitmap method to generate a Bitmap image of current page.

I then create a BindingSource component on the Form and set its DataSource property the PdfThumb class. After that, I set the GridControl’s DataSource to this BindingSource component. The GridControl will automatically read the scheme from this data source, but because we’re using the GridControl with a WinExplorerView there aren’t any columns. Instead, there’s a ColumnSet, which maps properties of the data source to the predefined properties of the WinExplorerView.

Since a future version of this application may allow a user to toggle between thumbnail sizes, we’ll set the values for SmallImageColumn, MediumImageColumnn, LargeImageColumn and ExtraLargeImageColumn members of that ColumnSet property. These are all set to the PdfPage’s ThumbnailImage property, and we’ll set the ColumnSet’s Description andTextColumn properties to the PageNumber property.

At this point, we have a PdfViewer control capable of displaying our PDF, a GridControl capable of displaying page thumbnails and a class capable of generating those thumbnails. The only thing we need to do is actually tell our application to generate those thumbnails at runtime when a PDF is loaded into the viewer.

Create a handler for the the PdfViewer’s DocumentChanged event as this will be fired every time a document is loaded into the PdfViewer control. After that, it’s just a matter of looping through the pages of the PdfViewer and creating an instance of the PdfPage class for each:

/// &amp;lt;summary&amp;gt;
/// Document changed event handler for the PdfViewer control
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
private void pdfViewer1_DocumentChanged(object sender, DevExpress.XtraPdfViewer.PdfDocumentChangedEventArgs e)
{
    IList&amp;lt;PdfPage&amp;gt; documentPages = new List&amp;lt;PdfPage&amp;gt;(pdfViewer1.PageCount);

    for (int i = 1; i &amp;lt;= pdfViewer1.PageCount; i++)
    {
        PdfPage documentPage = new PdfPage();
        documentPage.PageNumber = i;
        documentPage.SetThumbnailImage(pdfViewer1);

        documentPages.Add(documentPage);

    }   //End the for() loop

    bindingSource1.DataSource = documentPages;

}   //End the pdfViewer1_DocumentChanged() method

Once that’s done, our GridControl will automatically show those page thumbnails when the application is run and a Pdf is loaded into the viewer.

The final step is to handle the WinExplorerView’s DoubleClick event:

/// &amp;lt;summary&amp;gt;
/// Double click event handler for the Thumbnails WinExplorerView
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&amp;quot;sender&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
/// &amp;lt;param name=&amp;quot;e&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
private void wvThumbnails_DoubleClick(object sender, EventArgs e)
{
    PdfPage currentPage = (wvThumbnails.GetFocusedRow() as PdfPage);

    pdfViewer1.CurrentPageNumber = currentPage.PageNumber;

}   //End the wvThumbnails_DoubleClick() method

This code simply takes the current row (thumbnail) from the thumbnail grid and casts it to an instance of our PdfPage class. Since the class is nice enough to keep track of its page number on our behalf, we can tell the PdfViewer to navigate to corresponding page.

And that’s all there is to it! Once small class, a couple of event handlers and we’re done. In an upcoming post, we’ll look at adding a few additional features to this application such as:

  • Allowing the user to change the thumbnail size
  • Synchronizing the thumbnail sidebar scroll position to the PdfViewer scroll position
  • Showing a magnified image of the thumbnail when the user hovers over a thumbnail image

And don’t worry, we’ll also get back to the DxContactList Xpo tutorial soon! In the meantime, here is the full source code for this tutorial: PdfThumbnails.

Adding a thumbnail view & navigation to the PdfViewer control

Creating a sample WinForms application, part one

I thought that perhaps a good way to expose new DevExpress developers to the various products and controls might be to create a small, simple application. Borne from this idea is DxContactList, a basic application designed to be a digital address book. I came up with some simple functionality that help you become more familiar with some different controls & products:

  • XtraEditors as well as creating some custom controls
  • XtraGrid
  • XtraMap
  • XtraReports
  • eXpress Persistent Objects (Xpo), DevExpress’ ORM product that will handle our data-access and mapping.
  • XtraRichEdit control

I’ve already built the application and I hope to present it to you over the next few weeks in a series of posts. We’ll take a look at each portion of the application, deconstruct how I’ve built it and point out some tips and tricks to make your life easier if you’re trying to develop something similar.

I’ll be starting with the back-end & business logic which is chiefly comprised of our persistent classes. You way want to review some of the Xpo documentation to familiarize yourself with it first. Additionally, I find that the Xpo Best Practices knowledge base article is valuable in helping you avoid some common mistakes as you begin to learn.

Creating a sample WinForms application, part one