---
title: "Building Online Business Card Editor"
canonical: https://www.graphicsmill.com/docs/gm5/BuildingOnlineBusinessCardEditor.htm
product: "Graphics Mill 5.5 for .NET"
---

This documentation is for the old version. [Go to the latest Graphics Mill docs](https://www.graphicsmill.com/docs/gm/)

# Building Online Business Card Editor
 
The most obvious example of the Advanced PSD Add-on usage is an application which builds business cards based on a PSD template. This topic discusses a web application which works in this way: the user enters their personal details and a business card is generated.
 
**Note** 
The full source code of this example can be found at Aurigma Forums:
 
*http://forums.aurigma.com/yaf\_postst3045\_Web-Demos-Bundle-photo-cropping-TIFF-viewer-red-eye-removal-and-business-card-editor.aspx*
 
## Creating PSD Template in Adobe® Photoshop®
  
First of all we need to create a PSD template with high resolution (e.g. 300 DPI). Put a logo, captions, and placeholders (i.e. text layers which should be replaced during processing). As a result we will get something like this:
 ![Business card template.](https://www.graphicsmill.com/docs/gm5/MergingLayers1.png) 
In our example we use 6 placeholders (they are rounded by square brackets on the figure above). It is important to distinguish these layers during processing, that's why we assign them special names:
 
- FullName
- Position
- PhoneFax
- Mobile
- Email
- Website

This way the **Layers** window in Photoshop® will be looking in the following way:
 ![Template layers.](https://www.graphicsmill.com/docs/gm5/PhotoshopLayers.png) 
**Warning** 
When creating any artworks in Photoshop®, do not forget that Advanced PSD Add-on does not provide 100% PSD format support. Keep in mind that if some Photoshop® feature is not listed in the [Advanced PSD Add-on overview](https://www.graphicsmill.com/docs/gm5/AdvancedPSDAddonIntroduction.htm.md), most likely it will be disregarded when you process PSD file. That's why it is strongly recommended not to use vector masks, adjustment layers, etc.
 
If you cannot avoid using some of PSD features, feel free to create [support case↗](http://www.graphicsmill.com/account/cases/add) to discuss possible solutions.
 
## Creating Editor Page
  
When we prepared one or more PSD templates, it high time to start building our web application. The first step is to design the user interface.
 
Create new ASP.NET page, and put the following elements onto it:
 
- [BitmapViewer](https://www.graphicsmill.com/docs/gm5/T_Aurigma_GraphicsMill_WebControls_BitmapViewer.htm.md) control which will be used to [display a business card preview](https://www.graphicsmill.com/docs/gm5/DisplayingImageinBitmapViewerWebControl.htm.md);
- 6 server Aurigma.GraphicsMill.WebControls.BitmapViewer controls which will be used to input user's personal details.

The layout of this page should be looking as follows:
 ![Business card editor user interface.](https://www.graphicsmill.com/docs/gm5/BusinessCardEditor.png) 
Let's tweak parameters of the BitmapViewer.
 
- As our original template has high resolution it will be to large to be displayed. To make it loading faster and to reduce traffic, we will shrink the image displayed in the control. To do it, set the [Zoom](https://www.graphicsmill.com/docs/gm5/P_Aurigma_GraphicsMill_WebControls_BitmapViewer_Zoom.htm.md) property to **0.35** (35%) value.
- To provide the highest quality during zoom operation, set the [ZoomQuality](https://www.graphicsmill.com/docs/gm5/P_Aurigma_GraphicsMill_WebControls_BitmapViewer_ZoomQuality.htm.md) property to [High](https://www.graphicsmill.com/docs/gm5/T_Aurigma_GraphicsMill_WebControls_ZoomQuality.htm.md) value.
- One more trick to speed up loading speed - disable the low-resolution preview image. Set the [PreviewImageEnabled](https://www.graphicsmill.com/docs/gm5/P_Aurigma_GraphicsMill_WebControls_BitmapViewer_PreviewImageEnabled.htm.md) property to `false`.
- To avoid calculating and hardcoding width and height parameters of the control (to display the image without scrollbars), let's have the control to get these values automatically. It can be easily done using the [ZoomMode](https://www.graphicsmill.com/docs/gm5/P_Aurigma_GraphicsMill_WebControls_BitmapViewer_ZoomMode.htm.md). Just set it to the [ZoomControl](https://www.graphicsmill.com/docs/gm5/T_Aurigma_GraphicsMill_WebControls_ZoomMode.htm.md) value.
- To optimize the performance, set the [BitmapStateEnabled](https://www.graphicsmill.com/docs/gm5/P_Aurigma_GraphicsMill_WebControls_BitmapViewer_BitmapStateEnabled.htm.md) property to `false`. It will switch off bitmap state maintenance which is unnessary in our case (since we re-generate the bitmap during each page load).

For distinctness sake, let's name our **Input (Text)** controls as follows: **TextFullName**, **TextPosition**, **TextPhoneFax**, **TextMobile**, **TextEmail**, and **TextWebsite**.
 
## Processing Placeholders in Template
  
The code which processes a PSD template and replaces placeholders by the user data, we will reuse the **RasterizePsd** method described in the [Merging Layers](https://www.graphicsmill.com/docs/gm5/MergingLayers.htm.md) topic.
 
To process the template add the following code into the **Page\_Load** event handler:
 
```
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not Page.IsPostBack Then

        Dim updatedLayers As New System.Collections.Specialized.NameValueCollection

        updatedLayers.Add("FullName", TextFullName.Value)
        updatedLayers.Add("Position", TextPosition.Value)
        updatedLayers.Add("PhoneFax", TextPhoneFax.Value)
        updatedLayers.Add("Mobile", TextMobile.Value)
        updatedLayers.Add("Email", TextEmail.Value)
        updatedLayers.Add("Website", TextWebsite.Value)

        BitmapViewer1.Bitmap = RasterizePsd(Server.MapPath("TestImages/BusinessCard.psd"), updatedLayers)

    End If

End Sub
```
 
```csharp
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        System.Collections.Hashtable updatedLayers = new System.Collections.Hashtable();

        updatedLayers.Add("FullName", TextFullName.Value);
        updatedLayers.Add("Position", TextPosition.Value);
        updatedLayers.Add("PhoneFax", TextPhoneFax.Value);
        updatedLayers.Add("Mobile", TextMobile.Value);
        updatedLayers.Add("Email", TextEmail.Value);
        updatedLayers.Add("Website", TextWebsite.Value);

        BitmapViewer1.Bitmap = RasterizePsd(Server.MapPath("TestImages/BusinessCard.psd"), updatedLayers);
    }
}
```
 
After the page will be loaded in the browser, you will see the ready business card.
 
## Using Remote Scripting: Updating Business Card Without Roundtrip
  
For better interactivity let's have the business card to be updated on each text field change. To do it, it is necessary to use the [remote scripting approach](https://www.graphicsmill.com/docs/gm5/RemoteScriptingApproach.htm.md) (AJAX-like technique which enables to load data from server without roundtrips). It is done in two steps:
 
1. Add a server-side method marked with the [RemoteScriptingMethodAttribute](https://www.graphicsmill.com/docs/gm5/T_Aurigma_GraphicsMill_WebControls_RemoteScriptingMethodAttribute.htm.md) atribute. In this method we will just fill the **texFields** collection with updated text values and process the business card template again.
2. Call this method from the JavaScript using the **invokeRemoteMethod** function (see the [Remote Scripting Approach (Web Controls)](https://www.graphicsmill.com/docs/gm5/RemoteScriptingApproach.htm.md) topic for more details on this).

Here is a code of the remote method:
 
```
<Aurigma.GraphicsMill.WebControls.RemoteScriptingMethod()> _
Public Sub Refresh()
    Dim updatedLayers As New System.Collections.Hashtable

    updatedLayers.Add("FullName", Request.Form("TextFullName"))
    updatedLayers.Add("Position", Request.Form("TextPosition"))
    updatedLayers.Add("PhoneFax", Request.Form("TextPhoneFax"))
    updatedLayers.Add("Mobile", Request.Form("TextMobile"))
    updatedLayers.Add("Email", Request.Form("TextEmail"))
    updatedLayers.Add("Website", Request.Form("TextWebsite"))

    BitmapViewer1.Bitmap = RasterizePsd(Server.MapPath("TestImages/BusinessCard.psd"), updatedLayers)
End Sub
```
 
```csharp
[Aurigma.GraphicsMill.WebControls.RemoteScriptingMethod()]
public void Refresh()
{
    System.Collections.Hashtable updatedLayers = new System.Collections.Hashtable();

    updatedLayers.Add("FullName", Request.Form["TextFullName"]);
    updatedLayers.Add("Position", Request.Form["TextPosition"]);
    updatedLayers.Add("PhoneFax", Request.Form["TextPhoneFax"]);
    updatedLayers.Add("Mobile", Request.Form["TextMobile"]);
    updatedLayers.Add("Email", Request.Form["TextEmail"]);
    updatedLayers.Add("Website", Request.Form["TextWebsite"]);

    BitmapViewer1.Bitmap = RasterizePsd(Server.MapPath("TestImages/BusinessCard.psd"), updatedLayers);
}
```
 
**Note** 
You may noticed that text fields are accessed through the form rather than through the **Value** property of **Input (Text)** control (as it is implemented in the **Page\_Load**). It is caused by the fact that the remote method is called **before** initialization of these controls. Such behavior is stipulated for optimization purposes to process the callback before initialization of all controls. It allows to eliminate full page initialization.
 
We will call this remote method on each modification of our text fields. In other words, text fields should have the following event handlers:
 
```
onchange="refresh();" 
onkeyup="refresh();"
```
 
And the **refresh** function should be defined as follows:
 
```
var bitmapViewer1;

window.onload=function(){
    bitmapViewer1 = document.getElementById("<%=BitmapViewer1.ClientID%>");
    bitmapViewer1.addStatusChanged(bitmapViewer1_StatusChanged);
}

var needToRefresh = false;
        
function refresh(){    
    if (!needToRefresh){
        needToRefresh = true;
        window.setTimeout("delayedRefresh()", 2000)
    }
}

function delayedRefresh(){
    if (bitmapViewer1.getStatus() != "Busy"){
        needToRefresh = false;
        bitmapViewer1.invokeRemoteMethod("Refresh", null);
    }
}
  
function bitmapViewer1_StatusChanged(){
    if (needToRefresh){
        window.setTimeout("delayedRefresh()", 1000)
    }
}
```
 
This code may appear a bit tricky. To avoid executing our remote method too often, we force it to be called not more often than in 2 seconds since previous call, and not earlier than after 1 second after previous refresh.
 
As a result, our application will work as follows:
 ![Business card editor in action](https://www.graphicsmill.com/docs/gm5/BusinessCardEditorBrowser.png)
 
## See Also
  
### Manual
 
- [Merging Layers](https://www.graphicsmill.com/docs/gm5/MergingLayers.htm.md)
- [Remote Scripting Approach (Web Controls)](https://www.graphicsmill.com/docs/gm5/RemoteScriptingApproach.htm.md)
- [Displaying Image in BitmapViewer (Web Controls)](https://www.graphicsmill.com/docs/gm5/DisplayingImageinBitmapViewerWebControl.htm.md)
