|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionIn my last article we modified our ListBox to allow meta keys (SHIFT and CONTROL) to be used to select multiple items in the ListBox. In this article we're going to keep our attention focused on the user and handle a different input scenario: the almighty mouse wheel. BackgroundOnce again, let's review the requirements checklist we drew out in the second article:
It should be obvious which requirement to tackle next. It wouldn't be a good idea to optimize the event code when we haven't even handled all of the required events yet! Compatibility ShmemadabilityThe default effect of mouse wheel events depends on the derived
It would be nice to be able to control the mouse wheel, so that we can either default to the above behavior matrix, prevent wheel scrolling, or enforce it. It would be even nicer if this property could be set on the server control, either declaratively or programmatically. I like to have nice things, so let's do it like that. Mouse Wheel 101The good news is, we can support this for the 6 browsers we've targeted to this point (which, with the exception of Safari 2 thus far, encompasses the majority browsers supported by Microsoft's ASP.NET AJAX Extensions framework). There is a consistency problem that we have to hack around though. Both Opera and IE have an Painfully Familiar// 2.)
// Define the client control's class
//
DanLudwig.Controls.Client.ListBox = function(element)
{
// initialize base (Sys.UI.Control)
DanLudwig.Controls.Client.ListBox.initializeBase(this, [element]);
// declare fields for use by properties
this._mouseWheelScroll = null;
this._requiresContainerScroll = null;
this._scrollStateEnabled = null;
this._horizontalScrollEnabled = null;
this._scrollTop = null;
this._scrollLeft = null;
}
// 3d)
// Define the property get and set methods.
//
set_mouseWheelScroll : function(value)
{
if (this._mouseWheelScroll !== value)
{
this._mouseWheelScroll = value;
this.raisePropertyChanged('_mouseWheelScroll');
}
}
,
get_mouseWheelScroll : function()
{
return this._mouseWheelScroll;
}
,
You should be able to add a property like that in your sleep by now. We're going to write the server control property later, but let me warn you that it's not going to be a boolean. The client control's Ready... Set... HACK! _initializeEvents : function()
{
// handle mouse wheel events separately from all others
if (this.get_mouseWheelScroll() != null)
{
// IE and Opera have an onmousewheel event
this._onmousewheelHandler = Function.createDelegate(
this, this._onMouseWheel);
$addHandlers(this.get_element(),
{
'mousewheel' : this._onMouseWheel
}, this);
// also register the container's mouse wheel event
if (this.get_requiresContainerScroll())
{
$addHandlers(this.get_elementContainer(),
{
'mousewheel' : this._onMouseWheel
}, this);
}
// FF doesn't have an onmousewheel event
if (this.get_element().onmousewheel === undefined)
this.get_element().addEventListener('DOMMouseScroll',
this._onMouseWheel, false);
}
// rest of the event initialization code stays the same
}
,
// 3c)
// Define the event handlers
//
_onMouseWheel : function(e)
{
if (this._mouseWheelScroll == false)
{
e.preventDefault();
return false;
}
}
,
This code is sufficient enough to prevent mouse wheel scrolling when Before we do that though, let's stop and think. Are there any other properties we'll need to access? Perhaps. Instead of just adding the _initializeUI : function()
{
var listBox = this.get_element();
var container = this.get_elementContainer();
// hack to support mouse wheel scrolling
if (this.get_mouseWheelScroll() != null)
{
listBox._thisPrototype = this;
container._thisPrototype = this;
}
// rest of code stays the same
}
,
Which One is Backwards?Now we have everything we need to handle the case where Firefox, of course, exhibits the opposite behavior. When using the // 3c)
// Define the event handlers
//
_onMouseWheel : function(e)
{
var _this = this._thisPrototype
if (_this === undefined)
_this = this;
// stop the mouse wheel from scrolling
if (_this.get_mouseWheelScroll() == false)
{
e.preventDefault();
return false;
}
// enforce mouse wheel scrolling
else if (_this.get_mouseWheelScroll() == true)
{
var listBox = _this.get_element();
var container = _this.get_elementContainer();
var direction, scrollingElement;
if (this._thisPrototype === undefined) // IE & Opera
{
// negative wheelDelta should increase scrollTop,
// positive wheelDelta should decrease the scrollTop.
direction = (e.rawEvent.wheelDelta > 1) ? -1 : 1;
}
else
{
// detail's direction is opposite of wheelDelta
direction = (e.detail > 1) ? 1 : -1;
}
// scroll the correct element
if (_this.get_requiresContainerScroll())
scrollingElement = container;
else
scrollingElement = listBox;
// scroll the ListBox by the height of one item in the correct direction.
var stepSize = scrollingElement.scrollHeight / listBox.options.length;
var newScrollTop = scrollingElement.scrollTop + (stepSize * direction);
scrollingElement.scrollTop = newScrollTop;
// tell the browser we're taking care of the mouse wheel.
e.preventDefault();
return false;
}
}
,
Stick a Fork in ItCurrently, the only way to test this is by manually changing the using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DanLudwig.Controls.Web
{
public class ListBox : System.Web.UI.WebControls.ListBox,
System.Web.UI.IScriptControl
{
// all the server code we need will go right here
}
public enum ListBoxMouseWheelScrollSetting
{
NotSet,
Enforce,
Prevent
}
}
Our client control property won't recognize this enumeration though. So, we need two server control properties: one to set the value, and another to translate it into a value that the client control can work with: public virtual ListBoxMouseWheelScrollSetting MouseWheelScroll
{
set { this.ViewState["MouseWheelScroll"] = value; }
get
{
object output = this.ViewState["MouseWheelScroll"];
if (output == null)
output = ListBoxMouseWheelScrollSetting.NotSet;
return (ListBoxMouseWheelScrollSetting)output;
}
}
protected virtual bool? MouseWheelScrollClientValue
{
get
{
if (MouseWheelScroll.Equals(
ListBoxMouseWheelScrollSetting.Enforce))
return true;
else if (MouseWheelScroll.Equals(
ListBoxMouseWheelScrollSetting.Prevent))
return false;
return null;
}
}
We can now configure the server control's protected virtual IEnumerable<ScriptDescriptor> GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor(
"DanLudwig.Controls.Client.ListBox", this.ClientID);
descriptor.AddProperty("mouseWheelScroll",
this.MouseWheelScrollClientValue);
descriptor.AddProperty("requiresContainerScroll", this.RequiresContainerScroll);
descriptor.AddProperty("scrollStateEnabled", this.ScrollStateEnabled);
descriptor.AddProperty("horizontalScrollEnabled", this.HorizontalScrollEnabled);
descriptor.AddProperty("scrollTop", this.ScrollTop);
descriptor.AddProperty("scrollLeft", this.ScrollLeft);
return new ScriptDescriptor[] { descriptor };
}
Oh No, Not Again!This is enough to satisfy most user-input scenarios in most browsers, but there are still a couple of inconsistencies. See if you can figure out what there is to fix next time...
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||