本篇是《ASP.NET编程技巧》(http://blog.csdn.net/zhouwen/archive/2010/09/16/5889330.aspx)的姊妹篇,记录.Net学习笔记——细节问题!有的时候,细节决定成败!
(1)如何判断2个字符串对象是否指向同一个引用?
string s1 = "aa";
string s2 = "aa";
MessageBox.Show(object.ReferenceEquals(s1,s2).ToString()); //true
char[] ch = { 'a', 'a' };
string s1 = new string(ch);
string s2 = "aa";
MessageBox.Show(object.ReferenceEquals(s1,s2).ToString()); //false
(2)获取本机IP地址。
public static IPAddress[] GetLocalhostIPv4Addresses()
{
String LocalhostName = Dns.GetHostName();
IPHostEntry host = Dns.GetHostEntry(LocalhostName);
List<IPAddress> addresses=new List<IPAddress>();
foreach (IPAddress ip in host.AddressList)
{
if (ip.AddressFamily == AddressFamily.InterNetwork)
addresses.Add(ip);
}
return addresses.ToArray();
}
(3)如何把窗体坐标转换成屏幕坐标?
private void Form4_MouseMove(object sender, MouseEventArgs e)
{
textBox1.Text = string.Format("x:{0},y:{1}", e.X,e.Y);
Point p = new Point(e.X, e.Y);
p=this.PointToScreen(p); //把窗体坐标转换成屏幕坐标
textBox2.Text = string.Format("x:{0},y:{1}", p.X,p.Y);
p = this.PointToClient(p);
textBox3.Text = string.Format("x:{0},y:{1}", p.X, p.Y);
}
(4) 窗体显示帮助按钮
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.HelpButton = true;
this.MaximizeBox = false;
this.MinimizeBox = false;
点击帮助按钮,显示chm格式的帮助文件?
处理窗体的HelpButtonClicked事件:
System.Diagnostics.Process.Start("ArticleCollectorAPI.chm");
(5)控件的Anchor、Dock属性区别
Anchor用于设置控件与窗体边缘的距离保持不变!
Dock用于设置控件紧贴窗体的某个边缘或者填充整个窗体。
(6)使用DataGridView显示、更新、删除数据
string connstr = @"server=.\sqlexpress;initial catalog=MySchool;uid=sa;pwd=sa2008";
DataSet ds = new DataSet();
SqlDataAdapter da;
//显示数据
private void LoadData()
{
string strwhere;
if (comboBox1.Text == "男")
strwhere="sex='男'";
else
if (comboBox1.Text == "女")
strwhere="sex='女'";
else
strwhere="sex='男' or sex='女'";
string sql = "select StudentId,StudentNo,StudentName,Sex from Student where " + strwhere;
da = new SqlDataAdapter(sql, connstr);
ds.Tables.Clear();
da.Fill(ds);
dataGridView1.DataSource = ds.Tables[0];
}
//更新数据
private void button2_Click(object sender, EventArgs e)
{
//自动产生更新数据库的SQL语句,但其数据必须是来自同一个数据表。否则,更新失败!
SqlCommandBuilder cmdb = new SqlCommandBuilder(da);
da.Update(ds);
LoadData();
}
//删除数据
private void 删除ToolStripMenuItem_Click(object sender, EventArgs e)
{
if (dataGridView1.SelectedRows.Count== 0) //SelectionMode属性用FullRowSelect
{
MessageBox.Show("请选择!");
return;
}
if (MessageBox.Show("确认要删除吗?", "警告", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
{
using (SqlConnection conn = new SqlConnection(connstr))
{
string sql = "delete from Student where StudentId=" + (int)dataGridView1.SelectedRows[0].Cells[0].Value;
SqlCommand cmd = new SqlCommand(sql, conn);
conn.Open();
cmd.ExecuteNonQuery();
}
LoadData();
}
}
(7)关闭主窗体,出现是否退出的对话框:
private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (MessageBox.Show("确定要退出吗", "系统消息", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel)
{
e.Cancel = true;
}
}
private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Exit();
}
//“退出”菜单的事件代码
private void 退出ToolStripMenuItem_Click(object sender, EventArgs e)
{
this.Close();
}
(8)JavaScript访问服务端控件:
服务端控件声明:
<asp:TextBox ID="txtName" οnblur="CheckName()" runat=server></asp:TextBox><span id="error" style="color:Red"></span><br />
对应的JavaScript代码:
function CheckName()
{
if(document.forms(0).txtName.value.length!=6)
{
document.getElementById("<%= txtError.ClientID%>").value="error";
}
else
document.getElementById('error').innerHTML="";
}
(9)表单回发之后,保留密码框中的密码
一般来说,当表单回发之后,密码框将会被自动清空。但是有些时候,我们并不希望这样。这时,我们可以添加下面的代码来避免这个问题。
protectedvoidPage_Load(objectsender,EventArgse)
{
if(IsPostBack) //如果是页面回发
{
if(!(String.IsNullOrEmpty(txtPassword.Text.Trim())))
{
txtPassword.Attributes["value"]=txtPassword.Text;
}
}
}
(10)重写Equals 方法判断对象是否相等
默认情况下,Object类的Equals()方法用于判断2个对象是否相等,是比较2个对象是否引用同一个对象。如果要支持值相等,则需要重写Equals虚方法。
public override bool Equals(object obj)
{
//将要比较的对象转换为当前类型
Student target = obj as Student;
//如果为空、类型不同
if( target == null)
return false;
if (target.name == this.name &&
target.gender == this.gender &&
target.age == this.age)
{
return true;
}
return false;
}
(11)FileDialog的FilterIndex属性
注意msdn中的描述:
The index value of the first filter entry is 1.不是0.
(12)FileDialog的RestoreDirectory属性
msdn中的描述:假设用户在搜索文件的过程中更改了目录,那么,如果对话框会将当前目录还原为初始值,则值为 true;反之,值为 false。 默认值为 false。看了这个描述,是不是还是糊里糊涂的 o(∩_∩)o
其实,这个属性其实是控制当前程序中的System.Environment.CurrentDirectory的,也就是,当属性设置为true时,System.Environment.CurrentDirectory永远是程序从中启动的文件夹目录;而设置为false是,则每次使用OpenFileDialog选择完文件后,System.Environment.CurrentDirectory会变成最后一次打开文件的目录。
(13)页面刷新后保持滚动条不动,还在原来的位置
ASP.NET 2.0里提供了这样的一个方案,可以通过设置
<%@ Page MaintainScrollPositionOnPostBack="true" %>
或在编码里设置
Page.MaintainScrollPositionOnPostBack= true;
或在web.config里设置
<pages maintainScrollPositionOnPostBack="true" />
来达成。
(14)操作SQL Server数据库的bit字段
在SQL Server数据库的bit字段的值为1,或者是0。因此,向数据库中新增或修改时,该字段的值要为1或者是0。
但从数据库中取出bit字段的值时,对应的值为true或者为false。
(15)使用GUID
GUID(全局统一标识符)是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。通常平台会提供生成GUID的API。生成算法很有意思,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。GUID的唯一缺陷在于生成的结果串会比较大。
1. 一个GUID为一个128位的整数(16字节),在使用唯一标识符的情况下,你可以在所有计算机和网络之间使用这一整数。
2. GUID 的格式为“xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx”,其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字。例如:337c7f2b-7a34-4f50-9141-bab9e6478cc8 即为有效的 GUID 值。
3. 世界上(Koffer注:应该是地球上)的任何两台计算机都不会生成重复的 GUID 值。GUID 主要用于在拥有多个节点、多台计算机的网络或系统中,分配必须具有唯一性的标识符。
.NET中使用GUID
GUID 在 .NET 中使用非常广泛,而且 .NET Framework 提供了专门 Guid 基础结构。
Guid 结构的常用法包括:
1) System.Guid.NewGUID()
生成一个新的 GUID 唯一值
2) System.Guid.NewGUID() ToString()
将 GUID 值转换成字符串,便于处理
1、Guid.NewGuid().ToString("N") 结果为:
38bddf48f43c48588e0d78761eaa1ce6
2、Guid.NewGuid().ToString("D") 结果为:
57d99d89-caab-482a-a0e9-a0a803eed3ba
3、Guid.NewGuid().ToString("B") 结果为:
{09f140d5-af72-44ba-a763-c861304b46f8}
4、Guid.NewGuid().ToString("P") 结果为:
(778406c2-efff-4262-ab03-70a77d09c2b5)
可见默认的为第2种效果
同样,SQL Server也很好地集成了GUID的用途。SQL Server数据类型uniqueidentifier能够存储一个GUID数值。你可以通过使用NEWID()函数在SQL Server中生成这一数值,或者可以在SQL Server之外生成GUID,然后再手动地插入这一数值。
(16)内容页访问母版页控件
在子页中可以通过事件来访问母版页的控件属性
方法一如下:
(1)说明: 通过Master对象查找控件的形式转换为对应的控件类型
示例:this.lblContent.Text = (Master.FindControl("lblTime") as Label).Text;
this.lblContent.Text = (Master.FindControl("lblTime") as Label).Text;
(2)必须在子页面一些指定的方法里面
protected void Page_LoadComplete(object sender, EventArgs e)
{
this.lblContent.Text = (Master.FindControl("lblTime") as Label).Text;
}
其中,“Page_LoadComplete”是内容页面加载完成时触发的一个事件。
(3)也可以在事件中操作母版页功能
protected void Button1_Click(object sender, EventArgs e)
{
(Master.FindControl("lblTime") as Label).Text = "HeLLO you";
}
方法二 通过强引用
<%@ Page Language="C#" MasterPageFile="~/MasterPage.master" AutoEventWireup="true" CodeFile="Default2.aspx.cs" Inherits="Default2" Title="Untitled Page" %>
<%@ MasterType VirtualPath="~/MasterPage.master" %>
然后可以在母版页中定义公共属性或方法
public string GetUserName()
{
return Page.User.Identity.Name;
}
在内容页中调用
Label1.Text = "欢迎光临" + Master.GetUserName();
(17)转到前一页
方法一:
在asp.net的aspx里面的源代码中
<input type="button οnclick="javascript:window.history.go(-1);"value="返回上一页">
浅析:这个是用了HTML控件,通过一个onclick的事件,调用了javascript中的一个方法就可以了。这个是最简单的了,也同样适用于静态页面,ASP页面等。
方法二:
利用Reponse.Write
Response.Write("<script language=javascript>history.go(-2);</script>)
<a href="#" οnclick="javascript:history.back();">返回前一页</a>
因为在asp.net中的页面,当你按下一个button后,由于页面中会实现page.postback的缘故,实际上在这其中是刷新了两次页面,我们要的是第一次的,所以就......
方法三
利用Response.Redirect() 或 Server.Transfer()
在page_load中加入:
if(!IsPostBack)
ViewState["retu"]=Request.UrlReferrer.ToString();
而后在返回按钮事件中加入:
Response.Redirect(ViewState["retu"].ToString());
或Server.Transfer (ViewState["retu"].ToString());
Request.UrlReferrer可以获取客户端上次请求的url的有关信息,我们在使用这个的时候最好对其进行一个判断
if(ViewState["UrlReferrer"]!=null)
Response.Redirect(ViewState["UrlReferrer"].ToString();
else
Response.write("对不起,当前是最前页码“);
在使用Request.UrlReferrer时还要注意:
1. 如果上一页面使用document.location方法导航到当前页面,Request.UrlReferrer返回空值
2. 如果有A,B两个页面,在浏览器中直接请求A页面,在A页面的中Page_Load事件中导航到B 页面,则 Request.UrlReferrer返回空。因为在Page_load事件中页面还未初始化,所以无法记录当前页的信息,导航到b页面也就无法获得上一页面的信息
3. 点击刷新按钮不会改变Request.UrlReferrer
方法四:
在button的onClick事件中输入
this.RegisterClientScriptBlock("e", "<script language=javascript>history.go(-2);</script>");
(18)Web.config中注册用户控件和自定义控件
在ASP.NET 的早先版本里,我们通过在页面的顶部添加 <%@ Register %> 指令来引入和使用自定义服务器控件和用户控件时,象这样:
<%@ Register TagPrefix="scott" TagName="header" Src="Controls/Header.ascx" %>
<%@ Register TagPrefix="scott" TagName="footer" Src="Controls/Footer.ascx" %>
<%@ Register TagPrefix="ControlVendor" Assembly="ControlVendor" %>
<html>
<body>
<form id="form1" runat="server">
<scott:header ID="MyHeader" runat="server" />
</form>
</body>
</html>
注意到上面的前两个注册指令是用来注册用户控件的(是在.ascx文件里实现的),最后这个是用来注册编译进一个程序集 .dll 文件里的自定义控件的。注册完后,我们能够在页面的任何地方用设定好的 tagprefix (标识前缀)和标识符号名( tagname)来声明这些控件。这行之有效,但管理起来会很痛苦,当我们要在我们的网站的许多页面上使用控件的话,尤其是,假如你移动了.ascx 文件,需要更新所有的注册声明的话。
处理方案:
ASP.NET 2.0 使得控件声明极其干净而且管理起来极其容易。不用在页面上重复这些声明,只需在应用的web.config 文件的新的 pages->controls 部分声明一次即可:
<?xml version="1.0"?>
<configuration>
<system.web>
<pages>
<controls>
<add tagPrefix="scottgu" src="~/Controls/Header.ascx" tagName="header"/>
<add tagPrefix="scottgu" src="~/Controls/Footer.ascx" tagName="footer"/>
<add tagPrefix="ControlVendor" assembly="ControlVendorAssembly"/>
</controls>
</pages>
</system.web>
</configuration>
能够用这种方式同时声明用户控件和编译好的自定义控件。当使用这个技巧时,Visual Studio是完全支持这两者的,而且 VS 2005 Web Site 项目 和 VS 2005 Web Application 项目也都支持这两者。Visual Studio会在设想器里以所见即所得(WYSIWYG)模式显示这些控件,也会在后台编码文件里提示控件字段的声明。
一旦你在web.config 文件中声明好这些控件后,就能够在你网站上的任何一个页面,母板页或者用户控件中使用它们了,象这样(不再需要注册指令):
<html>
<body>
<form id="form1" runat="server">
<scottgu:header ID="MyHeader" runat="server" />
</form>
</body>
</html>
但要注意:如果在Web.config中注册用户控件,ASCX控件文件跟ASPX页面文件不能放在同一个目录下,否则会报错。
(19)WinForm中将Form显示在Panel中(C#)
pnlOPContainer.Controls.Clear();//移除所有控件
FrmAddUser frmAddUser = new FrmAddUser();
frmAddUser.TopLevel = false;
frmAddUser.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
pnlOPContainer.Controls.Add(frmAddUser);
frmAddUser.Show();
(20)DBNULL和NULL
Null是.net中无效的对象引用。
DBNull是一个类。DBNull.Value是它唯一的实例。它指数据库中数据为空时,在.net中的值。
null表示一个对象的指向无效,即该对象为空对象。
DBNull.Value表示一个对象在数据库中的值为空,或者说未初始化,DBNull.Value对象是指向有效的对象。
DBNull在DotNet是单独的一个类型 System.DBNull 。它只有一个值 DBNull.Value 。DBNull 直接继承 Object。
这里容易犯的一个错误是,把ExecuteScalar返回DBNull与null的情况混淆,例如:
string sql = string.Format("SELECT SUM(Cost) FROM CardRecord WHERE CardNO ='{0}'",txtCardNO.Text);
如果没有数据,这里就会出错。
你可以用Convert.IsDBNull来判断一个值是否DBNull。
if (Convert.IsDBNull(command.ExecuteScalar()) == true)
{
MessageBox.Show("no");
connection.Close();
return;
}
注意Convert.IsDBNull(null)是false,也就是说null跟DBNull.Value是不等的。
(21)装箱和拆箱
object o = 2.56;
float f = (float)o;//运行时报异常。
C#中有两种类型变量,一种是值类型变量, 例如:int, float,double等等;
另一种是引用类型变量例如:string,用class定义的类型等等;
那么这两种类型变量的区别是,前一个是分配在栈中,后一个是分配在堆中。
对于如下操作
object obj = 0.0;
在c#中称为装箱操作,即把本应该放在栈中的数据放在堆中。
那么如果要拆箱,即把object返还成float变量,
不能用
float f2 = (float)(obj);
去获得,而要用
float f2 = float.Parse(obj.ToString());
为什么前者不行呢,因为如果float存在栈中,长度是固定的。但是通过object来存放在堆中,
那么它的长度就不仅仅是float数据的长度,还要包括一些其他信息,那么按照第一种方法进行强转的时候,
很可能按照object存放的起始位置,以及float的变量长度,是无法把相应字节转换成float变量,因此会产生异常。
下面的代码是可以的:
object o = 2.56f;
float f = (float)o;