2014年2月20日 星期四

[ASP.NET]如何抓取大型控制項中每列資料的內容

最近想寫一個程式當作自我測驗的工具

利用GridView撈出資料庫的資料當題目

然後在每一列設一行TextBox作為輸入

答案行則預設Visible=false隱藏答案

最後填完後按下外部button檢查全部TextBox跟答案是否相同

當然這不是一種好的設計方式

因為使用者可以用看網頁原始碼的方式看到答案

有人提供比較好的解決方式是把出入的內容去跟資料庫裡面的內容做比較

不過這個程式只有我自己會用到所以暫時沒這問題


其中有遇到一個問題

我要怎麼用外部button讓每列的資料進行比較?

如果是在資料繫結階段結束後GridView會觸發rowDataBound()事件

ListView則是觸發itemDataBound()事件

都可以在這些事件中抓取網頁中的資料進行比對

但現在資料都已經在頁面上我要怎麼「主動」讓每列重新作出類似itemDataBound()的動作

後來看了人家的意見發現自己有點想太多了

就直接用迴圈去撈每列資料不就好了(是的,其實很簡單,我在想啥Orz)

最後用ListView解法如下

protected void Button1_Click(object sender, EventArgs e)
{
    for (int i = 0; i < ListView1.Items.Count; i++)
    {
        TextBox tb = ListView1.Items[i].FindControl("TextBox1") as TextBox;
        HtmlTableCell ans = ListView1.Items[i].FindControl("answer") as HtmlTableCell;
        if (ans != null)
        {
            if (tb.Text.Trim().Equals(ans.InnerHtml.Trim()))
            {
                Response.Write("答對<br>");
            }
            else
            {
                Response.Write("答錯<br>");
            }
        }
    }
}

ListView有items GridView有rows

都能取得整個資料的集合

所以只要用陣列的方式讀出每一列再用FindControl()就能抓到你要的資料了

2014年2月18日 星期二

[ASP.NET]如何刪除重複的值(物件)

昨天遇到一個問題

就是下拉選單的資料是從現有的方法來的

下拉選單的資料不應該有重複

但問題現有的方法沒辦法做distinct過濾重複的資料

又不能自己寫ADO.NET連資料庫

所以你還是只能用他給你的資料做處理

比較土法煉鋼的方式就是寫一個陣列判斷陣列中有無該值

有就繼續下一個沒有就存進陣列

但這種方式的時間複雜度應該也是最差的

其實類似的問題之前在Java已經有解過-大樂透程式

在C#上我找到的方法是使用Enumerable的Distinct方法

最後解法如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class distinct : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void Button1_Click(object sender, EventArgs e)
    {
        List<distinctexample> list = new List<distinctexample>();
        list.Add(new distinctExample(45));
        list.Add(new distinctExample(32));
        list.Add(new distinctExample(9));
        list.Add(new distinctExample(32));
        list.Add(new distinctExample(66));

        foreach (distinctExample x in list)
        {
            Response.Write(x.number+" ");
        }
        Response.Write("
");
        IEnumerable<distinctexample> distinctList = list.Distinct();
        foreach (distinctExample distinctResult in distinctList)
        {
            Response.Write(distinctResult.number+" ");
        }
    }
}
public class distinctExample : IEquatable<distinctexample>
{
    public bool Equals(distinctExample anyNumber)
    {
        if(Object.ReferenceEquals(anyNumber,null))
            return false;
        if (Object.ReferenceEquals(this, anyNumber))
            return true;
        return number.Equals(anyNumber.number);
    }

    public override int GetHashCode()
    {
        int hashdistinctExample = number == null ? 0 : number.GetHashCode();
        return hashdistinctExample;
    }

    public distinctExample(int number)
    {
        this.number = number;
    }
    public int number
    {
        get;
        set;
    }
}

記得實作Equals跟GetHashCode這兩個方法才能進行distinct

Enumerable也有提供不少常用的方法像是OrderBy、Max、Min、Reverse...

參考資料:Enumerable.Distinct 方法
Enumerable 方法
C# 3.0 泛型集合 Generic Collection 中的 List

2014年2月15日 星期六

[ASP.NET]如何抓到頁面中不是ASP.NET控制項的HTML資料

最近碰到一個超奇怪的程式

裡面一堆奇奇怪怪的問題跟限制

這次遇到的其中一個問題是請你用現有的搜尋找出資料後

判斷資料欄位中是否全部都是"未繳款"的狀態

如果全部都是"未繳款"才可以刪除否則不給使用者刪

一開始想到的解法是用ADO.NET下SQL指令

select count(status) as count from bill where status!='0'

計算status不等於0的欄位有多少

如果不等於0就表示有其中一筆資料已繳款用這樣的方式做判斷

結果後來才知道禁止直接跟對方資料庫做連結

只能用現有的東西Orz

後來想到之前寫的人資料呈現在網頁上是用ListView

ListView可以直接帶出資料來

我直接抓網頁上的資料來判斷就行(題外話,為啥有人要用ListView做出一個很像GridView的東西,你直接用GridView不就好了......)

而在每筆資料進行databound時都會觸發ItemDataBound()這個方法

我就在這方法抓每筆資料裡面我要的資料就行

結果看他網頁原始檔發現他的資料並沒有指派給ASP.NET的控制項

一般如果是用ASP.NET幫你建起來的表單裡面每列資料大致上都會長的類似這樣

<asp:Label> ID="co_vidLabel" runat="server" Text='<%# Eval("co_vid") %>' />

Eval("co_vid")代表進行DataBind的資料庫欄位

另一種方式是Bind("co_vid")

前者是單向繫結後者是雙向繫結

就是前者只能讀後者如果你網頁上會觸發Edit的功能那麼該欄位除了讀也能寫

看到的網頁只有<td><%# Eval("co_vid") %></td>

並沒有像我上面寫的還有個<asp:Label>可以接住Eval("co_vid")的值

因為如果有ASP.NET的控制項那我只要用下面方法就可以抓到控制項的值

protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    //方法一 有asp.net控制項的抓法
    Label covid = e.Item.FindControl("co_vidLabel") as Label;
    if (covid != null)
    Response.Write(covid.Text + "<br>");
}

如果是用GridView也有類似的RowDataBound方法

以上方法行不通只能另想辦法了

一開始在網路上看到這個方法二抓取資料來源控制項的資料

但我試了之後...自己電腦上可以伺服器上行不通

這個程式一堆地方都是謎

更別說知道他資料是不是DataSet或DataTable來的

後來翻到其他程式突然看到某個寫法

ListViewItem item = e.Item;
HtmlTableCell td = item.FindControl("tdCancelFlag") as HtmlTableCell;

看了之後上MSDN查才知道ASP.NET除了提供抓取控制項的方法也有提供抓取HTML ID元件的方法

最後解法如下

protected void ListView1_ItemDataBound(object sender, ListViewItemEventArgs e)
{
    //方法三,使用HtmlTableCell的InnerText屬性跟InnerText屬性
    ListViewItem item = e.Item;
    HtmlTableCell td = item.FindControl("tdCancelFlag") as HtmlTableCell;

    if (!td.InnerText.Trim().Equals("未繳款"))
    {
        DelButton.Enabled = false;
    }
}

其中HtmlTableCell類別有提供InnerText跟InnerHtml這兩個屬性

依照MSDN上面HtmlTableCell類別的對於兩個屬性的定義

InnerHtml 取得或設定在指定的 HTML 伺服器控制項的開始和結尾標記之間的內容。
InnerText 取得或設定在指定的 HTML 伺服器控制項的開始和結尾標記之間的文字。

字面上的意思來看應該InnerHtml會回傳整個Html標籤包含內容InnerText則是純內文

不過看回傳值都一樣

我還怕是不是顯示在網頁所以被網頁給解碼

所以輸出成txt文字檔

可是看起來兩個輸出完全一樣

InnerHtml並沒有如我預期出現HTML標籤

不曉得有哪位高手知道這兩者到底差在哪裡嗎Orz


一開始用這方法有遇到一個bug

Equals("未繳款")這邊怎樣都是false

回家後才想到該不會是回傳值中間有空白...

所以用Trim()去頭去尾就解決這個問題了

另外注意用這個方法你要抓的HTML標籤必須加上runat="server"他才能按圖索驥抓到該ID

<td id="tdCancelFlag" runat="server">
    <%# cancel_flagToCh(Eval("cancel_flag").ToString()) %>
</td>