2013年11月4日 星期一

[ASP.NET] 解決DropDownList內ListItem Value重複的問題

說明:在設計DropDownList的清單項目時,一般是不會有Value相同的情況,如果有重複,DropDownList會傳回Index最前面的那筆ItemList,例:

<asp:DropDownList ID="ddlTest" runat="server" AutoPostBack="True">
        <asp:ListItem Value="0">請選擇</asp:ListItem>
        <asp:ListItem Value="1">台北</asp:ListItem>
        <asp:ListItem Value="1">新北</asp:ListItem>
        <asp:ListItem Value="2">桃園</asp:ListItem>
        <asp:ListItem Value="2">新竹</asp:ListItem>
</asp:DropDownList>

點選新北,此時DropDownList卻顯示台北,而且無法觸發 SelectedIndexChanged 事件。


作法:參考ASP.NET魔法學院的作法,override DropDownList的方法,將VB版本改為C#:
1. 新增一專案,在類別內加入System.Web.UI.WebControls參考

namespace WebControls
{
    public class CDropDownList: DropDownList
    {
        //將DropDownList的selectedIndex也PostBack回Server端
        protected override void AddAttributesToRender(HtmlTextWriter writer)
        {
            bool blAutoPostBack = false;
            string strScript = string.Empty;

            blAutoPostBack = this.AutoPostBack;
            this.AutoPostBack = false;
            base.AddAttributesToRender(writer);

            if (blAutoPostBack)
            {
                base.Attributes.Remove("onchange");
                strScript = String.Format("__doPostBack('{0}',{1})", this.ClientID, "this.selectedIndex");
                writer.AddAttribute(HtmlTextWriterAttribute.Onchange, strScript);
                this.AutoPostBack = true;
            }
        }

 //判斷PostBack的selectedIndex與原selectedIndex是否相同,不同則觸發SelectedIndexChanged 事件
        protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
        {
            string[] strValues = new string[35]; //可自己定義陣列大小
            int iSelectedIndex = 0;

            this.EnsureDataBound();
            strValues = postCollection.GetValues(postDataKey);

            if (strValues != null && Page.Request.Form["__EVENTARGUMENT"].ToString() != "")
            {
                iSelectedIndex = Convert.ToInt32(this.Page.Request.Form["__EVENTARGUMENT"]);
            }

            if (this.SelectedIndex != iSelectedIndex)
            {
                base.SetPostDataSelection(iSelectedIndex);
                return true;
            }
            return false;
        }
    }
}

2. 在web專案加入此專案建置後的dll,在工具箱->選擇項目->加入該元件即可,可自訂前置詞(TagPrefix),定義控制項所在命名空間的別名,預設為cc1。


參考資料:

2013年11月3日 星期日

[Excel] VLOOKUP說明與example

說明:做資料比對時,有時會出現兩個工作表需要比對或合併的情形,例:
sheet 1(全員工)
工號 部門代號
001   A01
002   B01
...
999   Z01

sheet 2(組織調整某些員工)
工號 新部門代號
002   U01
008   Z01
....
999   C01

要在sheet 1合併全公司員工的新部門代號,由於非全部員工都有異動,因此sheet 2只有部門調整的員工資料,資料少時還可以一筆一筆對,但資料一多,該如何處理?


作法:有對應的key值,可利用VLOOKUP函數來處理,此函數有四個引數,語法:
VLOOKUP(lookup_value, table_array, col_index_num, [range_lookup])

lookup_value:表示要主頁面參照的key值,如以工號為準尋找該員工的資料。注意:該值需在table_array最前面,或第一欄,否則顯示#N/A。
table_array:表示要參照的一系列資料,如sheet 2(有工號與新部門代號)
col_index_num:表Excel要傳回參照的那一欄資料(從1開始),如sheet 2有兩欄,要新部門代號所以填2。
[range_lookup]:非必要,bool值,填0 (false)表完全符合,不填或非0則為true,表大約符合,一般填0。

因此,在sheet 1拉一個新欄位打上VLOOKUP(sheet1A:A, sheet2!A:B, 2, 0)即可顯示新工號的欄位。

P.S. HLOOKUP可用來尋找列資料。


參考資料:
http://felin0630.pixnet.net/blog/post/24888627-%E2%96%8Cexcel%E3%80%82%E5%87%BD%E6%95%B8-%E2%96%8Cexcel%E8%B3%87%E6%96%99%E6%AF%94%E5%B0%8D%E5%B0%8F%E6%8A%80%E5%B7%A7(excel%E5%87%BD%E6%95%B8-
Excel說明

2013年10月24日 星期四

[ASP.NET] RadioButtonList內Item改回水平排列

說明: 一般使用RadioButtonList控制項,其Item預設為垂直排列(vertical),例:
o 選項一
o 選項二
該怎麼改成水平排列(horizontal)?
o 選項一 o 選項二

作法:
只要將此控制項的RepeatDirection屬性改為horizontal即可。


參考資料:
http://blog.yam.com/highscope/article/31505801

2013年10月23日 星期三

[ASP.NET] 子視窗日曆傳值回母視窗控制項上

說明:點選母視窗上的TextBox後,彈出一日曆子視窗,點選日期後回傳至母視窗TextBox上。

作法:
Parent.aspx
<head runat="server">
    <title></title>
<script language="javascript" type="text/javascript">
function SetDate(ClientID)
{
    var Url = '../modaldialog/SetDate.aspx?DialogClientID=' + ClientID;
    window.open(Url, 'Calendar', 'width=335, height=170');
}
</script>
</head>
<body>
    <form id="form1" runat="server">
        <asp:TextBox ID="txtDate" runat="server" onclick="SetDate(this.id)"></asp:TextBox>
    </form>
</body>

Son.aspx
protected void Calendar1_SelectionChanged(object sender, System.EventArgs e)
    {
        if (Request.QueryString["DialogClientID"] != null)
        {
            string strClientID = Request["DialogClientID"].ToString().Trim();
            StringBuilder sbJScript = new StringBuilder();
            sbJScript.Append("window.opener.document.getElementById('" + strClientID + "').value = '" + this.Calendar1.SelectedDate.ToString("yyyy/MM/dd") + "';");
            sbJScript.Append("window.close();");
            ClientScript.RegisterStartupScript(this.GetType(), "ReturnValue", sbJScript.ToString(), true);
        }
    }


參考資料:
http://social.msdn.microsoft.com/Forums/zh-TW/b415ff68-2dcc-4e1c-b202-e41b4e29f8a9/a-popup-b-a-

2013年10月22日 星期二

[ASP.NET] MasterPage套用多語系

說明:一般ASP.NET網頁都是override InitializeCulture method來轉換語系,但因MasterPage是繼承MasterPage class(UserControl),無提供InitializeCulture method。

作法:
1. 自訂一BasePage class(放於App_Code)並繼承Page class,在BasePage中override InitializeCulture method,再使各page從繼承Page改為namespace.BasePage,例:
BasePage.cs
namespace Web
{
    public class BasePage : Page
    {
        protected override void InitializeCulture()
        {
            string culture = Session["culture"].ToString();
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture);
            Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
            base.InitializeCulture();
        }
    }
}

再搭配如DropDownList等控制項供選擇,例:
xxx.aspx
 <asp:DropDownList ID="ddlLanguage" runat="server" AutoPostBack="True" onselectedindexchanged="ddlLanguage_SelectedIndexChanged">
    <asp:ListItem Value="zh-TW" Text="<%$ Resources:PageResource, LanguageZh %>"></asp:ListItem>
    <asp:ListItem Value="en-US" Text="<%$ Resources:PageResource, LanguageEn %>"></asp:ListItem></asp:DropDownList>

xxx.aspx.cs
protected void ddlLanguage_SelectedIndexChanged(object sender, EventArgs e)
{
        if (this.ddlLanguage.SelectedIndex > 0)
        {
            Session["culture"] = this.ddlLanguage.SelectedValue;
            //reload request page
            Server.Transfer(Request.Path);
        }
}

參考資料:
http://stackoverflow.com/questions/11805897/masterpage-initializeculture-no-suitable-method-found-to-override-error

2. 透過global.asax。


2013年10月14日 星期一

[ASP.NET/ Crystal Report] 實作multiple table資料來源且table間無關聯的報表

說明:使用Crystal Reports實做一報表,繫結DB table為2個以上,且table之間無關聯,

作法:
1. 使用DataSet建立報表資料結構,新增DataTable以對應不同DB table,可不需建立DataTableAdapter方法
2. 新增Crystal Report檔,使用資料庫專家->ADO.NET資料集,選取建立的table結構
3. 將資料庫欄位拉至報表上
4. 新增一web page以呈現報表,拉一CrystalReportViewer控制項,依需求調整控制項面板
5. 在code behind繫結資料來源:

例:
//需import該dll
using CrystalDecisions.CrystalReports.Engine;

private ReportDocument rdReport;
protected void Page_Load(object sender, EventArgs e)
{
    rdReport = new ReportDocument();
    if  (!IsPostBack)
    {
        BindReport();
    }
}

private void BindReport()
{
    DataSet dsReport = GetReportData();
    string strRptName = Page.MapPath("ReportView.rpt");
     
    rdReport.Load(strRptName);
    rdReport.SetDataSource(dsReport);
    CrystalReportViewer1.ReportSource = rdReport;
    CrystalReportViewer1.DataBind();
}

private DataSet GetReportData()
{
     DataSet dsReturn = new DataSet();
     DataTable dtTemp = null;

     //此處以DataTable回傳各DB table資料,若table之間有關聯,可以join方式回傳即可,無關聯則取得各table資料,再依Step1建立之table name加進DataSet中即可
    dtTemp = 各sql回傳資料;
    dtTemp.TableName = "Step1中建立的table name";
    dsReturn.Tables.Add(dtTemp);

    return dsReturn;
}


參考資料:
http://csharp.net-informations.com/dataset/dataset-multiple-tables-sqlserver.htm
http://www.blueshop.com.tw/board/show.asp?subcde=BRD20110419171449396
http://www.functionx.com/csharp2/dataset/Lesson02b.htm

2013年10月1日 星期二

[ASP.NET] 如何在GridView加上CheckBox全選與取消全選

說明:
在GridView加上CheckBox針對目標列資料勾選或全選以刪除資料。

作法:
//xxx.aspx
<GridView>
  <Columns>
  <asp:TemplateField>
    <HeaderTemplate>
          <asp:CheckBox ID="cbxHead" runat="server" />
    </HeaderTemplate>
    <ItemTemplate>
          <asp:CheckBox ID="cbxRow" runat="server" />
     </ItemTemplate>
</asp:TemplateField>
</Columns>
</GridView>


//xxx.aspx.cs
 protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
 {
         if (e.Row.RowType == DataControlRowType.Header)
        {
            CheckBox cbxHead = (CheckBox)e.Row.FindControl("cbxHead");
            cbxHead.Attributes.Add("onclick", "for(i=0;i<" + GridView1.ClientID + ".all.tags('input').length;i++){if (" + GridView1.ClientID + ".all.tags('input')[i].type=='checkbox'){" + GridView1.ClientID + ".all.tags('input')[i].checked=checked}}");
        }
}