Sunday, September 30, 2012

Simple JQuery image button mouse over (hover) effect

Simple JQuery Image Button Hover Effect

Overview:

   Here is just a quick down and dirty JQuery function to handle the MouseEnter and MouseLeave events of the target control labeled with the class "button" in this case it's an image control. As you can see below hover accepts two arguments, these arguments are then MouseEnter and Mouseleave events.

By changing these arguments you can control many other elements properties and add other effects. Thats really all there is too it

JQuery function:

     Just drop this in your page head section and set your appropriate image paths.

<script type="text/javascript">
 
        $(document).ready(function () {
          
            $(".button").hover(function () {
                $(this).attr("src""./app_themes/default/images/submit_btn_hvr.png");
            }, function () {
                $(this).attr("src""./app_themes/default/images/submit_btn.png");
            });
                                   
        });
 
    </script>

Markup:

Make sure that you set your initial ImageURl to the same path as the second argument in the hover method, otherwise you won't have an image on page load
 <asp:ImageButton ID="Imagebutton1" class="button" ImageUrl="~/app_themes/default/images/submit_btn.png"
        runat="server" />

Saturday, September 29, 2012

Handling backspace/delete with AJAX MaskedEditExtender in chrome

      Another one of these annoying behaviours with AJAX controls is with the Masked Edit Extender and Chrome browsers. As designed (according to Microsoft) the AJAX MaskedEditExtender does not handle the backspace or delete key strokes. I have not tested this with all mask types, but one in particular is the date mask type.

      I wanted to add just a touch of control to my birthdate text control. Nothing real fancy just format "99/99/9999" to restrict the user gobbly gook. All works as advertised except that you can't use the backspace key if you make a mistake. I don't know about you all, but that key has been my friend since day one, so not being able to use it got me Goggling.  With some searching I came across this javascript that handles the key presses and enables delete in the MaskedEditExtender.

   
Here's the markup. Just need to add the key handlers to the textbox and put the extenders id as function arguments.



  <asp:TextBox ID="ctl_birthday"  runat="server" 
                    onkeydown='KeyDownHandler("MaskedEditExtender1");' ></asp:TextBox>
                    <AJAX:RoundedCornersExtender TargetControlID="ctl_birthday" ID="RoundedCornersExtender1" 
                    runat="server" Corners="All" Radius="8">
                    </AJAX:RoundedCornersExtender>
                    <AJAX:MaskedEditExtender MaskType="Date" Mask="99/99/9999" ID="MaskedEditExtender1" 
                    TargetControlID="ctl_birthday" runat="server">
                    </AJAX:MaskedEditExtender>


Here is the javascript for the handler:


function KeyUpHandler(sender) { }
 
function KeyDownHandler(maskExtenderId) {
 
    if (navigator.appName != "Microsoft Internet Explorer") {
 
 
 
        if (event.keyCode == 35 || event.keyCode == 36) { // Home and End buttons functionality
 
 
 
            var txtElement = $get(event.srcElement.id);
 
            var txtElementText = GetTextElementValue(event.srcElement.id);
 
 
 
            if (event.keyCode == 36) {//Home button
 
                setCaretPosition(txtElement, 0);
 
            }
 
            if (event.keyCode == 35) {//End button
 
                setCaretPosition(txtElement, txtElementText.length);
 
            }
 
        }
 
 
        if (event.keyCode == 8 || event.keyCode == 46) {
 
 
 
            var txtElement = $get(event.srcElement.id);
 
            var txtElementText = GetTextElementValue(event.srcElement.id);
 
            var txtElementCursorPosition = doGetCaretPosition(txtElement);
 
            var maskExtender = $find(maskExtenderId);
 
 
            var start = txtElement.selectionStart;
 
            var end = txtElement.selectionEnd;
 
            var selectedSymbols = end - start;
 
 
 
            if (event.keyCode == 8) //BackSpace
            {
 
                if (selectedSymbols > 0) {//if there is selection(more then 1 symbol)
 
 
                    var str1 = txtElementText.substr(0, start);
 
                    var str2 = txtElementText.substr(end);
 
                    var str = str1 + str2;
 
                    if (str.length < txtElementText.length) str = appendStrWithChar(str, txtElementText, "_");
 
                    SetTextElementValue(event.srcElement.id, str);
 
                    //txtElement.value = str;
 
                    maskExtender._LogicTextMask = deletePromptChars(str, "_");
 
                    setCaretPosition(txtElement, start);
 
                }
 
                else {
 
                    if ((txtElementCursorPosition - 1) >= 0) {
 
                        var symbol_to_delete = txtElementText[txtElementCursorPosition - 1];
 
                        if (symbol_to_delete == "_") {
 
                            setCaretPosition(txtElement, txtElementCursorPosition - 1);
 
                        }
 
                        else {
 
                            var str1 = txtElementText.substr(0, txtElementCursorPosition - 1);
 
                            var str2 = txtElementText.substr(txtElementCursorPosition);
 
                            var str = str1 + str2;
 
                            if (str.length < txtElementText.length) str = appendStrWithChar(str, txtElementText, "_");
 
                            SetTextElementValue(event.srcElement.id, str);
 
                            //txtElement.value = str;
 
                            maskExtender._LogicTextMask = deletePromptChars(str, "_");
 
                            setCaretPosition(txtElement, txtElementCursorPosition - 1);
 
                            //var real_text = deletePromptChars(str, "_");
 
                        }
 
                    }
 
                }
 
 
 
            }
 
            if (event.keyCode == 46) //Delete
            {
 
                if (txtElementCursorPosition >= 0 && txtElementCursorPosition < txtElementText.length
 
                        && ((selectedSymbols <= 1 && txtElementText[txtElementCursorPosition] != "_") || selectedSymbols > 1)) {
 
 
                    if (selectedSymbols > 1) {//if there is selection(more then 1 symbol)
 
                        var str1 = txtElementText.substr(0, start);
 
                        var str2 = txtElementText.substr(end);
 
                        var str = str1 + str2;
 
                        if (str.length < txtElementText.length) str = appendStrWithChar(str, txtElementText, "_");
 
                        SetTextElementValue(event.srcElement.id, str);
 
                        //txtElement.value = str;
 
                        maskExtender._LogicTextMask = deletePromptChars(str, "_");
 
                        setCaretPosition(txtElement, start);
 
                    }
 
                    else {//no selection or 1 symbol selected
 
                        var symbol_to_delete = txtElementText[txtElementCursorPosition];
 
 
                        if (symbol_to_delete != "_") {
 
                            var str1 = txtElementText.substr(0, txtElementCursorPosition);
 
                            var str2 = txtElementText.substr(txtElementCursorPosition + 1);
 
                            var str = str1 + str2;
 
                            if (str.length < txtElementText.length) str = appendStrWithChar(str, txtElementText, "_");
 
                            SetTextElementValue(event.srcElement.id, str);
 
                            //txtElement.value = str;
 
                            maskExtender._LogicTextMask = deletePromptChars(str, "_");
 
                            setCaretPosition(txtElement, txtElementCursorPosition);
 
                        }
 
                    }
 
                }
 
 
 
            }
 
        }
 
 
    }
 
 
}
 
function GetTextElementValue(elementId) {
 
    var textBox = $get(elementId), text;
 
    if (textBox.AjaxControlToolkitTextBoxWrapper) {
 
        text = textBox.AjaxControlToolkitTextBoxWrapper.get_Value();
 
    }
 
    else {
 
        text = textBox.value;
 
    }
 
 
    return text;
 
}
 
 
function SetTextElementValue(elementId, someText) {
 
    var textBox = $get(elementId);
 
    if (textBox.AjaxControlToolkitTextBoxWrapper) {
 
        textBox.AjaxControlToolkitTextBoxWrapper.set_Value(someText);
 
    }
 
    else {
 
        textBox.value = someText;
 
    }
 
}
 
 
function appendStrWithChar(str, templateStr, appChar) {
 
    var newStr = str;
 
    var difference = templateStr.length - newStr.length;
 
 
    if (difference > 0) {
 
        for (i = 0; i < difference; i++) { newStr = newStr + "_"; }
 
    }
 
    return newStr;
 
}
 
 
function deletePromptChars(str, promptChar) {
 
    var newStr = str;
 
    for (i = 0; i < newStr.length; i++) {
 
        if (str[i] == promptChar) {
 
            newStr = newStr.substr(0, i);
 
            return newStr;
 
        }
 
    }
 
}
 
 
function doGetCaretPosition(ctrl) {
 
    var CaretPos = 0; // IE Support
 
    if (document.selection) {
 
        ctrl.focus();
 
        var Sel = document.selection.createRange();
 
        Sel.moveStart('character', -ctrl.value.length);
 
        CaretPos = Sel.text.length;
 
    }
 
    // Firefox support
 
    else if (ctrl.selectionStart || ctrl.selectionStart == '0')
 
        CaretPos = ctrl.selectionStart;
 
    return (CaretPos);
 
}
 
 
function setCaretPosition(ctrl, pos) {
 
    if (ctrl.setSelectionRange) {
 
        ctrl.focus();
 
        ctrl.setSelectionRange(pos, pos);
 
    }
 
    else if (ctrl.createTextRange) {
 
        var range = ctrl.createTextRange();
 
        range.collapse(true);
 
        range.moveEnd('character', pos);
 
        range.moveStart('character', pos);
 
        range.select();
 
    }
 
}


Happy Coding...

Merlin

Friday, September 28, 2012

Randomizing LINQ to SQL Query Results

Another stumper solved today.. Something as simple as randomizing LINQ results

It's not something built-in. There is no Random query operator provided by Microsoft. In addition, it can't be done simply by using the System.Random class, because everything in a LINQ to SQL query must be translatable to SQL. That being the case...Let's detail the solution that uses a SQL user-defined function. The most common way to sort records randomly is to use the NEWID SQL Server function. This is what this solution uses.
First, create the following view:

CREATE VIEW RandomView
AS
SELECT NEWID() As ID
Then create the following function that uses the view:CREATE FUNCTION GetNewId
(
)
RETURNS uniqueidentifier
AS
BEGIN
RETURN (SELECT ID FROM RandomView)
END
The view is required because it's not possible to directly use NEWID in a scalar function.

And now we can use this in any LINQ to SQL query to retrieve truly random records.



 Dim p = (From u In db.DBUserPhotos
                Join t2 In db.DBUsers On u.UserID Equals t2.ID
        Where (u.IsMain = True)
        Where (t2.IsActive = True)
        Where (u.IsFlagged = False)
        Where (t2.IsFlagged = False)
        Where (u.IsPublic = True)
        Where (u.IsEnabled = True)
        Order By db.GetNewId
                Select New MainPhotoInfo With { _
                    .ID = t2.ID, _
                    .Username = t2.Username, _
                    .PhotoLocation = u.PhotoLocation, _
                    .Email = t2.Email}).Take(10)


Happy Coding



Tuesday, September 25, 2012

Using ResolveUrl to correct paths in html controls

Just a quick post. When i'm working on my development server and debug locally I run into issues on html controls and paths.  Such as: http://localhost:9999/YourVirtualDirectory/

The directory does not really exist so when the dom objects access paths like this, they won't work

<script type="text/javascript" src="~/Scripts/jquery.lavalamp.js"></script>

Neither will this

<script type="text/javascript" src="../Scripts/jquery.lavalamp.js"></script>

But this will

<script type="text/javascript" src='<%= ResolveUrl("~/Scripts/jquery.lavalamp.js") %>'></script>


So there ya have it. Use the ResolveUrl method to fix those pesky paths once and for all and not have to change it prior to moving to production.

Happy Coding..
Merlin

Centering content in a DIV element

Centering contents of a DIV element

No this isn't exactly brain surgery but it did stump me for a spell. I laid out a div with a width of 100% and a text-align:center, then I dropped in a table with 2 columns and a fixed width.

The desired results was a table centered in the page. Unfortunately that was not the case. I'm not sure why this didn't work, hopefully someone can shed some light on it for me.

Here's the code I tried:


<div style="text-align:centerwidth:400px;">
        <table border="0" cellpadding="0" cellspacing="0" width="400">
            <tr>
                <td>fdsdf
                </td>
            </tr>
        </table>
    </div>

By simply adding the margin: auto; attribute to the table, it magically moves to the center of the div as desired.


 <table border="0" cellpadding="0" cellspacing="0" width="400" style="margin:auto;">


If anyone has a reason behind this I would like to hear it. Just one of those weird quirks that makes me go hmmm... Could be by design, just seems wonky. I also dropped some controls in the DIV and it didn't center them either.  Hmmm.

Happy Coding...
Merlin

Sunday, September 23, 2012

Ajax AutoCompleteExtender Page Method Magic

Using Page Methods with AJAX Auto Complete Extenders

    Using AJAX AutocompleteExtender Page Methods can be a tricky proposal, as I have discovered recently while working on a project. There seems to be a few key settings that seem to be elusive. Countless searches later I have put it all together and though I would share it with with you.
 
      Why use page methods instead of a Webservice file ASMX?? If you only have one extender and/or you want to keep everything in one file, using page methods is a quick way to do it.

Here's the quick checklist:


  • Script Manager must have it's EnablePageMethods property set to true
  • Page method must be marked Public and Shared
  • Method must be in the aspx file and not in a user control
  • Method must be marked with   <webmethod> and <Scriptmethod attribute>
  • Method must be declared specifically as outlined below

<WebMethod()> _
    <Script.Services.ScriptMethod()> _
    Public Shared Function GetCompletionList(ByVal prefixText As StringByVal count As IntegerAs System.String()
        Return {""""}
    End Function

The part that stumped me for so long is the fact you can't use the Auto Complete Extender AND use the page methods in a User Control. Dino Esposito touches on this in this MSDN Magazine Article

"A callable page method is a public static (or Shared in Visual Basic® .NET) method defined in the codebehind class and decorated with the same WebMethod attribute used for Web service methods. At present, this is limited to ASPX pages-both inline and codebehind code-but might be extended in the future to user controls and custom controls."

Mr. Esposito's article goes in depth on the usage and options available

Hope this helps someone...  Happy Coding