Decrease Font Size
Increase Font Size
   BLOG

SQL Server cannot process this media family

by bryian 29. May 2010 18:16

Problem:

You are getting the error message shown below while trying to restore a database.

An exception occurred while executing a Transact-SQL statement or batch. (Microsoft.SqlServer.ConnectionInfo)
------------------------------
ADDITIONAL INFORMATION:
The media family on device 'C:\ysatech\db\MyDB.bak' is incorrectly formed.

SQL Server cannot process this media family.
RESTORE HEADERONLY is terminating abnormally. (Microsoft SQL Server, Error: 3241)
For help, click: here


Solution:

Make sure that you are not restoring the backup from SQL 2008 to SQL 2005 instance.

ASP.NET - Limit number of characters in TextBox control

by bryian 19. May 2010 18:03

Limit the Number of Characters in a Textarea or Text Field |

Limit number of characters in a multiline TextBox? |

TextBox Characters Counter using JavaScript |

Limiting word count in multiline TextBox |

textarea characters limit? |

Limit Character count in Multiline Textbox |

ASP.NET limit TextBox characters

 

Introduction

Recently, I was revisiting the JavaScript used to limit the number of characters in a TextBox control. This client-side script utilized document.getElementById method and the control ID for HTML markup that was generated by ASP.NET. The problem with this script was that it did not work correctly with multiple TextBox controls on the web page and not cross-browser compatible. So I decided to rewrite it to ease the mentioned problems. Shown in Listing 1 is the new content of the JavaScript. There are two functions resided in it namely validateLimit and get_object. The former function accepts three parameters.

1. TextBox object
2. HTML Div id (to hold the text)
3. Maximum number of characters the TextBox control can hold

The purpose of the later function is to ensure that modern and older browsers are able to access the form elements.

Listing 1

    function validateLimit(obj, divID, maxchar) {

        objDiv = get_object(divID);

        if (this.id) obj = this;

        var remaningChar = maxchar - trimEnter(obj.value).length;

        if (objDiv.id) {
            objDiv.innerHTML = remaningChar + " characters left";
        }
        if (remaningChar <= 0) {
            obj.value = obj.value.substring(maxchar, 0);
            if (objDiv.id) {
                objDiv.innerHTML = "0 characters left";
            }
            return false;
        }
        else
        { return true; }
    }

    function get_object(id) {
        var object = null;
        if (document.layers) {
            object = document.layers[id];
        } else if (document.all) {
            object = document.all[id];
        } else if (document.getElementById) {
            object = document.getElementById(id);
        }
        return object;
    }
    //http://lawrence.ecorp.net/inet/samples/regexp-format.php#convert
    function trimEnter(dataStr) {
        return dataStr.replace(/(\r\n|\r|\n)/g, "");
    }

Putting everything together.

Listing 2

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Without master page</title>
     <script type="text/javascript" src="js/JScript.js" ></script>
</head>
<body>
    <form id="form1" runat="server">
     <div> <br />
    <div id="lblMsg1">240 characters left</div>
    <asp:TextBox ID="TextBox1" runat="server" Height="50px" MaxLength="240" TextMode="MultiLine"
                    Width="600px" ToolTip="Summary:(240 characters)" 
                    onkeyup="return validateLimit(this, 'lblMsg1', 240)"></asp:TextBox>
        <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" 
            ControlToValidate="TextBox1" Display="Dynamic" 
            SetFocusOnError="True">*</asp:RequiredFieldValidator>
    <br /><br /><br />
    <div id="lblMsg2">300 characters left</div>
    <asp:TextBox ID="TextBox2" runat="server" Height="50px" MaxLength="300" TextMode="MultiLine"
                    Width="600px" ToolTip="Summary:(300 characters)" 
                    onkeyup="return validateLimit(this, 'lblMsg2', 300)"></asp:TextBox>
        <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" 
            ControlToValidate="TextBox2" Display="Dynamic" 
            SetFocusOnError="True">*</asp:RequiredFieldValidator>
    <br /> <br />
    <asp:Button ID="Button1" runat="server" onclick="Button1_Click" Text="Button" />
    <br />
    </div>
    </form>
</body>
</html>

Here is the output:

Figure 1

characters count

History

  • May 20, 2010: First release.
  • May 20, 2010
    • Added new function trimEnter to replace enter key with empty string.

Conclusion

If you find any bugs or disagree with the contents, please drop me a line and I'll work with you to correct it. I would suggest downloading the demo and explore it in order to grasp the full concept of it because I might left out some useful information.

IE, Firefox, Google Chrome, Safari

Tested on IE 6.0/7.0/8.0, Google Chrome, Firefox, Safari

Resources

document.getElementById On All Browsers – Cross browser getElementById
Modifying Strings with Regular Expressions

Watch this script in action

Demo

Downloads

Download

Joomla - PDF not working in IE8

by bryian 5. May 2010 15:27

 

PDF Display Fix in IE8 |

Joomla! PDF Display Fix |

Joomla! PDF problems with IE8 |

PDF button does not work in Internet Explorer |

Joomla! PDF not working in IE8

 

The usual symptom is that the pop-up window is empty. Go to Joomla Documentation -- http://docs.joomla.org/PDF_Display_Fix_in_IE7. Follow the steps and add the highlighted code displayed below into the respective PHP file. This will take care of the PDF display problem in IE8.

browser_detection.pdf and icon.pdf

Tags: , , ,

PHP

ASP-NET AJAX MultiHandleSliderExtender - Slide by Year and Month

by bryian 6. March 2010 18:27

ASP.NET How to use MultiHandleSliderExtender|

multi handle slider extender|

MultiHandleSlider Control|

MultiHandleSliderExtender|

Ajax MultiHandleSliderExtender|

ASP.NET Charts storage mode |

AJAX Control Toolkit|

OnClientDrag, OnClientDragEnd, RaiseChangeOnlyOnMouseUp

Last updated - 03/09/2010

Replaced the PageMethods with client-side JavaScript Array. I received a feedback from the CodeProject member quoted "Here is the problem the dragging is sending many requests to the server and the last one gets executed but then that means the database has to work that extra bit and also the IIS." I thinks he has a point, so I decided to store the year and month data for each RowNumber on the client-side JavaScript array. This will allow the sliders' range label be updated through the client-side. See New Update 1 section.

Introduction

In this tutorial, I will demonstrate how to use MultiHandleSlider extender to choose or display year and month in a range. This control eliminates the need to use four DropDownlist controls to hold the range values and validation control for validating user selection. Then we will use the Column Chart to display number of cars on Sesame Street based on the selected range values. This chart allows the user to drill down into details for the selected car brands. See figure 1.

Figure 1
MultiHandleSlider results

Getting Started

  1. Download AJAX Control Toolkit Release Notes - May 2009 Release
  2. Download Samples Environment for Chart Controls
  3. Displayed below is the snapshot of the project solution explorer. You are welcome to download this demo.

Figure 2

Project Structure

Back-end database

First, add a new column to the table, name it YearMonth and populate it with the concatenation of the Year and Month data. See figure 3. The original table is on the left. With this setup, we can easily select data within the desired year and month range from the table.

Figure 3
Add column to table

Use ROW_NUMBER function to generate row number for each row of the result set. Then we can use the row number to populate the slider range of values. The query shown below return sixty two rows and this means that the slider range of values goes from one to sixty two. In other words we can set the minimum and maximum properties of the slider to one and sixty two respectively.

Figure 4
Create Row Number

Putting everything together

For simplification's sake, I use XML as Data Source in this tutorial. There are two XML file in the App_Data folder namely CarsList.xml and SliderRange.xml. The former file contains all the data in the table that is shown in figure 3. The latter xml file hold the result set shown in figure 4. Create two custom entity classes named CarsList and Range to hold the public properties. See listing 1.

Listing 1

public class CarsList
{
    public CarsList(){}
    public int CarCount { get; set; }
    public int YearMonth { get; set; }
    public string CarBrand { get; set; }
    public string Date { get; set; }
}
public class Range
{
    public Range(){}
    public string Year { get; set; }
    public string Month { get; set; }
    public int RowNumber { get; set; }
}

Let start by adding a Script Manager on to the page and set the EnablePageMethods and EnablePartialRendering properties to true. By setting the EnablePageMethods property to true, the client script can access the static page methods in an ASP.NET page. The EnablePartialRendering property allows us to specify only the region of the page to be refreshed. Next, drag a TextBox, MultiHandleSliderExtender, four HiddenField and two Label controls on to the page and wrap it inside an UpdatePanel. Set the TargetControlID of the MultiHandleSliderExtender control to the ID of the TextBox control. Add two handles to the control and set its ControlID to rangeStart and rangeEnd respectively. See listing 2. The purpose of the Label controls is to display the selected range values. The HiddenField controls are used to hold the handles' values. Initialize the MultiHandleSliderExtender with the setting shown below.

  • OnClientDrag= Drag, The event raised when the user drags the handle.
  • OnClientDragEnd = DragEnd, The event raised when the user stops dragging the handle.
  • Increment = 1, determines the number of points to increment or decrement the slider values.
  • RaiseChangeOnlyOnMouseUp = true, fires the change event on the extended TextBox only when the left mouse button is released.
  • EnableRailClick = false

Listing 2

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePageMethods="true" EnablePartialRendering="true" /> 
    <div>
    <asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<table>
    <tr><td colspan="2">
       <asp:TextBox ID="txtSlider" runat="server"></asp:TextBox>  
    
    <cc1:MultiHandleSliderExtender ID="MultiHandleSliderExtender1" runat="server" ShowHandleDragStyle="true"
    BehaviorID="mhSlider" TargetControlID="txtSlider" Length="500" ShowInnerRail="true"
        EnableMouseWheel="false" Increment="1" 
        RaiseChangeOnlyOnMouseUp="true" EnableRailClick="false"
       OnClientDragEnd="DragEnd" OnClientDrag="Drag" 
        ShowHandleHoverStyle="true" Maximum="222" Minimum="1">
        <MultiHandleSliderTargets>
         <cc1:MultiHandleSliderTarget ControlID="rangeStart" />
            <cc1:MultiHandleSliderTarget ControlID="rangeEnd" />
        </MultiHandleSliderTargets>  
   </cc1:MultiHandleSliderExtender>
   <br />
   </td></tr>
   <tr>
   <td><asp:Label ID="lblStartRange" runat="server" Text=""></asp:Label></td>
   <td><asp:Label ID="lblEndRange" runat="server" Text=""></asp:Label> </td>
   </tr>  
    <tr><td>
    <asp:HiddenField ID="rangeEnd" Value="10" runat="server" />
    <asp:HiddenField ID="rangeStart" Value="1" runat="server" />
     <asp:HiddenField ID="hdfTrackRangeStart" runat="server" Value="0" />
 <asp:HiddenField ID="hdfTrackRangeEnd" runat="server" Value="0" />
    </td></tr>
</table>
</ContentTemplate>            
</asp:UpdatePanel>
</div>

Shown below is the client-side code. Dragging the handle will trigger the ASP.NET page method SliderRange to update the lblStartRange and lblEndRange value. Once the users release the handle, the DragEnd function will be executed.

Listing 3

<script type="text/javascript">
    var isDragging = false;
    function Drag(sender, args) {
        GetSliderRange($get("rangeStart").value, $get("rangeEnd").value);
    }

    function DragEnd(sender, args) {
        //prevent postback on slider click
        if ($get("hdfTrackRangeStart").value !== $get("rangeStart").value) {
             __doPostBack("btnLoadChart", "");
        }
        if ($get("hdfTrackRangeEnd").value !== $get("rangeEnd").value && $get("hdfTrackRangeEnd").value !== '0') {
             __doPostBack("btnLoadChart", "");
        }
    }

    function GetSliderRange(startV, endV) {
        PageMethods.SliderRange(startV, endV, this.callback);
    }

    function callback(result) {
        var arrResult = result.split("--");
        $get("lblStartRange").innerHTML = arrResult[0];
        $get("lblEndRange").innerHTML = arrResult[1];
    }
</script>

Create a generic List<T> of Range objects in the code behind and mark it as static so that is accessible from the client script or other static method. Shown below are the contents in the Page_Load event. The PopulateSlider() method read the contents in the SliderRange.xml file and store it in the lstSliderRange. Initialize the MultiHandleSliderExtender minimum and maximum value to 1 and biggest RowNumber in lstSliderRange respectively. See listing 4.

Listing 4

protected static List<Range> lstSliderRange = null;
protected void Page_Load(object sender, EventArgs e)
    {
	  Chart1.Click += new ImageMapEventHandler(Chart1_Click);
        PopulateSlider();

        if (!Page.IsPostBack)
        {
            //slider min and max value
            MultiHandleSliderExtender1.Minimum = 1;
            MultiHandleSliderExtender1.Maximum = 
			int.Parse(lstSliderRange.Max(r => r.RowNumber).ToString());

            //hidden field
            rangeEnd.Value = MultiHandleSliderExtender1.Maximum.ToString();
            rangeStart.Value = MultiHandleSliderExtender1.Minimum.ToString();

		PopulateChart(int.Parse(rangeStart.Value), int.Parse(rangeEnd.Value));
        }
        SetLabel();
    }

Displayed below is the implementation of PopulateSlider() method. The lstSliderRange object is cached to increase performance and its contents are fetched again when the file contents changes. Depending on how often we update the data source, we can cache it based on the changes in the database, folder, file or time based expiration. Read more about ASP.NET Caching technique from here.

Listing 5

//get slider range
    void PopulateSlider()
    {
        //Cache the frequently used data
        if (Cache["Cache_lstSliderRange"] == null)
        {
            XDocument xmlDoc = XDocument.Load(Server.MapPath(Utilities.Instance.SliderRangeXMLPath));
            lstSliderRange = (from c in xmlDoc.Descendants("Range")
                              select new Range
                              {
                                  Month = (string)c.Attribute("Month"),
                                  Year = (string)c.Attribute("Year"),
                                  RowNumber = (int)c.Attribute("RowNumber")
                              }).ToList();

            Cache.Insert("Cache_lstSliderRange", lstSliderRange,
                new System.Web.Caching.CacheDependency(Server.MapPath(Utilities.Instance.SliderRangeXMLPath)));
        }
        else
        {
            lstSliderRange = Cache["Cache_lstSliderRange"] as List<Range>;
        }
    }

The SetLabel() method display the MultiHandleSliderExtender start and end range values in the lblStartRange and lblStartEnd Label controls. The SliderRange method is decorated with [System.Web.Services.WebMethod] making the method accessible from client-side JavaScript. The GetSliderText method accept two parameters, the first parameter refer to the row number and the second parameter refers to the left or right handle. For instance, calling SliderRange(2, 10) will yield "From Year: 2005 Month:02--To Year: 2005 Month:10". First, it will query the lstSliderRange object and retrieve the Year and Month from the result set. If the pos==s, set the text to From and To if pos==e. See listing 6.

Listing 6

//set the slider start and end label
    void SetLabel()
    {
        string[] arrLabel = SliderRange(rangeStart.Value, rangeEnd.Value).Split("--".ToCharArray());
        lblStartRange.Text = arrLabel[0];
        lblEndRange.Text = arrLabel[2];
    }

    [System.Web.Services.WebMethod]
    public static string SliderRange(string start, string end)
    {
        if (lstSliderRange != null)
        {
            return GetSliderText(start, "s") + "--" + GetSliderText(end, "e");
        }
        else
        {
            return "";
        }
    }

    protected static string GetSliderText(string rn, string pos)
    {
        string strRangeText = string.Empty;
        IEnumerable<Range> rangeText;

        rangeText = lstSliderRange.Where(r => r.RowNumber == int.Parse(rn))
            .Select(r => new Range
            {
                Year = r.Year,
                Month = r.Month
            });

        if (pos == "s")
        {
            strRangeText = "<b>From</b> Year: " + rangeText.First().Year + " Month: " + rangeText.First().Month;
            return strRangeText;
        }
        else
        {
            strRangeText = "<b>To</b> Year: " + rangeText.First().Year + " Month: " + rangeText.First().Month;
            return strRangeText;
        }
    }

At this point, you should see something like below on the browser.

Figure 5
Check point

Chart Control

Make sure to download the Microsoft Chart Controls for Microsoft .NET Framework 3.5 because the Chart controls required System.Web.DataVisualization.Design.dll and System.Web.DataVisualization.dll. I also included both the dlls in the sample code. Some of the codes in this section are from Samples Environment for Chart Controls. First, let create a method to bind the data source to the Column Chart. This method accepts two parameters, start and end range values. Then use LINQ to query the CarsList.xml data source and find all the records where YearMonth between start and end range values. Group the result by car brands, stores it in lstCarsnTotal and bind it to the chart. See listing 7.

Listing 7

void PopulateChart(int start, int end)
    {
        List<CarsList> lstCarsnTotal = new List<CarsList>();

        XDocument xmlDoc = XDocument.Load(Server.MapPath(Utilities.Instance.CarsListXMLPath));
        lstCarsnTotal = (from c in xmlDoc.Descendants("Car")
                         where (int)c.Attribute("YearMonth") >= GetRange(start) && (int)c.Attribute("YearMonth") <= GetRange(end)
                         group c by (string)c.Attribute("CarBrand") into g

                         select new CarsList
                         {
                             CarCount = g.Sum(c => (int)c.Attribute("Count")),
                             CarBrand = g.Key
                         }).ToList();
	  Chart1.Series["Default"].ChartType = SeriesChartType.Column;
        Chart1.Series["Default"].Points.DataBindXY(lstCarsnTotal, "CarBrand", lstCarsnTotal, "CarCount"); 
}

//return YearMonth
    protected static int GetRange(int rn)
    {
        IEnumerable<Range> rangeText;

        rangeText = lstSliderRange.Where(r => r.RowNumber == rn)
            .Select(r => new Range
            {
                Year = r.Year,
                Month = r.Month
            });

        return int.Parse(rangeText.First().Year + rangeText.First().Month);
    }

Now, when the user click on the column chart, a GridView will appears next to it. The PopulateGrid method takes the car brand as an argument. Then use LINQ to query the SliderRange.xml data source where YearMonth between the selected range values and CarBrand equal to the selected car brand. See listing 8.

Listing 8

protected void Chart1_Click(object sender, ImageMapEventArgs e)
    {
        if (!GridView1.Visible)
        {
            GridView1.Visible = true;
        }
        //kept track of selected car type
        ChartPostBackValue.Value = e.PostBackValue;
        lblCarBrand.Text = "Car Brand: " + e.PostBackValue;

        PopulateGrid(e.PostBackValue);
        PopulateChart(int.Parse(rangeStart.Value), int.Parse(rangeEnd.Value));
    }

    void PopulateGrid(string strPostBavkVal)
    {
        List<CarsList> lstCarsnTotal = new List<CarsList>();

        XDocument xmlDoc = XDocument.Load(Server.MapPath(Utilities.Instance.CarsListXMLPath));
        lstCarsnTotal = (from c in xmlDoc.Descendants("Car")
                         where (int)c.Attribute("YearMonth") >= GetRange(int.Parse(rangeStart.Value))
                         && (int)c.Attribute("YearMonth") <= GetRange(int.Parse(rangeEnd.Value)) && (string)c.Attribute("CarBrand") == strPostBavkVal
                         select new CarsList
                         {
                             CarCount = (int)c.Attribute("Count"),
                             CarBrand = (string)c.Attribute("CarBrand"),
                             Date = (string)c.Attribute("Date")
                         }).ToList();

        GridView1.DataSource = lstCarsnTotal;
        GridView1.DataBind();
    }

Known Issue

On design time, we will see the error "MultiHandleSliderExtender could not be set on property MultiHandleSliderTargets" but code work fine at run time. I have downloaded the example and latest version of Ajax Control Toolkit from CodePlex but didn't solve the problem. A workaround is adding the TagPrefix next to the MultiHandleSliderTargets tag during design time and remove it at run time. Hopefully someone can shed some light on this.

Points of Interest

The Default2.aspx in the sample code includes a master page. If you use a master page, make sure to use the Control.ClientID. For some reason the client __doPostBack function do not work with master page, the work around was to call the button click event. See listing 9. Hopefully someone can shed some light on this too.

Listing 9

<script type="text/javascript">
    var isDragging = false;
    function Drag(sender, args) {
        GetSliderRange($get("<%= rangeStart.ClientID %>").value, $get("<%= rangeEnd.ClientID%>").value);
    }
    function DragEnd(sender, args) {
        //prevent postback on slider click
        if ($get("<%= hdfTrackRangeStart.ClientID %>").value !== $get("<%= rangeStart.ClientID %>").value) {
            $get("<%= btnLoadChart.ClientID %>").click();
            //__doPostBack("<%= btnLoadChart.ClientID %>", "");
        }
        if ($get("<%= hdfTrackRangeEnd.ClientID %>").value !== $get("<%= rangeEnd.ClientID %>").value && $get("<%= hdfTrackRangeEnd.ClientID %>").value !== '0') {
            $get("<%= btnLoadChart.ClientID %>").click();
            //__doPostBack("<%= btnLoadChart.ClientID %>", "");
        }
    }

    function GetSliderRange(startV, endV) {
        PageMethods.SliderRange(startV, endV, this.callback);
    }

    function callback(result) {

        var arrResult = result.split("--");

        $get("<%= lblStartRange.ClientID %>").innerHTML = arrResult[0];
        $get("<%= lblEndRange.ClientID %>").innerHTML = arrResult[1];

    }
    </script>

I have noticed that, clicking on the handle will trigger the DragEnd function and cause unnecessary post back. To remedy this problem, compare the old selected range value and the current selected range value, if they are not equal then permit the call of client-side __doPostBack function. See listing 10.

Listing 10

function DragEnd(sender, args) {
        //prevent postback on slider click
        if ($get("hdfTrackRangeStart").value !== $get("rangeStart").value) {
             __doPostBack("btnLoadChart", "");
        }
        if ($get("hdfTrackRangeEnd").value !== $get("rangeEnd").value && $get("hdfTrackRangeEnd").value !== '0') {
             __doPostBack("btnLoadChart", "");
        }
    }

The chart displayed correctly on my local machine but it displayed a sequence of strange characters in the hosting environment. After doing some research, I discovered that I didn't set appropriate permissions on the storage folder and EnableSessionState on the page directive was set to false. The list of ASP.NET Charts storage mode is available here.

New Update 1

Create a client-side Array object named arrRange, loop through each rows in the lstSliderRange object and add the values year and month to it. Place this method inside the !Page.IsPostBack block. See listing 11.

Listing 11

void CreateArray()
    {
        foreach (Range r in lstSliderRange)
        {
            Page.ClientScript.RegisterArrayDeclaration("arrRange", "'"+r.Year +"--" + r.Month+"'");
        }
    }

Instead of using the PageMethods to lookup the RowNumber text, call the client-side GetSliderText function. See listing 12.

Listing 12

function GetSliderRange(startV, endV) {
        $get("<%= lblStartRange.ClientID %>").innerHTML = GetSliderText(arrRange[startV - 1], 's');
        $get("<%= lblEndRange.ClientID %>").innerHTML = GetSliderText(arrRange[endV - 1], 'e');
        // alert(arrRange[startV - 1]);
        // PageMethods.SliderRange(startV, endV, this.callback);
    }

    function GetSliderText(r, p) {
        var arrResult = r.split("--");
        var strText = '';
        if (p === 's') {
            strText = "<b>From</b> Year: " + arrResult[0] + " Month: " + arrResult[1];

        }
        else {
            strText = "<b>To</b> Year: " + arrResult[0] + " Month: " + arrResult[1];
        }
        return strText;
    }

Conclusion

If you find any bugs or disagree with the contents, please drop me a line and I'll work with you to correct it. I would suggest downloading the demo and explore it in order to grasp the full concept of it because I might left out some useful information.

IE, Firefox, Google Chrome, Safari

Resources

ASP.NET Charts - Storage Mode
MultiHandleSlider Demonstration
ScriptManager..::.EnablePageMethods Property
Using Microsoft's Chart Controls In An ASP.NET Application: Rendering the Chart
Working with Client-Side Script

Watch this script in action

Demo

Downloads

Download