使用总结
1.前语
这三个月作者使用VS2019中的.NET FrameWork(基于C#)及Sql Server2008完成了一个网页的设计,网页的设计中遇到了各种各样的问题,例如用户对发布所有信息的编辑、管理员界面、用户发布信息的富文本编辑器前后端截取、Echart的使用、在当前页面跳出消息框、Ajax异步渲染的使用(基于ScriptManager及UpdatePanel控件)、群聊(聊天)界面的制作、鼠标悬停某一块时的模态框提示(基于简单的JavaScript及Boostrap的模态框)、视频上传(基于ffmepg程序)等本人认为有一定技巧的操作。接下来的目录也将分别就上述点展开具体的使用。
2.DataList(或FormView、GridView……中Button绑定)(可用于管理员界面、用户删除、编辑已发布内容的事件)
所谓DataList中的Button绑定是指,假设一个用户发表了多个内容,例如帖子、评论等。那么,我通过DataList将每一条内容展示出来后,如何对每一条内容中的Button按钮做OnClick,即点击事件的判断?想象一个Button不在DataList中的时候可以直接写OnClick事件,但如果Button是对应了DataList中的每一条Item的内容时,该如何写OnClick事件?写法实际上很简单,首先要给DataList做一个OnItemCommand事件绑定,其次,给DataList中的Button按钮添加CommandArgument和CommandName属性,其中,CommandArgument一般是一条Item(记录)对应的数据库主键,例如一个帖子则是帖子的id(如postid,基于数据库表的字段),一个用户则可以是用户的id(如userid,也是基于数据表的字段);CommandName则是一个给定的,固定的名称,这个名称在后台cs文件中能使用。
以我写的代码为例,如下是前台的代码。
<asp:DataList ID="DataList1" runat="server" DataSourceID="SqlDataSource1" OnItemCommand="DataList1_ItemCommand" RepeatDirection="Horizontal" RepeatColumns="3">
<ItemTemplate>
<div style="margin-left:15px;margin-top:10px">
<asp:Button ID="Button3" runat="server" Text='<%# Eval("ggameit_name") %>' Cssclass="button" CommandArgument='<%# Eval("ggameit_id") %>' CommandName="select" />
</div>
</ItemTemplate>
</asp:DataList>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:yinliwangConnectionString2 %>" SelectCommand="SELECT top 6* FROM [ggameitem_Table]">
</asp:SqlDataSource>
后台的代码如下(cs文件)。
实际上,通过看后台的cs代码读者大概能明白是如何使用的,即点击一个Button后,该Button由于是DataList中的内容,所以会先触发DataList的OnitemCommand事件,对应的是后台的DataList1_ItemCommand这一函数,该函数包含一个DataListCommandEventArgs对象,当DataList中触发了一个Event事件之后(在这里是Button),后台能获取e这一变量对应的CommandName,我的代码中写的Button的CommandName是select,如果e.CommandName等于Button的CommandName,则触发Show函数,Show函数是一个自己定义的函数,可以有参数,参数可以通过e.CommandArgument,亦即Button对应的CommandArgument来获取后传输。Show函数中则可以写你要做的事情,比如一个sql语句的执行等等。
protected void DataList1_ItemCommand(object source, DataListCommandEventArgs e)
{
if (e.CommandName == "select")
{
Show(e.CommandArgument.ToString());
}
}
public void Show(string id)
{
this.SqlDataSource2.SelectCommand = "SELECT top 7* FROM [ggame_Table] inner join [ggameitem_Table] on ggameit_id =ggame_tpit inner join [guser_Table] on [ggame_Table].guser_id = [guser_Table].guser_id where ggameit_id=" + id;
}
3.局部刷新的使用(基于ScriptManager及UpdatePanel控件)
实际上.NET中局部刷新的使用是比较简单的,首先要在页面中插入ScriptManager控件,一个页面只需要放一个ScriptManager,并且直接放在form的后面即可大致模板如下。
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager2" runat="server" EnableScriptLocalization="true" EnablePageMethods="true"> </asp:ScriptManager>
……………………(页面其他代码)
</form>
接下来是思考异步渲染是要怎么异步渲染法,读者应该明白,.NET中的服务器控件Button点击之后,如果没有指定局部刷新的话,整个页面都会刷新一次,而指定局部刷新的好处是只有你指定的需要刷新的地方才会有变化。例如,用户点赞、收藏之后的点赞数、收藏数的改变。
以上实际上表明了局部刷新需要有两个最基本的内容:
第一:指定需要刷新的部分————这是通过UpdatePanel
第二:指定什么时候刷新需要刷新的部分,比如我是点击一个Button之后刷新某一局部,这意味着我需要给Button写OnClick事件,其次,我需要让需要刷新的部分去与Button做牵连,相当于某一局部能判断Button是否点击了,这个判断是写在UpdatePanel中的。接下来通过代码进行陈述。
(一)UpdatePanel的组成
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="Label2" runat="server" Text="Label"></asp:Label>
</ContentTemplate>
<Triggers> <asp:AsyncPostBackTrigger ControlID="Button2" /> </Triggers>
</asp:UpdatePanel>
实际上代码很简单,局部的内容放在ContentTemplate中,例如在这块我指定的是ID为Label2的内容需要局部刷新。在此基础上,如上面所分析的,我要判断Button是否点击了,或者说Button点击之后能触发局部去刷新,只需要通过上面写的Triggers(触发器)、(Triggers中放触发局部刷新的按钮(即Control))ControllID对应的就是Button的ID。
<Triggers> <asp:AsyncPostBackTrigger ControlID="Button2" /> </Triggers>
接下来只需要在后台写你的Button事件即可,PS,既然要局部刷新,那Button事件中肯定要有改变局部内容的代码(比如赋值Label等于…………),不然你使用局部刷新干嘛呢?
(二)局部刷新如何实现页面提示和跳转
读者如果使用过一些JS的函数,例如Alert等等的,则会发现其弹窗警告基本是在一个空白的页面,而不是在当前有内容的页面直接弹窗提示,实际上,要实现在当前页面直接弹出提示(乃至跳转),也可以通过局部刷新来实现。具体做法大致如上。
第一步:还是写一个UpdatePanel,这个UpdatePanel的ContentTemplate可以是空的,也可以是非空的,根据你的需求来写。如下所示,其中仍旧必须为UpdatePanel设置触发器,触发器要对应你点击的按钮的ID。
<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">
<ContentTemplate>
…………………………
</ContentTemplate>
<Triggers> <asp:AsyncPostBackTrigger ControlID="Button2" /> </Triggers>
</asp:UpdatePanel>
后台文件中只要写下面一段代码即可。这段代码对应的是Button2的OnClick事件的函数。这段代码中最重要的是ScriptManager.RegisterStartupScript这一行,其中前两个是指定UpdatePanel的ID,第四个参数写警告或者跳转,如果不要跳转的话把location.href及之后的删除即可。
protected void Button2_Click(object sender, EventArgs e)
{
if (Session["user_id"] == null)
{
ScriptManager.RegisterStartupScript(UpdatePanel6, UpdatePanel6.GetType(), "", "<script>alert('请登录');location.href='login.aspx'</script>", false);
}
else
{
Response.Redirect("personspacetest.aspx?id=" + Session["user_id"]);
}
}
4.Echart的使用(重点)
(C#使用Echart的配置方法会有一点难度,读者主要需要理清逻辑,具体的代码可以直接复制粘贴,对某一部分内容进行修改,这部分我展示我的代码的时候会对每一块进行具体的说明)
C#中使用Echart有不少方法,本人使用的方法大致原理是,后台写一个函数接口,这一函数接口会在网页的QueryString为某一指定值时写在页面中,先看个效果读者可能更容易理解,如下图。
这里页面展示的内容的数据形式是字典,或者说应该是Json格式,读者可以稍微去了解下Json格式是啥,如果学过Python的话应该很容易理解(或者Java的Map),大致意思就是Key(键):Value(值)。
我实现的Echarts效果图如下:
(一)Echart前端配置
我们先看看前端Echart部分的代码,
(Echarts的引入我就不必再说了,使用Echarts需要去官网下载所需的包,然后在head中的Script引入Echarts包,此外,本方法使用Echarts还需要引入Jquery和Js,读者随便引入两个即可
我的网页引入的是如下两个包
<script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
接下来我们看前端Echart部分的代码(在代码下面部分我对具体的细节进行一个阐释)
<div id="charts" style="width:200px;height:200px;"></div>
<script type="text/javascript">
// 基于准备好的dom,初始化ECharts实例
var myChart = echarts.init(document.getElementById('charts'));
// 指定图表的配置项和数据
var options = {
title: {
text: ""
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'line'
}
}, grid: {
bottom: '38%'
},
legend: {
data: [] //'体重、体脂率、体质指数' legend同Python的Matplotlib中的legend,就是图最上边的标识
},
xAxis: {
data: [] //横坐标对应的内容,在我的项目中对应的是时间"
,
axisLabel: {
interval: 0, // 坐标刻度之间的显示间隔,默认就可以了
rotate: 15 // 调整数值改变倾斜的幅度(范围-90到90)
},
},
yAxis: {},
series: [{
type: 'bar',
data: [20, 30, 25, 40, 27],
itemStyle: {
normal: {
label: {
show: true, //开启显示数值
position: 'top', //数值在上方显示
textStyle: { //数值样式
color: '#D1DBFF', //字体颜色
fontSize: 14 //字体大小
}
}
}
}
}, {
type: "line",
data: [60, 70, 80, 65, 55],
}, {
type: "line",
data: [60, 70, 80, 65, 55],
}
}
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(options);
window.addEventListener("resize", function () { myChart.resize(); });
//ECharts 支持常规的鼠标事件类型,包括 'click'、'dblclick'、'mousedown'、'mousemove'、'mouseup'、'mouseover'、'mouseout'、'globalout'、'contextmenu' 事件
// 处理点击事件并且跳转到相应的百度搜索页面
myChart.on('click', function (params) {
window.open('https://www.baidu.com/s?wd=' + encodeURIComponent(params.name));
});
//通过Ajax获取数据
$.ajax({
type: 'post',
url: "echartslist.aspx?action=LoadChart",
contentType: 'application/json; charset=utf-8',
dataType: "json",
async: false,
beforeSend: function () {
myChart.showLoading(); //显示加载动画效果
},
success: function (obj) {
console.log(obj);
if (obj) {
options.xAxis.data = obj.xList; //给X轴数据
options.series = obj.series; //给series数据
options.legend.data = obj.legend; //给legend数据
myChart.hideLoading(); //隐藏动画加载效果
myChart.setOption(options); //设置图表实例的配置项以及数据
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
}
});
</script>
一、准备一个div,div取一个id,这里我取的id是charts,取id的目的是为了和Script中的**document.getElementById(‘charts’)**联系上,这个id为charts的div也就是之后Echarts具体展示的地方,所以需要给div设置width和height,这决定了Echarts图的大小(和位置)。
二、前面我的图表中有3组数据,分别对应了体重、体脂率、体质指数,Echarts前端数据的配置实际上就是在series这块,3个部分对应了3组data,这里读者可以不用管,data中的数据在后端会重新配置一次。
三、$.ajax之后的部分主要是对其url解释一下,这里我们实现后端向前端传输数据的方式是通过API接口实现的,也就是第一张图显示的那样,
url: "echartslist.aspx?action=LoadChart"中echartslist.aspx是该图表对应的网页,?action=LoadChart实际上是QueryString的配置,这一配置会在cs文件中进行实现。
(二)Echart后端配置
接下来我们看后端的内容
protected void Page_Load(object sender, EventArgs e)
{
string action = Request.QueryString["action"];
switch (action)
{
case "LoadChart"://查询数据
LoadChart();
break;
default:
break;
}
}
正如我前面所说,要实现当QueryString等于action=LoadChart时,在页面展示数据,则只需要在Page_Load函数中设置如上的代码,此外,如何实现第一张图那样在页面上写数据则是接下来的LoadChart函数要做的。
在写LoadChart函数前需要在cs中引入Newtonsoft.Json包,写法即using Newtonsoft.Json;
在下面的代码中会有一个Series类,读者只需要直接复制粘贴就可以,不需要对该类进行修改。
private void LoadChart()
{
//legend集合
List<string> legendList = new List<string>();
legendList.Add("体重");
legendList.Add("体脂率");
legendList.Add("体质指数");
//X轴集合
List<string> xList = new List<string>();//"对应的是我图上的时间"
//series集合
List<Series> seriesList = new List<Series>();
//定义一个Series对象,这里我定义了3个对象,因为分别对应了3组数据
Series seriesObj = new Series();
Series seriesObj2 = new Series();
Series seriesObj3 = new Series();
seriesObj.name = "体重";
seriesObj.type = "line"; //line表示折现 柱形 bar
seriesObj.data = new List<int>(); //先初始化 不初始化后面直直接data.Add(x)会报错
seriesObj2.name = "体脂率";
seriesObj2.type = "line"; //line表示折现 柱形 bar
seriesObj2.data = new List<int>(); //先初始化 不初始化后面直直接data.Add(x)会报错
seriesObj3.name = "体质指数";
seriesObj3.type = "line"; //line表示折现 柱形 bar
seriesObj3.data = new List<int>(); //先初始化 不初始化后面直直接data.Add(x)会报错
//这里我是从我的数据表中获取体重、体脂率、体质指数的记录数据
string sql = "select * from grecord_Table where guser_id =1";
//这里我的代码就是一个与数据库连接的函数使用,按理来说读者应该有一个自己的可以获取的方法,这里的代码是需要读者修改成自己的方法的。
DataTable dt = YF.MsSqlHelper.YFMsSqlHelper.Query(sql).Tables[0];
//下面是向Series中添加数据
if (dt.Rows.Count >= 1)
{
for (int i = 0; i < dt.Rows.Count; i++)
{
xList.Add(dt.Rows[i]["grecord_time"].ToString());
seriesObj.data.Add(int.Parse(dt.Rows[i]["grecord_weight"].ToString()));
seriesObj2.data.Add(int.Parse(dt.Rows[i]["grecord_bmi"].ToString()));
seriesObj3.data.Add(int.Parse(dt.Rows[i]["grecord_bfr"].ToString()));
}
}
//接下来向seriesList中把3个数据对象都加进去
seriesList.Add(seriesObj);
seriesList.Add(seriesObj2);
seriesList.Add(seriesObj3);
var newObj = new
{
xList = xList, //X轴集合
series = seriesList, //series集合
legend = legendList //legend集合
};
//下面几行代码则是为何页面会显示出Json数据的原因,即通过Response.Write将数据写在了页面中,JsonConvert……则是先开始为何要引入一个Json包的原因。
string json = JsonConvert.SerializeObject(newObj);
Response.Write(json);
Response.Flush();
Response.Close();
}
}
public class Series
{
/// series序列组名称
public string name
{
get;
set;
}
/// series序列组呈现图表类型
public string type
{
get;
set;
}
/// series序列组呈现对应的Y轴刻度
public int yAxisIndex
{
get;
set;
}
/// series序列组的数据为数据类型数组
public List<int> data
{
get;
set;
}
}
到这里Echart基本就可以实现了,需要注意的点在代码中我已经注释过了,第一是需要将YF……那段改成自己的获取数据库数据的函数或代码,第二是向SeriesObj中添加自己所需要的数据,这两部分是读者需要略微修改的。
5.鼠标悬停的模态框
这部分主要是给和我一样没系统学习过JavaScript等的人用的,鼠标悬停时的模态框显示还是比较简单的。我实现的效果是鼠标停在用户的图片上时能在侧边给我显示出一个用户的详细信息。具体如下图所示,这里我设置了一个id为myModa的模态框,这引用的是Bootstrap的模态框样式,具体的代码我就不详细解释了,Script中的mouseover和mouseout对应的是鼠标悬停和鼠标移出两个事件,当事件触发时则通过$(‘#模态框id’)的形式找到模态框,让模态框对应的div,让其show,否则则hide模态框。
<script type="text/javascript">
$(document).ready(function () {
$("#myModa<%# Eval("guser_id") %>").hide();
$("#categort<%# Eval("guser_id") %>").mouseover(function () {
$("#myModa<%# Eval("guser_id") %>").show();
});
$("#myModa<%# Eval("guser_id") %>").mouseover(function () {
$("#myModa<%# Eval("guser_id") %>").show();
});
$("#categort<%# Eval("guser_id") %>").mouseout(function () {
$("#myModa<%# Eval("guser_id") %>").hide();
});
$("#myModa<%# Eval("guser_id") %>").mouseout(function () {
$("#myModa<%# Eval("guser_id") %>").hide();
});
})
</script>
<div id="categort<%# Eval("guser_id") %>"><img src="<%# Eval("guser_pfp") %>" style="border-radius:10px;width:150px;height:150px"/></div>
<div id="myModa<%# Eval("guser_id") %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true" style="position:absolute;margin-left:100px;width:100px">
<div class="modal-dialog">
<div class="modal-content" style="width:200px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" >
×
</button>
<h4 class="modal-title" id="myModalLabel">
<asp:Label ID="ggame_nameLabel" runat="server" Text='<%# Eval("guser_name") %>' style="font-size:14px;color:steelblue;font-weight:bold" />
</h4>
</div>
<div class="modal-body">
他的签名:<asp:Label ID="ggame_introLabel" runat="server" Text='<%# Eval("guser_slogan") %>' />
<br />
所在省市:<asp:Label ID="Label1" runat="server" Text='<%# Eval("gcity_name") %>' />
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal -->
</div>
6.富文本编辑器前后端读取问题
某些时候读者可能需要引入富文本编辑器,例如在我完成项目的过程中就使用了wangeditor这一富文本编辑器,然而该编辑器存在的一些麻烦之处是标题和正文的输入的内容是基于JavaScript异步生成的,这导致C#的后端没法直接通过简单的赋id读取,这时便需要通过特殊的后端函数来实现,以我的项目为例。首先富文本编辑页面的图片如下
此外,在正文写下东西之后文本对应的位置可以通过F12查看,如下图
可见正文内容是在一个id为w-e-text-1的span里面。分析到此,我们先看看前端代码。
(一)前端代码
<asp:Button ID="Button1" runat="server" class="btn btn-default" style="background-color:dodgerblue;border-color:dodgerblue;width:150px;font-size:20px;color:white" Text="确认发布" OnClick="Button1_Click1"/>
<script>
$(function () {
$("[id$='Button1']").click(function () {
var username = $("img").eq(1).attr("src");
var cont = $('#w-e-text-1')[0].innerText;
var userid = <%=Session["user_id"]%>;
var title = $('#txtUserName').val();
$.ajax({
type: 'post',
url: "postingtest.aspx/test",
contentType: "application/json",
dataType: "json",
data: "{data:'" + username + "',userid:'" +<%=Session["user_id"]%> + "',cont:'" + cont + "',title:'" + title + "'}",
success: function (data) {
alert(data.d);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
alert(XMLHttpRequest.responseText);
}
});
})
})
</script>
关于前端代码,前端向后端传送数据是在Button1(即发布按钮)点击之后触发事件,ajax中的url对应的是该页面的后端函数,在这一函数中可以写自己要做的代码。此外,正如前面分析的,正文的内容是在id为w-e-text-1的span的第一个子元素的文本内容里,所以我获取正文内容的代码是: var cont = $(‘#w-e-text-1’)[0].innerText; 在这里读者可以去网上搜搜jq(jquery)获取元素的方式,根据自己的情况决定情况,另外,需要注意的是,ajax中的data一栏对应的json字典的键必须和后端接收函数的参数相同。
(二)后端代码
后端代码如下:
using System.Web.Services;
[WebMethod]
public static string test(string data,string userid,string cont,string title)
{
data = data.Substring(22, data.Length - 23);
//data = data.Replace(" ", "+");
//data.Replace(" ", "+");
data = data.Trim().Replace("%", "").Replace(",", "").Replace(" ", "+");
string[] str = data.Split(','); //base64Str为base64完整的字符串,先处理一下得到我们所需要的字符串
string a = str[0];
if (a.Length % 4 > 0)
{
a = a.PadRight(a.Length + 4 - a.Length % 4, '=');
}
byte[] imageBytes = Convert.FromBase64String(a);
//读入MemoryStream对象
MemoryStream memoryStream = new MemoryStream(imageBytes, 0, imageBytes.Length);
memoryStream.Write(imageBytes, 0, imageBytes.Length);
// 转成图片
System.Drawing.Image image = System.Drawing.Image.FromStream(memoryStream);
// 图片名称
string iname = DateTime.Now.ToString("yyMMddhhmmss");
string file = "E:\\Apex\\" + iname + ".jpg";
string file2 = "E:\\aps.net\\yinliwang\\web2\\WebApplication1\\gpostimg\\"+iname+".jpg";
//string file2 = "E:\\aps.net\\备份(2121)\\" + iname + ".jpg";
image.Save(file2);
string sqlfile = "gpostimg/" + iname + ".jpg";
string sql = "insert into gpost_Table values(" + userid + ",getdate(),'"+title+"',1,'"+cont+"','"+sqlfile+"',1,3,1)";
int row = YF.MsSqlHelper.YFMsSqlHelper.ExecuteSql(sql);
// 将图片存到本地
data = file;
return "发布成功";
//return data+userid+cont+" title:"+title;
}
PS:我贴出的代码中写的东西不是很重要,我的代码是在获取并保存图片和文字等等的,读者主要需要去理解里面的逻辑和大致用法。
后端代码需要注意的有,第一,必须引入using System.Web.Services包。第二,必须在方法前声明[WebMethod],第三,方法只能写成静态的,这意味着该方法中的参数都是通过前端传过来的,在方法中同样没法通过id直接获得文本内容。