Author Archives: glayaar

Introducing ImageAbsolutePath Feature in SqlReportViewer

The recently released service pack includes a number of enhancements to the SqlReportViewer control. One of the enhancements is the addition of ImageAbsolutePath property. This property is introduced for use in scenario involving multiple server environment.

In the initial release, ImageAbsolutePath is inferred from ImageVirtualPath. In this case, the control always assume that the SqlReportViewer and SQL Reporting Service is located on the same server. However, based on customer feedback, scenarios involving multiple server location is common in enterprise application deployment.

For example, the SqlReportViewer service is hosted on ssrs-1 server while the SqlReportingService is located on web-1. The scenario requires user to set both the ImageVirtualPath and ImageAbsolutePath properties, because the inferred absolute path from a remote location will not return a correct result. A code snippet to address such scenario would be:

<Intersoft:SqlReportViewer x:Name="ReportViewer1" 
	ReportProxyHandler="http://web-1/SqlReportViewerHandler.ashx" 
	ReportName="/Northwind Reports/SalesOrderInvoice"
	ReportServer="http://ssrs-1/ReportServer"
	ImageVirtualPath="http://ssrs-1/TempImages/"
	ImageAbsolutePath="C:\inetpub\wwwroot\TempImages" /> 

The above code snippet assumes that the report images will be stored to the C:\inetpub\wwwroot\TempImages folder on the ssrs-1 server and virtually hosted on http://ssrs-1/TempImages. In this case, you need to configure ssrs-1 as the image server by creating the TempImages virtual directory.

If opening up the SQL report server as public web server is not option, simply set the ImageVirtualPath property to the web-1 server. Everything else should remain the same. In this case, you need to configure the IIS in the web-1 server by creating a virtual directory and map it to the network path in the ssrs-1 server.

I hope this post gives you insights on the new ImageAbsolutePath feature and how it can be used along with other properties to achieve advanced deployment scenarios. Any questions or feedback are welcomed.

For the complete list of enhancements and fixes in the latest ClientUI release, please visit our support page.

Regards,
Glenn Layaar

Working with SqlReportViewer Parameter

The latest ClientUI release comes up with a host of new amazing controls that have been awaited by many developers. One of the new controls is SqlReportViewer, which is used to display SQL Reporting Service reports in Silverlight or WPF applications.

In this blog post, I will share how to work with the report parameter features in SqlReportViewer, e.g. fill the parameter with default value, hide some parameters, and use custom validation to validate the inserted parameter value.

Consider this scenario, we have a holiday request form that a user needs to fill. The report by default requires these parameters to be filled:

  • Company Name
  • Username
  • First Name
  • Last Name
  • Start Date
  • End Date
  • Holiday Name

In this scenario, the Silverlight application will retrieve the Company Name, Username, First Name, and Last Name based on the user login information. Since these information are already predefined, SqlReportViewer has the capabilities to hide and automatically fill these parameters value.

As shown in the above image, Company Name, Username, First Name, and Last Name by default has been set programmatically. Company Name and Username is hidden as well, since we would like the value not to be modified by the user.

In order to achieve this behavior, you need to bind the ReportParameters property to a ObservableCollection<ISqlParameterMetadata> using TwoWay mode.

<Intersoft:SqlReportViewer ReportParameters="{Binding ReportParameters, Mode=TwoWay}"
x:Name="ReportViewer1" />

The default value and Visible property is modified during ObservableCollection<ISqlParameterMetadata> collection changed event, see the code snippet below.

void _reportParameters_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    if (e.NewItems != null)
    {
        foreach (object paramObj in e.NewItems)
        {
            SqlReportParameterMetadata sqlParamObj = paramObj as SqlReportParameterMetadata;
            if (sqlParamObj != null)
            {
                switch(sqlParamObj.Name)
                {
                    case "CompanyName":
                        sqlParamObj.Value = this._companyName;
                        sqlParamObj.Visible = Visibility.Collapsed;
                        break;
                    case "Username":
                        sqlParamObj.Value = this._userName;
                        sqlParamObj.Visible = Visibility.Collapsed;
                        break;
                    case "FirstName":
                        sqlParamObj.Value = this._firstName;
                        break;
                    case "LastName":
                        sqlParamObj.Value = this._lastName;
                        break;
                }
            }
        }
    }

    OnPropertyChanged("ReportParameters");
}

public ObservableCollection<ISqlParameterMetadata> ReportParameters
{
    set
    {
        if (_reportParameters != value)
        {
            _reportParameters = value;
            _reportParameters.CollectionChanged += new System.Collections.Specialized.
                NotifyCollectionChangedEventHandler(_reportParameters_CollectionChanged);
            OnPropertyChanged("ReportParameters");
        }
    }
    get
    {
        return _reportParameters;
    }
}

For simplicity, the sample is using a static value to retrieve the default parameter value.

this._companyName = "Intersoft Solutions";
this._userName = "jdoe@intersoft.com";
this._firstName = "John";
this._lastName = "Doe";

A custom validation which does not allow StartDate earlier than EndDate has also been provided.

This behavior is achieved by using a custom SubmitParameterCommand and binding IsReportParameterError property to a boolean property with TwoWay mode. Setting IsReportParameter property to False aborts the report execution to the reporting services.

<Intersoft:SqlReportViewer Intersoft:DockPanel.IsFillElement="True" x:Name="ReportViewer1"
    IsReportParameterError="{Binding IsReportParameterError, Mode=TwoWay}"
    SubmitParameterCommand="{Binding SubmitParameterCommand}" />

During command execution, the code below will validate if the EndDate is later than StartDate. It will also set the IsReportParameter to False if the validation fail.

public ReportSampleViewModel()
{
    _submitParameterCommand = new DelegateCommand(ExecuteSubmitParameter, CanSubmitParameter);
}

public DelegateCommand SubmitParameterCommand
{
    get { return _submitParameterCommand; }
}

private bool CanSubmitParameter(object parameter)
{
    return true;
}

private void ExecuteSubmitParameter(object parameter)
{
    ObservableCollection<ISqlParameterMetadata> sqlParams = parameter as ObservableCollection<ISqlParameterMetadata>;

    if (sqlParams != null)
    {
        ISqlParameterMetadata sqlStartParam = sqlParams.Where(x => x.Name == "StartDate").FirstOrDefault();
        ISqlParameterMetadata sqlEndParam = sqlParams.Where(x => x.Name == "EndDate").FirstOrDefault();

        if (sqlStartParam != null && sqlStartParam.Value != null &&
            sqlEndParam != null && sqlEndParam.Value != null)
        {

            DateTime startDate = (DateTime)sqlStartParam.Value;
            DateTime endDate = (DateTime)sqlEndParam.Value;

            if (endDate.CompareTo(startDate) > 0)
            {
                this.IsReportParameterError = false;
                sqlStartParam.ClearError("Value");
            }
            else
            {
                this.IsReportParameterError = true;
                sqlStartParam.SetError("Value", "Start date must be earlier than End date");
            }
        }
    }
}

Here is the link to the sample project and the link to the RDL used in this sample.

Conclusion

In this post, I have discussed the report parameter feature in SqlReportViewer. The feature allows you to insert default parameters value, hide parameters, and use custom validation to validate the parameter. If you would like to learn more about SqlReportViewer, please see SqlReportViewer Overview.

As an extra note, hiding parameter feature is an enhancement in the latest SqlReportViewer build and requires the latest ClientUI hotfix.

SqlReportViewer is available in the latest 2011 R2 release which you can download here. If you have questions or feedback regarding SqlReportViewer or other ClientUI controls, feel free to post them to our community forum.

Regards,
Glenn Layaar

Advanced Currency Input Control for Silverlight & WPF

One of the new controls introduced in ClientUI 4 is UXCurrencyEditor, an advanced input control specifically designed for currency and numerical input with support for .NET standard and custom numeric format.

In this post, I will share a number of useful features in the UXCurrencyEditor control, and how it can be useful in your Silverlight and WPF business applications.

Introducing UXCurrencyEditor

Often times, users are required to input numerical data such as currency, item quantity, or percentage value. Some of these scenarios also require different text between edit and display. In ClientUI 4, Intersoft has provided a versatile currency input control named UXCurrencyEditor.

Let’s take a quick example of a common business form where the currency value shows a currency sign in the display mode, but exclude the sign in the edit mode. For instance, the value of 25000 will have the following results.

EditText 25,000.00
DisplayText $25,000.00

This could be achieved using the supported patterns in the provided EditMask and DisplayMask property. These patterns conform to the .NET standard and custom numeric format. Some patterns are only suitable for DisplayMask and have been noted in the list below.

The following table lists the supported standard and numeric pattern:

Standard Format
Valid EditMask Format Specifier Description
V C, c Currency
A currency value.
V D, d Decimal
Integer digits with optional negative sign.
X E, e Exponential (scientific)
Exponential notation.
V F, f Fixed-point
Integral and decimal digits with optional negative sign.
X G, g General
The most compact of either fixed-point or scientific notation.
V N, n Number
Integral and decimal digits, group separators, and a decimal separator with optional negative sign.
V P, p Percent
Number multiplied by 100 and displayed with a percent symbol.
X R, r Round-trip
A string that can round-trip to an identical number.
X X, x Hexadecimal
A hexadecimal string.
*Currently UXCurrencyEditor standard format will not accept custom group sizes. The group sizes will always be 3.
Custom Format
Valid EditMask Format Specifier Description
V 0 Zero placeholder
Replaces the zero with the corresponding digit if one is present; otherwise, zero appears in the result string.
V # Digit placeholder
Replaces the pound sign with the corresponding digit if one is present; otherwise, no digit appears in the result string.
V . Decimal point
Determines the location of the decimal separator in the result string.
V , Group separator and number scaling
Replaces the pound sign with the corresponding digit if one is present; otherwise, no digit appears in the result string.
V % Percentage placeholder
Multiplies a number by 100 and inserts a localized percentage symbol in the result string.
X E0, E+0, E-0, e0, e+0, e-0 Exponential notation
If followed by at least one 0 (zero), formats the result using exponential notation. The case of "E" or "e" indicates the case of the exponent symbol in the result string. The number of zeros following the "E" or "e" character determines the minimum number of digits in the exponent. A plus sign (+) indicates that a sign character always precedes the exponent. A minus sign (-) indicates that a sign character precedes only negative exponents.
V \ Escape character
Causes the next character to be interpreted as a literal rather than as a custom format specifier.
V ‘string’, "string" Literal string delimiter
Indicates that the enclosed characters should be copied to the result string unchanged.
V ; Section separator
Defines sections with separate format strings for positive, negative, and zero numbers.

Based on the supported pattern described above, the UXCurrencyEditor control with the currency value in the earlier scenario will have the EditMask property set to “#,##0.00” and the DisplayMask property set to “C”.

<Intersoft:UXCurrencyEditor EditMask="#,##0.00"
                 DisplayMask="C" UseEditMaskAsDisplayMask="False" />

As shown in the snippet code above, you need to set the UseEditMaskAsDisplayMask property to False for the DisplayMask property to take effect.

The Value property of the UXCurrencyEditor control represents the numerical value of the EditText. We decided to use decimal data type to hold the UXCurrencyEditor value in order to accommodate large number value commonly used in financial applications.

UXCurrencyEditor also allows null value which is controlled by the AllowNull property. By default this property value is set to False, hence null value is not allowed by default. When AllowNull is enabled, users can set the value to null at runtime by selecting the whole text and press the Delete key (Ctrl + A, Del).

Negative Value

Another important feature that we think useful in financial applications is the special handling for negative value. By default, UXCurrencyEditor shows negative value in Red foreground and appends the text with – (negative sign). You can easily change the negative format by using the pattern ; (section separator). For instances, in order to show -10000 as (10,000.00), you set the negative format to (#,##0.00). See the following example.

CurrencyNegativeValue

<Intersoft:UXCurrencyEditor EditMask="#,##0.00;(#,##0.00)" />

Spin Feature

UXCurrencyEditor also supports spinning feature based on the active caret position. The Up arrow key is used to increment the value, while the down arrow key is used to decrement the value. The caret position determines the amount of value to be incremented or decremented.

5|00.00 increment/decrement the value by 100.
|500.00 increment/decrement value by 1000.

Localization (Culture)

One of the most difficult challenges in business forms is how the input control can be customized to adapt the currency settings of the user’s culture, such as the currency sign, group separator, and the decimal point. Luckily, UXCurrencyEditor already supports such localization feature by simply setting the Culture property to desired culture string.

The following table shows various results of the UXCurrencyEditor using different culture.  The example uses a Value of 10000 and EditMask set to “C”.

Culture EditText
CodeSnipppet
en-US $10,000.00
<Intersoft:UXCurrencyEditor
		EditMask="C" Culture="en-US" />
en-GB £10,000.00
<Intersoft:UXCurrencyEditor
		EditMask="C" Culture="en-GB" />
id-ID Rp10.000
<Intersoft:UXCurrencyEditor
		EditMask="C" Culture="id-ID" />

Editing Behavior

Although the UXCurrencyEditor is derived from UXTextBox, the editing behavior is vastly different since UXCurrencyEditor expected numerical data entry. There are several default TextBox operations which are considered invalid and ignored in the UXCurrencyEditor such as:

  • Paste

    Paste is only valid if all the text are numeric.
  • Insert

    Only numerical value is valid.

Let’s take an example of UXCurrencyEditor with EditMask #,##0.00

<Intersoft:UXCurrencyEditor EditMask="#,##0.00" />
EditText Action Result
Description
0|.00 Input A 0|.00
Input A is ignored. No change occured, the only accepted input is numerical value.
0|.00 Input 3 3|.00
0 is replaced by 3. Input in major number section is appended to the right and the caret position is not moved after successful insertion. If afterward user press 2 the result will be 32|.00
0.|00 Input 1 0.1|0
0 is replaced by 1. Input in minor number section is appended to the right and the caret position is moved after successful insertion.
0.12| Input 3 0.23|
12 become 23. The minor number format only accept 2 number, UXCurrencyEditor append the 3 to the minor number and get the 2 latest number.
1,23|4.00 Pressing BackSpace 12|4.00
3 is deleted, since ‘,’ is not needed in the new value ‘,’ is also omitted. Caret position stays in front of 4.
1,|23|4.00 Pressing BackSpace 1|4.00
Hightligted text is deleted, ‘,’ is omitted for the same reason as above scenario. Caret positioned at the first deleted hightlight text.
|1,234.00| Pressing BackSpace [AllowNull=True] |

[AllowNull=False] 0|.00

If AllowNull is False, reset EditText to 0.00 and positioned caret at default position. Otherwise, EditText is set to empty string.
|1,234.00| Pasting text 1234567 1,234,567|.00
The current value will be overrided with the new value. The new value is also formatted using the EditMask. It is also valid to paste value with group separator, such as 1,234,567.

Summary

In this post, you have learned the key features of UXCurrencyEditor. You can play around with the UXCurrencyEditor features through the properties, or try some readily designed samples here. To see all available features, please see UXCurrencyEditor Overview.

You will need the latest 2010 R2 bits which you can download it here. If you have questions or feedback about UXMaskedInput or other ClientUI controls, please feel free to post them to our community forum.

Regards,

Glenn Layaar

Advanced Masked Input Control for Silverlight & WPF

One of the new advanced input controls introduced in ClientUI 4 is the UXMaskedInput control. The control allows patterned data input entry, such as phone number, social security number, and more.

In this post, I will share a number of useful features in the UXMaskedInput control, and how it can be useful in your Silverlight and WPF business applications.

Introducing UXMaskedInput

In many business scenarios, users are required to entry patterned data such as phone number, customer ID, or social security number. Such scenarios require a specialized data entry control, commonly referred as masked input, in order to validate the inputted data. In the ClientUI 4, Intersoft has provided an advanced masked input control named UXMaskedInput.

Using the common business scenarios mentioned above, for example a phone number entry requires optional country code in parentheses followed by optional area code and the required phone number separated by dash, (062)021-6601234, this could be achieved by using the supported patterns in UXMaskedInput through the EditMask property. Here is a list of the supported pattern:

Character Description
0 Digit (0 to 9, entry required, plus [+] and minus [–] signs not allowed).
9 Digit or space (entry not required, plus and minus signs not allowed).
# Digit or space (entry not required; spaces are displayed as blanks while in Edit mode, but blanks are removed when data is saved; plus and minus signs allowed).
L Letter (A to Z, entry required).
? Letter (A to Z, entry optional).
A Letter or digit (entry required).
a Letter or digit (entry optional).
$ Any character or a space (entry required).
C Any character or a space (entry optional).
< Causes all characters to be converted to lowercase.
> Causes all characters to be converted to uppercase.
\ Causes the character that follows to be displayed as the literal character (for example, \A is displayed as just A).
*Other characters as considered as literal. By default the EditMask is set to aaaaa.

By using the patterned list, the phone number will have EditMask (###)999-0000000.

<Intersoft:UXMaskedInput EditMask="(###)999-0000000" />

The Value of the UXMaskedInput is determined by the IsSaveLiteral and IsSaveMask property. These properties modify the Value by keeping the literal pattern or the mask character. For example, the UXMaskInput with EditMask set to “(999)000-0000”, the possible value with combination of these properties are shown in the next table.

IsSaveLiteral IsSaveMask Value
True True (___)555-1234
True False (   )555-1234
False True ___5551234
False False 5551234
<Intersoft:UXMaskedInput EditMask="(999)000-0000"
                         IsSaveLiteral="True"  IsSaveMask="True"/>

UXMaskedInput also allows null value which is controlled by the AllowNull property. By default this property value is set to False, hence null value is not allowed by default.

If the value is null, the UXMaskedInput shows an empty string during edit mode and display mode. By deleting the whole edit text (Ctrl + A, Del) in UXMaskedInput the value is automatically set to null, if permissible.

If you prefer, you can also customize how UXMaskedInput displays its text initially. There are two options available, EditMask or Value, which can be set through the DisplayMode property. The EditMask option displays the edit text, while the Value option displays the Value as detailed in the previous paragraph.

With the EditMask set to “(999)000-0000” and Value set to “5551234” the possible displayed text is illustrated in the following table.

DisplayMode Displayed Text
Snippet Code
EditMask (___)555-1234
<Intersoft:UXMaskedInput EditMask="(999)000-0000"
		Value="5551234"
		DisplayMode="EditMask"/>
Value 5551234

Assuming IsSaveLiteral and IsSaveMask is set to False

<Intersoft:UXMaskedInput EditMask="(999)000-0000"
	  Value="5551234"
	  DisplayMode="Value"
	  IsSaveLiteral="False" IsSaveMask="False"/>

Custom Mask Character

By default the mask character in the UXMaskedInput is an underscore character “_”. However, you can customize the mask character through the provided MaskCharacter property.

UXMaskedInput with custom mask character ..

CustomMaskCharacter

Editing Behavior

Although the UXMaskedInput is derived from UXTextBox, the editing behavior is significantly different since UXMaskedInput expected patterned data entry. The behavior differences also mean that there is default TextBox operation which is considered invalid and will be ignored in the UXMaskedInput such as the paste action. In this case, the paste action is valid only when all the text is selected.

UXMaskedInput implements sophisticated editing experiences that allow end users to input data more intuitively. In the following table, I will highlight some of the unique editing behaviors implemented in the control. Let’s take an example of the masked input control with EditMask set to “999-???-000”.

<Intersoft:UXMaskedInput EditMask="999-???-000" />
EditText Action Result
Description
|___-___-___ Input an “A” character ___-A|__-___
The “A” character is inserted in the optional letter section since the digit section is optional. If the digit section is not optional, the “A” character is rejected.
123-|ABC-___ Pressing BackSpace 123|-ABC-___
Caret position is moved no character is deleted since literal could not be deleted.
123-A|BC-___ Pressing BackSpace 123-|_BC-___
Caret position is moved, the character A deleted, and replaced by the MaskCharacter.
12|3-AB|C-___ Pressing BackSpace 12|_-__C-___
Inputted character is replaced by the MaskCharacter while Literal is unchanged. Caret is set on the first highlight position.
|123-ABC-456| Pressing BackSpace [AllowNull=True]

[AllowNull=False] |___-___-___

Deleting all the text behavior depends on the AllowNull property. If the UXMaskedInput allow null, the result is empty UXMaskedInput, otherwise all the character is replaced by the MaskCharacter except Literal.
|123-ABC-456| Pasting text 789-DEF-456 |789-DEF-456
If the text is a valid value (literal is allowed) the pasted text is accepted. 789DEF456 or 456 is also accepted.

Conclusion

In this post, you have now learned the key features of UXMaskedInput. You can play around with the UXMaskedInput features through the properties, or try some readily designed samples here. To see all available features, please see UXMaskedInput Overview.

You will need the latest 2010 R2 bits which you can download it here. If you have questions or feedback about UXMaskedInput or other ClientUI controls, feel free to post them to our community forum.

Regards,

Glenn Layaar

WebExpander with Google Maps

In the 2010 R1 release, Intersoft introduces a new ASP .NET collection control under the WebEssentials suite. Further information about WebEssentials can be found here. One of the controls in the WebEssentials is WebExpander. In this post, I will show you how to mashup Google Maps service with WebExpander to display contact list address. The following screenshot illustrates the above scenario. You can achieve the above scenario by customizing the WebExpander’s Mode and Flow property, and use custom inline template for the WebExpander’s header and content section. You also need to implement WebExpander’s OnClick client side event to call the Google Maps service. I have provided a working sample which you can download at the end of this post. Now, let’s start customizing the WebExpander.

  • Customize the Mode property I’m going to set the Mode property to Fixed.
  • Customize the Flow property I’m going to set the Flow property to Right because I want the Google Maps service to expand to the right of the header.
  • Customize WebExpander’s header and content section As you can see from the above screenshot, there are two sections in the WebExpander. The list of contacts and the Google map address of the selected contact. The list of contacts is the header section and the Google map is the content section. The following code shows the customization of the header and content section using custom template.
    <ISWebEssentials:WebExpanderItem Name="WebExpander1_item0" 
        State="Expanded">
        <headertemplate>
            <div style="margin-left: 10px; padding-top: 12px;
                font-family: Segoe UI;">
            <div style="background-repeat: no-repeat;
                background-position: left center;
                height: 50px; padding-left: 50px; font-size: 12px;
                background-image: url('images/photo1.png');">
                <div style="line-height: 80px; font-size: 12px;
                    font-weight: bold;
                    line-height: 80px;">
                    Patrice Johnson</div></div>
            <div style="height: 18px; line-height: 18px;
                padding-top: 2px; font-size: 11px;">
                191 North Cowley Drive</div>
            </div>
        </headertemplate>
        <contenttemplate>
            <div id="canvas_map_0" style="width: 623px;
                height: 483px;">
                <div style="text-align: center;
                    padding-top: 240px;">
                    Loading Map ...</div>
            </div>
        </contenttemplate>
    </ISWebEssentials:WebExpanderItem>

    The content section is set using a predefined size to hold the Google Maps service.

  • OnClick client side event The Google Maps service API is called in the WebExpander’s OnClick event handler. In order to get the correct map size, you should ensure that the content section is fully expanded before calling the Google Maps API.
    function WebExpander1_OnClick(ctrlId, itemName)
    {
        var exp = ISGetObject(ctrlId);
        var itemObj = exp.Items.GetNamedItem(itemName);
    
        var intervalExpandObj = setInterval(function ()
        {
            if (itemObj.State == "Expanded")
            {
                clearInterval(intervalExpandObj);
                intervalObj = null;
    
                var idx = itemName.substring(itemName.length - 1);
    
                 //Function to call Google Maps API
                ShowMapByAddress(parseInt(idx));
            }
        }, 50);
    }

Once you have finished configuring the WebExpander, you will get something similar to the above screenshot. In summary, I have explained some WebExpander features such as mode, flow, custom template, and client side API which you can use to create a mashup service between WebExpander and Google Maps. For further information about WebExpander features, please visit WebExpander product page here. As mentioned earlier, you can download the sample for this post here. You can also explore other WebExpander samples in the online demo here. If you have questions or feedback about this post or anything related to WebExpander control, feel free to post it to Intersoft community forum. Regards, Glenn Layaar Intersoft Member