ASP.NET AJAX MultiHandleSliderExtender - 按年和月滑动






4.68/5 (14投票s)
教程 - 如何利用 ASP.NET MultiHandleSlider 扩展器来选择一个范围内的年份和月份,并设置图表控件使其正常工作。
引言
在本教程中,我将演示如何使用 MultiHandleSlider
扩展器来选择或显示一个范围内的年份和月份。该控件消除了使用四个 DropDownList
控件来保存范围值和验证控件来验证用户选择的需要。然后,我们将使用柱状图根据选定的范围值显示芝麻街汽车的数量。此图表允许用户深入查看所选汽车品牌的详细信息。参见图 1。
入门
- 下载 AJAX Control Toolkit 版本说明 - 2009 年 5 月发布
- 下载 图表控件示例环境
- 下面显示的是项目解决方案资源管理器快照。欢迎下载此演示。
后端数据库
首先,向表中添加一个新列,命名为 YearMonth,并用年份和月份数据的连接填充它。参见图 3。左侧是原始表。有了这个设置,我们就可以轻松地从表中选择所需年份和月份范围内的所有数据。
使用 ROW_NUMBER
函数为结果集中的每一行生成行号。然后,我们可以使用行号来填充滑块的范围值。下面显示的查询返回六十二行,这意味着滑块的范围值从一到六十二。换句话说,我们可以将滑块的最小值和最大值属性分别设置为一和六十二。
将所有内容整合
为了简化起见,本教程中使用 XML 作为数据源。App_Data 文件夹中有两个 XML 文件,分别是 CarsList.xml 和 SliderRange.xml。前者包含图 3 中所示表中的所有数据。后者 XML 文件包含图 4 中所示的结果集。创建两个名为 CarsList
和 Range
的自定义实体类来保存公共属性。参见列表 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; }
}
让我们首先在页面上添加一个 ScriptManager
,并将 EnablePageMethods
和 EnablePartialRendering
属性设置为 true
。通过将 EnablePageMethods
属性设置为 true
,客户端脚本就可以访问 ASP.NET 页面的静态页面方法。EnablePartialRendering
属性允许我们仅指定要刷新的页面区域。接下来,将一个 TextBox
、MultiHandleSliderExtender
、四个 HiddenField
和两个 Label
控件拖放到页面上,并将它们包装在 UpdatePanel
中。将 MultiHandleSliderExtender
控件的 TargetControlID
设置为 TextBox
控件的 ID。向控件添加两个滑块,并分别将其 ControlID
设置为 rangeStart
和 rangeEnd
。参见列表 2。Label
控件的目的是显示选定的范围值。HiddenField
控件用于保存滑块的值。使用如下所示的设置初始化 MultiHandleSliderExtender
。
OnClientDrag= Drag
- 当用户拖动滑块时引发的事件。OnClientDragEnd = DragEnd
- 当用户停止拖动滑块时引发的事件。Increment = 1
- 确定滑块值递增或递减的点数。RaiseChangeOnlyOnMouseUp = true
- 仅当释放鼠标左键时,才在扩展的 TextBox 上触发更改事件。EnableRailClick = false
.
<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>
下面显示的是客户端代码。拖动滑块将触发 ASP.NET 页面方法 SliderRange
来更新 lblStartRange
和 lblEndRange
的值。一旦用户释放滑块,DragEnd
函数将被执行。
<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>
在代码隐藏中创建一个通用的 List<T>
Range
对象,并将其标记为 static
,以便客户端脚本或其他静态方法可以访问它。下面显示的是 Page_Load
事件中的内容。PopulateSlider()
方法读取 SliderRange.xml 文件中的内容并将其存储在 lstSliderRange
中。将 MultiHandleSliderExtender
的最小值和最大值分别初始化为 1 和 lstSliderRange
中的最大 RowNumber
。参见列表 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();
}
下面显示的是 PopulateSlider()
方法的实现。lstSliderRange
对象被缓存以提高性能,并在文件内容更改时再次获取其内容。根据数据源更新的频率,我们可以基于数据库、文件夹、文件或基于时间的过期进行缓存。在此处阅读更多关于 ASP.NET 缓存技术的信息。
//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>;
}
}
SetLabel()
方法在 lblStartRange
和 lblStartEnd
Label
控件中显示 MultiHandleSliderExtender
的开始和结束范围值。SliderRange
方法带有 [System.Web.Services.WebMethod]
装饰,使其可以通过客户端 JavaScript 访问。GetSliderText
方法接受两个参数:第一个参数是指行号,第二个参数是指左滑块或右滑块。例如,调用 SliderRange(2, 10)
将返回“从年份:2005 月份:02--到年份:2005 月份:10”。首先,它将查询 lstSliderRange
对象并从结果集中检索年份和月份。如果 pos==s
,则文本设置为 **From**,如果 pos==e
,则设置为 **To**。参见列表 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 = "From Year: " + rangeText.First().Year +
" Month: " + rangeText.First().Month;
return strRangeText;
}
else
{
strRangeText = "To Year: " + rangeText.First().Year +
" Month: " + rangeText.First().Month;
return strRangeText;
}
}
此时,您应该在浏览器中看到类似以下的界面。
图表控件
请确保下载 Microsoft Chart Controls for Microsoft .NET Framework 3.5,因为图表控件需要 System.Web.DataVisualization.Design.dll 和 System.Web.DataVisualization.dll。我也将这两个 DLL 包含在示例代码中。此部分中的一些代码来自 图表控件示例环境。首先,让我们创建一个方法将数据源绑定到柱状图。此方法接受两个参数:开始和结束范围值。然后,使用 LINQ 查询 CarsList.xml 数据源,找到 YearMonth
在开始和结束范围值之间的所有记录。按汽车品牌对结果进行分组,将其存储在 lstCarsnTotal
中,并将其绑定到图表。参见列表 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);
}
现在,当用户单击柱状图时,一个 GridView
将出现在其旁边。PopulateGrid
方法以汽车品牌作为参数。然后,使用 LINQ 查询 SliderRange.xml 数据源,其中 YearMonth
在选定范围值之间,并且 CarBrand
等于选定的汽车品牌。参见列表 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();
}
已知问题
在设计时,我们会看到错误“MultiHandleSliderExtender 无法设置在 MultiHandleSliderTargets 属性上”,但代码在运行时可以正常工作。我下载了示例和最新版本的 AJAX Control Toolkit 从 CodePlex 下载,但未能解决问题。一种变通方法是在设计时在 MultiHandleSliderTargets
标签旁边添加 TagPrefix
,并在运行时将其删除。希望有人能对此有所阐明。
关注点
示例代码中的 Default2.aspx 包含一个母版页。如果您使用母版页,请确保使用 Control.ClientID
。出于某种原因,客户端 __doPostBack
函数不能与母版页一起工作;变通方法是调用按钮单击事件。参见列表 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>
我注意到,单击滑块会触发 DragEnd
函数并导致不必要的发布回退。为了解决这个问题,请比较先前选定的范围值与当前选定的范围值;如果它们不相等,则允许调用客户端 __doPostBack
函数。参见列表 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", "");
}
}
图表在我的本地计算机上显示正常,但在托管环境中显示一系列奇怪的字符。经过一些研究,我发现我没有在存储文件夹上设置适当的权限,并且页面指令上的 EnableSessionState
被设置为 false
。ASP.NET 图表存储模式的列表可以在这里找到:here。
新更新 1
创建一个名为 arrRange
的客户端 Array
对象,循环遍历 lstSliderRange
对象中的每一行,并将年份和月份的值添加到其中。将 CreateArray()
方法放在 !Page.IsPostBack
块内。参见列表 11。
void CreateArray()
{
foreach (Range r in lstSliderRange)
{
Page.ClientScript.RegisterArrayDeclaration("arrRange",
"'"+r.Year +"--" + r.Month+"'");
}
}
不要使用 PageMethods 来查找 RowNumber 文本,而是调用客户端 GetSliderText
函数。参见列表 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;
}
结论
如果您发现任何错误或不同意内容,请给我留言,我会与您合作进行纠正。我建议下载演示并进行探索,以充分理解其概念,因为我可能遗漏了一些有用的信息。
资源
- ASP.NET 图表 - 存储模式
- MultiHandleSlider 演示
- ScriptManager..::.EnablePageMethods 属性
- 在 ASP.NET 应用程序中使用 Microsoft Chart Controls:渲染图表
- 处理客户端脚本
历史
- 2010/03/07 - 首次发布。
- 2010/03/09 - 将 PageMethods 替换为客户端 JavaScript
Array
。
我收到一位 CodeProject 成员的反馈,他说:“问题是,拖动会向服务器发送许多请求,最后一个会执行,但这同样意味着数据库需要额外工作,IIS 也需要。” 我认为他说得有道理,所以我决定将每个 RowNumber 的年份和月份数据存储在客户端 JavaScript 数组中。这将允许通过客户端更新滑块的范围标签。参见新更新 1 部分。