為 Windows Form 自動設定等待游標

0 comments
在 Windows Form 應用程式中,我們通常會透過變換不同的滑鼠游標形狀(例如沙漏形狀的等待游標),來回應需要使用者等待處理完成的請求,直到處理完成再將游標恢復為預設狀態為止。

為達此目地,一般會在可能需要等待處理的程序中,先將表單的 Cursor 屬性設為 Cursors.WaitCursor,並在作業完成時再將其設為 Cursors.DefaultCursor。同時,為了避免因執行階段的異常情況(Exception)而導致游標狀態異常,會將重設游標的程式碼置於例外處理陳述式的 Finally 區塊中,以確保游標會回復成正常狀態。

雖然,控制游標的作法很簡單,但撰寫仍略嫌瑣碎,如果透過自訂實作 IDisposable 介面的類別,來封裝變更、重設游標的細節,便可讓程式碼更為簡潔,如以下範例:
using(new WaitCursor()) {
//... some long-running code here
}

採用此實作方式,或許會讓你覺得事情已經簡單到不能再簡單了,但事實上,AutoWaitCursor 類別提供了更佳的實現方法。它會自動偵測表單應用程式是否處於忙碌狀態(停止回應),若忙碌逾指定的 Delay 時間(預設為 25 毫秒),則會自動顯示忙碌游標(預設為等待游標),直到應用程式恢復閒置後重設狀態。你只需在表單建構式中,加入少量的程式碼,便可以為你表單應用程式自動設置等待游標的功能,如以下範例:
AutoWaitCursor.MainWindowHandle = this.Handle;
AutoWaitCursor.Start();

值得注意的是,如果你的表單應用程式使用多執行緒,除非表單的主執行緒被封鎖,否則游標將不會自動改變。

參考資料:
Automatically setting the wait cursor for an application

繼續閱讀...

Printing PDF File from ASP.NET

0 comments
使用 PDF(Portable Document Format)文件格式的好處在於,不論使用何種電腦平台或作業系統,都能忠實重現文件的原貌,也不會因為印表機的不同而影響排版。因此,PDF 格式在不同設備上輸出列印的一致性,非常適合用在印表機列印輸出的需求。

在網頁設計實務上,需要動態產生文件及講求排版格式的列印功能亦頗為常見。本文將示範如何使用 iTextShap 建立 PDF 文件,並加入 JavaScript 指令碼透過 Acrobat API 將文件自動輸出至指定的印表機。

下列程式碼範例會假設已經在 ASP.NET Web 網頁建立列印按鈕,並指定如下的 Click 事件處理常式。範例中的 PrintButton_Click 方法,會在指定的目錄下將建立含有列印指令碼的 PDF 文件,並透過泛型處理常式將文件傳送到用戶端,然後輸出至使用者的印表機。
protected void PrintButton_Click(object sender, EventArgs e)
{
Document document = new Document();
MemoryStream ms = new MemoryStream();
PdfWriter writer = PdfWriter.GetInstance(document, ms);
document.Open();

// 加入自動列印指令碼
writer.AddJavaScript(@"
var pp = this.getPrintParams();
pp.interactive = pp.constants.interactionLevel.silent;
pp.pageHandling = pp.constants.handling.none;
var fv = pp.constants.flagValues;
pp.flags |= fv.setPageSize;
pp.flags |= (fv.suppressCenter | fv.suppressRotate);
this.print(pp);
");

document.Add(new Paragraph("Testing Silent Printing with iTextSharp."));
document.Close();

Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = "application/pdf";
Response.AddHeader("Content-Disposition", "inline; filename=Report.pdf");
Response.BinaryWrite(ms.ToArray());
Response.End();
}

參考資料:
Silent Printing on Web based Java Application

繼續閱讀...

Returning Value from ASPxPopupControl

0 comments
ASPxPopupControl 是 Developer Express 在 ASPxperience Suite 裡面所提供的 Web 控制項。你不僅可以利用它填入 HTML 內容外,也可以指定特定的網頁來為你的網頁加入快顯視窗功能。

當你使用 ASPxPopupControl 的 ContentUrl 屬性設定開啟網頁時,它會建立內嵌框架載入網頁內容。為了利於在內嵌網頁中傳遞值給主網頁及隱藏快顯視窗所需的物件參考,需在其 init 事件加入必要的初始化程式碼。
<%@ Page Language="C#" AutoEventWireup="true" %>
<html>
<head runat="server">
<title>Returning Value from ASPxPopupControl</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<table border="0">
<tr>
<td>
<dx:ASPxTextBox ID="supplierTextBox" runat="server"
ClientInstanceName="supplierTextBox" Width="80" />
</td>
<td>
<dx:ASPxButton ID="popupButton" runat="server" Text="..." />
</td>
</tr>
</table>
<dx:ASPxPopupControl ID="suppliersPopup" runat="server"
ClientInstanceName="suppliersPopup" AllowDragging="True"
AllowResize="True" CloseAction="CloseButton"
ContentUrl="SlavePage.aspx" Width="400" Height="450"
ContentStyle-Paddings-Padding="2" HeaderText="Select Supplier"
PopupElementID="popupButton">
<ClientSideEvents Init="function(s, e) {
var iframe = s.GetContentIFrame();
iframe.contentLoaded = false;
var controlCollection = ASPxClientControl.GetControlCollection();
iframe.popupArguments = {
popupContainer: controlCollection.Get('suppliersPopup'),
controlToAssign: controlCollection.Get('supplierTextBox')
};
}"></ClientSideEvents>
</dx:ASPxPopupControl>
</div>
</form>
</body>
</html>

以下程式碼示範如何在內嵌框架頁面中傳值給主網頁並隱藏快顯視窗:
<%@ Page Language="C#" AutoEventWireup="true" %>
<html>
<head runat="server">
<title>Select Supplier</title>
<script type="text/javascript">
function getOuterFrame() {
var iframes = window.parent.document.getElementsByTagName("iframe");
for (var i = 0, len = iframes.length; i < len; i++) {
var doc = iframes[i].contentDocument || iframes[i].contentWindow.document;
if (doc === document) {
return iframes[i];
}
}
return null;
}
function hidePopupWindow() {
var outerFrame = getOuterFrame();
if (outerFrame !== null) {
outerFrame.popupArguments.popupContainer.Hide();
}
}
function setReturnValue(v) {
var outerFrame = getOuterFrame();
if (outerFrame !== null) {
var controlToAssign = outerFrame.popupArguments.controlToAssign;
if (controlToAssign) {
controlToAssign.SetText(v);
}
}
hidePopupWindow();
}
</script>
<script runat="server">
void OkButton_Click(object sender, EventArgs e)
{
if (grid.FocusedRowIndex > -1)
{
object key = grid.GetRowValues(grid.FocusedRowIndex, grid.KeyFieldName);
Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
"SetReturnValue", "setReturnValue('" + key.ToString() + @"');
", true);
}
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<dx:ASPxGridView ID="grid" runat="server"
ClientInstanceName="grid" DataSourceID="NorthwindDataSource"
KeyFieldName="SupplierID" AutoGenerateColumns="false" Width="100%">
<Columns>
<dx:GridViewDataCheckColumn>
<DataItemTemplate>
<input type="radio" name="radioButton" />
</DataItemTemplate>
</dx:GridViewDataCheckColumn>
<dx:GridViewDataColumn FieldName="CompanyName" Caption="Company Name" />
<dx:GridViewDataColumn FieldName="ContactName" Caption="Contact Name" />
</Columns>
<SettingsBehavior AllowFocusedRow="True" />
<ClientSideEvents FocusedRowChanged="function(s, e) {
var row = s.GetRow(s.GetFocusedRowIndex());
if(__aspxIE)
row.cells[0].childNodes[0].checked = true;
else
row.cells[0].childNodes[1].checked = true;
}" />
</dx:ASPxGridView>
<br />
<table border="0" align="center">
<tr>
<td>
<dx:ASPxButton ID="OkButton" runat="server" Text="OK"
OnClick="OkButton_Click" />
</td>
<td>
<dx:ASPxButton ID="CancelButton" runat="server" Text="Cancel">
<ClientSideEvents Click="function(s, e) {
hidePopupWindow();
}" />
</dx:ASPxButton>
</td>
</tr>
</table>
<asp:AccessDataSource ID="NorthwindDataSource" runat="server"
DataFile="~/App_Data/Northwind.mdb"
SelectCommand="SELECT * FROM Suppliers"></asp:AccessDataSource>
</div>
</form>
</body>
</html>

Download sample code

繼續閱讀...

SQL Server 連線共用的問題

0 comments
在預設的情況下,SQL Server 的 .NET Framework 資料提供者會自動提供連線共用(Connection Pooling),以利提升應用程式的延展性及效能。然而,當 ADO.NET 用戶端嘗試使用共用連線時,有可能會遇到如下的例外狀況:
在傳送要求至伺服器時發生傳輸層級的錯誤。 (provider: TCP 提供者, error: 0 - 遠端主機已強制關閉一個現存的連線。)

A transport-level error has occurred when sending the request to the server. (provider: TCP Provider, error: 0 - An existing connection was forcibly closed by the remote host.)

如果共用連線集區的可用連線與伺服器的連接已嚴重損毀,例如:伺服器重新啟動、容錯移轉、使用者處理序被刪除等,連線共用器並無法立即發現異常,直到將其傳回至 ADO.NET 呼叫端並嘗試與伺服器進行通訊後,才會偵測到連接已損毀。當發生此情況時,連線共用器會從集區中移除該連線,並且擲回如上的例外狀況。

連線共用機制並非能完全保證可用連線的有效性,除非你在連線字串中指定 Pooling=false 明確停用,否則你難以避免此類問題發生。除此之外,建議你在資料存取程式碼中加入例外處理常式因應,或是使用 Retryer Pattern 做最佳的容錯處理。

參考資料:
SQL Server 連接共用 (ADO.NET)

繼續閱讀...

ASP.NET DateDropDown Control

0 comments
DateDropDown 是一個使用 UpdatePanel 控制項所建立的連動下拉式日期使用者控制項(User Control)。你可以設定 MinDate 和 MaxDate 屬性,來限制使用者能夠選取的日期範圍,同時也可以搭配驗證控制項檢查使用者輸入的值。

DateDropDown 控制項的完整程式碼如下:
DateDropDown.ascx
<%@ Control Language="C#" AutoEventWireup="true" CodeFile="DateDropDown.ascx.cs" 
Inherits="DateDropDown" %>
<asp:UpdatePanel ID="uxUpdatePanel" runat="server">
<ContentTemplate>
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td><asp:DropDownList id="uxYearList" runat="server" AutoPostBack="true"
OnSelectedIndexChanged="uxYearList_SelectedIndexChanged">
</asp:DropDownList></td>
<td>年</td>
<td><asp:DropDownList id="uxMonthList" runat="server" AutoPostBack="true"
OnSelectedIndexChanged="uxMonthList_SelectedIndexChanged">
<asp:ListItem Text="請選擇"></asp:ListItem>
</asp:DropDownList></td>
<td>月</td>
<td><asp:DropDownList id="uxDayList" runat="server">
<asp:ListItem Text="請選擇"></asp:ListItem>
</asp:DropDownList></td>
<td>日</td>
</tr>
</table>
</ContentTemplate>
</asp:UpdatePanel>
<script language="javascript" type="text/javascript">
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(
function(sender, e) {
var elem = e.get_postBackElement();
if (elem.id == '<%= uxYearList.ClientID %>') {
$get('<%= uxMonthList.ClientID %>').disabled = true;
$get('<%= uxDayList.ClientID %>').disabled = true;
$get('<%= this.ClientID %>').value = '';
}
else if (elem.id == '<%= uxMonthList.ClientID %>') {
$get('<%= uxDayList.ClientID %>').disabled = true;
$get('<%= this.ClientID %>').value = '';
}
});
</script>

DateDropDown.ascx.cs
using System;
using System.Reflection;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

/// <summary>
/// 連動下拉式日期控制項
/// </summary>
[ValidationProperty("Text")]
public partial class DateDropDown : System.Web.UI.UserControl
{
readonly int _maxYear = DateTime.Today.Year + 20;
readonly int _minYear = DateTime.Today.Year - 20;
const string _promptText = "請選擇";

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
Initialize();
}

ScriptManager.RegisterHiddenField(uxUpdatePanel, this.ClientID, this.Text);
RegisterScript();
}

private void Initialize()
{
if (Initialized)
return;

Initialized = true;
PopulateYearList();
AssignValidation();
}

private void RegisterScript()
{
string script = string.Format(
@"$get('{0}').onchange = function() {{
$get('{1}').value =
($get('{2}').selectedIndex > 0 && $get('{3}').selectedIndex > 0
&& this.selectedIndex > 0) ?
$get('{2}').value + '-' + $get('{3}').value + '-' + this.value : '';
}};
", uxDayList.ClientID, ClientID, uxYearList.ClientID, uxMonthList.ClientID);

ScriptManager.RegisterStartupScript(Page, Page.GetType(), UniqueID + "_onchange",
script, true);
}

private bool Initialized
{
set
{
ViewState["Initialized"] = value;
}

get
{
if (ViewState["Initialized"] == null)
ViewState["Initialized"] = false;

return (bool)ViewState["Initialized"];
}
}

/// <summary>
/// 取得選取的日期字串
/// </summary>
public string Text
{
get
{
if (uxYearList.SelectedIndex == 0 || uxMonthList.SelectedIndex == 0
|| uxDayList.SelectedIndex == 0)
return string.Empty;

return new DateTime(int.Parse(uxYearList.SelectedValue),
int.Parse(uxMonthList.SelectedValue),
int.Parse(uxDayList.SelectedValue)).ToString("yyyy-MM-dd");
}
}

/// <summary>
/// 取得或設定驗證群組名稱
/// </summary>
public string ValidationGroup
{
get
{
return (string)ViewState["ValidationGroup"];
}
set
{
ViewState["ValidationGroup"] = value;
}
}

/// <summary>
/// 最小日期值
/// </summary>
public DateTime MinDate
{
set
{
ViewState["MinDate"] = value;
}

get
{
if (ViewState["MinDate"] == null)
ViewState["MinDate"] = new DateTime(_minYear, 1, 1);

return (DateTime)ViewState["MinDate"];
}
}

/// <summary>
/// 最大日期值
/// </summary>
public DateTime MaxDate
{
set
{
ViewState["MaxDate"] = value;
}

get
{
if (ViewState["MaxDate"] == null)
ViewState["MaxDate"] = new DateTime(_maxYear, 12, 31);

return (DateTime)ViewState["MaxDate"];
}
}

/// <summary>
/// 取得或設定選取的日期
/// </summary>
public DateTime SelectedDate
{
set
{
if (value < MinDate || value > MaxDate)
throw new ArgumentOutOfRangeException("SelectedDate",
"The value should be between 'MinDate' and 'MaxDate'.");

Initialize();
uxYearList.SelectedValue = value.Year.ToString();
(uxYearList as IPostBackDataHandler).RaisePostDataChangedEvent();
uxMonthList.SelectedValue = value.Month.ToString();
(uxMonthList as IPostBackDataHandler).RaisePostDataChangedEvent();
uxDayList.SelectedValue = value.Day.ToString();
ScriptManager.RegisterExpandoAttribute(uxUpdatePanel,
ClientID, "value", Text, false);
}

get
{
Initialize();

DateTime selectedValue;
if (DateTime.TryParse(Text, out selectedValue))
return selectedValue;

return DateTime.MinValue;
}
}

protected void AssignValidation()
{
foreach (Control control in this.Controls)
{
PropertyInfo property = control.GetType().GetProperty("ValidationGroup");
if (property != null)
{
property.SetValue(control, ValidationGroup, null);
}
}
}

private void PopulateYearList()
{
uxYearList.Items.Clear();
uxYearList.Items.Add(new ListItem(_promptText));

for (int y = MinDate.Year; y <= MaxDate.Year; y++)
{
uxYearList.Items.Add(new ListItem(y.ToString(), y.ToString()));
if (SelectedDate != DateTime.MinValue && y == SelectedDate.Year)
{
uxYearList.Items.FindByValue(y.ToString()).Selected = true;
}
}
}

private void PopulateMonthList(int year)
{
uxMonthList.Items.Clear();
uxMonthList.Items.Add(new ListItem(_promptText));

int minMonth = MinDate.Year == year ? MinDate.Month : 1;
int maxMonth = MaxDate.Year == year ? MaxDate.Month : 12;

for (int m = minMonth; m <= maxMonth; m++)
{
uxMonthList.Items.Add(new ListItem(m.ToString(), m.ToString()));
if (SelectedDate != DateTime.MinValue && m == SelectedDate.Month)
{
uxMonthList.Items.FindByValue(m.ToString()).Selected = true;
}
}

uxMonthList.Enabled = true;
}

private void PopulateDayList(int year, int month)
{
uxDayList.Items.Clear();
uxDayList.Items.Add(new ListItem(_promptText));

int minDay = MinDate.Year == year && MinDate.Month == month ? MinDate.Day : 1;
int maxDay = MaxDate.Year == year && MaxDate.Month == month ?
MaxDate.Day : DateTime.DaysInMonth(year, month);

for (int d = minDay; d <= maxDay; d++)
{
uxDayList.Items.Add(new ListItem(d.ToString(), d.ToString()));
if (SelectedDate != DateTime.MinValue && d == SelectedDate.Day)
{
uxDayList.Items.FindByValue(d.ToString()).Selected = true;
}
}

uxDayList.Enabled = true;
}

protected void uxYearList_SelectedIndexChanged(object sender, EventArgs e)
{
if (uxYearList.SelectedIndex == 0)
{
uxMonthList.SelectedIndex = uxDayList.SelectedIndex = 0;
uxMonthList.Enabled = uxDayList.Enabled = false;
return;
}

PopulateMonthList(int.Parse(uxYearList.SelectedValue));
}

protected void uxMonthList_SelectedIndexChanged(object sender, EventArgs e)
{
if (uxMonthList.SelectedIndex == 0)
{
uxDayList.SelectedIndex = 0;
uxDayList.Enabled = false;
return;
}

PopulateDayList(int.Parse(uxYearList.SelectedValue),
int.Parse(uxMonthList.SelectedValue));
}
}

下列程式碼範例要求使用者輸入一個介於 2009-07-01 到 2010-07-01 之間的日期,並使用 RequiredFieldValidator 來確認控制項的輸入。
<uc1:DateDropDown ID="DateDropDown1" runat="server" ValidationGroup="DateDropDown" 
MinDate="2009-07-01" MaxDate="2010-07-01" />
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server"
ControlToValidate="DateDropDown1" ValidationGroup="DateDropDown"
ErrorMessage="Please enter a Date!"></asp:RequiredFieldValidator>


Download DateDropDown.zip

繼續閱讀...

Problem Using INSTEAD OF Triggers with Entity Framework

0 comments
若你在使用識別資料行做為主索引鍵的資料表中,定義 INSTEAD OF INSERT 觸發程序,並透過 ObjectContext 的 SaveChanges 方法新增資料時,將會引發 OptimisticConcurrencyException,並導致整個交易復原。以下是發生例外狀況的錯誤訊息:
存放區更新、插入或刪除陳述式影響到非預期數目的資料列 (0)。這些實體載入之後可能被修改或刪除了。請重新整理 ObjectStateManager 實體。

Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.

如以下範例,會依據 AdventureWorks 實體資料模型的定義,將新的實體物件附加到物件內容:
using (AdventureWorksEntities context = new AdventureWorksEntities()) 
{
context.AddToSalesReason(new SalesReason() {
Name = "Recommendation", ReasonType = "Other", ModifiedDate = DateTime.Now
});
context.SaveChanges();
}

當呼叫 SaveChanges 方法時,物件服務會產生及執行以下 SQL 命令:
exec sp_executesql N'insert [Sales].[SalesReason]([Name], [ReasonType], [ModifiedDate])
values (@0, @1, @2)
select [SalesReasonID]
from [Sales].[SalesReason]
where @@ROWCOUNT > 0 and [SalesReasonID] = scope_identity()
'
,N'@0 nvarchar(14),@1 nvarchar(5),@2 datetime2(7)'
,@0=N'Recommendation',@1=N'Other',@2='2009-11-02 15:14:54.4856524'

因為該實體物件包含識別插入的實體索引鍵,所以會在 INSERT 陳述式後,搭配使用 SCOPE_IDENTITY 函數以取得插入識別欄位中的最後一個識別值。然而,如果此資料表已定義 INSTEAD OF INSERT 觸發程序,則 SCOPE_IDENTITY 函數會因為分屬不同的範圍(Scope)而傳回 NULL 值,於是導致 SaveChanges 失敗,而復原該筆交易並擲回如前述的 OptimisticConcurrencyException 例外狀況。

要解決這個問題,你可以使用預儲程序取代 INSTEAD OF INSERT 觸發程序,然後利用實體資料模型設計工具(Entity Designer),把實體類型的修改函式對應至預存程序。或者,你也可以使用 ExecuteCommand 擴充方法來直接執行新增作業的預儲程序。

繼續閱讀...

How to Fix "Unable to update the EntitySet" Error

0 comments
如果你嘗試對資料表實體集進行新增、修改、刪除資料時,有可能會發生無法更新實體集的例外狀況。以下是更新資料時所引發的錯誤訊息:
System.Data.UpdateException: 無法更新 EntitySet '實體集名稱',因為它有 DefiningQuery,但是在 <ModificationFunctionMapping> 項目中沒有 <UpdateFunction> 項目來支援目前的作業。

Unable to update the EntitySet 'EntitySet Name' because it has a DefiningQuery and no <UpdateFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.

當你將不含主索引鍵的資料表加入實體資料模型(Entity Data Model,EDM)時,Entity Framework 會因為找不到資料表主索引鍵,而將定義建立成唯讀的資料表,也就是在描述儲存結構之 SSDL 區段所定義的 EntitySet 項目中,加入 DefiningQuery 項目以對應到資料存放區的唯讀檢視。所以,當實體集使用定義查詢時,你就無法使用標準更新處理序將保存更新至資料來源。如果嘗試對唯讀的實體集進行更新動作,將會引發如上所述的例外狀況。

要解決這個問題,你可以為來源資料表加入主索引鍵,並透過更新資料庫模型來獲得解決。或者,你也可以利用實體資料模型支援使用預儲程序的功能,定義插入、更新、刪除函式對應來進行資料更新。

繼續閱讀...

在 .NET 應用程式中執行 T-SQL 指令碼檔

0 comments
當你在 .NET 應用程式,使用 ADO.NET 提交包含 GO 指令的 T-SQL 批次時,SQL Server 會引發的語法不正確的錯誤。這是因為 GO 指令是 sqlcmd 、osql 等公用程式和 SQL Server 指令碼編輯器所認識的指令,而非有效的 T-SQL 陳述式。

在執行含有一個以上 T-SQL 批次的指令碼時,SQL Server 公用程式會將 GO 視為 T-SQL 陳述式批次結束的信號,並將目前一個或多個 SQL 陳述式的集合傳送至 SQL Server,但並不包含 GO 指令。因此,當你使用 ADO.NET 執行多個 T-SQL 批次的指令碼時,你必須自行過濾 GO 指令,並分批執行 T-SQL 陳述式。

如以下範例使用 EmbeddedResourceTextReader 讀取內嵌資源的 SQL 指令碼檔內容,並透過 Regex 物件以 GO 關鍵字將指令碼分隔成批次陳述式,然後再逐一提交至 SQL Server 執行:
using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Text.RegularExpressions;

namespace RunSql
{
class Program
{
static void Main(string[] args)
{
string script = new EmbeddedResourceTextReader()
.GetFromResources("RunSql.Install.sql");

string[] stmts = Regex.Split(script, "\\sGO\\s", RegexOptions.IgnoreCase);

using (SqlConnection conn =
new SqlConnection(ConfigurationManager
.ConnectionStrings["DefaultConnection"]
.ConnectionString))
{
conn.Open();
using (SqlTransaction trans =
conn.BeginTransaction(IsolationLevel.ReadUncommitted))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
cmd.CommandType = CommandType.Text;

foreach (string stmt in stmts)
{
cmd.CommandText = stmt.Trim();
if (cmd.CommandText.Length > 0)
{
try
{
cmd.ExecuteNonQuery();
}
catch(SqlException)
{
trans.Rollback();
throw;
}
}
}
}
trans.Commit();
}
}
}
}
}

相較於 ADO.NET,使用 SQL Server Management Objects(SMO)就無須處理 GO 指令的問題,使用起來更為簡便。有關如何使用 SMO 執行 T-SQL 批次的範例程式碼,請參閱這裡

繼續閱讀...

Using DHTML Tooltips with AJAX

0 comments
DHTML Tooltips 是一個跨瀏覽器相容的程式庫,提供豐富的功能讓你可以輕鬆建立、自訂多樣的工具提示(Tooltip)或是彈跳式訊息(Pup-up Box)。

程式庫提供 Tip 函式接受訊息字串參數,而 TagToTip 函式則允許你提供元素的識別名稱以顯示其 HTML 的內容。這兩個函式的用法都相當簡單,在此就不再贅述,本文將會側重在兩者的併用,並透過範例逐步說明告訴你,如何結合 jQuery 的應用來動態載入伺服器所提供的提示訊息。

在以下的範例網頁片段中,你會看到座位平面圖,以及其所組成映射區域,並且繫結要實作動態提示訊息的 onmouseover 事件處理函式。
<img src="Images/seatingplan.png" usemap="#seatingPlan" />
<map name="seatingPlan">
<area shape="rect" id="Front Stalls" coords="22,100,493,236"
onmouseover="showSectionInfo(this, 61);" />
<area shape="rect" id="Rear Stalls" coords="22,252,492,388"
onmouseover="showSectionInfo(this, 62);" />
</map>

在定義 onmouseover 事件處理函式之前,我們先在 jQuery 的 DOM ready 事件中,預先建立一個供資料讀取的過程中顯示處理訊息的隱藏區塊,並將 Tooltips 程式庫中的 UnTip 函式繫結所有映射區域元素的 onmouseout 事件處理常式。
$(document).ready(function() {
$('<div></div>')
.attr('id', 'ajaxLoading')
.html('Loading...')
.hide()
.appendTo('body');

$("map[name='seatingPlan'] *")
.mouseout(UnTip);
});

當映射區域元素 onmouseover 事件被觸發時,將會執行以下函式並將所接收的傳遞參數做為請求參數,來進行遠端呼叫:
function showSectionInfo(element, sectionId) {
if (data(element) === undefined) {
tooltip.call(element, 'ajaxLoading');
$.ajax({
url: 'InfoHandler.ashx',
type: 'GET',
data: {
id: sectionId
},
dataType: 'html',
error: function(xhr) {
UnTip();
alert('The ajax request failed.');
},
success: function(response) {
data(element, response);
tooltip.call(element);
}
});
} else {
tooltip.call(element);
}
}

在叫用 jQuery 的 ajax 函式處理非同步請求前,會先透過以下所定義的 tooltip 函式叫用 TagToTip 函式將先前所建立的隱藏訊息顯示出來,以回應使用者的請求:
function tooltip(elementId) {
var options = [ABOVE, true, SHADOW, true];
if (elementId === undefined) {
Tip.apply(this, $.merge([data(this)], options));
} else {
TagToTip.apply(this, $.merge([elementId], options));
}
}

當 AJAX 請求成功後,會透過如下的 data 函式將回應結果暫存到元素物件中,提供給後續可能的相同觸發動作讀取之用,以避免冗餘的資料請求。
function data(element, value) {
return value === undefined ? $.data(element, 'tooltip') :
$.data(element, 'tooltip', value);
}

最後,再次呼叫 tooltip 函式將回應結果委由 Tip 函式顯示出來。

繼續閱讀...

jQuery Floating Layer Plugin

3 comments
在許多網站設計中,浮動圖層(Floating Layer)經常被應用在較長的網頁文件中,以呈現能隨著瀏覽器的捲軸移動的浮動廣告或選單。

Floating Layer Plugin 是以 jQuery 原型物件(Prototype Object)為基礎所擴充的外掛程式庫。相較於先前版本,這次的版本除了將程式碼最佳化外,也增添了若干的新功能。

makeFloating( [options] )
透過元素選取(Selector)將特定的 DOM 元素封裝為具有 jQuery 函式功能的外覆物件後,你可以呼叫此函式來建立自訂的浮動圖層。函式可以接受傳遞物件參數來提供選項設定屬性,如以下所示:
屬性型別說明
positionobject指定 x 和 y 屬性來控制浮動區塊的定位方式。除了絕對座標來定位外,你也可以在 x 軸指定 "left"、"right"、"center" 決定水平位置,在 y 軸指定 "top"、"bottom"、"center" 來決定垂直位置。
範例:
  • {x:0, y:250}
  • {x:'right', y:'top'}
durationnumber指定動畫的顯示速度,以毫秒為單位。
easingstring指定變速移動的特效。
fixedboolean指定是否要固定圖層位置而不隨捲軸移動。

應用範例
首先,除了必要的 jQuery 程式庫外,你還需要下載本程式庫並引入到你的網頁中。然後,在網頁的文件的主體中加入區塊圖層,如以下範例:
<div id="floatlayer" style="
width: 50px;
height: 50px;
border: solid 1px #cccccc;
background-color: #d0d0ff;
z-index: 100;
display: none">
<!-- Place your content here -->
</div>

接下來,你可以處理 jQuery 的 DOM ready 事件,在網頁載入完成後,選取如上的圖層元素並叫用 makeFloating 函式:
$(document).ready(function() {
$('#floatlayer').makeFloating();
});

在預設的情況下,浮動圖層會配合網頁的可見區域以水平置左、垂直置中的方式定位,且會隨瀏覽器的捲軸移動產生擺動(Swing)的動畫效果。如果要變更浮動圖層的預設行為,你可以透過物件參數提供自訂的選項設定。如以下範例會建立水平置右、垂直置中,並加入線性(Linear)動畫效果的浮動圖層:
$('#floatlayer').makeFloating({
position: { x: 'right', y: 'center' },
easing: 'linear'
});

因為程式所表現的動畫效果是透過 jQuery 的 animate 函式來實現,所以 easing 參數只能支援內建的 linearswing 兩種特效。如果內建的動畫效果不能符合你的期望,建議你可以搭配 Easing Plugin 來擴增多種慢入(Easing In)和慢出(Easing Out)的動畫效果,如以下範例就使用了更為順暢的慢出特效:
$('#floatlayer').makeFloating({
position: { x: 'right', y: 'center' },
easing: 'easeOutBounce'
});

當然,如果你不需要動態移動的效果,你也可以透過物件參數的 fixed 屬性來固定圖層的位置:
$('#floatlayer').makeFloating({
position: { x: 'right', y: 'center' },
fixed: true
});

另外,程式庫還提供 floatingPosition 函式,讓你可以在建立浮動圖層物件後,重新定位浮動的目標位置,如以下範例:
$('#floatlayer').floatingPosition({ x: 'center', y: 'center' });


Download jquery.floatinglayer.zip

繼續閱讀...