python1

  • 538
  • 0
  • 2022-07-04

python1

python 入門到分析股市 :: 2019 iT 邦幫忙鐵人賽 https://ithelp.ithome.com.tw/users/20111390/ironman https://ithelp.ithome.com.tw/images/ironman/10th/fb.jpg python 入門到分析股市 :: 2019 iT 邦幫忙鐵人賽 https://ithelp.ithome.com.tw/users/20111390/ironman zh-TW Tue, 07 Jun 2022 20:30:10 +0800 [Day30]心得分享 https://ithelp.ithome.com.tw/articles/10201985?sc=rss.iron https://ithelp.ithome.com.tw/articles/10201985?sc=rss.iron 心得

一開始想要學習Python是因為想要學習金融方面的數據分析,先從udemy上面學習Python課程名稱
Machine Learning A-Z™: Hands-On Python & R In Data Science後來要寫大綱的時候才發現,這個主題好像不是我想要的....,應該是要找數據分析的課程。

後來才發現到這個課程Learning Python for Data Analysis and Visualization,才已這個為我的主題大綱,本來是想說一邊看影片學習一邊寫文章,後來發現這樣我只會看程式碼,可是不知道如何講理論,才又開始買書,從書中學理論、實作,如果有時間的話再看影片來補充資料。

後來發現資料科學比我想像中的精深,在找資料的同時發現到很多key word-> Machine Learning、Deep Learning、Artificial Intelligence、Scikit-Learn....在不斷看新課程的同時,也發現自己時間不夠學習這麼多知識,所以只能挑幾個比較符合主題的來論述。

在網路上找了一張學習地圖
https://ithelp.ithome.com.tw/upload/images/20181105/20111390K5Fi8wMwAX.png

http://foundation.datasci.tw/learning-map/

光是機率和統計那關我就卡了蠻久的,太久沒有碰統計,真的都忘光了,花了好多時間,卻也只是學到統計基本而已,所以又跑去買書,想說等寫完鐵人30天再來惡補。

真的覺得寫30天是一個非常快速學習的過程,可以逼自己學習新的技術。經過這次的30天,讓我多了好多計畫,希望自己之後可以認真的執行。應該會將練習的程式碼放在git上面。

筆者還會繼續研究股市,只是文章會放到部落格,希望到時候有毅力可以繼續寫下去。

2018年Python最好的20個數據分析庫

網站列出了2018年Python最好的數據分析庫,這30天介紹了

  • 統計數據
  1. NumPy
  2. Pandas
  • 視覺化
  1. Matplotlib
  2. Seaborn

之後有興趣的人可以進一部了解機器學習、深度學習、自然語言處理、數據採集的部分。

之前的章節

]]> Summer 2018-11-14 00:00:25
[Day29] 使用Prophet預測股票 https://ithelp.ithome.com.tw/articles/10208534?sc=rss.iron https://ithelp.ithome.com.tw/articles/10208534?sc=rss.iron 前言

今天是鐵人的第29天,要來使用Prophet來預測股票。
Prophet:為Facebook提供的套件,用來預測系統,主要用時間序來做分析。
架構...]]> 前言

今天是鐵人的第29天,要來使用Prophet來預測股票。
Prophet:為Facebook提供的套件,用來預測系統,主要用時間序來做分析。
架構如下
https://ithelp.ithome.com.tw/upload/images/20181107/20111390lwYBwCpKAp.png
(圖片來源Url)

Modeling:建立時間序列的模型。這邊使用股票來做模型。
Forecast Evaluation:模型評估。
Surface Problems:呈現問題。
Visually Inspect Forecasts:以視覺化的方式呈現預測結果。


前置作業

打開Anaconda Prompt看是否有安裝Prophet(使用「conda list」指令),沒有就執行

conda install -c conda-forge fbprophet 

預先載入套件

介紹底下的圖表之前先預先載入需要的套件

# basic
import numpy as np
import pandas as pd

# get data
import pandas_datareader as pdr

# visual
import matplotlib.pyplot as plt
%matplotlib inline

#time
import datetime as datetime

#Prophet
from fbprophet import Prophet


from sklearn import metrics

讀取股票

我們讀取以下股票
2492:華新科

start = datetime.datetime(2015,1,5)
df_2492 = pdr.DataReader('2492.TW', 'yahoo', start=start)

收盤價

如果只是用單純的收盤價繪製圖表,之前也介紹過了,範例如下:

plt.style.use('ggplot')
df_2492['Adj Close'].plot(figsize=(12, 8))

https://ithelp.ithome.com.tw/upload/images/20181107/2011139039zPt9RRVX.png

使用Prophet來預測股票

new_df_df_2492['y'] = np.log(new_df_df_2492['y'])
# 定義模型
model = Prophet()

# 訓練模型
model.fit(new_df_df_2492)

# 建構預測集
future = model.make_future_dataframe(periods=365) #forecasting for 1 year from now.

# 進行預測
forecast = model.predict(future)

figure=model.plot(forecast)

https://ithelp.ithome.com.tw/upload/images/20181108/20111390JjJIfN0MWw.png
其中黑色代表實際的值,請注意黑色只到今天這個日期,藍線代表的是預測值,而淡藍色的陰影區表示不確定性,未來時間距離越遠,不確定區域就會越來越大。

如果不考慮未來的不確定性,只有看預測和實際數據的最後800筆(一年252筆),只想看實際和預測之間的視覺差距。

df_2492_close = pd.DataFrame(df_2492['Adj Close'])
two_years = forecast.set_index('ds').join(df_2492_close)
two_years = two_years[['Adj Close', 'yhat', 'yhat_upper', 'yhat_lower' ]].dropna().tail(800)
two_years['yhat']=np.exp(two_years.yhat)
two_years['yhat_upper']=np.exp(two_years.yhat_upper)
two_years['yhat_lower']=np.exp(two_years.yhat_lower)
two_years[['Adj Close', 'yhat']].plot(figsize=(8, 6));

https://ithelp.ithome.com.tw/upload/images/20181108/20111390koTe8eA7Ov.png

橘色是實際的股價,藍色是預測的股價,從圖來看可以知道實際的股價和預測的股價非常相近。

計算誤差

現在來計算實際的股價和預測股價的誤差

two_years_AE = (two_years.yhat - two_years['Adj Close'])
two_years_AE.describe()
# 輸出結果
count    800.000000
mean      -1.451949
std       14.302471
min      -88.053614
25%       -2.389504
50%        0.129703
75%        2.074819
max       64.853188
dtype: float64

平均誤差了-1.451949,最大差別了64.853188,最小-88.053614

  • MSE:均方誤差
print ("MSE:",metrics.mean_squared_error(two_years.yhat, two_years['Adj Close']))
# 輸出結果
MSE: 206.41313426786152

MSE越接近0越好

  • MAE:平均絕對誤差
print ("MAE:",metrics.mean_absolute_error(two_years.yhat, two_years['Adj Close']))
# 輸出結果
MAE: 7.052998896728383

看了一下誤差數,Prophet預測這個股票是算準確的,但參考文章的內容卻是說用來預測股票是不適合的XDD。

  • 加上預測的上下限
fig, ax1 = plt.subplots(figsize=(10, 8))
ax1.plot(two_years['Adj Close'])
ax1.plot(two_years.yhat)
ax1.plot(two_years.yhat_upper, color='black',  linestyle=':', alpha=0.5)
ax1.plot(two_years.yhat_lower, color='black',  linestyle=':', alpha=0.5)

ax1.set_title('華新科技實際和預測的誤差')
ax1.set_ylabel('Price')
ax1.set_xlabel('Date')

https://ithelp.ithome.com.tw/upload/images/20181108/20111390CSpbi3dAH1.png
橘色是實際的價格,藍色是是預測的,上下限用灰色。除了2018年7月的時候波動性太大造成的預測比較不準外,其他其實都蠻相近的。

從這幾個分析來看Prophet比較一個月內的預測,時間拉得越長預測會越不準。

參考資料

Prophet

之前的章節

]]> Summer 2018-11-13 00:01:13
[Day28]投資組合的風險評估 - 對數收益率 https://ithelp.ithome.com.tw/articles/10207621?sc=rss.iron https://ithelp.ithome.com.tw/articles/10207621?sc=rss.iron 前言

昨天用算術收益率來介紹夏普指數(Sharpe ratio),今天要用更貼近金融的對數收益率來算出夏普指數。

預先載入套件

介紹底下的圖表之...]]> 前言

昨天用算術收益率來介紹夏普指數(Sharpe ratio),今天要用更貼近金融的對數收益率來算出夏普指數。

預先載入套件

介紹底下的圖表之前先預先載入需要的套件

# basic
import numpy as np
import pandas as pd

# get data
import pandas_datareader as pdr

# visual
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

#time
import datetime as datetime

讀取股票

我們讀取以下股票
2330:台積電
2492:華新科
3045:台灣大
2412:中華電

start = datetime.datetime(2015,1,5)
df_2330 = pdr.DataReader('2330.TW', 'yahoo', start=start)
df_2492 = pdr.DataReader('2492.TW', 'yahoo', start=start)
df_3045 = pdr.DataReader('3045.TW', 'yahoo', start=start)
df_2412 = pdr.DataReader('2412.TW', 'yahoo', start=start)

股票間的比較

  • 結合四個股票
df_total = pd.concat([df_2330['Adj Close'],df_2492['Adj Close'],df_3045['Adj Close'],df_2412['Adj Close']], axis=1)
df_total.columns=['2330', '2492', '3045', '2412']
  • 日收益率平均
df_total.pct_change().mean()
# 輸出結果
2330    0.000663
2492    0.003169
3045    0.000090
2412    0.000356
dtype: float64
  • 日收益率相關分析
df_total.pct_change().corr()
# 輸出結果
	        2330	    2492	    3045	    2412
2330	1.000000	0.181331	0.224544	0.318880
2492	0.181331	1.000000	0.086055	0.067629
3045	0.224544	0.086055	1.000000	0.333560
2412	0.318880	0.067629	0.333560	1.000000

相關分析這邊指的是研究兩個或兩個以上隨機變亮之間相互依存關係的方向和密切程度的方法。
如果用r表示相關程度。
r > 0 線性正相關。
r < 0 線性負相關。
r = 0 兩個變量之間不存在線性關係。
r數值的相關性如下

r的數值範圍 相關程度
0 <= r < 0.3 低度相關
0.3 <= r < 0.8 中度相關
0.8 <= r < 1 高度相關

因此可以看到台積電(2330)和中華電(2412)是中度相關,中華電(2412)和台灣大(3045)也是中度相關,其他都是低度相關

幾何收益率(對數收益率)和算術平均收益率

  • 算術平均收益率(arithmetic return)
df_total.pct_change().head()
# pct_change():(df_total[0] - df_total[1]) / df_total[1]

https://ithelp.ithome.com.tw/upload/images/20181105/201113900SzqENP58A.png

  • 幾何收益率(對數收益率)(log return)
    假設初期投資1元,第一次結算價值(1+R1)元,第二期再用(1+R1)做投資,第二級結算就為(1+R1)(1+R2)元...以此類推,幾何平均收益率加上了複利的想法所以優於算術平均收益率。
log_ret = np.log(df_total/df_total.shift(1))
#np.log() 以自然數e為基底
#df.shift() shift對數據進行位移的動作

https://ithelp.ithome.com.tw/upload/images/20181105/20111390cA6UNj47qt.png

從兩張圖可以看到算術平均收益率和幾何平均收益率是非常相近的

畫出對數收益率的直方圖

plt.rcParams['axes.unicode_minus']=False
log_ret.hist(bins=100, figsize=(12, 8))

https://ithelp.ithome.com.tw/upload/images/20181105/20111390Alm69jxblk.png
從上面四張圖可以兩兩比較每個股票收益率

  • 對數收益率平均
log_ret.mean()
# 輸出結果
2330    0.000557
2492    0.002445
3045    0.000045
2412    0.000334
dtype: float64
  • 對數收益率共變異數(協方差)(pairwise Covariance)
    共變異數用兩表示兩個隨機變量x,Y的相關性,如果共變異數為正,則代表X,Y同向變化,共變異數為負,則說明X,Y反向運動。絕對值的共變異數越大則越相關。
    公式:https://ithelp.ithome.com.tw/upload/images/20181106/201113903fx1CkxDq8.png
log_ret.cov()

https://ithelp.ithome.com.tw/upload/images/20181105/20111390ixcK9gnSZP.png

用協方差來算出夏普指數(Sharpe ratio)

我們將我們的投資金額用以下的比例分配
2330:台積電 30%
2492:華新科 20%
3045:台灣大 25%
2412:中華電 25%

  • 股票投資比重
weight = np.array([0.3, 0.2, 0.25, 0.25])
print('股票投資比重--->', weight)
  • 平均日收益率
    使用對數收益率的平均乘上比重(weight)再乘上交易日(252)
exp_ret = np.sum((log_ret.mean() * weight )* 252)
print('投資組合的預期報酬--->', exp_ret)
  • 波動性(expected volatility)
    夏普指數的分母(denominator)
exp_vol = np.sqrt(np.dot(weight.T, np.dot(log_ret.cov()*252, weight)))
print('預期波動性--->',exp_vol)

np.dot:矩陣乘法,等同線性代數中乘法,用以下的例子解釋
https://ithelp.ithome.com.tw/upload/images/20181106/20111390oFKGqNEN7H.png
np.sqrt:矩陣開根號

  • 夏普指數(Sharpe ratio)
SR = exp_ret/exp_vol
print('夏普指數--->', SR)
# 輸出結果
股票投資比重---> [0.3  0.2  0.25 0.25]
投資組合的預期報酬---> 0.18986638440106773
預期波動性---> 0.17017725257437183
夏普指數---> 1.115697788798719

參考網站

綠角財經筆記:標準差怎麼算

之前的章節

]]> Summer 2018-11-12 12:58:01
[Day27]投資組合的風險評估 - 算術平均數 https://ithelp.ithome.com.tw/articles/10207232?sc=rss.iron https://ithelp.ithome.com.tw/articles/10207232?sc=rss.iron 前言

我們在投資股票的時候,總不會把雞蛋放在同一個籃子裡,會投資多張股票,而投資組合如何分配和會有哪些風險和報酬就是今天要討論的內容。

專有名詞

前言

我們在投資股票的時候,總不會把雞蛋放在同一個籃子裡,會投資多張股票,而投資組合如何分配和會有哪些風險和報酬就是今天要討論的內容。

專有名詞

收益率(Daily Return):代表股票在一天交易中的價值變化百分比
累計超額收益率(Cumulative Return):累計超額收益率為每隻股票在形成期內月超額收益率的簡單加總。
夏普指數(Sharpe ratio):(平均年化報酬率-無風險利率)/年化標準差


收益率的標準差(Standard deviation):
假設連續五天的收益率分別是(17%,22%,15,-5%,5%)
平均報酬率(使用算術平均數) = (17+22+15+(-5)+5) / 5 = 10.8
收益率和平均之間的差(Deviation)和平方差(Deviation Squared)

收益率 Deviation Deviation Squared
17 6.2 38.44
22 11.2 125.44
15 4.2 17.64
-5 -15.8 249.64
5 -5.8 33.64

收益率的變異數 = (38.44+125.44+17.64+249.64+33.64) / (5-1) = 116.2
收益率的標準差 = 收益率的變異數開根號 = 10.78

剛剛算的收益率的標準差是使用算術平均數,但股票市場比較類似對數收益率(明天會介紹),有以上的基本知識後再來討論投資的風險和評估。

預先載入套件

介紹底下的圖表之前先預先載入需要的套件

# basic
import numpy as np
import pandas as pd

# get data
import pandas_datareader as pdr

# visual
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

#time
import datetime as datetime

讀取股票

我們讀取以下股票
2330:台積電
2492:華新科
3045:台灣大
2412:中華電

start = datetime.datetime(2015,1,5)
df_2330 = pdr.DataReader('2330.TW', 'yahoo', start=start)
df_2492 = pdr.DataReader('2492.TW', 'yahoo', start=start)
df_3045 = pdr.DataReader('3045.TW', 'yahoo', start=start)
df_2412 = pdr.DataReader('2412.TW', 'yahoo', start=start)

收盤價和第一天的收盤價的比較

for stock in [df_2330, df_2492, df_3045, df_2412]:
    stock['normalized_price']=stock['Adj Close']/stock['Adj Close'].iloc[0]

資產分配

我們將我們的投資金額用以下的比例分配
2330:台積電 30%
2492:華新科 20%
3045:台灣大 25%
2412:中華電 25%
之後將分配到的比例乘上每一日的收益率

for stock, weight in zip([df_2330, df_2492, df_3045, df_2412],[0.3, 0.2, 0.25, 0.25]):
    stock['weighted daily return']=stock['normalized_price']*weight

並將剛剛算出來的normalized_price統合到一張表

df_total=pd.concat([df_2330['weighted daily return'], df_2492['weighted daily return'], df_3045['weighted daily return'], df_2412['weighted daily return']],axis=1)
df_total.columns=['2330', '2492', '3045', '2412']

投資

將四個股票的收益率乘上你要投資的總金額,現在假設是10萬,並且多增加一個Total Pos欄位將四個股票目前的金額做加總作為總收益

df_total_money = df_total* 100000
df_total_money['Total Pos']=df_total_money.sum(axis=1)
df_total_money.head()

然後將剛剛總收益繪製圖表

fig = plt.figure(figsize=(10, 6))
plt.plot(df_total_money['Total Pos'], '-' , label="總收益")
plt.plot(df_total_money['2330'], '-' , label="台積電")
plt.plot(df_total_money['2492'], '-' , label="華新科")
plt.plot(df_total_money['3045'], '-' , label="台灣大")
plt.plot(df_total_money['2412'], '-' , label="中華電")
plt.title('收益曲線',loc='right')

plt.xlabel('日期')
plt.ylabel('金額')
plt.grid(True, axis='y')
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181105/20111390wgJhB4nLa2.png
可以看到總收益受到華新科的變動而增加

統計計算

  • 日收益率
df_total_money['daily return']=df_total_money['Total Pos'].pct_change()
  • 累積收益率
    計算最後一天和第一天的變化的百分比
print("累積收益率",df_total_money['Total Pos'].iloc[-1]/df_total_money['Total Pos'].iloc[0]-1)
# 輸出結果
累積收益率 2.074405312864754
  • 平均日收益率
print("平均收益率:",df_total_money['daily return'].mean())
# 輸出結果
平均收益率: 0.0014348141399964849
  • 標準差DAY20有介紹
print("收益率標準差:",df_total_money['daily return'].std())
# 輸出結果
收益率標準差: 0.021708565259989755
  • 使用密度圖來顯示日收益率
plt.rcParams['axes.unicode_minus']=False
fig = plt.figure(figsize=(10, 6))
sns.distplot(df_total_money['daily return'].dropna(),bins=100, label="總收益率")
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181105/20111390oFNIWMgfFU.png

夏普指數(Sharpe ratio)

SR=df_total_money['daily return'].mean()/df_total_money['daily return'].std()
# 輸出結果
0.06770475726506646

代表投資者每增加1%的風險可以換來6%的收益

一年總共有252天的交易日

ASR=np.sqrt(252)*SR
# 輸出結果
1.0747797017961567

參考網站

綠角財經筆記:標準差怎麼算

之前的章節

]]>
Summer 2018-11-11 00:23:20
[Day26] 財報爬蟲和分析 https://ithelp.ithome.com.tw/articles/10204773?sc=rss.iron https://ithelp.ithome.com.tw/articles/10204773?sc=rss.iron 前言

今天是鐵人的第26天,主要介紹爬取財報的資料,和財報的基本分析。

安裝的套件

需要的套件requests,如果沒...]]> 前言

今天是鐵人的第26天,主要介紹爬取財報的資料,和財報的基本分析。

安裝的套件

需要的套件requests,如果沒有這個套件用以下的語法新增

pip install requests

預先載入套件

介紹底下的圖表之前先預先載入需要的套件

# basic
import numpy as np
import pandas as pd

# get data
import pandas_datareader as pdr

# visual
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

#requests
import requests

爬取財報的資料 - 營益

資料來源

網站:公開資訊觀測站
爬取的內容:

  1. 營益分析表

要分析的function

def remove_td(column):
    remove_one = column.split('<')
    remove_two = remove_one[0].split('>')
    return remove_two[1].replace(",", "")

def translate_dataFrame(response):
     # 拆解內容
    table_array = response.split('<table')
    tr_array = table_array[1].split('<tr')
    
    # 拆解td
    data = []
    index = []
    column = []
    for i in range(len(tr_array)):
        td_array = tr_array[i].split('<td')
        if(len(td_array)>1):
            code = remove_td(td_array[1])
            name = remove_td(td_array[2])
            revenue  = remove_td(td_array[3])
            profitRatio = remove_td(td_array[4])
            profitMargin = remove_td(td_array[5])
            preTaxIncomeMargin = remove_td(td_array[6])
            afterTaxIncomeMargin = remove_td(td_array[7])
            if(type(code) == float):
                data.append([code, revenue, profitRatio, profitMargin, preTaxIncomeMargin, afterTaxIncomeMargin])
                index.append(name)
            if( i == 1 ):
                column.append(code)
                column.append(revenue)
                column.append(profitRatio)
                column.append(profitMargin)
                column.append(preTaxIncomeMargin)
                column.append(afterTaxIncomeMargin)
                
    return pd.DataFrame(data=data, index=index, columns=column)

抓取資料的程式

def financial_statement(year, season):

    if year >= 1000:
        year -= 1911
        
    url = 'http://mops.twse.com.tw/mops/web/ajax_t163sb06'
    form_data = {
        'encodeURIComponent':1,
        'step':1,
        'firstin':1,
        'off':1,
        'TYPEK':'sii',
        'year': year,
        'season': season,
    }

    response = requests.post(url,form_data)
    response.encoding = 'utf8'
    
    df = translate_dataFrame(response.text)
    return df

stock = financial_statement(107,2)
stock = stock.astype(float)

抓取到的資料如下圖
https://ithelp.ithome.com.tw/upload/images/20181106/2011139090XgNFaF3f.png

營利分析

抓取完資料之後要來做簡單幾個例子的分析,示範如下

  • 只拿某個股票的營利
stock.loc['一零四']
# 輸出結果
公司代號          3130
營業收入        782.06
毛利率(%)       89.89
營業利益率(%)     21.27
稅前純益率(%)     22.96
稅後純益率(%)     18.55
Name: 一零四, dtype: object
  • 拿兩個以上的股票
stock.loc[['華新科','一零四']]

https://ithelp.ithome.com.tw/upload/images/20181106/20111390llGKr5S41R.png

  • 畫出毛利率的分布圖
plt.rcParams['axes.unicode_minus']=False
fig = plt.figure(figsize=(10, 6))
stock['毛利率(%)'].hist(bins=range(-100,100) , label="毛利率(%)")
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181106/20111390kyvNiBVLhm.png

  • 利用DataFrame來選股
    設定以下的條件
  1. 毛利率(%)大於30
  2. 營業利益率(%)大於30
cond1 = stock['毛利率(%)'] > 30
cond2 = stock['營業利益率(%)'] > 30
stock[cond1 & cond2]

https://ithelp.ithome.com.tw/upload/images/20181106/201113900MzVC5Su5N.png


心得

今天學習了如何抓取財報的資料,並且轉成DataFrame,再用DataFrame來設條件選取想要的股票。這是一個非常基礎的示範。各位讀者也可以設定自己想要的條件來抓取股票

參考網站

FinLab

之前的章節

]]> Summer 2018-11-10 00:35:18
[Day25]繪製K線圖 https://ithelp.ithome.com.tw/articles/10206894?sc=rss.iron https://ithelp.ithome.com.tw/articles/10206894?sc=rss.iron 前言

今天鐵人25天要來繪製K線圖,在繪製K線圖之前要先安裝Ta-lib
Ta-lib:python套件,專門用來計算金融技術指標。裡面...]]> 前言

今天鐵人25天要來繪製K線圖,在繪製K線圖之前要先安裝Ta-lib
Ta-lib:python套件,專門用來計算金融技術指標。裡面有很多好用的function,今天用來算出均線。
K線圖:根據股價一天走勢的 4 個價位:開盤價、收盤價、最高價、最低價繪製而成。開盤價與收盤價,會構成 K 線的實體(Body),最高價、最低價分別成為上影線(Shadow)及下影線。如果收盤價高於開盤價,Body 為紅色;收盤價低於開盤價,Body 為黑色或是綠色。

安裝Ta-lib

  • 下載
    安裝的網址Url到此網頁後尋找
    https://ithelp.ithome.com.tw/upload/images/20181101/20111390RbCpVPElP1.png
    其中cp36表示python3.6,win32表示32位,amd64表示64位,請自行下載對應的檔案

  • 從Anaconda Prompt安裝,cd 到下載的資料夾,執行以下指令,請將 「TA_Lib-0.4.17-cp36-cp36m-win_amd64.whl」換成自己下載的檔案名稱

pip install TA_Lib-0.4.17-cp36-cp36m-win_amd64.whl

載入預設套件

# basic
import numpy as np
import pandas as pd

# get data
import pandas_datareader as pdr

# visual
import matplotlib.pyplot as plt
import mpl_finance as mpf
%matplotlib inline
import seaborn as sns

#time
import datetime as datetime

#talib
import talib

取得股票

使用pandas_datareader抓取某個股票,這邊使用台積電(2330)

start = datetime.datetime(2018,4,1)
df_2330 = pdr.DataReader('2330.TW', 'yahoo', start=start)

繪製蠟燭

使用set_xticklabels來繪製蠟燭

df_2330.index = df_2330.index.format(formatter=lambda x: x.strftime('%Y-%m-%d')) 

fig = plt.figure(figsize=(24, 8))

ax = fig.add_subplot(1, 1, 1)
ax.set_xticks(range(0, len(df_2330.index), 10))
ax.set_xticklabels(df_2330.index[::10])
mpf.candlestick2_ochl(ax, df_2330['Open'], df_2330['Close'], df_2330['High'],
                      df_2330['Low'], width=0.6, colorup='r', colordown='g', alpha=0.75); 

https://ithelp.ithome.com.tw/upload/images/20181101/20111390O4mIIoA5n8.png
從上圖可以看到很基本的k線圖,接下來要加上10日均線和30日均線

加上均線

加上10日均線和30日均線

sma_10 = talib.SMA(np.array(df_2330['Close']), 10)
sma_30 = talib.SMA(np.array(df_2330['Close']), 30)

fig = plt.figure(figsize=(24, 8))
ax = fig.add_subplot(1, 1, 1)
ax.set_xticks(range(0, len(df_2330.index), 10))
ax.set_xticklabels(df_2330.index[::10])
mpf.candlestick2_ochl(ax, df_2330['Open'], df_2330['Close'], df_2330['High'],
                      df_2330['Low'], width=0.6, colorup='r', colordown='g', alpha=0.75)
plt.rcParams['font.sans-serif']=['Microsoft JhengHei'] 
ax.plot(sma_10, label='10日均線')
ax.plot(sma_30, label='30日均線')
ax.legend();

https://ithelp.ithome.com.tw/upload/images/20181101/201113904xyAN6uwEy.png

加上成交量

sma_10 = talib.SMA(np.array(df_2330['Close']), 10)
sma_30 = talib.SMA(np.array(df_2330['Close']), 30)

fig = plt.figure(figsize=(24, 15))
ax = fig.add_axes([0,0.2,1,0.5])
ax2 = fig.add_axes([0,0,1,0.2])

ax.set_xticks(range(0, len(df_2330.index), 10))
ax.set_xticklabels(df_2330.index[::10])
mpf.candlestick2_ochl(ax, df_2330['Open'], df_2330['Close'], df_2330['High'],
                      df_2330['Low'], width=0.6, colorup='r', colordown='g', alpha=0.75)
plt.rcParams['font.sans-serif']=['Microsoft JhengHei'] 
ax.plot(sma_10, label='10日均線')
ax.plot(sma_30, label='30日均線')

mpf.volume_overlay(ax2, df_2330['Open'], df_2330['Close'], df_2330['Volume'], colorup='r', colordown='g', width=0.5, alpha=0.8)
ax2.set_xticks(range(0, len(df_2330.index), 10))
ax2.set_xticklabels(df_2330.index[::10])

ax.legend();

https://ithelp.ithome.com.tw/upload/images/20181101/20111390Od6WRbnZdt.png
接下來要來加上KD指標

加上KD指標

公式:
未成熟隨機值(RSV):(今日收盤價 - 最近9天的最低價) / (最近9天最高價 - 最近9天的最低價)
當日K值:前日K值 * (2/3) + 當日RSV值 * (1/3)
當日D值:前日D值 * (2/3) + 當日K值 * (1/3)

K值就是日常的波動,D值就是稍大一點的趨勢,而RSV就是K的次一級的級數了。三者的關係就像是浪花(RSV),和波浪(K,D)的關係。

KD指標之所以被廣為運用,在於它涵蓋了一定時間內最高價與最低價的概念,因此可以很靈敏地反映出價格的變化。

KD黃金交叉:當KD指標的K值由下往上突破D值,建議買進、做多。
KD死亡交叉:當KD指標的K值由上往下跌破D值時,建議賣出、做空。
KD鈍化:K值在高檔 (K > 80) 或低檔( K < 20) 區連續3天,因為當一檔股票高檔鈍化(K值>80,3天以上),表示非常的強勢通常會再漲的機會就會變得非常高。

sma_10 = talib.SMA(np.array(df_2330['Close']), 10)
sma_30 = talib.SMA(np.array(df_2330['Close']), 30)
df_2330['k'], df_2330['d'] = talib.STOCH(df_2330['High'], df_2330['Low'], df_2330['Close'])
df_2330['k'].fillna(value=0, inplace=True)
df_2330['d'].fillna(value=0, inplace=True)

fig = plt.figure(figsize=(24, 20))
ax = fig.add_axes([0,0.3,1,0.4])
ax2 = fig.add_axes([0,0.2,1,0.1])
ax3 = fig.add_axes([0,0,1,0.2])

ax.set_xticks(range(0, len(df_2330.index), 10))
ax.set_xticklabels(df_2330.index[::10])
mpf.candlestick2_ochl(ax, df_2330['Open'], df_2330['Close'], df_2330['High'],
                      df_2330['Low'], width=0.6, colorup='r', colordown='g', alpha=0.75)
plt.rcParams['font.sans-serif']=['Microsoft JhengHei'] 
ax.plot(sma_10, label='10日均線')
ax.plot(sma_30, label='30日均線')

ax2.plot(df_2330['k'], label='K值')
ax2.plot(df_2330['d'], label='D值')
ax2.set_xticks(range(0, len(df_2330.index), 10))
ax2.set_xticklabels(df_2330.index[::10])

mpf.volume_overlay(ax3, df_2330['Open'], df_2330['Close'], df_2330['Volume'], colorup='r', colordown='g', width=0.5, alpha=0.8)
ax3.set_xticks(range(0, len(df_2330.index), 10))
ax3.set_xticklabels(df_2330.index[::10])

ax.legend();
ax2.legend();

https://ithelp.ithome.com.tw/upload/images/20181108/201113901h6zvsah62.png
KD值的前8天會是Nan,為了讓時間序列相同,因此將Nan值填入0,所以前8天的KD值不具任何參考作用。

以上就完成基礎的k線圖了。可喜可賀。


心得

股市走勢可以使用KD值做參考,當遇到KD黃金交叉時買入,KD死亡交叉賣出。

參考資料

https://www.cmoney.tw/notes/note-detail.aspx?nid=6460

之前的章節

]]> Summer 2018-11-09 00:01:55
[Day24] 分析股票 - 基礎股票分析 https://ithelp.ithome.com.tw/articles/10205068?sc=rss.iron https://ithelp.ithome.com.tw/articles/10205068?sc=rss.iron 前言

今天是鐵人的第24天,要來用前一天介紹的抓取股票資訊來分析股票,取出歷史收盤資料畫出圖表、取得收益率(投資風險會使用到)、每天的波動。

預先載入套件 前言

今天是鐵人的第24天,要來用前一天介紹的抓取股票資訊來分析股票,取出歷史收盤資料畫出圖表、取得收益率(投資風險會使用到)、每天的波動。

預先載入套件

介紹底下的圖表之前先預先載入需要的套件

# basic
import numpy as np
import pandas as pd

# get data
import pandas_datareader as pdr

# visual
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns

#time
import datetime as datetime

股票分析

股票數據是一種時間序列數據,因此有時間順序,因此可以對股票數據進行歷史趨勢分析和圖表化,
以下用台積電(2330)和華新科(2492)進行比較

start = datetime.datetime(2015,1,5)
df_2330 = pdr.DataReader('2330.TW', 'yahoo', start=start)
df_2492 = pdr.DataReader('2492.TW', 'yahoo', start=start)

取出的股價格式如下
https://ithelp.ithome.com.tw/upload/images/20181108/20111390Bi3GwsGsvg.png

示範1. 台積電(2330)和華新科(2492)的收盤價

fig = plt.figure()
df_2330['Adj Close'].plot(label="台積電")
df_2492['Adj Close'].plot(label="華新科")
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181031/20111390OIOvM8PsVV.png
其中藍色是台積電(2330),橘色是華新科(2492),圖表的線條顏色可以利用以下的方式取得

sns.palplot(sns.color_palette())

示範2. 台積電(2330)和華新科(2492)的交易量

fig = plt.figure()
df_2330['Volume'].plot(label="台積電")
df_2492['Volume'].plot(label="華新科")
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181031/20111390AAFCN4x3m5.png

示範3. 台積電(2330)和華新科(2492)每天的波動

df_2330['High-Low'] = df_2330['High'] - df_2330['Low']
df_2492['High-Low'] = df_2492['High'] - df_2492['Low']

fig = plt.figure()
df_2330['High-Low'].plot(label="台積電")
df_2492['High-Low'].plot(label="華新科")
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181031/20111390OlqWnWGfw6.png
可以看到華新科的波動比較大

示範4. 台積電(2330)的收益率,使用直方圖
收益率:代表股票在一天交易中的價值變化百分比

plt.rcParams['axes.unicode_minus']=False
fig = plt.figure(figsize=(10, 6))
df_2330['daily-return'] = df_2330['Adj Close'].pct_change()
df_2330['daily-return'].plot(label="台積電", kind='hist')
plt.legend()

https://ithelp.ithome.com.tw/upload/images/20181031/20111390UaghgIBncj.png
可以看到收益率都是落在0以上,因此可以判定總體價格是在上升的

示範4. 華新科(2492)的收益率,使用seaborn的kde密度圖

fig = plt.figure(figsize=(10, 6))
df_2492['daily-return'] = df_2492['Adj Close'].pct_change()
sns.distplot(df_2492['daily-return'].dropna(),bins=100, label="華新科")
plt.legend()
# pct_change() = (df_2492['Adj Close'][0] - df_2492['Adj Close'][1]) / df_2492['Adj Close'][1]

https://ithelp.ithome.com.tw/upload/images/20181031/20111390ndJlolWiWN.png

比較多個股票

DataReader之前示範都一次只有讀取一支股票,但DataReader可以一次讀取多個股票,使用方式就是傳入一個list
2492:華新科
2330:台積電
3045:台灣大
2412:中華電
2409:友達

start = datetime.datetime(2015,1,5)
campany = ['2492.TW', '2330.TW', '3045.TW', '2412.TW', '2409.TW']
df_stock = pdr.DataReader(campany, 'yahoo', start=start)

獲取的股票資訊是multiple index,假設要取得2330.TW的收盤價要使用以下的方式

df_stock['Adj Close']['2330.TW']

分析股票間彼此的關係

範例1. 取得每個股票的收盤價

adjClose = df_stock['Adj Close']
adjClose.plot()

https://ithelp.ithome.com.tw/upload/images/20181031/201113901EhwrGGLsB.png

範例2. 比較台灣大(3045)和中華電(2412)的收益率關係,使用閃點圖

plt.rcParams['axes.unicode_minus']=False
adjClose_pct = adjClose.pct_change()
sns.jointplot('2412.TW','3045.TW',adjClose_pct, kind="scatter")

https://ithelp.ithome.com.tw/upload/images/20181031/20111390lzyRyF2Nas.png
其中x軸是代表中華電,y軸是台灣大,這張圖表傳達的訊息

  1. 當x和y的座標都小於0,代表那天中華電和台灣大都是跌,大於0則漲
  2. 當x和y的值越相近時,則閃點圖會越趨向一直線。代表兩個股票越正相關

範例3. 跟範例2相同,但加上線性回歸線

sns.regplot('2412.TW','3045.TW',adjClose_pct)

https://ithelp.ithome.com.tw/upload/images/20181101/20111390ieKmoPSlSU.png

範例4. 同時比較5家公司

sns.pairplot(adjClose_pct.dropna())

https://ithelp.ithome.com.tw/upload/images/20181031/20111390F67wa6A6dS.png
x和y座標會同時列出5家公司,兩兩座比較,可以用範例2的分析方式來分析兩家公司。

之前的章節

]]> Summer 2018-11-08 09:18:15
[Day23]抓取股票 - twstock、pandas-datareader https://ithelp.ithome.com.tw/articles/10205113?sc=rss.iron https://ithelp.ithome.com.tw/articles/10205113?sc=rss.iron 前言

今天是鐵人的第23天,主要介紹兩個抓取股票的套件,本來是要用爬蟲的方式擷取網路的股價資訊,後來發現這兩個套件,就想說那就用這兩個套件來取得歷史資料。之後幾天才會有抓取綜合...]]> 前言

今天是鐵人的第23天,主要介紹兩個抓取股票的套件,本來是要用爬蟲的方式擷取網路的股價資訊,後來發現這兩個套件,就想說那就用這兩個套件來取得歷史資料。之後幾天才會有抓取綜合損益表和資產負債表的文章。

  1. twstock
  2. pandas-datareader

twstock

可以下以下的指令,看Anaconda是否有安裝twstock

conda list

如果沒有安抓的話可以使用以下語法安裝

pip install twstock

twstock套件有非常多好用的function,以下示範列出台灣的所有股票,只是這個function非常吃資源,筆者跑了一次等了很久才出現。

import twstock
print(twstock.codes)

如果只是看某個股票可以使用

print(twstock.codes['6207'])
# 輸出結果
StockCodeInfo(type='股票', code='6207', name='雷科', ISIN='TW0006207004', start='2002/12/23', market='上櫃', group='電子零組件業', CFI='ESVUFR')

twstock.Stock()

之後實現要抓取的股票的歷史資料(抓取最近31天的資料),假設要抓雷科(6207)

# 這是抓取歷史資料
stock_6207 = twstock.Stock('6207')

price_6207 = stock_6207.price[-5:]       # 近五日之收盤價
high_6207 = stock_6207.high[-5:]         # 近五日之盤中高點
low_6207 = stock_6207.low[-5:]           # 近五日之盤中低點
date_6207 = stock_6207.date[-5:]         # 近五日的日期

print('price_6207--->', price_6207)
print('high_6207--->', high_6207)
print('low_6207--->', low_6207)
print('date_6207--->', date_6207)
# 輸出結果
price_6207---> [30.25, 29.05, 29.35, 28.25, 28.45]
high_6207---> [30.4, 30.45, 29.75, 28.8, 29.1]
low_6207---> [29.35, 29.05, 29.0, 28.0, 28.0]
date_6207---> [datetime.datetime(2018, 10, 22, 0, 0), datetime.datetime(2018, 10, 23, 0, 0), datetime.datetime(2018, 10, 24, 0, 0), datetime.datetime(2018, 10, 25, 0, 0), datetime.datetime(2018, 10, 26, 0, 0)]

twstock.fetch()

如果要抓取超過31天的資料可以使用fetch(),以下示範抓雷科(6207),2017年10月的資料

stock_6207_2017_10 = stock_6207.fetch(2017,10)      # 獲取 2017 年 10 月之股票資料

當然還可以使用取得從過去到現在的股價,下面示範從2018年01月到現在的資料,並且用開盤價和收盤價畫成圖表

import matplotlib.pyplot as plt
import pandas as pd


stock_6207_2018 = stock_6207.fetch_from(2018,1)     # 獲取 2018 年 01 月至今日之股票資料
stock_6207_2018_pd = pd.DataFrame(stock_6207_2018)
stock_6207_2018_pd = stock_6207_2018_pd.set_index('date')


fig = plt.figure(figsize=(10, 6))
plt.plot(stock_6207_2018_pd.close, '-' , label="收盤價")
plt.plot(stock_6207_2018_pd.open, '-' , label="開盤價")
plt.title('雷科股份2018 開盤/收盤價曲線',loc='right')
# loc->title的位置
plt.xlabel('日期')
plt.ylabel('收盤價')
plt.grid(True, axis='y')
plt.legend()
fig.savefig('day20_01.png')

https://ithelp.ithome.com.tw/upload/images/20181026/20111390yoo59kX1ZY.png

realtime()

這個套件不只可以抓取歷史資料也可以抓取即時資料。

stock_6207_real = twstock.realtime.get('6207')
# 抓取多個股票的方式 twstock.realtime.get(['2330', '2337', '2409'])
stock_6207_real
# 輸出結果
{'timestamp': 1540449000.0,
 'info': {'code': '6207',
  'channel': '6207.tw',
  'name': '雷科',
  'fullname': '雷科股份有限公司',
  'time': '2018-10-25 14:30:00'},
 'realtime': {'latest_trade_price': '28.25',
  'trade_volume': '61',
  'accumulate_trade_volume': '931',
  'best_bid_price': ['28.20', '28.15', '28.10', '28.05', '28.00'],
  'best_bid_volume': ['7', '29', '15', '10', '105'],
  'best_ask_price': ['28.25', '28.30', '28.40', '28.45', '28.50'],
  'best_ask_volume': ['6', '2', '1', '11', '5'],
  'open': '28.20',
  'high': '28.80',
  'low': '28.00'},
 'success': True}

從上面可以看出抓取即時資料只會抓出一筆,所以需要用schedule定時去抓,筆者等30天後在實作看看用aws的lambda去抓取。


pandas-datareader

可以下以下的指令,看Anaconda是否有安裝pandas-datareader

conda list

如果沒有的話則下

conda install pandas-datareader

抓取股票資訊

使用以下函式抓取yahoo歷史資料,台灣股市的話要用 股票代號 加上 .TW

import pandas_datareader as pdr
df_2330 = pdr.DataReader('2330.TW', 'yahoo')

回傳的格式規格

參數名稱 描述
Open 開盤價
High 最高價
Low 最低價
Close 收盤價
Volume 交易量
Adj Close 經過調整的收盤價

如果要加上擷取某個時段的歷史資料

startTime = '2018-10-01'
endTime = '2018-10-30'
df_2330 = pdr.DataReader('2330.TW', 'yahoo', startTime, endTime)

參考資料

pandas_datareader

之前的章節

]]> Summer 2018-11-07 09:12:46
[Day22]分析股票 - 股票基本常識 https://ithelp.ithome.com.tw/articles/10207014?sc=rss.iron https://ithelp.ithome.com.tw/articles/10207014?sc=rss.iron 前言

今天是鐵人的第22天,主要要來介紹股票和投資股票前的一些數學常識(從網路文章中擷取),今天程式的部分比較少,主要是講股票的觀念。接下來的幾個章節才會開始進入股票分析。 前言

今天是鐵人的第22天,主要要來介紹股票和投資股票前的一些數學常識(從網路文章中擷取),今天程式的部分比較少,主要是講股票的觀念。接下來的幾個章節才會開始進入股票分析。

股票分析的過程

使用python來分析真實世界的財經資料

  1. 股票的資料:通常是csv檔案,在day6的時候有介紹檔案的部分
  2. 資料的分析:統計模型,day7~day14介紹資料的分析
  3. 視覺化資料:將資料轉換成圖表,day15~day19

股票市場

  • 初級市場:又稱「發行市場」,資金需求者(包括政府單位、金融機構及公民營企業)為籌集資金,出售有價證券給最初購買者之發行市場
  • 次級市場:又稱「流通市場」,是初級市場發行後之有價證券買賣的交易市場。我們平常所看到的股價與交易資訊大多來自次級市場

交易所

在台灣處理股票的單位叫做「台灣證券交易所」股份有限公司,成立為1961年10月3日。

  • 交易時間:星期一到星期五的 09:00 ~ 13:30。
    • 休假停市日除經過特別公告外,與金融業的例行假日相同
    • 台北市宣布停止上班上課時,則台灣証券交易所休市

市場概況

資料來源1
台灣證券交易所
證券櫃買賣中心 - 上櫃
證券櫃買賣中心 - 興櫃

公開發行公司 上市公司 上櫃公司 興櫃公司
數量 920 763 257
市值 285,972.81(億元) 2,607,551(佰萬) 510,665(佰萬)

股票的歷史資料

用套件抓取某個股票的歷史資料時,格式通常會如下

日期 最高價High 最低價Low 開盤價Open 收盤價Close 成交量Volume 調整後收盤價Adj Close
2015-01-05 140.5 137.5 140.5 139.5 32046000.0 121.474525

股票的價格取決於競標的形式。
開盤價:指的是9點開盤之前,把每一個股票買賣當量做統計所訂出的價格。
收盤價:股票收盤之前,把每一個股票買賣當量做統計所訂出的價格。
日報酬率:把前一天的收盤價和今天的收盤價做比較。

關於投資幾個數學常識

  1. 關於收益率
    假設你投資現金有10萬,第一個月收益100%,資金到達20萬,但第二個月損失50%,資金卻會跌回10萬
money = 10
first_month = 10*(1+1)
second_month = first_month*(1-0.5)
print('第一個月收益:',first_month)
print('第二個月收益:',second_month)
# 輸出結果
第一個月收益: 20
第二個月收益: 10.0

結論是虧損比賺錢容易

  1. 關於漲跌停
    假設你股票有10萬,有以下兩種情況
    (1). 第一天漲10%,第二天跌10%。
    (2). 第一天跌10%,第二天漲10%,不管是哪一種資金最後都是9.9萬。
money = 10
up_first_day = 10*(1+0.1)
up_second_day = up_first_day*(1-0.1)

down_first_day = 10*(1-0.1)
up_second_day = down_first_day*(1+0.1)
print('第一種股票餘額:',up_second_day)
print('第二種股票餘額:',up_second_day)
# 輸出結果
第一種股票餘額: 9.9
第二種股票餘額: 9.9

結論是漲跌不管前後餘額都一樣

  1. 關於波動性
    假設你股票有10萬,第一年賺40%,第二年賠20%,第三年賺30%,第四年賠20%,第五年賺50,第六年賠20%
import math as math
money = 10
six_year = 10*(1+0.4)*(1-0.2)*(1+0.3)*(1-0.2)*(1+0.5)*(1-0.2)
rate = math.pow((six_year/money),1/6)-1
print('六年後的資本額:',six_year, "   年報酬率:",rate)
# 輸出結果
六年後的資本額: 13.977600000000004    年報酬率: 0.05739868994194808

波動性大的股票,年報酬率有時候還會輸績優股

  1. 每天只賺1%
    假設你股票有50萬,每天只賺1%,一年會有250個交易日
money = 50
day = 250
print('一年的資本額:',math.pow(1+0.01,day) * money, "   二年的資本額:",math.pow(1+0.01,day*2) * money)
# 輸出結果
一年的資本額: 601.6077884148732    二年的資本額: 7238.638621628698

就算每次報酬只有1%,但只要穩定投資,也可以成長為大資本

  1. 投資組合
    假設你股票有100萬,投資以下組合 無風險資產A(每年5%),高風險資產B(每年-20%~40%),投資80萬到A,20萬到B
money = 100
big_risk = 20
print('最佳投資:', ( (100-big_risk)*(1+0.05) + (big_risk)*(1+0.4)))
print('最差投資:', ( (100-big_risk)*(1+0.05) + (big_risk)*(1-0.2)))
print('平均投資:', ( (100-big_risk)*(1+0.05) + (big_risk)*0.5*(1-0.2) + (big_risk)*0.5*(1+0.4) ))
# 輸出結果
最佳投資: 112.0
最差投資: 100.0
平均投資: 106.0

最好的年報酬率可以到0.12,最差就攤平,這就是保本基金CPPI技術的雛形。

  1. 關於做空
    做空最大的收益率就是100%,前提是這支股票下市,但做多的收益率是沒有上限的,因此不要永遠的做空。除非你不相信人類社會會前進。

  2. 關於賭場贏利
    分析了澳門賭客1000個數據,發現勝率為0.53,輸的是0.47,但是如果加上人性去算,
    贏錢而離場的人平均贏利為0.34,但輸錢而離場的人卻虧損0.72,因此賭場並不需要做局來降低勝率,只要依靠人性的弱點就可以持續贏,股市亦如此。

0.53*0.34+0.47*(-0.72)
# 輸出結果
-0.15819999999999995

所以加上人性後來看,莊家還是賺錢的

從以上結論來看,所以為什麼要用程式交易,因為程式看的是數據,不會受心理因素影響。這也是筆者正在學習的,因為在寫這文章之前,筆者完全不懂Python,也不懂股票,更別說什麼數據分析的演算法,現在文章寫到20天,也只是了解Python基礎語法,和資料處理和圖形化,真正要進入股市分析也是一知半解,所以接下來的文章排版會更亂,都是用在網路上看到什麼內容在拿出來分享。

參考來源

(1) https://community.bigquant.com/t/%E9%87%8F%E5%8C%96%E5%B0%8F%E8%AF%BE%E5%A0%82-Finance%E7%B3%BB%E5%88%97%E7%AC%AC%E4%B8%80%E8%AE%B2-%E5%85%B3%E4%BA%8E%E6%8A%95%E8%B5%84%E7%9A%8410%E4%B8%AA%E6%95%B0%E5%AD%A6%E5%B8%B8%E8%AF%86/123

之前的章節

]]> Summer 2018-11-06 00:01:39
[Day21]統計學基礎 - 隨機變數(Random Variable) https://ithelp.ithome.com.tw/articles/10202496?sc=rss.iron https://ithelp.ithome.com.tw/articles/10202496?sc=rss.iron 前言

今天要來介紹隨機變數(Random Variable),統計的母體是某個要預測事物的所有可能發生結果的集合,隨機變數則是一個不確定性事件結果的數值函數(Function)...]]> 前言

今天要來介紹隨機變數(Random Variable),統計的母體是某個要預測事物的所有可能發生結果的集合,隨機變數則是一個不確定性事件結果的數值函數(Function),也就是說把不確定性事件的結果用數值來表示,即為隨機變數。根據隨機變數可能取值的結果,可以分成兩種:

  1. 離散型隨機變數(Discrete Random Variable):隨機變數取值的範圍有限個(ex 擲硬幣只有兩種結果,所以取擲的範圍有限制)
  2. 連續型隨機變數(Continuous Random Variable):隨機變數可以在一個區間上任意取值(ex 股票中的收益率就是連續型隨機變數)

離散型隨機變數(Discrete Random Variable):

設X是一個隨機變數,如果它全部可能的取值只有有限個或可數無窮個,則稱X為一個離散型隨機變數。
假設現在有n個數據,每個數據發生的機率即為
概率質量函數(Probability Mass Function): P(X = xi) = Pi,i = 1,2,...
使用Python中的choice()來產生特定機率隨機數

choice(a, size=None, p=None)

a:隨機變數所有可能的取值
size:產生的陣列大小
p:與a同等大小,指定每種數據發生的機率

import numpy as np
import pandas as pd

ironman = np.random.choice([1,2,3,4,5], size=1000, p=[0.25,0.1,0.35,0.2,0.1])
valueCount = pd.Series(ironman).value_counts()
print('valueCount/1000---->', valueCount/1000)
# 輸出結果
valueCount/1000----> 3    0.365
1    0.245
4    0.191
5    0.119
2    0.080

從上面的例子可以看到,原本設定,[1,2,3,4,5]出現的機率為[0.25,0.1,0.35,0.2,0.1],使用choice隨機產生1000個數字其中[1,2,3,4,5]所佔的機率為[0.245,0.080,0.365,0.191,0.119]兩邊所呈現的機率很相近。當樣本越高,結果會越接近設定的機率。

連續型隨機變數(Continuous Random Variable):

即在一定區間內變數取值有無限個,或數值無法一一列舉出來。用來表示某個區間內每個數值所發生的機率為
機率密度函數(Probability Density Function)https://ithelp.ithome.com.tw/upload/images/20181024/201113905PdfAl1yYy.png

以下圖為例,在5~6發生的機率即為紅色的面積
https://ithelp.ithome.com.tw/upload/images/20181024/2011139010LKDX07HM.png
下面使用台灣50的日收益率來顯示機率分佈的過程

from scipy import stats
import matplotlib.pyplot as plt
%matplotlib inline

tw50 = pd.read_csv('tw50.csv', sep='\t', index_col='Date')

tw50_gaussian = stats.kde.gaussian_kde(tw50.ROI)
# gaussian_kde-> 高斯核函數,kde 是核函數密度估計
x_value = np.arange(-5,5,0.02)

fig = plt.figure()
plt.subplot()
plt.plot(x_value, tw50_gaussian(x_value))
plt.title("台灣50指數收益率的概率密度曲線圖")

https://ithelp.ithome.com.tw/upload/images/20181024/20111390bXyS7z7N1j.png

期望值和變異數

期望值(Expectation)是隨機變數所有可能取值結果的均值,用來呈現母體的中心位置:
間斷型公式:https://ithelp.ithome.com.tw/upload/images/20181024/20111390Vw9PqDvjjG.png
連續型公式:https://ithelp.ithome.com.tw/upload/images/20181024/201113908focFcA4IM.png
將所有出現X樣本值*樣本機率相加
變異數:
間斷型公式:https://ithelp.ithome.com.tw/upload/images/20181024/20111390mMpzuDbVjY.png
連續型公式:https://ithelp.ithome.com.tw/upload/images/20181024/20111390zpkJVmlLUG.png

二項分佈

二項實驗:做了n次的試驗只有兩種結果(成功、失敗),p(成功的機率)+q(失敗的機率) = 1,每次試驗都是相互獨立的。X表示成功的次數其公式如下
https://ithelp.ithome.com.tw/upload/images/20181024/20111390HUCAPuxEIL.png

Numpy中產生二項分佈隨機數的函式如下:

binomial(n,p,size)

n:二項實驗的次數
p: 表示成功的機率
size: 隨機產生的數量

投擲100次硬幣,正面朝上的對應的機率為0.5,對應的分佈為b(100,0.5),之後產生20個來源於該二項分佈的隨機數

np.random.binomial(100,0.5,20)
# 輸出結果
array([56, 49, 49, 52, 44, 50, 40, 52, 46, 49, 54, 46, 55, 49, 57, 45, 45,
       54, 53, 47])

從上述的例子引申為,假設每次擲骰子正面朝上的機率為0.5,擲100次,有20次正面朝上的機率為

from scipy import stats
stats.binom.pmf(20,100,0.5)
# 輸出結果
4.228163267601253e-10

二項式分佈在金融市場中的應用

ret = tw50.ROI  #台灣50的收益率數據
ret.index = pd.to_datetime(tw50.index)
ret1 = ret['2016']

#台灣50上漲的機率p
p = len(ret1[ret>0]) /len(ret1)
print('p----->', p)

#預估未來20個交易日中,有7個是上漲的機率
prob =stats.binom.pmf(6,20,p)
print('prob----->', prob)
# 輸出結果
p-----> 0.5327868852459017
prob-----> 0.02093703958512302

常態分佈

二項式分配指的是,試驗次數n越大的時候,則無論成功機率p值為何,其分配會越來越呈現對稱的情形,當二項分佈的np > 5 且 nq > 5 時,通常會以常態分佈來估計機率。二項分配是一種間斷型的分配,從下圖可以得知當n越來越大時會越來越像常態分佈
https://ithelp.ithome.com.tw/upload/images/20181024/20111390HPfIvrMyxi.png

之前的章節

]]> Summer 2018-11-05 10:10:25
[Day20]統計學基礎 - 平均數、中位數、眾數、平均絕對偏差 https://ithelp.ithome.com.tw/articles/10204118?sc=rss.iron https://ithelp.ithome.com.tw/articles/10204118?sc=rss.iron 前言

今天是鐵人的第20天,在進入金融數據分析之前要先學習統計分析(Statistical Analysis),統計分析是以數據為基礎,對數據進行科學處理、分析。統計分析包括兩...]]> 前言

今天是鐵人的第20天,在進入金融數據分析之前要先學習統計分析(Statistical Analysis),統計分析是以數據為基礎,對數據進行科學處理、分析。統計分析包括兩大部分

  1. 描述統計(Descriptive Statistics)
  2. 推論統計(Inferential Statistics)

數據類型

在學習描述統計之前,先進行數據分類。

  • 定性數據(Qualiative Data):對事物性質進行描述數據。ex 股票所屬行業數據即為定性數據。ex 國巨屬於半導體業。
  • 定量數據(Quantitative Data):事物數量特徵的數據,由數字所組成。ex 每個股票的開盤價。

圖表

當拿到數據後拿來分析,最常使用圖表的方式呈現數據。有以下圖表可以呈現數據。

  • 直方圖:二維統計圖表,橫軸是類別或是數值間的範圍,縱軸是事件發生的頻率。可以看day15有介紹直方圖。
  • 圓餅圖:圓形統計圖表,每個扇區的弧長表示的數量的比例。
  • 折線圖:折線圖是用直線段將各數據點連接起來而組成的圖形,以折線方式顯示數據的變化趨勢。
  • 散佈圖:兩個變數之間關係的圖,又稱相關圖
  • 頻率分布表:統計學中表示樣本數據頻率分布規律的表格。

數據的位置

分析數據時,想要了解數據分布的位置,在統計分析中有專門的指標用於描述數據的位置。常用的指標有以下四種方式:

  • 樣本平均數(Sample Mean)
  • 中位數(Median)
  • 眾數(Mode)
  • 百分位數(Percentile)

樣本平均數(Sample Mean)

假設現在有n個樣本觀測值(X1,X2,.....Xn),在統計學中樣本平均數有兩種計算方式:

  • 算術平均數(Arithmetic Mean) https://ithelp.ithome.com.tw/upload/images/20181023/201113904xHPsp4MaE.png
  • 幾何平均數(Geometric Mean)https://ithelp.ithome.com.tw/upload/images/20181023/20111390RLjxtMEPkO.png

示範建立一個陣列

import numpy as np
x = np.random.randint(0,100,size = 20)
print('x----------->',x)
print('np.sort(x)----------->',np.sort(x))
# 輸出結果
x-----------> [38 60 17 34 13  8 34 35 21 49 61 48 93 12 63 29 80 86 61 60]
x.sort()-----------> [ 8 12 13 17 21 29 34 34 35 38 48 49 60 60 61 61 63 80 86 93]

顯示x的平均值

print('x.mean()----->',x.mean())
# 輸出結果
x.mean()-----> 45.1

中位數(Median)

將樣本從小到大做排序,如果樣本是奇數個中位數即為最中間的值,如果是偶數個,中位數是中間兩個數值的平均。

print('np.median(x)----->',np.median(x))
# 輸出結果
np.median(x)-----> 43.0
# 可以看到x的中間兩個位數為38, 48 中位數為(38+48)/2 = 43

眾數(Mode)

樣本中出現次數最多的數值。

from scipy import stats
print('stats.mode(x)----->',stats.mode(x).mode[0])
# 輸出結果
stats.mode(x)-----> 34
# 可以看到x陣列中出現最多的數字為34

百分位數(Sample Mean)

假設現在有n個樣本觀測值(X1,X2,.....Xn),第α百分位數: n*α%
統計學中經常將25百分位數、中位數和75百分位數組成四分位數(Quartile)。25百分位數叫做第一四分位數(下四分位數)、中位數為第二四分位數、75百分位數為第三四分位數(上四分位數)。

print('np.percentile(x, 25)----->',np.percentile(x, 25))
print('np.percentile(x, 75)----->',np.percentile(x, 75))
# 輸出結果
np.percentile(x, 25)-----> 27.0
np.percentile(x, 75)-----> 61.0
# 0.25*21 + 0.75*29 = 27
# 0.25*61 + 0.75*61 = 61

數據的離散度

要分析數據除了需要數據的位置,還需要反應數據分佈的特徵也稱為數據的變異性,常用的離散度指標有

  • 全距(Range)
  • 平均絕對偏差(Mean Absolute Deviation)
  • 變異數(Variance)
  • 標準差(Standard Deviation)

全距(Range)

指的是數據中的最大值和最小值,公式為:
全距 = 最大值 - 最小值
示範建立一個陣列

import pandas as pd
ironman = pd.Series([60,70,80,80,90,100])
print('ironman.mean()----->',ironman.mean())
# 輸出結果
ironman.mean()-----> 80.0

ironman的全距就是 100 - 60 = 40

平均絕對偏差(Mean Absolute Deviation)

採用的方式是用樣本與平均值(Mean)的差值來計算,當差值越大則表示數據值偏離均值越遠。這邊需要注意到的是平均偏差(Mean Deviation)的相加為0,所以需要加上絕對值來運算。公式如下:
https://ithelp.ithome.com.tw/upload/images/20181023/20111390dWBpk1bEL3.png

ironman的平均絕對偏差

print('ironman.mad()----->',ironman.mad())
# 輸出結果
ironman.mad()-----> 10.0
# |(60-80)| + |(70-80)| + (80-80) + (80-80) + (70-80) + (100-80) = 60
# MAD = 60 / 6 = 10

變異數(Variance)和標準差(Standard Deviation)

變異數也是用來描述數據的離散程度,標準差就是變異數得平方根
變異數公式:https://ithelp.ithome.com.tw/upload/images/20181023/20111390Y9wgoyeFXf.png
ironman的變異數

print('ironman.var()----->',ironman.var())
# 輸出結果
ironman.var()-----> 200.0
# 20*20 + 10 * 10 + 0*0 + 0*0 + 10 * 10 + 20*20 = 1000
# Variance = 1000 / (6-1) = 200

標準差(σ)公式:https://ithelp.ithome.com.tw/upload/images/20181023/20111390bxyLWuQ2br.jpg

ironman的標準差

print('ironman.std()----->',ironman.std())
# 輸出結果
ironman.std()-----> 14.142135623730951
# 200的平方根

之前的章節

 * [安裝Anaconda](https://ithelp.ithome.com.tw/articles/10199666)
 * [安裝Jupyter notebook](https://ithelp.ithome.com.tw/articles/10200046)
]]> Summer 2018-11-04 01:09:45
[Day19]seaborn - 樣式設定 https://ithelp.ithome.com.tw/articles/10206417?sc=rss.iron https://ithelp.ithome.com.tw/articles/10206417?sc=rss.iron 導讀
  • 安裝環境
    • 導讀

      前言

      今天是鐵人的第19天,前一天介紹了seaborn,今天要來介紹seaborn更進一步的設定->style樣式的設定。

      set_style() - 背景顏色

      下面例子顯示當設定不同的style所呈現出來的風格

      事先執行的程式碼

      ironman_np_x = np.linspace(1, 14, 100) #產生一個1~14的等差數列
      ironman_np_y1 = np.sin(ironman_np_x)
      ironman_np_y2 = np.sin(ironman_np_x+2)*1.25
      
      def drawPlot():
          plt.plot(ironman_np_x, ironman_np_y1)
          plt.plot(ironman_np_x, ironman_np_y2)
          
      def drawPlotFour():
          for i in range(4):
              plt.plot(ironman_np_x, np.sin(ironman_np_x+i)*(i+0.75), label="sin(x+i)*(i+0.75)")
          plt.legend()
      

      預設 matplotlib

      drawPlot()
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390mJhhY96sGc.png

      當設定完style之後的風格

      sns.set_style('whitegrid')
      drawPlot()
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390tZjAgqSokT.png

      set_style預設有五種風格

      1. darkgrid
      2. dark
      3. whitegrid
      4. white
      5. ticks
      • 取得目前的風格
      sns.axes_style()
      
      • 還原成初始風格
      sns.set()
      

      set_context() - 圖形大小

      用於圖形大小的設置

      sns.set_context('talk')
      drawPlot()
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390zjTm6ZuXpK.png
      可以看到跟原本圖形的座標顯示大小不一樣。

      set_context有四種風格

      1. paper
      2. notebook
      3. talk
      4. poster
      • 取得目前的風格
      sns.plotting_context()
      

      with

      在當前這個圖表設定style。以下方法來顯示兩張圖表不一樣的地方

      第一個先取得預設圖型

      drawPlotFour()
      

      https://ithelp.ithome.com.tw/upload/images/20181030/2011139007kwnoPVpX.png

      第二個使用with並且將style改掉

      with sns.axes_style('white'):
          drawPlotFour()
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390L7DHWbdLx2.png
      可以看到第二張圖已經被改掉了,但是如果又再一次的呼叫drawPlotFour()是不會改style

      color

      • 取得目前調色板的顏色
      sns.palplot(sns.color_palette())
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390fy0lSLpeDP.png
      可以看到上面四條線的顏色就是按照調色盤的顏色依序做配對,當超過調色盤數量的顏色時,會從第一個開始循環使用。

      • 設置調色盤的顏色
      sns.set_palette(sns.color_palette('bright'))
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390coV7CrDeww.png
      畫出來的線條,顏色會比之前亮

      • 設置自己定義的調色盤
      sns.color_palette([(0.45,0.72,0),(0.22,0.34,0),(0.62,1,0.32)]) # 輸入rgb
      

      https://ithelp.ithome.com.tw/upload/images/20181030/20111390Nlw24G5orA.png

      因為只有設置3個顏色,所以會有兩條重複的顏色。

      ]]> Summer 2018-11-03 01:24:41 [Day18]seaborn - distplot、heatmap() https://ithelp.ithome.com.tw/articles/10206304?sc=rss.iron https://ithelp.ithome.com.tw/articles/10206304?sc=rss.iron 導讀
      • 安裝環境
        • 導讀

          前言

          今天是鐵人的第18天,python中繪圖的套件除了Matplotlib還有seaborn,今天要來介紹seaborn。
          seaborn:是基於Matplotlib的一個擴展,也是同樣用於實現數據圖表化的一個套件。所以在使用方式上跟Matplotlib有些相似但也有不同的地方。

          預先載入套件

          介紹底下的圖表之前先預先載入需要的套件

          # basic
          import numpy as np
          import pandas as pd
          
          # matplotlib
          import matplotlib.pyplot as plt
          %matplotlib inline
          
          # seaborn
          import seaborn as sns
          

          載入資料

          seaborn github上面找尋一個function load_dataset 可以發現使用seaborn中的這個function他是去載入其他csv檔案,而csv檔案也是放在github seaborn-data,從這個專案可以看到底下有很多csv檔案,假設要使用flights.csv,語法就可以使用

          sns.load_dataset('flights')
          

          圖表介紹

          直方圖hist 和 密度圖kde

          • matplotlib直方圖hist呈現的方式
          ironman_series = pd.Series(np.random.randn(1000))
          ironman_series.plot(kind='hist')
          # 或者可以使用plot.hist(ironman_series)
          

          https://ithelp.ithome.com.tw/upload/images/20181029/20111390DbKTjIfcLW.png

          • matplotlib密度圖kde呈現的方式
          ironman_series.plot(kind='kde')
          

          https://ithelp.ithome.com.tw/upload/images/20181029/20111390sLl7C8aKDt.png

          • seaborn 預設上會同時畫出直方圖(hist)和密度圖(kde)
            語法:sns.distplot(a, bins=None, hist=True, kde=True, rug=False, fit=None, hist_kws=None, kde_kws=None, rug_kws=None, fit_kws=None, color=None, vertical=False, norm_hist=False, axlabel=None, label=None, ax=None)
          sns.distplot(ironman_series)
          

          (ps:筆者在實作這邊的時候有遇到warning,筆者是直接將seaborn和Matplotlib更新即可)
          https://ithelp.ithome.com.tw/upload/images/20181029/20111390MvCDPrsamj.png

          • seaborn的密度圖kde
            語法:sns.kdeplot(data, data2=None, shade=False, vertical=False, kernel='gau', bw='scott', gridsize=100, cut=3, clip=None, legend=True, cumulative=False, shade_lowest=True, cbar=False, cbar_ax=None, cbar_kws=None, ax=None, *kwargs)
            因為畫出的圖一樣就沒有另作出圖

          bar()

          • matplotlib使用bar呈現的方式
          ironman_df = sns.load_dataset('flights')
          ironman_df = ironman_df.pivot(index='month', columns='year', values='passengers')
          ironman_df.sum().plot(kind="bar")
          

          https://ithelp.ithome.com.tw/upload/images/20181029/201113901SGVBTQ2BS.png
          可以看到每年乘客的數量

          • seaborn的barplot
            語法:sns.barplot(x=None, y=None, hue=None, data=None, order=None, hue_order=None, estimator=<function mean at 0x0000022974A241E0>, ci=95, n_boot=1000, units=None, orient=None, color=None, palette=None, saturation=0.75, errcolor='.26', errwidth=None, capsize=None, dodge=True, ax=None, *kwargs)
          sns.barplot(ironman_df.sum().index,ironman_df.sum().values)
          

          https://ithelp.ithome.com.tw/upload/images/20181029/20111390I7cGuaXTKd.png

          從上面兩個圖表可以看的出來seaborn的圖比matplotlib好看

          熱圖:heatmap()

          語法:sns.heatmap(data, vmin=None, vmax=None, cmap=None, center=None, robust=False, annot=None, fmt='.2g', annot_kws=None, linewidths=0, linecolor='white', cbar=True, cbar_kws=None, cbar_ax=None, square=False, xticklabels='auto', yticklabels='auto', mask=None, ax=None, **kwargs)

          sns.heatmap(ironman_df)
          

          https://ithelp.ithome.com.tw/upload/images/20181029/20111390K9srUy3Nbd.png

          ]]> Summer 2018-11-02 00:00:36 [Day17]視覺化資料 - Matplotlib - 加上中文字、柱狀圖、boxplot https://ithelp.ithome.com.tw/articles/10202385?sc=rss.iron https://ithelp.ithome.com.tw/articles/10202385?sc=rss.iron 導讀
          • 安裝環境
            • 導讀

              前言

              今天是鐵人第17天,前幾天學習了一些Matplotlib的繪圖,今天要畫一個簡單的股票價格圖表,首先要讓Matplotlib可以顯示中文。還有介紹一些其他的圖表。

              • 柱狀圖:bar()、barh()
              • boxplot

              • 顯示中文
                在使用Matplotlib繪圖金融數據,需要將標題加上中文字,但Matplotlib預設中文輸出會出現亂碼,要先通過以下設定設定可以顯示中文。
              1. 先到下載字體的網站
                本次範例使用華康童童體
                下載ttf
                https://ithelp.ithome.com.tw/upload/images/20181019/20111390HoUt7vBgtw.png

              2. 打開Jupyter notebook,輸入以下程式碼

              import matplotlib
              matplotlib.matplotlib_fname()
              
              # 輸出結果
              'C:\\Users\\lasts\\Anaconda2\\envs\\env_Python3.6\\lib\\site-packages\\matplotlib\\mpl-data\\matplotlibrc'
              
              1. 將剛剛下載的ttf放到 C:\Users\lasts\Anaconda2\envs\env_Python3.6\lib\site-packages\matplotlib\mpl-data\matplotlibrc\fonts\ttf ,需要改成英文檔名「test-font.ttf」(可以自己定義名稱),中文檔名好像會失敗,筆者自己試會這樣,如果有試出來,在講一下,感謝。
                https://ithelp.ithome.com.tw/upload/images/20181019/20111390Wd3Bhrp6Xa.png

              2. 關閉Jupyter notebook,並且刪除暫存檔,windows這裡是在 C:\Users\lasts\ .matplotlib ,直接刪除.matplotlib這個資料夾

              3. 再次打開Jupyter notebook,執行以下的語法(主要是要產生fontList.json這個檔案)

              import matplotlib.pyplot as plt
              fig = plt.figure()
              plt.plot([1,2,3], '-')
              plt.title('台積電 2016年收盤價曲線',loc='right')
              

              ,會看到剛剛刪除的C:\Users\lasts.matplotlib資料夾會重新產生,之後打開C:\Users\lasts.matplotlib\fontList.json,收尋剛剛改的檔案名稱「test-font.ttf」,會看到name是「DFPTongTong-B5」
              https://ithelp.ithome.com.tw/upload/images/20181019/20111390uaqEYECJf4.png

              1. 打開 C:\Users\lasts\Anaconda2\envs\env_Python3.6\lib\site-packages\matplotlib\mpl-data\matplotlibrc 這個檔案,並尋找 font.family,拿掉#字號,並且改成
                https://ithelp.ithome.com.tw/upload/images/20181019/20111390pjQVPE0iGe.png

              2. 重新啟動Jupyter notebook,之後執行example1的範例,即可看到文字已經改變,這個只是示範某個字體,筆者自己主要還是使用微黑正體

              • example1
                示範2016年台積電收盤價曲線
              import pandas as pd
              import matplotlib.pyplot as plt
              
              %matplotlib inline
              
              TSMC = pd.read_csv('2330stk.csv', sep='\t', index_col='Date')
              TSMC.index = pd.to_datetime(TSMC.index) # 把index轉成datetime型態
              Close = TSMC.Close
              Open = TSMC.Open
              
              fig = plt.figure()
              plt.plot(Close['2016'], '-' , label="收盤價", marker=">")
              plt.plot(Open['2016'], '-' , label="開盤價", marker="*")
              plt.title('台積電 2016年收盤價/收盤價曲線',loc='right')
              # loc->title的位置
              plt.xlabel('日期')
              plt.ylabel('收盤價')
              plt.grid(True, axis='y')
              plt.legend()
              
              

              https://ithelp.ithome.com.tw/upload/images/20181022/20111390ZxEtkqb9Uo.png

              柱狀圖Bar Chart:bar()、barh()

              可以顯示每個數據區間坐落的樣本數。
              語法:pyplot.bar(x, height, width, bottom, *args)
              x-> 設定x軸
              height->高度
              width-> 寬度
              bottom-> 柱子底部的y座標
              範例:顯示台積電2016年的收盤價在哪個區間比較多

              barValue = [0,0,0,0,0,0]
              for i in Close:
                  if i > 190:
                      barValue[5]+=1
                  if i > 180:
                      barValue[4]+=1
                  if i > 170:
                      barValue[3]+=1
                  if i > 160:
                      barValue[2]+=1
                  if i > 150:
                      barValue[1]+=1
                  else:
                      barValue[0]+=1
              fig = plt.figure()
              plt.bar([140,150,160,170,180,190], height=barValue , width=5.2, bottom=2.0)
              

              https://ithelp.ithome.com.tw/upload/images/20181022/20111390XUVqjrRy1z.png

              用相同的數據繪製水平的柱狀圖
              語法:bar(y, width, height, *args)
              y-> 設定x軸
              width->寬度
              height-> 高度

              fig = plt.figure()
              plt.barh([140,150,160,170,180,190], barValue , height=5.2)
              

              https://ithelp.ithome.com.tw/upload/images/20181022/201113903tkJ5qn4xO.png

              盒鬚圖:boxplot()

              語法:pyplot.boxplot(x, *args)
              x-> 數據
              

              使用台積電的開盤價、最高價、最低價、和收盤價這四個數據來繪製

              import numpy as np
              boxData = TSMC.iloc[:,:4]
              fig = plt.figure()
              plt.boxplot(np.array(boxData), labels=('收盤價','開盤價','最高價','最低價'))
              

              https://ithelp.ithome.com.tw/upload/images/20181022/20111390QSJJUVVfsb.png

              ]]> Summer 2018-11-01 00:04:33 [Day16]視覺化資料 - Matplotlib - legend、subplot、GridSpec、annotate https://ithelp.ithome.com.tw/articles/10201670?sc=rss.iron https://ithelp.ithome.com.tw/articles/10201670?sc=rss.iron 導讀
              • 安裝環境
                • 導讀

                  前言

                  今天是鐵人賽的第16天,持續focus on 圖表的部分,今天的重點放在如何讓圖表更加符合自己想要的樣式。

                  • legend()
                    前一天的圖表都只有顯示數據的圖形,可是沒有顯示數據的名稱,當今天有兩種數據,可以使用ax.legend()來顯示數據的名稱,名稱定義在label中
                    語法:legend(*args)
                    其中一個args為loc,主要是設定顯示的位置
                  描述 數字
                  best 最適宜 0
                  upper right 右上角 1
                  upper left 左上角 2
                  lower right 右下角 3
                  lower left 左下角 4
                  right 右側 5
                  center left 左側中間 6
                  center right 右側中間 7
                  lower center 下方中間 8
                  upper center 上方中間 9
                  center 最適宜 10
                  import matplotlib.pyplot as plt
                  import numpy as np
                  
                  
                  plt.style.use('classic')
                  %matplotlib inline
                  
                  ironman = np.linspace(0,10, 1000)  #產生 0~10區間的 等間隔序列
                  fig , ax = plt.subplots()
                  plt.plot(ironman, np.sin(ironman), '.', color='SteelBlue', label='Sine')
                  plt.plot(ironman, np.cos(ironman), '-', color='RosyBrown', label='Cosine')
                  ax.axis('equal')
                  leg = ax.legend(loc='upper right', shadow=True) 
                  # loc->名稱放在圖表的右上角 frameon->名稱是否用框框 shadow->是否有陰影...
                  

                  https://ithelp.ithome.com.tw/upload/images/20181016/20111390JPIvxA6s6K.png

                  • subplot()
                    子圖表:當同樣的資料想要用不同的視角呈現時即可使用子圖表。
                    以下範例示範在標準的axes中顯示另外一個自己定義的axes。語法: plt.axes([bottom, left, width, height])
                  fig , ax = plt.subplots()
                  axe_base = plt.axes()
                  axe_small = plt.axes([0.65, 0.65, 0.2, 0.2])
                  

                  https://ithelp.ithome.com.tw/upload/images/20181016/201113908bnoV6y7na.png

                  接下來示範繪製多個子圖

                  fig , ax = plt.subplots()
                  fig.subplots_adjust(hspace=0.4, wspace=0.4) #設定子圖的間隔
                  for i in range(1,7):
                      plt.subplot(2, 3, i)
                      plt.text(0.5, 0.5, str((2, 3, i)), fontsize=18, ha='center')
                  

                  https://ithelp.ithome.com.tw/upload/images/20181016/20111390EGdCZj8RDK.png

                  • subplots()
                    一次準備好多個網格
                  fig , ax = plt.subplots(2, 3, sharex=True, sharey=True)
                  # sharex->是否共享x軸  sharey->是否共享y軸
                  for i in range(2):
                      for j in range(3):
                          ax[i, j].text(0.5, 0.5, str((i, j)), fontsize=18, ha='center')
                          #在各個子網格寫上文字
                  

                  https://ithelp.ithome.com.tw/upload/images/20181016/20111390584qslAfeP.png

                  • GridSpec()
                    繪製更複雜的網格
                  ironman_grid = plt.GridSpec(2, 3, wspace=0.4, hspace=0.3)
                  print(ironman_grid)
                  plt.subplot(ironman_grid[:,0]) #all row, 0 column
                  plt.subplot(ironman_grid[0, 1:]) #0 row, 1之後的所有column
                  plt.subplot(ironman_grid[1, 1]) #1 row, 1 column
                  plt.subplot(ironman_grid[1, 2]) #1 row, 2 column
                  

                  https://ithelp.ithome.com.tw/upload/images/20181016/20111390OwBbcH7r5U.png

                  • annotate()
                    當想要在圖表上特別標示某個數據時,可以使用annotate將會特別顯示箭頭和文字
                  ironman = np.linspace(0,10, 1000)  #產生 0~10區間的 等間隔序列
                  fig , ax = plt.subplots()
                  plt.plot(ironman, np.sin(ironman), '.', color='SteelBlue', label='Sine')
                  plt.plot(ironman, np.cos(ironman), '-', color='RosyBrown', label='Cosine')
                  ax.axis('equal')
                  leg = ax.legend(loc='upper right', shadow=True) 
                  ax.annotate(s="first node", xy=(4.44,-1), xytext=(5,-2), 
                              arrowprops=dict(arrowstyle='->', connectionstyle="arc3,rad=.2"))
                  # s->標示的文字內容, xy->要標示的數據位置, xytext->顯示的文字位置,arrowprops->箭頭的參數
                  ax.annotate(s="second node", xy=(8,1), xytext=(5,1.5), 
                              arrowprops=dict(arrowstyle='->', connectionstyle="arc3,rad=-.2"))
                  

                  https://ithelp.ithome.com.tw/upload/images/20181017/20111390hNWMraFLmd.png

                  ]]> Summer 2018-10-31 00:02:23 [Day15]視覺化資料 - Matplotlib - 折線圖、座標、直方圖、圓餅圖 https://ithelp.ithome.com.tw/articles/10201004?sc=rss.iron https://ithelp.ithome.com.tw/articles/10201004?sc=rss.iron 前言

                  今天是鐵人賽的第15天,終於要進入畫圖表的部分,Python主要有兩個畫圖表的套件

                  1. Matplotlib : 官網
                  2. Seaborn
                    現在先來介紹 Matplotlib,它可以在不同的作業系統和繪圖後端執行。
                  • 折線圖:plot()
                    語法:plot(x,y...)
                    在Jupyter notebook提供一個方法可以直接在螢幕上秀出劃出的圖表%matplotlib inline
                    範例:先建立一個矩陣從-10到10(100個等差數值),x為剛剛建立的矩陣,y為sin(),畫圖的樣式為「.」
                  import numpy as np
                  import matplotlib.pyplot as plt
                  %matplotlib inline
                  ironman = np.linspace(-10,10,100)
                  fig = plt.figure() #定義一個圖像窗口
                  plt.plot(ironman, np.sin(ironman), '.') #定義x,y和圖的樣式
                  

                  https://ithelp.ithome.com.tw/upload/images/20181012/20111390gpywUoqTQp.png

                  如果想要同時畫兩條線可以使用以下的方式

                  fig = plt.figure()
                  plt.plot(ironman, np.sin(ironman), '.', ironman, np.cos(ironman), '--')
                  

                  https://ithelp.ithome.com.tw/upload/images/20181016/20111390hcHSZmBBiC.png

                  將繪製出來的圖表做儲存,之後在放置程式相同的資料夾即可看到day15_01.png這個檔案。
                  如果想看有哪些格式可以儲存,只要下「fig.canvas.get_supported_filetypes()」這行程式碼。

                  fig.savefig('day15_01.png')
                  

                  讀取圖形

                  from IPython.display import Image
                  Image('day15_01.png')
                  
                  • 將圖表畫格線
                    範例:設定Matplotlib的樣式為bmh,畫出格線。
                    如果想看有哪些Style可以使用,只要下「plt.style.available」這行程式碼。
                  plt.style.use('bmh')
                  fig = plt.figure()
                  ax = plt.axes() #畫上刻度
                  

                  https://ithelp.ithome.com.tw/upload/images/20181012/20111390ftRxMHNAWQ.png

                  • 控制axes的範圍
                    plt.xlim(start, end)->控制x的範圍
                    plt.ylim(start, end)->控制x的範圍
                    plt.axis([xstart, xend, ystart, yend]) -> 一次設定x和y軸
                    plt.axis除了可以設定x和y軸也可以設定其他樣式,ex: equal可以讓x和y軸的單位一樣。
                    plt.axis的使用方式可以參考文件

                  • 控制顯示的座標xticks()、yticks()
                    語法:xticks(location,lables[,rotation]) yticks(location,lables[,rotation])
                    示範用日期來當x軸的座標

                  fig = plt.figure()
                  plt.xticks(range(7), ['2018-10-10','2018-10-11','2018-10-12',
                                        '2018-10-13','2018-10-14','2018-10-15','2018-10-16'], rotation=45)
                  

                  https://ithelp.ithome.com.tw/upload/images/20181017/201113901Sn8F4LAAq.png

                  • 將圖表加上label
                    plt.title("title") -> 圖表標題
                    plt.xlabel("xlabel") -> 圖表x軸標題
                    plt.ylabel("ylabel") -> 圖表y軸標題

                  將一開始畫的sin()曲線圖加上線條圖形

                  plt.style.use('bmh')
                  fig = plt.figure()
                  ax = plt.axes()
                  plt.plot(ironman, np.sin(ironman), '.') #定義x,y和圖的樣式
                  plt.xlim(-15, 15)
                  plt.ylim(-1.5, 1.5)
                  plt.title("Sine curve y=sin(x)")
                  plt.xlabel("x")
                  plt.ylabel("sin(x)")
                  

                  https://ithelp.ithome.com.tw/upload/images/20181015/20111390UeUJr86CbX.png

                  • 控制線條的顏色和樣式
                    語法:plt.plot(x, y, color='color', linestyle='linestyle', marker='marker', linewidth='linewidth')
                    • color:color也可以直接簡寫成c,可以填入 16進位編碼(000000 ~ FFFFFF)、HTML中支援的顏色名稱(ex LightCoral)

                    • linestyle:linestyle也可以直接簡寫成ls,可填入的值如以下

                  描述 符號
                  solid 實線 -
                  dashed 虛線 --
                  dashdot 線點 -.
                  dotted 點線 :
                  None 不畫線
                  • marker:點的形狀,型態
                  描述 符號
                  point .
                  circle 圓圈 o
                  triangle_down 向下三角形 v
                  triangle_up 向上三角形 ^
                  triangle_left 向左三角形 <
                  triangle_right 向右三角形 >
                  square 正方形 s
                  pentagon 五邊形 p
                  hexagonl 六邊形 h
                  plus 加號 +
                  x 打叉 x
                  diamond 鑽石 D
                  star 星號 *
                  vline 豎線
                  • linewidth:線的寬度,可以簡寫成lw

                  • 繪製誤差圖
                    語法:plt.errorbar(x,y..)

                  ironman_error_x = np.linspace(-10,10,50)
                  ironman_error_y = np.random.randint(-10,10,50)
                  fig = plt.figure()
                  plt.errorbar(ironman_error_x, ironman_error_y, yerr=2, fmt='o', color='SteelBlue', ecolor='LightSteelBlue', elinewidth=2)
                  # ecolor->誤差的顏色  elinewidth->誤差的寬度
                  

                  https://ithelp.ithome.com.tw/upload/images/20181015/20111390ziAVySN6VG.png

                  • 直方圖
                    用在表示間斷型隨機變數
                  fig = plt.figure()
                  ironman_hist = np.random.randn(1000)
                  plt.hist(ironman_hist, bins=20, color='SteelBlue', edgecolor="LightSteelBlue") 
                  #bins 定義有幾個直方圖 color->直方圖顏色 edgecolor->邊界顏色
                  

                  https://ithelp.ithome.com.tw/upload/images/20181015/20111390IErYQWcGTi.png

                  取得直方圖每個柱子的數量,和間距可以使用np.histogram

                  counts, bin_edges = np.histogram(ironman_hist, bins=20)
                  print('counts----->', counts)
                  print('bin_edges----->', bin_edges)
                  
                  # 輸出
                  counts-----> [  2   3  10  10  32  56  74 118 131 167 121  90  77  53  33  10   8   4
                     0   1]
                  bin_edges-----> [-3.26928919 -2.91739998 -2.56551077 -2.21362156 -1.86173235 -1.50984314
                   -1.15795393 -0.80606471 -0.4541755  -0.10228629  0.24960292  0.60149213
                    0.95338134  1.30527055  1.65715976  2.00904897  2.36093818  2.71282739
                    3.06471661  3.41660582  3.76849503]
                  
                  • 圓餅圖
                  fig = plt.figure()
                  labels = ["day1","day2","day3","day3","day5","day6"]
                  word_size = [2000, 3250, 4600 , 2300, 3600, 5600]
                  explode = [0 , 0, .05, 0, 0, 0]  #0則不突凸出,值越大 則凸出越大
                  plt.pie(word_size, explode= explode, labels = labels, autopct= "%3.1f%%")
                  # autopct-->項目百分比的格式
                  

                  https://ithelp.ithome.com.tw/upload/images/20181015/20111390CW0DLtUc2H.png

                  之前的章節

                  ]]> Summer 2018-10-30 00:02:12
                  [Day14]Learning Pandas - Time、eval https://ithelp.ithome.com.tw/articles/10200929?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200929?sc=rss.iron 導讀
                  • 安裝環境
                    • 導讀

                      前言

                      今天是鐵人的第14天,持續Pandas,終於到Pandas的最後一天了,今天要來介紹Pandas中時間的處理

                      以下示範用to_datetime建立時間

                      import pandas as pd
                      ironman_date = pd.to_datetime('2018-10-11 22:30:00')
                      ironman_date
                      
                      # 輸出結果
                      Timestamp('2018-10-11 22:30:00')
                      

                      用ironman_date來建立連續5天的向量。其中的D代表天,還可以使用以下的編碼代替。
                      天(D)、年(Y)、月(M)、星期(w)、小時(h)、分(m)、秒(s)

                      ironman_date + pd.to_timedelta(np.arange(5), 'D') 
                      
                      # 輸出結果
                      DatetimeIndex(['2018-10-11 22:30:00', '2018-10-12 22:30:00',
                                     '2018-10-13 22:30:00', '2018-10-14 22:30:00',
                                     '2018-10-15 22:30:00'],
                                    dtype='datetime64[ns]', freq=None)
                      

                      範例:使用時間當作index

                      ironman_index = pd.DatetimeIndex(['2018-10-11','2018-10-12','2017-10-11','2017-10-12'])
                      ironman_data = pd.Series(np.arange(4), index = ironman_index)
                      ironman_data
                      
                      # 輸出結果
                      2018-10-11    0
                      2018-10-12    1
                      2017-10-11    2
                      2017-10-12    3
                      dtype: int32
                      

                      取得2018年的資料

                      ironman_data['2018']
                      
                      # 輸出結果
                      2018-10-11    0
                      2018-10-12    1
                      dtype: int32
                      

                      建立時間序列:date_range()、period_range()、timedelta_range()
                      時間序列也有類似數字的序列range()的函數-> pd.date_range(),同樣也是產生有規則的時間序列
                      freq可以使用->天(D)、年(A)、月(M)、星期(w)、小時(H)、分(T)、秒(S)

                      pd.date_range('2018-10-11', '2018-10-15')
                      pd.date_range('2018-10-11', periods=5, freq='D') #freq預設是D(也可以使用3小時20分->3H20T)
                      
                      # 輸出結果 (上面兩個語法都是產生從'2018-10-11'開始5天的日期)
                      DatetimeIndex(['2018-10-11', '2018-10-12', '2018-10-13', '2018-10-14',
                                     '2018-10-15'],
                                    dtype='datetime64[ns]', freq='D')
                      

                      高效率的Pandas:eval()、query()
                      使用eval()對於陣列的運算更有效率
                      示範:有用eval和沒有的差別

                      rng = np.random.RandomState(42)
                      df1, df2, df3, df4 = (pd.DataFrame(rng.rand(100000, 100)) for i in range(4))
                      %timeit df1+df2+df3+df4
                      %timeit pd.eval('df1+df2+df3+df4')
                      
                      # 輸出結果(可以看到eval()比較快)
                      105 ms ± 12.2 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
                      77.8 ms ± 1.54 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
                      

                      query()是等同於eval()的高效率運算
                      示範:有用query和沒有的差別

                      ironman_df = pd.DataFrame(rng.rand(100000, 3) , columns=['A','B','C'])
                      %timeit (ironman_df['A'] < ironman_df['B'])
                      %timeit ironman_df.query('A < B')
                      
                      # 輸出結果(可以看到query()比較快)
                      324 µs ± 38.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
                      4.85 ms ± 509 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
                      
                      ]]> Summer 2018-10-29 10:20:06 [Day13]Learning Pandas - 處理資料分組 https://ithelp.ithome.com.tw/articles/10200565?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200565?sc=rss.iron 導讀
                      • 安裝環境
                        • 導讀

                          前言

                          今天是鐵人第13天,不要懷疑還是在Pandas,再過幾天就可以跳脫了....,今天主要是介紹Pandas裡面資料計算和分組

                          資料計算sum()、mean()、median()、min()、max()

                          以下用Series示範上述涵式

                          import numpy as np
                          import pandas as pd
                          ironman_ser = pd.Series(np.random.randint(10, size=6))
                          print(ironman_ser)
                          print('陣列中的總和--------------->',ironman_ser.sum())
                          print('陣列中的平均值------------->',ironman_ser.mean())
                          print('陣列中middle two的平均值--->',ironman_ser.median())
                          print('陣列中最小值--------------->',ironman_ser.min())
                          print('陣列中最大值--------------->',ironman_ser.max())
                          
                          # 輸出結果
                          0    7
                          1    4
                          2    6
                          3    7
                          4    7
                          5    8
                          dtype: int32
                          陣列中的總和---------------> 39
                          陣列中的平均值-------------> 6.5
                          陣列中middle two的平均值---> 7.0
                          陣列中最小值---------------> 4
                          陣列中最大值---------------> 8
                          

                          示範用DataFrame進行運算,預設會用欄做計算,如果要改成用列作計算可使用axis

                          ironman_df = pd.DataFrame({'A':np.random.randint(10, size=6), 'B':np.random.randint(10, size=6)})
                          print(ironman_df)
                          ironman_df.sum()  #如果想用列作計算可以使用 ironman_df.sum(axis='columns')
                          
                          # 輸出結果
                             A  B
                          0  3  6
                          1  3  1
                          2  1  8
                          3  0  6
                          4  4  5
                          5  0  1
                          
                          A    11
                          B    27
                          dtype: int64
                          

                          Pandas中使用的計算方法

                          計算 描述
                          count() 元素總數
                          first(),last() 第一個,最後一個元素
                          mean(),median() 全部平均,中位數平均
                          min(),max() 最小值,最大值
                          std(),var() 標準差,變異數
                          mad() 平均絕對差
                          prod() 元素的積
                          sum() 所有元素的總和

                          GroupBy

                          之前介紹的聚合資料運算都是針對全部的元素,如果想要針對某些索引做計算可以透過groupby
                          示範建立一個DataFrame,並算出每個team('A','B','C')的總和

                          ironman_df = pd.DataFrame({'team':['A','B','C','C','B','B'],'number':np.random.randint(10, size=6)}, columns=['team','number'])
                          print(ironman_df)
                          ironman_df.groupby('team').sum() #
                          
                          # 輸出結果
                            team  number
                          0    A       9
                          1    B       6
                          2    C       9
                          3    C       4
                          4    B       2
                          5    B       9
                                  number
                          team	
                          A	      9
                          B	     17
                          C	     13
                          
                          • aggregate()
                            如果同時間要拿到sum()、min()、max()可以使用聚合(aggregate)
                          ironman_df.groupby('team').aggregate(['sum','max','min'])
                          
                          # 輸出結果
                                  number
                               sum   max	min
                          team			
                          A	   9	9	  9
                          B	  17	9	  2
                          C	  13	9	  4
                          
                          • fliter()
                            過濾掉計算結果不符合的部分。ex: 將每個team總和大於10留下,剩下過濾掉
                          def filter_func(x):
                              return x['number'].sum() > 10
                          ironman_df.groupby('team').filter(filter_func)
                          
                          # 輸出結果(可以看到A team被過濾掉了)
                             team	number
                          1	  B	    6
                          2	  C	    9
                          3	  C	    4
                          4	  B	    2
                          5	  B	    9
                          
                          • transform
                            當需要將原始資料和計算後的資料作運算,可以使用轉換(transform)。ex: 將每個team的元素和總和做計算。
                          ironman_df.groupby('team').transform(lambda x: x- x.sum())
                          
                          # 輸出結果
                          	number
                          0	     0
                          1	   -11
                          2	    -4
                          3	    -9
                          4	   -15
                          5	    -8
                          
                          • apply
                            使用呼叫函式的方式,可以回傳Pandas物件或是純量(這是跟transform比較不一樣的地方)。ex: 將每個team的元素和總和做計算。
                          def apply_func(x):
                              x['number'] = x['number'] - x['number'].sum()
                              return x
                          ironman_df.groupby('team').apply(apply_func)
                          
                          # 輸出結果
                             team	number
                          0	A	    0
                          1	B	  -11
                          2	C	   -4
                          3	C	   -9
                          4	B	  -15
                          5	B	   -8
                          

                          其他指定groupby分組條件的方式
                          以下示範自己定義不同的分組組別來做總和。

                          newList = [0,1,1,2,0,2]
                          ironman_df.groupby(newList).sum()
                          
                          # 輸出結果
                          	number
                          0	   11
                          1	   15
                          2	   13
                          

                          樞紐分析表(Pivot Table)

                          現在要用seaborn內建的數據庫來表示樞紐分析表,seaborn有哪些內建的數據庫可以看這裡
                          pviot_table函式呼叫的參數
                          DataFrame.pviot_table(data, values=None, index=None, columns=None, aggfunc='mean', fill_value=None, margins=False, dropna=True, margins_name='All')
                          以下示範鐵達尼存活的平均值用艙做分類

                          import seaborn as sns
                          titanic = sns.load_dataset('titanic')
                          titanic.pivot_table('survived', index='sex', columns='class')
                          
                          # 輸出結果(aggfunc預設是平均值)
                          class	   First	  Second	    Third
                          sex			
                          female	0.968085	0.921053	0.500000
                          male	0.368852	0.157407	0.135447
                          

                          對向量中的字串做操作

                          可操作的函式如下
                          len() lower() translate() islower()
                          ljust() upper() starswitch() issupper()
                          rjust() find() endswitch() isnumeric()
                          center() rfind() isalnum() isdecimal()
                          zfill() index() isalpha() split()
                          strip() rindex() isdigit() rsplit()
                          rstrip() capitalize() isspace() partition()
                          lstrip() swapcase() istitle() rpartition()
                          match() extract() findall() replace()
                          contains() count() get() slice_replace()
                          slice() cat() repeat() normalize()
                          pad() wrap() join() get_dummies()

                          範例:Series回傳小寫字串,如果是DataFrame需要針對某個欄位做操作

                          ironman = pd.Series(['Elephant','Lion','Lamigo','Guardian'])
                          ironman.str.lower()
                          
                          # 輸出結果
                          0    elephant
                          1        lion
                          2      lamigo
                          3    guardian
                          dtype: object
                          

                          範例:用正規表示

                          ironman.str.extract('([ELa-z]+)')
                          
                          # 輸出結果
                          	       0
                          0	Elephant
                          1	Lion
                          2	Lamigo
                          3	uardian
                          

                          範例:文字切片

                          ironman.str.slice(0,3)
                          
                          # 輸出結果
                          0    Ele
                          1    Lio
                          2    Lam
                          3    Gua
                          dtype: object
                          
                          ]]> Summer 2018-10-28 10:58:54 [Day12]Learning Pandas - 資料合併 https://ithelp.ithome.com.tw/articles/10200433?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200433?sc=rss.iron 導讀
                          • 安裝環境
                            • 導讀

                              前言

                              今天是第12天,還在Pandas....,主要是介紹Pandas裡面資料合併,Pandas學習的時間比我想中還長


                              資料合併Concat、Append、Merge、Join

                              • Concat
                                示範用Series串接資料(會保留重複index)
                              ironman1 = pd.Series(['A','B','C'], index=[0,1,2])
                              ironman2 = pd.Series(['D','E','F'], index=[2,3,4])
                              pd.concat([ironman1,ironman2])
                              
                              # 輸出結果,可以看到有重複的index->2
                              0    A
                              1    B
                              2    C
                              2    D
                              3    E
                              4    F
                              dtype: object
                              

                              示範用DataFrame串接資料

                              # 先定義一個function
                              def make_dataFrame(cols, ind):
                                  data = {c:[str(c) + str(i) for i in ind]
                                                             for c in cols}
                                  return pd.DataFrame(data, ind)
                              ironman3 = make_dataFrame('AB',[1,2])
                              ironman4 = make_dataFrame('AB',[3,4])
                              pd.concat([ironman3,ironman4])
                              
                              # 輸出結果
                              	A	B
                              1	A1	B1
                              2	A2	B2
                              3	A3	B3
                              4	A4	B4
                              

                              合併資料可採取使用join,outer->聯集、inner->交集(如果資料不存在時有NaN時)

                              ironman5 = make_dataFrame('BC',[3,4])
                              print(ironman3)
                              print(ironman5)
                              print(pd.concat([ironman3,ironman5], join='inner'))
                              
                              # 輸出結果,因為ironman3沒有columns C 而 ironman5沒有columns A,所以使用inner(交集)合併時,只會留下columns B
                              
                                  A   B
                              1  A1  B1
                              2  A2  B2
                                  B   C
                              3  B3  C3
                              4  B4  C4
                                  B
                              1  B1
                              2  B2
                              3  B3
                              4  B4
                              
                              • Append: 等同Concat只是呼叫更簡單
                                用上述ironman1和ironman2作範例
                              ironman1.append(ironman2)
                              
                              # 輸出結果
                              0    A
                              1    B
                              2    C
                              2    D
                              3    E
                              4    F
                              dtype: object
                              

                              *merge: 也是合併資料的一種,但可以有更多的應用ex: join

                              ironman6 = pd.DataFrame({'player':['彭政閔','林智勝','郭阜林','詹子賢'],
                                                      'number':[23,32,5,39]})
                              ironman7 = pd.DataFrame({'player':['彭政閔','郭阜林','林智勝','詹子賢'],
                                                      'team':['兄弟象','統一獅','兄弟象','兄弟象']})
                              ironman8 = pd.merge(ironman6, ironman7)
                              ironman8
                              
                              # 輸出結果
                              player	number	team
                              0	彭政閔	23	兄弟象
                              1	林智勝	32	兄弟象
                              2	郭阜林	5	統一獅
                              3	詹子賢	39	兄弟象
                              

                              多對一join

                              ironman9 = pd.DataFrame({'team':['兄弟象','統一獅','lamigo','富邦悍將'],
                                                      'manager':['Snyder','黃甘霖','洪一中','陳連宏']})
                              ironman10 = pd.merge(ironman8, ironman9)
                              ironman10
                              
                              # 輸出結果
                              	player	number	team	manager
                              0	彭政閔	   23	兄弟象	   Snyder
                              1	林智勝	   32	兄弟象	   Snyder
                              2	詹子賢	   39	兄弟象	   Snyder
                              3	郭阜林	   5	統一獅	   黃甘霖
                              

                              多對多join

                              ironman11 = pd.DataFrame({'team':['兄弟象','兄弟象','統一獅','lamigo','富邦悍將'],
                                                      'coach':['伯 納','丘昌榮','高政華','吳俊良','林宗男']})
                              ironman12 = pd.merge(ironman8, ironman11)
                              ironman12
                              
                              # 輸出結果
                              	player	number	team	coach
                              0	彭政閔	  23	兄弟象	   伯 納
                              1	彭政閔	  23	兄弟象	   丘昌榮
                              2	林智勝	  32	兄弟象	   伯 納
                              3	林智勝	  32	兄弟象	   丘昌榮
                              4	詹子賢	  39	兄弟象	   伯 納
                              5	詹子賢	  39	兄弟象	   丘昌榮
                              6	郭阜林	  5	    統一獅	   高政華
                              

                              當兩邊index不一樣時可以使用left_on和right_on來指定merge的index

                              ironman6 = pd.DataFrame({'player':['彭政閔','林智勝','郭阜林','詹子賢'],
                                                      'number':[23,32,5,39]})
                              ironman13 = pd.DataFrame({'name':['彭政閔','郭阜林','林智勝','詹子賢'],
                                                      'team':['兄弟象','統一獅','兄弟象','兄弟象']})
                              pd.merge(ironman6, ironman13, left_on="player", right_on="name").drop('name',axis=1)
                              
                              # 輸出結果,將重複的column「name」使用drop語法刪除。
                              
                                    player	number	team
                              0	彭政閔	      23	兄弟象
                              1	林智勝	      32	兄弟象
                              2	郭阜林	       5	統一獅
                              3	詹子賢	      39	兄弟象
                              

                              在merge中使用inner(交集)、outer(聯集)、lqft(左),(right)右

                              ironman6 = pd.DataFrame({'player':['彭政閔','林智勝','郭阜林','詹子賢'],
                                                      'number':[23,32,5,39]})
                              ironman14 = pd.DataFrame({'player':['彭政閔','林智勝','張志豪',"藍寅倫"],
                                                      'team':['兄弟象','兄弟象','兄弟象','lamigo']})
                              pd.merge(ironman6, ironman14, how="outer")
                              
                              # 輸出結果(缺失的資料用NaN帶入)
                                     player	number	  team
                              0	   彭政閔	  23.0	  兄弟象
                              1	   林智勝	  32.0	  兄弟象
                              2	   郭阜林	  5.0	    NaN
                              3	   詹子賢	  39.0	    NaN
                              4	   張志豪	  NaN	  兄弟象
                              5	   藍寅倫	  NaN	  lamigo
                              

                              當欄位名稱相同時使用suffixes來改變欄位名稱

                              ironman6 = pd.DataFrame({'player':['彭政閔','林智勝','陳江和','張正偉'],
                                                      'number':[23,32,31,8]})
                              ironman15 = pd.DataFrame({'player':['彭政閔','林智勝','陳江和','張正偉'],
                                                      'number':[23,31,8,59]})
                              pd.merge(ironman6,ironman15, on="player", suffixes=["_職棒","_國家隊"])
                              
                              # 輸出結果(缺失的資料用NaN帶入)
                              	player	number_職棒	number_國家隊
                              0	 彭政閔	   23	        23
                              1	 林智勝	   32	        31
                              2	 陳江和	   31	         8
                              3	 張正偉	    8	        59
                              
                              ]]> Summer 2018-10-27 11:17:45 [Day11]Learning Pandas - 處理空值的資料和使用多重index https://ithelp.ithome.com.tw/articles/10200052?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200052?sc=rss.iron 前言

                              昨天介紹Pandas的資料操作,當index沒有對應到的時候會出現NaN,今天要來介紹Pandas中如果資料是空值(或者沒有)時可以採取的資料型態。昨天也介紹Pandas...]]> 前言

                              昨天介紹Pandas的資料操作,當index沒有對應到的時候會出現NaN,今天要來介紹Pandas中如果資料是空值(或者沒有)時可以採取的資料型態。昨天也介紹Pandas資料型態和Numpy不同的是會有index,可是昨天示範都只有單一的index,今天的內容一部分也會講MultiIndex。

                              今天學習得的重點

                              1. 處理Pandas中缺失的資料
                              2. MultiIndex

                              Pandas缺失的資料

                              • None:是一種object
                              import numpy as np
                              import pandas as pd
                              ironman = np.array([1,None,3,4])
                              ironman
                              
                              # 輸出結果
                              array([1, None, 3, 4], dtype=object)
                              

                              None在數值運算中會出現錯誤,ex: sum(),min(),因此出現了NaN這樣的數值缺失資料型態

                              • NaN:特殊浮點數數值
                              ironman = np.array([1,np.nan,3,4])
                              ironman.dtype
                              
                              # 輸出結果
                              dtype('float64')
                              

                              使用NaN進行數值運算
                              sum()改成用nansum(), min()改成用nanmin(), max()改成用nanmax()

                              np.nansum(ironman),np.nanmin(ironman),np.nanmax(ironman)
                              
                              # 輸出結果
                              (8.0, 1.0, 4.0)
                              

                              Null的操作

                              isnull():檢查空值,回傳布林值
                              notnull():檢查不是空值,回傳布林值
                              dropna():刪除空值
                              fillna():填入空值

                              ironman = pd.Series([1,np.nan,3,4,None])
                              ironman.isnull()
                              
                              #輸出結果
                              0    False
                              1     True
                              2    False
                              3    False
                              4     True
                              dtype: bool
                              

                              notnull和遮罩一起的應用,取出陣列中不是空值的部分

                              ironman[ironman.notnull()]
                              
                              # 輸出結果
                              0    1.0
                              2    3.0
                              3    4.0
                              dtype: float64
                              

                              示範刪除DataFrame空值,會直接刪除整欄和整列

                              ironman = pd.DataFrame([
                                  [1,np.nan,3],
                                  [4,5,6],
                                  [7,8,np.nan]])
                              ironman.dropna()
                              
                              # 輸出結果
                              	0	1	 2
                              1	4	5.0	 6.0
                              

                              示範將null填入0

                              ironman = pd.Series([1,np.nan,3,4,None])
                              ironman.fillna(0)
                              
                              # 輸出結果
                              0    1.0
                              1    0.0
                              2    3.0
                              3    4.0
                              4    0.0
                              dtype: float64
                              

                              MultiIndex

                              • 建立一個MultiIndex,使用from_tuples
                              index = [('Taichung',2018),('Taichung',2017),
                                       ('Changhua',2018),('Changhua',2017),
                                       ('Taipei',2018),('Taipei',2017),
                                       ('Kaohsiung',2018),('Kaohsiung',2017)]
                              index = pd.MultiIndex.from_tuples(index)
                              index
                              
                              # 輸出結果
                              MultiIndex(levels=[['Changhua', 'Kaohsiung', 'Taichung', 'Taipei'], [2017, 2018]],
                                         labels=[[2, 2, 0, 0, 3, 3, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0]])
                              
                              • 用MultiIndex建立Series
                              elementary = [
                                  800,780,
                                  620,580,
                                  1500,1350,
                                  1100,998
                              ]
                              ironman = pd.Series(elementary, index=index)
                              ironman
                              
                              # 輸出結果
                              Taichung   2018     800
                                         2017     780
                              Changhua   2018     620
                                         2017     580
                              Taipei     2018    1500
                                         2017    1350
                              Kaohsiung  2018    1100
                                         2017     998
                              dtype: int64
                              

                              用MultiIndex取得資料,所有2017年國小數

                              ironman[:,2017]
                              
                              #輸出結果
                              Taichung      780
                              Changhua      580
                              Taipei       1350
                              Kaohsiung     998
                              dtype: int64
                              

                              其他取得資料的方式

                              ironman[ironman > 600]               #遮罩
                              ironman[['Changhua','Kaohsiung']]    #fancy索引
                              
                              • MultiIndex和DataFrame之間做轉換
                                unstack(): MultiIndex->DataFrame
                                stack(): DataFrame->MultiIndex
                              ironman_unstack = ironman.unstack()
                              ironman_unstack
                              
                              # 輸出結果
                              	        2017	2018
                              Changhua	580	    620
                              Kaohsiung	998	    1100
                              Taichung	780	    800
                              Taipei	    1350	1500
                              
                              • 將MultiIndex的index給予名稱
                              ironman.index.names = ['area','year']
                              ironman
                              
                              # 輸出結果
                              area       year
                              Taichung   2018     800
                                         2017     780
                              Changhua   2018     620
                                         2017     580
                              Taipei     2018    1500
                                         2017    1350
                              Kaohsiung  2018    1100
                                         2017     998
                              dtype: int64
                              
                              • 以下示範column的index
                              ironmanIndex = pd.MultiIndex.from_product([[2017, 2016, 2018], [1, 2]],names=['year','enroll'])
                              ironmanColumns = pd.MultiIndex.from_product([['azure','ai&data','web'], ['自我挑戰組', '主題競賽']],names=['subject','type'])
                              number = np.random.randint(50, size=(6,6))
                              
                              ironmanNumber = pd.DataFrame(number, index = ironmanIndex, columns=ironmanColumns )
                              ironmanNumber
                              
                              # 輸出結果
                              	subject	             azure	             ai&data	                web
                                  type	    自我挑戰組  主題競賽	  自我挑戰組	主題競賽  自我挑戰組	主題競賽
                                  year  enroll						
                                  2017	1	    44	      6	         6	      33	     29	      6
                                          2	    3	      24	    14	       7	      9	      0
                                  2016	1	    45	      18	    10	      36	     43	     27
                                          2	    42	      23	    44	      21	     17	     34
                                  2018	1	    29	      22	    42	      29	      4	     12
                                          2	    6	      5	        31	       9	     48	      4
                              
                              • order MultiIndex
                                用上述的ironmanNumber當例子,排序year
                              ironmanNumber.sort_index()
                              
                              # 輸出結果
                              	subject	             azure	             ai&data	                web
                                  type	    自我挑戰組  主題競賽	  自我挑戰組	主題競賽	自我挑戰組	主題競賽
                                  year  enroll	
                                  2016	1	    45	      18	    10	      36	       43	     27
                                          2	    42	      23	    44	      21	       17	     34
                                  2017	1	    44	      6	         6	      33	       29	      6
                                          2	    3	      24	    14	       7	        9	      0
                                  2018	1	    29	      22	    42	      29	        4	     12
                                          2	    6	      5	        31	       9	       48	      4
                              

                              *sum(),mean(), max() on MultiIndex
                              用ironmanNumber來取得每年參加的組數平均

                              ironmanNumber.mean(level='year')
                              
                              # 輸出結果
                              	subject	             azure	             ai&data	                web
                                  type	    自我挑戰組  主題競賽	  自我挑戰組	主題競賽  自我挑戰組	主題競賽
                                  year	
                                  2017	        23.5	15.0	      10.0	 20.0	    19.0	  3.0
                                  2016	        43.5	20.5	      27.0	 28.5	    30.0	 30.5
                                  2018	        17.5	13.5	      36.5	 19.0	    26.0	  8.0
                              

                              導讀

                              ]]> Summer 2018-10-26 09:23:35
                              [Day10]Learning Pandas - Series、DataFrame、Index https://ithelp.ithome.com.tw/articles/10204656?sc=rss.iron https://ithelp.ithome.com.tw/articles/10204656?sc=rss.iron 前言

                              終於換到下一個主題了,今天要來介紹Pandas,提供高效率,資料更容易使用的架構,且讓資料更容易分析的開源程式碼。想像Pandas是Numpy陣列的加強版

                              前言

                              終於換到下一個主題了,今天要來介紹Pandas,提供高效率,資料更容易使用的架構,且讓資料更容易分析的開源程式碼。想像Pandas是Numpy陣列的加強版

                              前置作業

                              打開Anaconda Prompt看是否有安裝pandas(使用「conda list」指令),沒有就執行

                              pip install pandas
                              

                              Pandas物件

                              pandas底下有三大物件Series和DataFrame和Index,主要都是用這三個物件在做運算。

                              Series(一維陣列)

                              跟numpy的陣列不同的是,可以定義自己的index(任何資料型態),也可想像成是特殊化的Dictionary
                              以下示範建立一個簡單的Series

                              import numpy as np
                              import pandas as pd
                              ironman = pd.Series([0.11,0.22,0.33,0.44])
                              ironman
                              
                              # 輸出結果
                              0    0.11
                              1    0.22
                              2    0.33
                              3    0.44
                              dtype: float64
                              

                              從上面的例子可以看出 [0,1,2,3]為Series的index

                              如果想從Series中單純取得value可以使用values屬性。取得index可以使用index屬性

                              print('ironman.values------->',ironman.values)
                              print('ironman.index------->',ironman.index)
                              
                              # 輸出結果
                              ironman.values-------> [0.11 0.22 0.33 0.44]
                              ironman.index-------> RangeIndex(start=0, stop=4, step=1)
                              

                              以下範例示範如果想要自己定義Series的index,在這邊使用string。

                              ironman = pd.Series([0.11,0.22,0.33,0.44], index=['a','b','c','d'])
                              ironman
                              
                              # 輸出結果
                              a    0.11
                              b    0.22
                              c    0.33
                              d    0.44
                              dtype: float64
                              

                              在這邊可以看到index從原本的[0,1,2,3]變成自己定義的['a','b','c','d']

                              剛剛說Series和dict很類似,可以使用dict來建立Series,以下範例顯示從dict->Series

                              dic_ironman = {
                                  'a': 11,
                                  'b': 22,
                                  'c': 33
                              }
                              ironman = pd.Series(dic_ironman)
                              ironman
                              
                              # 輸出結果
                              a    11
                              b    22
                              c    33
                              dtype: int64
                              

                              DataFrame(多個Series組成)

                              DataFrame跟Series一樣,可以指定index,但這邊可以想像成DataFrame是多個Series組成。

                              number = pd.Series({'taipei':200, 'taichung': 300, 'changhua': 400, 'kaohsiung' : 150})
                              mayor = pd.Series({'taipei': 'Kui', 'taichung': 'Ha', 'changhua': 'Chin', 'kaohsiung' : 'Lui'})
                              ironman_df = pd.DataFrame({'number':number, 'mayor':mayor})
                              ironman_df
                              
                              # 輸出結果
                              |  | number | mayor |
                              | -------- | -------- | -------- |
                              | taipei     | 200     | Kui     |
                              | taichung     | 300     | Ha     |
                              | changhua     | 400     | Chin     |
                              | kaohsiung     | 150     | Lui     |
                              

                              不同於Series只有index和values屬性,DataFrame還有columns屬性

                              print('ironman_df.values------->',ironman_df.values)
                              print('ironman_df.index------->',ironman_df.index)
                              print('ironman_df.columns------->',ironman_df.columns)
                              
                              # 輸出結果
                              ironman_df.values-------> [[200 'Kui']
                               [300 'Ha']
                               [400 'Chin']
                               [150 'Lui']]
                              ironman_df.index-------> Index(['taipei', 'taichung', 'changhua', 'kaohsiung'], dtype='object')
                              ironman_df.columns-------> Index(['number', 'mayor'], dtype='object')
                              

                              剛剛示範從dict建立Series,而DataFrame既然是由Series組成則代表:

                              1. Series可以建立DataFrame
                              2. dict也可以建立DataFrame
                              pd.DataFrame(number, columns=['number']) #從單一Series
                              pd.DataFrame({'number':{'taipei':200, 'taichung': 300, 'changhua': 400, 'kaohsiung' : 150}}) #從dict建立
                              
                              # 輸出結果
                              |  | number |
                              | -------- | -------- |
                              | taipei     | 200     |
                              | taichung     | 300     |
                              | changhua     | 400     | 
                              | kaohsiung     | 150     |
                              

                              上述兩個DataFrame輸出結果都一樣,所以只有印出一個

                              我們在day9學習structured array,也可以從structured array建立DataFrame

                              team =np.zeros(4, dtype={'names':('name','number','team'),'formats':('U10','i2','U10')})
                              team['name'] =['彭政閔','林智勝','蘇偉達','陽耀勳']
                              team['number'] = [23,32,96,23]
                              team['team'] = ['兄弟象','兄弟象','兄弟象','lamigo']
                              pd.DataFrame(team)
                              
                              # 輸出結果
                              	name	number	team
                              0	彭政閔	  23	兄弟象
                              1	林智勝	  32	兄弟象
                              2	蘇偉達	  96	兄弟象
                              3	陽耀勳	  23	lamigo
                              

                              Index(不可修改的陣列)

                              也可想像成(immutable array)或是一個(ordered set)

                              ironman_index = pd.Index([0.11,0.22,0.33,0.44])
                              ironman_index
                              
                              # 輸出結果
                              Float64Index([0.11, 0.22, 0.33, 0.44], dtype='float64')
                              

                              因為是不可以修改的,所以當執行修改的動作時會出現錯誤

                              DataFrame、Series的存取、修改

                              前幾天學習Numpy,其中有學到使用切片、遮罩、fancy的方式存取ndarray,現在要在DataFrame、Series上使用這些方法

                              Series

                              • 切片方式
                              ironman = pd.Series([0.11,0.22,0.33,0.44], index=['a','b','c','d'])
                              ironman['a':'c']
                              
                              # 輸出結果
                              a    0.11
                              b    0.22
                              c    0.33
                              dtype: float64
                              

                              雖然建立Series有設定文字的index,但Series還是有隱含的整數索引,因此可以使用整數來切片

                              ironman[0:3]
                              
                              # 輸出結果
                              a    0.11
                              b    0.22
                              c    0.33
                              dtype: float64
                              
                              • 遮罩的方式
                              ironman[ironman > 0.22]
                              
                              # 輸出結果
                              c    0.33
                              d    0.44
                              dtype: float64
                              

                              從上面的例子看出從ironman中取出值大於0.22

                              • fancy的方式
                                重溫一下fancy就是指傳遞一個陣列當作index去取得元素。
                              ironman[['a','d']]
                              
                              # 輸出結果
                              a    0.11
                              d    0.44
                              dtype: float64
                              
                              • loc
                                當用切片取得陣列,index也是數字情況下就要使用loc,以下範例展示有使用loc和沒有的取得陣列結果

                              沒有使用loc

                              ironman = pd.Series([0.11,0.22,0.33,0.44], index=[1,3,5,7])
                              ironman[1:3]
                              
                              # 輸出結果
                              3    0.22
                              5    0.33
                              dtype: float64
                              

                              會發現取出來的結果是隱含的整數索引[1~(3-1)],而不是index

                              使用loc

                              ironman.loc[1:3]
                              
                              # 輸出結果
                              1    0.11
                              3    0.22
                              dtype: float64
                              

                              這個例子取出來的結果才是使用index

                              DataFrame

                              以下示範用兩個欄位相除來寫入新的欄

                              number = pd.Series({'taipei':200, 'taichung': 300, 'changhua': 400, 'kaohsiung' : 150})
                              area = pd.Series({'taipei': 22, 'taichung': 25, 'changhua': 35, 'kaohsiung' : 10})
                              ironman_pd = pd.DataFrame({'number':number, 'area':area})
                              ironman_pd['divided'] = ironman_pd['number'] / ironman_pd['area']
                              ironman_pd
                              
                              # 輸出結果
                              	        number	area	divided
                              taipei	    200	     22	   9.090909
                              taichung	300	     25	   12.000000
                              changhua	400	     35	   11.428571
                              kaohsiung	150	     10	   15.000000
                              
                              • 將陣列作轉置
                              ironman_pd.T
                              
                              # 輸出結果
                                        taipei	   taichung	    changhua	   kaohsiung
                              number	  200.000000	300.0	   400.000000	    150.0
                              area	  22.000000	    25.0	   35.000000	    10.0
                              divided	   9.090909	    12.0	   11.428571	    15.0
                              
                              • 使用iloc來做切片
                              ironman_pd.iloc[:2,:2]
                              
                              # 輸出結果
                              	      number	area
                              taipei	    200	     22
                              taichung	300	     25
                              

                              使用loc來做遮罩

                              ironman_pd.loc[ironman_pd.divided > 12]
                              
                              # 輸出結果
                                          number	area	divided
                              kaohsiung	 150	 10	     15.0
                              

                              Pandas資料操作

                              Numpy的ufunc都可以在Series和DataFrame上操作。

                              • 絕對值abs
                              ironman_series = pd.Series({'a':-50, 'b': 20, 'c': -30, 'd' : 22, 'e' : -40})
                              print(np.abs(ironman_series))
                              
                              # 輸出結果
                              a    50
                              b    20
                              c    30
                              d    22
                              e    40
                              dtype: int64
                              
                              • 索引對齊
                                只要是對齊不到index都會使用NaN表示
                              ironman_series = pd.Series({'a':-50, 'b': 20, 'c': -30, 'd' : 22, 'e' : -40})
                              ironman_series2 = pd.Series({'a':12, 'c': -15, 'd': -10, 'f' : -31, 'g' : 20})
                              ironman_series.add(ironman_series2)
                              
                              # 輸出結果
                              a   -38.0
                              b     NaN
                              c   -45.0
                              d    12.0
                              e     NaN
                              f     NaN
                              g     NaN
                              dtype: float64
                              

                              可以指定當對齊不到的index時,指定特定值

                              ironman_series.add(ironman_series2, fill_value = 0)
                              
                              # 輸出結果
                              a   -38.0
                              b    20.0
                              c   -45.0
                              d    12.0
                              e   -40.0
                              f   -31.0
                              g    20.0
                              dtype: float64
                              

                              Python的運算子和Pandas之間的對應

                              運算子 Pandas方法
                              + add()
                              - sub(),subtract()
                              * mul(),multiply()
                              / truediv(),div(),divide()
                              // floordiv()
                              % mod()
                              ** pow()

                              心得分享

                              之後的金融分析,很多資料都是用DataFrame儲存,所以可以快速看過一次,我也是在分析的時候,忽然忘記怎麼用,就會回來翻一下。

                              之前的章節導覽

                              ]]>
                              Summer 2018-10-25 00:05:17
                              [Day09]Learning Numpy - Fancy、sort、structured array https://ithelp.ithome.com.tw/articles/10204336?sc=rss.iron https://ithelp.ithome.com.tw/articles/10204336?sc=rss.iron 前言

                              今天是鐵人賽的第九天,前天介紹了如何建立Numpy的陣列,和陣列的一些基本屬性(size、shape...),和兩個以上的陣列可以如何合併和分割。昨天介紹了陣列的一些運算...]]> 前言

                              今天是鐵人賽的第九天,前天介紹了如何建立Numpy的陣列,和陣列的一些基本屬性(size、shape...),和兩個以上的陣列可以如何合併和分割。昨天介紹了陣列的一些運算(sum、min、max..),陣列的broadcasting特性,還有使用運算子和遮罩的特性取得想要的元素。今天持續要來介紹Numpy

                              • Fancy索引
                              • sort
                              • structured array

                              Fancy索引

                              費氏的觀念就是傳遞一個陣列當作index去取得元素。以下面的程式當作例子:

                              • 一維陣列
                                1. 先建立一個一維陣列
                              import numpy as np
                              x = np.random.randint(0,100,size = 10)
                              print('x----------->',x)
                              
                              # 輸出結果
                              x-----------> [66 74 84 13 37 83 58 90 84 23]
                              
                              1. 假設要取4個不同的元素
                              [x[0],x[3],x[2],x[6]]
                              
                              # 輸出結果
                              [66, 13, 84, 58]
                              
                              1. 使用fancy索引取出元素,這邊需要注意的是ironman輸出的結果會是以給予的index陣列為shape而不是以x為shape
                              ironman = np.array([
                                  [0,3],
                                  [2,6]
                              ])
                              x[ironman]
                              
                              # 輸出結果
                              array([[66, 13],
                                     [84, 58]])
                              
                              • 多維陣列
                                建立一個3*4的陣列,並使用fancy索引取得元素
                              y = np.random.randint(0,100,(3,4))
                              print('y-------->',y)
                              row = np.array([0,1,2])
                              col = np.array([2,1,3])
                              print('y[row,col]----->',y[row,col])
                              
                              # 輸出結果
                              y--------> [[65 76 49 29]
                               [24 96 13 20]
                               [91 58 37 24]]
                              y[row,col]-----> [49 96 24]
                              
                              # 49-> 第0 row,第2 col 
                              # 96-> 第1 row,第1 col 
                              # 24-> 第2 row,第3 col 
                              
                              • fancy的廣播效果
                              newRow = row[:, np.newaxis]
                              print('newRow----->',newRow)
                              print('y[newRow,col]----->',y[newRow,col])
                              
                              # 輸出結果
                              newRow-----> [[0]
                               [1]
                               [2]]
                              y[newRow,col]-----> [[49 76 29]
                               [13 96 20]
                               [37 58 24]]
                               
                               #[49,13,37] 第0~2 row ,第2 col 
                               #[76,96,58] 第0~2 row ,第1 col 
                               #[29,20,24] 第0~2 row ,第3 col 
                              
                              • 使用fancy索引修改元素
                              x = np.random.randint(0,100,size = 10)
                              print('x--------------->',x)
                              i = np.array([2,1,5,7])
                              print('i--------------->',i)
                              x[i] = 33
                              print('x--------------->',x)
                              
                              # 輸出結果
                              x---------------> [91 38 11 54 82 67 63  4  7 34]
                              i---------------> [2 1 5 7]
                              x---------------> [91 33 33 54 82 33 63 33  7 34]
                              #可以看到index 1,2,5,7 已經被改成33
                              

                              陣列排序:sort()

                              將陣列從小到大做排序

                              x = np.random.randint(0,100,size = 10) #隨機產生陣列,沒有排序
                              print('x--------------->',x)
                              x.sort()
                              print('x--------------->',x)
                              
                              # 輸出結果
                              x---------------> [15 85 51 98 65 43 78 61 85 17]
                              x---------------> [15 17 43 51 61 65 78 85 85 98]
                              
                              • argsort():傳回被排序過的索引值
                              x = np.random.randint(0,100,size = 5)
                              print('x--------------->',x)
                              print('np.argsort(x)--------------->',np.argsort(x))
                              
                              # 輸出結果
                              x---------------> [40 22 91 18 74]
                              np.argsort(x)---------------> [3 1 0 4 2]
                              # x排序過後會變成 [18 22 40 74 91]
                              # 18在x的index為 3
                              # 22在x的index為 1
                              # 40在x的index為 0
                              # 74在x的index為 4
                              # 91在x的index為 2 -> [3 1 0 4 2]
                              
                              • 針對欄或列做排序
                                剛剛都是示範全部的元素做排序,但Numpy可以針對欄或列做排序,以下示範針對每一欄做排序
                              x = np.random.randint(0,100,(4,4))
                              print('x--------------->',x)
                              np.sort(x,axis=0)
                              
                              # 輸出結果
                              x---------------> [[72 22 23 27]
                               [50 39 78  3]
                               [91 27 12 47]
                               [75 75 78 94]]
                              array([[50, 22, 12,  3],
                                     [72, 27, 23, 27],
                                     [75, 39, 78, 47],
                                     [91, 75, 78, 94]])
                              # [72 50 91 75] 排序後 [50 72 75 91]
                              # [22 39 27 75] 排序後 [22 27 39 75]
                              # [23 78 12 78] 排序後 [12 23 78 78]
                              # [27 3 47 84] 排序後 [3 27 47 94]
                              
                              • 部分排序:Partitioning
                                以下示範陣列中最小的四個排在左側(順序沒有排序),其他元素放右側(順序沒有排序),只是單純選出最小四個元素放在左邊
                              x = np.random.randint(0,50,size=10)
                              print('x--------------->',x)
                              np.partition(x,4)
                              
                              # 輸出結果
                              x---------------> [48 29 38 24  9 46  6 29  3 21]
                              array([ 6,  9,  3, 21, 24, 29, 29, 46, 38, 48])
                              

                              結構化的資料(structured array)

                              numpy的資料型態有以下幾種:

                              字元 說明
                              b 位元組
                              i 有號整數
                              u 無號整數
                              f 浮點數
                              c 複數浮點數
                              S,a 字串
                              U unicode字串
                              v void
                              • create
                                以下示範如何建立結構化的資料
                              team =np.zeros(4, dtype={'names':('name','number','team'),'formats':('U10','i2','U10')})
                              # 建立標題name 資料型別是unicode字串 長度10
                              # 建立標題number 資料型別是有號整數 長度2
                              # 建立標題team 資料型別是unicode字串 長度10
                              print('team.dtype------------>',team.dtype)
                              team['name'] =['彭政閔','林智勝','蘇偉達','陽耀勳']
                              team['number'] = [23,32,96,23]
                              team['team'] = ['兄弟象','兄弟象','兄弟象','lamigo']
                              print('team------------>',team)
                              
                              # 輸出結果
                              team.dtype------------> [('name', '<U10'), ('number', '<i2'), ('team', '<U10')]
                              team------------> [('彭政閔', 23, '兄弟象') ('林智勝', 32, '兄弟象') ('蘇偉達', 96, '兄弟象')
                               ('陽耀勳', 23, 'lamigo')]
                              
                              • get
                                只取得背號的方法
                              print(team['number'])
                              
                              # 輸出結果
                              [23 32 96 23]
                              

                              之前的章節導覽

                              ]]> Summer 2018-10-24 00:01:19
                              [Day08]Learning Numpy - Ufuncs、broadcasting、運算子 https://ithelp.ithome.com.tw/articles/10204089?sc=rss.iron https://ithelp.ithome.com.tw/articles/10204089?sc=rss.iron 前言

                              今天是鐵人賽的第八天,昨天介紹了如何建立Numpy的陣列,和陣列的一些基本屬性(size、shape...),和兩個以上的陣列可以如何合併和分割,今天要來介紹Numpy的...]]> 前言

                              今天是鐵人賽的第八天,昨天介紹了如何建立Numpy的陣列,和陣列的一些基本屬性(size、shape...),和兩個以上的陣列可以如何合併和分割,今天要來介紹Numpy的函式。


                              Ufuncs

                              Numpy陣列中的計算,許多Ufuncs都是在C程式實作,計算速度比較快,接下來介紹一些比較常用的Ufuncs

                              絕對值:abs()

                              x = np.array([-2,-1,0,1,-3])
                              abs(x)
                              
                              # 輸出結果
                              array([2, 1, 0, 1, 3])
                              

                              三角函數:abs()、cos()、tan()

                              theta = np.linspace(0, np.pi, 3)  #建立一個角度的陣列
                              print("sin()---->", np.sin(theta))
                              print("cos()---->", np.cos(theta))
                              print("tan()---->", np.tan(theta))
                              
                              # 輸出結果
                              sin()----> [0.0000000e+00 1.0000000e+00 1.2246468e-16]
                              cos()----> [ 1.000000e+00  6.123234e-17 -1.000000e+00]
                              tan()----> [ 0.00000000e+00  1.63312394e+16 -1.22464680e-16]
                              

                              指數和對數:exp()、exp2()、power()、log()、log2()、log10()

                              x = [1,2,9,10, 16]
                              print("exp()---->", np.exp(x))          # e^x
                              print("exp2()---->", np.exp2(x))        # 2^x
                              print("power()---->", np.power(3, x))   # 3^x
                              print("log()---->", np.log(x))          # log自然對數
                              print("log2()---->", np.log2(x))        # 以2為基底的對數
                              print("log10()---->", np.log10(x))      # 以10為基底的對數
                              
                              # 輸出結果
                              exp()----> [2.71828183e+00 7.38905610e+00 8.10308393e+03 2.20264658e+04
                               8.88611052e+06]
                              exp2()----> [2.0000e+00 4.0000e+00 5.1200e+02 1.0240e+03 6.5536e+04]
                              power()----> [       3        9    19683    59049 43046721]
                              log()----> [0.         0.69314718 2.19722458 2.30258509 2.77258872]
                              log2()----> [0.         1.         3.169925   3.32192809 4.        ]
                              log10()----> [0.         0.30103    0.95424251 1.         1.20411998]
                              

                              外積:outer()

                              y = np.arange(1,6)
                              np.multiply.outer(y, y)
                              
                              # 輸出結果
                              array([[ 1,  2,  3,  4,  5],
                                     [ 2,  4,  6,  8, 10],
                                     [ 3,  6,  9, 12, 15],
                                     [ 4,  8, 12, 16, 20],
                                     [ 5, 10, 15, 20, 25]])
                              

                              sum():陣列做加總

                              如果想要計算陣列中所有元素的加總,可以使用sum()

                              import numpy as np
                              L = np.random.random(100)  #在0到1間亂數值取100個
                              np.sum(L)
                              
                              # 輸出結果
                              48.87105412779822
                              

                              sum()除了可以使用一維陣列,現在用多維陣列

                              m = np.random.random((3,4))
                              print(m)
                              print('column 加總', m.sum(axis=0)) #column加總
                              print('row 加總', m.sum(axis=1)) #row加總
                              
                              # 輸出結果
                              [[0.90141178 0.44072501 0.5813923  0.06718993]
                               [0.74907766 0.65631017 0.47611455 0.33805445]
                               [0.79914804 0.9814414  0.23869758 0.10919935]]
                              column 加總 [2.44963747 2.07847657 1.29620443 0.51444374]
                              row 加總 [1.99071901 2.21955683 2.12848637]
                              

                              min(),max():陣列中的最大值和最小值

                              取得陣列中元素的最大值和最小值

                              ironman_int = np.random.randint(0,10000,200) #在整數0~10000之間取值200個
                              print('min---->', np.min(ironman_int))
                              print('max---->', np.max(ironman_int))
                              
                              min----> 22
                              max----> 9946
                              

                              min()、max()除了可以使用一維陣列,現在用多維陣列

                              ironman_int_multi = np.random.randint(0,10000,(3,4)) #建立3*4的陣列,值介在整數0~10000之間
                              print('ironman_int_multi---->', ironman_int_multi)
                              
                              print('ironman_int_multi.min(axis=0)----->', ironman_int_multi.min(axis=0))
                              print('ironman_int_multi.max(axis=1)----->', ironman_int_multi.max(axis=1))
                              
                              # 輸出結果
                              ironman_int_multi----> [[9931 4228 5996 3076]
                               [2969 9102  374 1900]
                               [5982 1051 9939 4320]]
                              ironman_int_multi.min(axis=0)-----> [2969 1051  374 1900]
                              ironman_int_multi.max(axis=1)-----> [9931 9102 9939]
                              
                              • 比較np.min和min
                                之前在第二天的時候有學到也可以使用Python內建的min()來取得陣列的最小值,在這邊比較兩者運算所耗費的時間。
                              big_array = np.random.random(10000000) #在0到1間亂數值取10000000個
                              %timeit min(big_array)
                              %timeit np.min(big_array)
                              
                              # 輸出結果
                              612 ms ± 23.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
                              7.38 ms ± 1.04 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
                              

                              Numpy其他陣列計算函式

                              名稱 說明
                              np.sum 所有元素的加總
                              np.prod 所有元素的乘積
                              np.mean 所有元素的平均值
                              np.std 標準差
                              np.var 變異量
                              np.min 找出元素的最小值
                              np.max 找出元素的最大值
                              np.argmin 找出最小值的索引
                              np.argmax 找出最大值的索引
                              np.median 元素的中位數
                              np.any 當陣列中有任一值是True或是非零值時傳回True
                              np.all 當陣列中有所有值是True或是非零值時傳回True

                              broadcasting

                              numpy的陣列具有broadcasting的特性,為了就是當不同shape的陣列可以進行運算。陣列可以做的運算只有(+,-),當使用乘法運算時兩個ndarray必須相同的shape

                              a = np.array([0,1,2])
                              a + 5
                              
                              # 輸出結果
                              array([5, 6, 7])
                              

                              上述的例子,可以想像成將數值5拉長成[5,5,5],然後再與a相加到執行結果
                              下面用更加複雜的例子

                              a = np.arange(3)
                              b = np.arange(3)[:, np.newaxis]
                              print(a)
                              print(b)
                              print(a+b)
                              
                              # 輸出結果
                              [0 1 2]
                              [[0]
                               [1]
                               [2]]
                              [[0 1 2]
                               [1 2 3]
                               [2 3 4]]
                              

                              比較運算子(==,!=,<,<=,>,>=)

                              以下建立一個ndarray,並且比較其中元素大於2

                              x = np.array([1,2,3,4,5])
                              x>2
                              
                              # 輸出結果
                              array([False, False,  True,  True,  True])
                              

                              對於比較結果也可計算,計算大於2的有幾個

                              np.count_nonzero(x>2)
                              
                              # 輸出結果
                              3
                              

                              除了使用count_nonzero計算數量,也可使用sum做運算,好處是可以針對欄或列運算,以下範例是每一列大於5的計數。

                              x = np.random.randint(10, size=(3,4))
                              print(x)
                              np.sum(x>5,axis = 1)
                              
                              # 輸出結果
                              [[0 7 2 8]
                               [8 9 3 9]
                               [8 0 2 1]]
                              array([2, 3, 1])
                              

                              如果想要執行陣列中所有的值是否都小於8,用上述的x當陣列

                              np.all(x<8,axis = 0)
                              
                              # 輸出結果
                              array([False, False,  True, False])
                              

                              邏輯運算子(&,|,^,~)

                              當計算陣列中的元素需要符合兩個以上條件,ex 陣列中 > 5 且 < 8 的元素計算,就需要透過邏輯運算子來完成。
                              以下範例是算出數值大於5且小於8

                              x = np.random.randint(10, size=(4,5))
                              print(x)
                              np.sum((x > 5) & (x <8))
                              
                              # 輸出結果
                              [[4 2 2 4 0]
                               [4 7 0 1 4]
                               [9 1 7 6 9]
                               [4 3 7 7 8]]
                              5
                              

                              遮罩: 回傳特定條件的陣列

                              上述的例子可以學到使用陣列和比較運算子運算後傳回布林的陣列。現在要用回傳的布林運算子來取得想要的元素陣列。
                              以下範例是回傳大於5且小於8

                              x = np.random.randint(10, size=(4,5))
                              print(x)
                              x[(x>5) & (x <8)]
                              
                              # 輸出結果
                              [[2 5 7 4 7]
                               [6 2 3 9 3]
                               [9 9 7 8 8]
                               [2 6 8 3 3]]
                              array([7, 7, 6, 7, 6])
                              

                              之前的章節導覽

                              • 安裝環境
                              • Numpy
                              • 程式碼位置
                                • github
                                  因為作者本身也是第一次學習Python和寫程式文章,所以編排上會有點亂,觀念可能也會錯誤,如果有疑問可以提出一起討論,等30天完成之後有其他時間會將之前寫的文章加入一些想法。
                              ]]> Summer 2018-10-23 00:05:01
                              [Day07]Learning Numpy - 建立、合併、分割 https://ithelp.ithome.com.tw/articles/10203624?sc=rss.iron https://ithelp.ithome.com.tw/articles/10203624?sc=rss.iron 前言

                              學習資料科學主要有幾個套件非常重要,重要和常用的套件如下:

                              • 數據統計
                                • Numpy:Numberical Python的簡稱,提供一個高效的介面,用來在大量資料緩衝區中進行儲存和操作。某些時候Numpy的陣列就像是Python內建的list型態,但Numpy提供更有效率的儲存和操作。Numpy陣列幾乎是Python整個資料科學生態的核心。
                                • Pandas:提供高效率,資料更容易使用的架構,且讓資料更容易分析的開源程式碼。想像Pandas是Numpy陣列的加強版。
                                • SciPy:科學計算的另一個核心庫是 SciPy。它基於 NumPy,其功能也因此得到了擴展。SciPy 主數據結構又是一個多維數組,由 Numpy 實現。這個軟件包包含了幫助解決線性代數、概率論、積分計算和許多其他任務的工具。此外,SciPy 還封裝了許多新的 BLAS 和 LAPACK 函數。
                                • StatsModels:Statsmodels 是一個 Python 模塊,它為統計數據分析提供了許多機會,例如統計模型估計、執行統計測試等。在它的幫助下,你可以實現許多機器學習方法並探索不同的繪圖可能性。
                              • 視覺化
                                • Matplotlib:Matplotlib 是一個用於創建二維圖和圖形的底層庫。藉由它的幫助,你可以構建各種不同的圖標,從直方圖和散點圖到費笛卡爾座標圖。此外,有許多流行的繪圖庫被設計為與matplotlib結合使用。
                                • Seaborn:Seaborn 本質上是一個基於 matplotlib 庫的高級 API。它包含更適合處理圖表的默認設置。此外,還有豐富的可視化庫,包括一些複雜類型,如時間串行、聯合分佈圖(jointplots)和小提琴圖(violin diagrams)。

                              但我的30天裡面只有針對以下四個套件Numpy、Pandas、Matplotlib、Seaborn有比較詳細的介紹,而今天要先來介紹Numpy。


                              前置作業

                              打開Anaconda Prompt看是否有安裝Numpy(使用「conda list」指令),沒有就執行

                              pip install numpy
                              

                              建立ndarray

                              numpy有很多內建函式可以建立ndarray,現在將相同性質的函式放在一起介紹

                              建立值全部都一樣的陣列

                              • zeros():產生全部為零的陣列
                              • ones():全部為1的陣列
                              • empty():無初始值的陣列
                              • 語法:zeros(shape, dtype='dtype',*args),zeros()可改成ones()或empty()
                                示範建立上述四種陣列
                              import numpy as np
                              ironman_zeros = np.zeros(10, dtype=int) #建立一個內容為0 長度為10的整數陣列
                              ironman_ones = np.ones((3,5) ,dtype=float)  #建立一個內容為1 3x5 的浮點數陣列
                              ironman_empty = np.empty((3,2) )  #建立一個內容為1 3x2 的無初始值陣列
                              ironman_full = np.full((4,2), 2.2) #建立一個內容為2.2 4x2 的浮點數陣列
                              
                              print('ironman_zeros---->', ironman_zeros)
                              print('ironman_ones---->', ironman_ones)
                              print('ironman_empty---->', ironman_empty)
                              print('ironman_full---->', ironman_full)
                              
                              # 輸出結果
                              ironman_zeros----> [0 0 0 0 0 0 0 0 0 0]
                              ironman_ones----> [[1. 1. 1. 1. 1.]
                               [1. 1. 1. 1. 1.]
                               [1. 1. 1. 1. 1.]]
                              ironman_empty----> [[1.37961302e-306 1.00136557e-307]
                               [1.60218763e-306 1.69121367e-306]
                               [6.23040373e-307 1.60219035e-306]]
                              ironman_full----> [[2.2 2.2]
                               [2.2 2.2]
                               [2.2 2.2]
                               [2.2 2.2]]
                              

                              建立等差數列

                              • arange(start, stop, step, dtype=None):step為等差值
                              • linspace(start, stop, num, endpoint=True):num代表陣列的大小
                                示範建立上述兩種陣列
                              ironman_arange = np.arange(0,20,2) #建立一個依序填滿的陣列 0到20間隔為2
                              ironman_linspace = np.linspace(0,1,5) #建立一個5個值的陣列,在0到1間平均分布
                              
                              print('ironman_arange---->', ironman_arange)
                              print('ironman_linspace---->', ironman_linspace)
                              
                              # 輸出結果
                              ironman_arange----> [ 0  2  4  6  8 10 12 14 16 18]
                              ironman_linspace----> [0.   0.25 0.5  0.75 1.  ]
                              

                              建立隨機數列

                              • 建立0~1的浮點數
                                • random.random(size)
                                • random.ranf(size)
                                • random.sample(size)
                              ironman_random = np.random.random((3,3)) #建立一個3*3 陣列,在0到1間亂數值
                              ironman_ranf = np.random.ranf((5)) #建立一個5 陣列,在0到1間亂數值
                              ironman_sample = np.random.sample((2,3)) #建立一個2*3 陣列,在0到1間亂數值
                              
                              print('ironman_random---->', ironman_random)
                              print('ironman_ranf---->', ironman_ranf)
                              print('ironman_sample---->', ironman_sample)
                              
                              # 輸出結果
                              ironman_random----> [[0.64353867 0.04610781 0.78987197]
                               [0.75626182 0.29991636 0.92585699]
                               [0.63849181 0.16742412 0.2124621 ]]
                              ironman_ranf----> [0.94065174 0.76792842 0.58185392 0.51702131 0.13499956]
                              ironman_sample----> [[0.2181921  0.88624691 0.40748053]
                               [0.72171909 0.2094024  0.58329048]]
                              
                              • 建立常態分布又稱為高斯分布
                                • random.normal(loc='loc', scale='scale', size=None):loc為平均值、scale為標準差
                              ironman_normal = np.random.normal(0,1,(3,3)) #建立一個3*3陣列,內容為常態分佈的亂數值 平均0,標準差1
                              print('ironman_normal---->', ironman_normal)
                              
                              # 輸出結果
                              ironman_normal----> [[ 0.29675527  0.5483744  -0.68793494]
                               [-0.82978447 -0.14248656 -0.01019463]
                               [-0.89532166 -0.0651036  -0.27911572]]
                              
                              • 建立隨機整數陣列
                                • random.randint(low, high, size)
                              ironman_randint = np.random.randint(0,10,(3,3)) #建立一個3*3陣列,內容介於0到10的整數
                              print('ironman_randint---->', ironman_randint)
                              
                              # 輸出結果
                              ironman_randint----> [[3 0 7]
                               [0 0 7]
                               [1 7 8]]
                              

                              Numpy 陣列屬性

                              剛剛示範了如何建立陣列,現在來看關於陣列,有什麼基本屬性可以使用。

                              • 陣列屬性
                                • ndim():取得陣列的維度數量
                                • shape():陣列的形狀
                                • size():陣列的數量
                                • dtype():資料型態
                                • itemsize():陣列中元素的大小(位元組為單位)
                                • nbytes():陣列的大小(位元組為單位) 一般來說 nbytes = itemsize * size

                              先建立三個陣列各別是一維陣列、二維陣列、三維陣列,再用此三個陣列來顯示屬性。

                              ironman_x1 = np.random.randint(10, size=6)    #一維陣列
                              ironman_x2 = np.random.randint(10, size=(3,4))     #二維陣列
                              ironman_x3 = np.random.randint(10, size=(2,3,2))   #三維陣列
                              
                              print("ironman_x1---->", ironman_x1)
                              print("ironman_x2---->", ironman_x2)
                              print("ironman_x3---->", ironman_x3)
                              
                              # 輸出結果
                              ironman_x1----> [2 3 9 7 5 3]
                              ironman_x2----> [[4 5 3 3]
                               [7 9 9 9]
                               [7 3 2 3]]
                              ironman_x3----> [[[9 7]
                                [7 5]
                                [1 2]]
                              
                               [[2 8]
                                [1 5]
                                [8 4]]]
                              

                              ndim()

                              print("ironman_x1 ndim:     ", ironman_x1.ndim)
                              print("ironman_x2 ndim:     ", ironman_x2.ndim)
                              print("ironman_x3 ndim:     ", ironman_x3.ndim)
                              
                              # 輸出結果
                              ironman_x1 ndim:      1
                              ironman_x2 ndim:      2
                              ironman_x3 ndim:      3
                              

                              shape()

                              print("ironman_x1 shape:     ", ironman_x1.shape)
                              print("ironman_x2 shape:     ", ironman_x2.shape)
                              print("ironman_x3 shape:     ", ironman_x3.shape)
                              
                              # 輸出結果
                              ironman_x1 shape:      (6,)
                              ironman_x2 shape:      (3, 4)
                              ironman_x3 shape:      (2, 3, 2)
                              

                              size()

                              print("ironman_x1 size:     ", ironman_x1.size)  # 6 x 1
                              print("ironman_x2 size:     ", ironman_x2.size)  # 3 x 4
                              print("ironman_x3 size:     ", ironman_x3.size)  # 2 x 3 x 4
                              
                              # 輸出結果
                              ironman_x1 size:      6
                              ironman_x2 size:      12
                              ironman_x3 size:      12
                              

                              dtype()

                              print("ironman_x1 dtype:     ", ironman_x1.dtype)
                              print("ironman_x2 dtype:     ", ironman_x2.dtype)
                              print("ironman_x3 dtype:     ", ironman_x3.dtype)
                              
                              # 輸出結果
                              ironman_x1 dtype:      int32
                              ironman_x2 dtype:      int32
                              ironman_x3 dtype:      int32
                              

                              itemsize()

                              print("ironman_x1 itemsize:     ", ironman_x1.itemsize)
                              print("ironman_x2 itemsize:     ", ironman_x2.itemsize)
                              print("ironman_x3 itemsize:     ", ironman_x3.itemsize)
                              
                              # 輸出結果
                              ironman_x1 itemsize:      4
                              ironman_x2 itemsize:      4
                              ironman_x3 itemsize:      4
                              

                              nbytes()

                              print("ironman_x1 nbytes:     ", ironman_x1.nbytes)
                              print("ironman_x2 nbytes:     ", ironman_x2.nbytes)
                              print("ironman_x3 nbytes:     ", ironman_x3.nbytes)
                              
                              # 輸出結果
                              ironman_x1 nbytes:      24
                              ironman_x2 nbytes:      48
                              ironman_x3 nbytes:      48
                              

                              陣列索引

                              使用陣列的索引來取值和修改值

                              # 取得陣列值
                              print("ironman_x1[2]----->", ironman_x1[2])
                              print("ironman_x2[2,2]----->", ironman_x2[2,2])
                              print("ironman_x3[1,1,1]----->", ironman_x3[1,1,1])
                              # 修改陣列值
                              ironman_x1[2] = 7
                              ironman_x2[2,2] = 4
                              ironman_x3[1,1,1] = 3
                              print("ironman_x1[2]----->", ironman_x1[2])
                              print("ironman_x2[2,2]----->", ironman_x2[2,2])
                              print("ironman_x3[1,1,1]----->", ironman_x3[1,1,1])
                              
                              # 輸出結果
                              ironman_x1[2]-----> 9
                              ironman_x2[2,2]-----> 2
                              ironman_x3[1,1,1]-----> 5
                              ironman_x1[2]-----> 7
                              ironman_x2[2,2]-----> 4
                              ironman_x3[1,1,1]-----> 3
                              

                              陣列切片

                              可以使用切片的方式來取得元素和修改元素

                              一維陣列

                              ironman_x4 = np.arange(10)
                              print('ironman_x4---->', ironman_x4)
                              print('ironman_x4[:5]----->',ironman_x4[:5])         # 前面5個元素
                              print('ironman_x4[5:]----->',ironman_x4[5:])         # index5 之後的元素
                              print('ironman_x4[4:7]----->',ironman_x4[4:7])       # index 4~(7-1) 的元素
                              print('ironman_x4[::2]----->',ironman_x4[::2])       # 間隔2的所有元素
                              print('ironman_x4[1::2]----->',ironman_x4[1::2])     # index1開始 間隔2的所有元素
                              print('ironman_x4[::-1]----->',ironman_x4[::-1])     # 反轉元素
                              
                              # 輸出結果
                              ironman_x4----> [0 1 2 3 4 5 6 7 8 9]
                              ironman_x4[:5]-----> [0 1 2 3 4]
                              ironman_x4[5:]-----> [5 6 7 8 9]
                              ironman_x4[4:7]-----> [4 5 6]
                              ironman_x4[::2]-----> [0 2 4 6 8]
                              ironman_x4[1::2]-----> [1 3 5 7 9]
                              ironman_x4[::-1]-----> [9 8 7 6 5 4 3 2 1 0]
                              

                              二維陣列

                              ironman_x5 = np.random.randint(10, size=(3,4))     #二維陣列
                              print('ironman_x5---->', ironman_x5)
                              print('ironman_x5[:2, :3]----->',ironman_x5[:2, :3])         # 取得  2列        3欄
                              print('ironman_x5[:3, ::2]----->',ironman_x5[:3, ::2])       # 取得  3列        偶數欄
                              print('ironman_x5[:,0]----->',ironman_x5[:,0])               #取得 所有列      第(0+1)欄
                              print('ironman_x5[2,:]----->',ironman_x5[2,:])               #取得 第(2+1)列   所有欄
                              
                              # 輸出結果
                              ironman_x5----> [[4 4 0 9]
                               [3 7 3 2]
                               [1 1 2 1]]
                              ironman_x5[:2, :3]-----> [[4 4 0]
                               [3 7 3]]
                              ironman_x5[:3, ::2]-----> [[4 0]
                               [3 3]
                               [1 2]]
                              ironman_x5[:,0]-----> [4 3 1]
                              ironman_x5[2,:]-----> [1 1 2 1]
                              

                              異動子陣列,原來的陣列會被異動,使用.copy()就可以複製一份資料


                              重塑(reshape)

                              示範建立一個一維陣列變成3*1

                              x = np.array([1,2,3])
                              print('x----->', x)
                              x_reshape = x.reshape((3,1))         # 透過reshape建立欄向量
                              print('x_reshape----->', x_reshape)
                              
                              # 輸出結果
                              x-----> [1 2 3]
                              x_reshape-----> [[1]
                               [2]
                               [3]]
                              

                              串接(concatenate,vstack,hstack)

                              • concatenate():串接
                              • vstack():垂直串接
                              • hstack():水平串接
                                示範用剛剛建立的x陣列串接另外建立的y陣列
                              y = np.array([4,5,6])
                              print('concatenate----->',np.concatenate([x,y]))    # 串接陣列
                              print('vstack----->',np.vstack([x,y]))              # 垂直串接
                              print('hstack----->',np.hstack([x,y]))              # 水平串接
                              
                              # 輸出結果
                              concatenate-----> [1 2 3 4 5 6]
                              vstack-----> [[1 2 3]
                               [4 5 6]]
                              hstack-----> [1 2 3 4 5 6]
                              

                              分割(split,hsplit,vsplit)

                              • split():分割
                              • vsplit():垂直分割
                              • hsplit:水平分割
                              z = np.array([1,2,3,4,5,6,7,8,9])
                              z1,z2,z3 = np.split(z,[3,5])              # 分割3個陣列 [index 0~(3-1) ] [ index 3~(5-1)] [index (5~end)]
                              print("z1---->", z1)
                              print("z2---->", z2)
                              print("z3---->", z3)
                              z = np.array([[1,2,3],[4,5,6],[7,8,9]])
                              print('vsplit----->',np.vsplit(z,[2]))    # 垂直分割
                              print('hsplit----->',np.hsplit(z,[2]))    # 水平分割
                              
                              # 輸出結果
                              z1----> [1 2 3]
                              z2----> [4 5]
                              z3----> [6 7 8 9]
                              vsplit-----> [array([[1, 2, 3],
                                     [4, 5, 6]]), array([[7, 8, 9]])]
                              hsplit-----> [array([[1, 2],
                                     [4, 5],
                                     [7, 8]]), array([[3],
                                     [6],
                                     [9]])]
                              

                              心得分享

                              忍不住加上心得分享,最近已經寫完30天,所以打算來將之前的文章做排版,忽然間覺得Numpy看完第一天其實就可以了,= =a,我自己整理的時候看到今天這樣長,都有一種快放棄的感覺,現在看了一下後面的金融分析,恩,我覺得如果想要更進一步的才看Day8、Day9。不然其實看完Day7就綽綽有餘了.....。

                              之前的章節導覽

                              ]]> Summer 2018-10-22 13:02:35
                              [Day06]儲存資料 - File、檔案類型(csv、xml、json、yaml) https://ithelp.ithome.com.tw/articles/10202695?sc=rss.iron https://ithelp.ithome.com.tw/articles/10202695?sc=rss.iron 導讀
                              • 安裝環境
                                • 導讀

                                  前言

                                  如果要分析金融的數據,數據一定會儲存在某個地方,可以是檔案,也可以放資料庫。今天要來介紹儲存檔案的方法,並且介紹檔案可以存成什麼類型


                                  os

                                  os套件是Python中常用的文件和目錄的操作,在os中常用的函式

                                  名稱 說明
                                  system() 執行系統command
                                  mkdir(E) 建立E目錄,如果E目錄已存在會發生錯誤
                                  rmdir(E) 移除E目錄
                                  remove(E) 移除E檔案,如果E是目錄就會出錯
                                  getcwd() 取得目前所在路徑
                                  rename(src, dst) 將 src 改名為 dst

                                  先示範os的函式

                                  import os
                                  path = "myDir"
                                  os.mkdir(path) # 建立目錄
                                  os.rmdir(path) # 移除目錄
                                  os.system("mkdir dir2") #執行作業系統命令
                                  os.rename('day06.txt', 'day06_change.txt')
                                  print("當前目錄位置--->",os.getcwd())
                                  
                                  # 輸出結果
                                  當前目錄位置---> C:\Users\lasts\Desktop\git\demo\python\learn_python_data_analysis\01.Install
                                  
                                  • os.path有以下常用的函式
                                    名稱 | 說明
                                    ------------- | -------------
                                    isfile(E) | E檔案是否存在
                                    isdir(E) | E目錄是否存在
                                    join(src, dst) | 拼接目錄,只有拼接,不會建立目錄
                                    exists | 判斷文件或目錄是否存在
                                    getsize | 取得文件大小
                                    abspath | 獲得檔案的絕對路徑
                                  file = "day06-01.txt"
                                  print("完整路徑名稱--->",os.path.abspath(file))  # 完整路徑名稱
                                  print(file," 是否存在--->",os.path.exists(file)) # 是否存在
                                  print(file," 檔案大小--->",os.path.getsize(file)) #取得檔案大小
                                  
                                  # 輸出結果
                                  完整路徑名稱---> C:\Users\lasts\Desktop\git\demo\python\learn_python_data_analysis\01.Install\day06-01.txt
                                  day06-01.txt  是否存在---> True
                                  day06-01.txt  檔案大小---> 0
                                  
                                  • os.walk
                                    取得目錄下所有檔案和目錄
                                  import os
                                  for root, dirs, files in os.walk("day06-dir"):
                                      for name in files:
                                          print("檔案--->",os.path.join(root, name))
                                      for name in dirs:
                                          print("目錄--->",os.path.join(root, name))
                                      print()
                                  
                                  # 輸出結果
                                  目錄---> day06-dir\first
                                  目錄---> day06-dir\second
                                  目錄---> day06-dir\third
                                  
                                  檔案---> day06-dir\first\first-01.txt
                                  檔案---> day06-dir\first\first-02.txt
                                  檔案---> day06-dir\first\first-03.txt
                                  
                                  檔案---> day06-dir\second\second-01.txt
                                  

                                  open()

                                  檔案的輸出和輸入
                                  語法:open(filename, mode)
                                  mode傳入的是字串,第一個字母代表操作,第二個字母代表類型
                                  操作的部分

                                  文字 說明
                                  r 讀取
                                  w 寫入。檔案不存在則新增,檔案存在則寫入存在檔案
                                  X 寫入。只有當檔案不存在時才可寫入
                                  a 在檔案結尾處寫入

                                  類型

                                  文字 說明
                                  t 文字
                                  b 二進位

                                  以下示範寫入文字

                                  content = '''Python first   
                                  測試中文
                                  第三行
                                  '''  # '''...'''  保留原本的格式
                                  f = open('firstFile.txt','wt') # 寫入模式,檔案如果已經存在會被覆蓋
                                  f.write(content)
                                  f.close()
                                  

                                  讀取文字

                                  f2 = open('firstFile.txt','r') # 讀取模式
                                  for line in f2:
                                      print(line, end="")
                                  f2.close()
                                  
                                  # 輸出結果
                                  Python first   
                                  測試中文
                                  第三行
                                  

                                  文字檔結構

                                  • CSV:純文字的方式儲存檔案資料,已逗號分隔資料,可用Microsoft Excel打開檔案。
                                    格式如下
                                  股票,收盤價,單量
                                  台積電,238,23
                                  大同,40,121
                                  華新科,174,105
                                  
                                      * 以下示範寫入CSV / 讀取CSV
                                  
                                  import csv
                                  with open('day06-csv.csv', 'w', newline='') as csvfile:
                                      writer = csv.writer(csvfile) # 建立 CSV 檔寫入器
                                      writer.writerow(['股票', '收盤價', '單量'])
                                      writer.writerow(['台積電', 238, 23])
                                      writer.writerow(['大同', 40, 121])
                                      writer.writerow(['華新科', 174, 105])
                                      
                                  # ---------------------------------------------------------- read
                                  
                                  with open('day06-csv.csv', newline='') as csvfile:
                                  
                                    rows = csv.reader(csvfile)  # 讀取 CSV 檔案內容
                                    for row in rows:  # 以迴圈輸出每一列
                                      print(row)
                                  
                                  # 輸出結果
                                  ['股票', '收盤價', '單量']
                                  ['台積電', '238', '23']
                                  ['大同', '40', '121']
                                  ['華新科', '174', '105']
                                  
                                  • XML:可延伸標記式語言,是一種標記式語言。
                                    格式如下
                                  <stock>
                                    <tsmc>
                                     <close name="收盤價">238</close>
                                     <buy name="單量">23</buy>
                                    </tsmc>
                                  </stock>
                                  
                                    * 以下示範寫入XML / 讀取XML
                                  
                                  import xml.etree.ElementTree as ET
                                  data = ET.Element('stock') #建立節點stock
                                  tsmc = ET.SubElement(data, 'tsmc')  # 在stock節點底下建立子節點
                                  close = ET.SubElement(tsmc, 'close')  
                                  buy = ET.SubElement(tsmc, 'buy')  
                                  close.set('name','收盤價') # 設定節點的名稱
                                  buy.set('name','單量')  
                                  close.text = '238'  
                                  buy.text = '23'
                                  
                                  mydata = ET.tostring(data)  
                                  myfile = open("day06-xml.xml", "wb")  
                                  myfile.write(mydata)  
                                  myfile.close()
                                      
                                  # ---------------------------------------------------------- read
                                  
                                  tree = ET.parse('day06-xml.xml')  
                                  root = tree.getroot()
                                  
                                  for elem in root:  
                                      for subelem in elem:
                                          print(subelem.get('name'),"----->",subelem.attrib)
                                  
                                  # 輸出結果
                                  收盤價 -----> {'name': '收盤價'}
                                  單量 -----> {'name': '單量'}
                                  
                                  • JSON:輕量級的資料交換語言
                                    格式如下
                                  {
                                    "stock": [
                                      {
                                        "name": "台積電",
                                        "close": "238",
                                        "buy": "23"
                                      },
                                      {
                                        "name": "大同",
                                        "close": "40",
                                        "buy": "121"
                                      },
                                      {
                                        "name": "華新科",
                                        "close": "174",
                                        "buy": "105"
                                      }
                                    ]
                                  }
                                  
                                       * 以下示範寫入JSON / 讀取JSON
                                  
                                  import json
                                  
                                  data = {}  
                                  data['stock'] = []  
                                  data['stock'].append({  
                                      'name': '台積電',
                                      'close': '238',
                                      'buy': '23'
                                  })
                                  data['stock'].append({  
                                      'name': '大同',
                                      'close': '40',
                                      'buy': '121'
                                  })
                                  data['stock'].append({  
                                      'name': '華新科',
                                      'close': '174',
                                      'buy': '105'
                                  })
                                  
                                  with open('day06-json.json', 'w') as outfile:  
                                      json.dump(data, outfile,ensure_ascii=False,indent=2)
                                      
                                  # ---------------------------------------------------------- read
                                  
                                  with open('day06-json.json') as json_file:  
                                      data = json.load(json_file)
                                      for p in data['stock']:
                                          print('股票: ' + p['name'])
                                          print('收盤價: ' + p['close'])
                                          print('單量: ' + p['buy'])
                                          print('')
                                  
                                  # 輸出結果
                                  股票: 台積電
                                  收盤價: 238
                                  單量: 23
                                  
                                  股票: 大同
                                  收盤價: 40
                                  單量: 121
                                  
                                  股票: 華新科
                                  收盤價: 174
                                  單量: 105
                                  
                                  • YAML:是一個可讀性高,用來表達資料序列的格式
                                    格式如下
                                  stock:
                                    buy: '121'
                                    close: '238'
                                    name: '台積電'
                                  
                                       * 以下示範寫入YAML / 讀取YAML
                                  
                                  import yaml
                                  cfg = {
                                    "stock": dict(
                                        name='台積電',
                                        close='238',
                                        buy='121'
                                     )  
                                  }
                                  with open("day06_yml.yml", 'w') as outfile:
                                      yaml.dump(cfg, outfile, default_flow_style=False,allow_unicode=True) 
                                      
                                  # ---------------------------------------------------------- read
                                  
                                  with open("day06_yml.yml", 'r') as yml:
                                      contents = yaml.safe_load(yml)
                                      print("取得股票--->",contents['stock'])
                                  
                                  # 輸出結果
                                  取得股票---> {'buy': '121', 'close': '238', 'name': '台積電'}
                                  

                                  參考文件

                                  XML
                                  JSON
                                  YAML

                                  ]]> Summer 2018-10-21 00:10:39 [Day05]Python 基礎語法 - function、import https://ithelp.ithome.com.tw/articles/10201851?sc=rss.iron https://ithelp.ithome.com.tw/articles/10201851?sc=rss.iron 導讀
                                  • 安裝環境
                                    • 導讀

                                      前言

                                      今天是鐵人競賽第5天,前兩天介紹了Python的基本資料型別、序列結構、程式迴圈。今天要來介紹

                                      1. 自己定義函式 function
                                      2. 引用模組 import

                                      function

                                      函式(function):可以指派他們參數,也可以把他們當作引數傳給其他函式,以及回傳值。
                                      語法: def functionName():
                                      一樣用「:」和縮排來表示function區塊

                                      def GetArea(height,width):
                                          area = width * height
                                          return area
                                      ret1 = GetArea(6,9)
                                      print('ret1--->',ret1)
                                      
                                      # 輸出結果
                                      ret1---> 54
                                      
                                      • 輸入參數名稱
                                        呼叫function時可以直接輸入參數名稱,這樣可以不用管參數的順序
                                        以下範例示範GetArea這個function,第一個參數是height,第二個是width,
                                        可是呼叫時指定參數名稱,就可以不用管順序。
                                      def GetArea(height,width):
                                          area = width * height
                                          print('height--->',height)
                                          print('width--->',width)
                                          return area
                                      ret1 = GetArea(6,9)
                                      ret2 = GetArea(width=6, height=9)
                                      
                                      # 輸出結果
                                      height---> 6
                                      width---> 9
                                      height---> 9
                                      width---> 6
                                      
                                      • 參數預設值
                                        參數可以使用預設值,(有設值的參數必須是參數串列的最後)
                                      def rightFunction(first, second = 12):
                                          return first
                                      def wrongFunction(first=12, second):  # 出錯 non-default argument follows default argument
                                          return first
                                      
                                      • 不定數目參數涵式
                                        可用在不確定參數會有多少時
                                      def sumNumber(*params):
                                          total = 0
                                          for param in params:
                                              total +=param
                                          return total
                                      
                                      print("2個參數: sumNumber(7,8) = %d" % sumNumber(7,8))
                                      print("3個參數: sumNumber(9,7,8) = %d" % sumNumber(9,7,8))
                                      print("2個參數: sumNumber(7,8,12,11) = %d" % sumNumber(7,8,12,11))
                                      
                                      # 輸出結果
                                      2個參數: sumNumber(7,8) = 15
                                      3個參數: sumNumber(9,7,8) = 24
                                      2個參數: sumNumber(7,8,12,11) = 38
                                      
                                      • 將function當作參數傳遞
                                      def run_otherFunc(func):
                                          func(7,8)
                                      run_otherFunc(GetArea)
                                      
                                      # 輸出結果
                                      height---> 7
                                      width---> 8
                                      

                                      全域變數和區域變數

                                      若有相同名稱的全域和區域變數,以區域變數為優先
                                      以下範例示範在scope這個function裡面沒有定義變數var2,
                                      可是因為var2是全域變數所以在scope裡面還是可以讀取到var2

                                      def scope():
                                          var1 = "區域變數"
                                          print(var1, var2) # 輸出為 區域變數 20
                                      var1 = "全域變數"
                                      var2 =20
                                      scope()
                                      print(var1, var2) # 輸出為 全域變數 20
                                      
                                      # 輸出結果
                                      區域變數 20
                                      全域變數 20
                                      

                                      python的內建函數

                                      python除了可以自己定義function,還有python內建函數,以下列出比較常用的函數

                                      example1 = abs(-10)
                                      print(example1)  #取得絕對值 輸出 10
                                      example2 = chr(66)
                                      print(example2)  #取得整數66的字元 輸出 B
                                      example3 = divmod(33,6)
                                      print(example3)  #取得33除以6的商數和餘數 輸出 (5, 3)
                                      example4 = float(33)
                                      print(example4)  #轉成浮點數 輸出 33.0
                                      example5 = hex(33)
                                      print(example5)  #轉成16進位 輸出 0x21
                                      example6 = int(22.79)
                                      print(example6)  #轉成整數(無條件捨去) 輸出 22
                                      example7 = oct(33)
                                      print(example7) #轉成八進位 輸出 0o41
                                      example8 = ord("B")
                                      print(example8) #取得Unicode編碼 輸出 66
                                      example9 = pow(2,5)
                                      print(example9) #取得2的5次方 輸出 32
                                      example10 = round(22.79)
                                      print(example10)  #轉成整數(四捨五入) 輸出 23
                                      example11 = sum([11,22,33,44])
                                      print(example11)  #取得list的總和 輸出 110
                                      

                                      import package

                                      如果想要引用其他module裡面的function、class、variable。就需要使用import的方式引用進來。
                                      類似要引用Nunpy、Pandas、Matplotlib...
                                      import的方法有四種
                                      1.

                                         import 套件名稱
                                      套件名稱.函數名稱 # 使用函數的方式
                                      
                                      1. 使用套件的函數可以不必輸入套件名稱
                                      from 套件名稱 import *
                                      函數名稱 # 使用函數的方式
                                      
                                      1. 如果兩個套件有相同的函數名稱,可以使用第三種方法
                                      from 套件名稱 import 涵式1, 涵式2, .....
                                      涵式1 # 使用函數的方式
                                      
                                      1. 將套件名稱取成另外一個簡短的別名
                                      from 套件名稱 as 別名
                                      別名.函數名稱 # 使用函數的方式
                                      
                                      ]]> Summer 2018-10-20 00:13:02 [Day04]Python 基礎語法 - list、tuple、dict、for、while https://ithelp.ithome.com.tw/articles/10200547?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200547?sc=rss.iron 導讀
                                      • 安裝環境
                                        • 導讀

                                          前言

                                          今天鐵人競賽第4天,之前介紹了Python的基本資料型別,Boolean、Int、Float、String。接下來要來介紹序列結構(list、tuple、字典),用來表達一系列的項目。


                                          list(串列)

                                          串列:由零或多個元素組成的,以「,」逗號分隔,並且放在[]裡面。
                                          語法:[element1,element2.....]
                                          以下示範建立基本的串列結構,和如何取值

                                          ironman_list = ["中信兄弟", "統一獅", "lamigo", "富邦悍將"]
                                          print('總冠軍隊伍', ironman_list[0]) 
                                          print('亞軍隊伍', ironman_list[-2])
                                          
                                          # 輸出結果
                                          總冠軍隊伍 中信兄弟
                                          亞軍隊伍 lamigo
                                          
                                          • range()
                                            range:建立整數的串列可用range
                                            語法: range(起始值 [,終止值, 間格值])
                                            ironman_list1 = range(5)
                                            ironman_list2 = range(3,8)
                                            ironman_list3 = range(3,8,2)
                                            print(list(ironman_list1))
                                            print(list(ironman_list2))
                                            print(list(ironman_list3))
                                            
                                            # 輸出結果
                                            [0, 1, 2, 3, 4]
                                            [3, 4, 5, 6, 7]
                                            [3, 5, 7]
                                            

                                          進階的串列操作

                                          • 串列重複
                                            解釋:串列重複n次
                                            語法:[list]*n 串列重複n次

                                            ironman_list1 = [1,2,3,4,5,6]
                                            ironman_list2 = ironman_list1*2
                                            print(ironman_list2)
                                            
                                            # 輸出結果
                                            [1, 2, 3, 4, 5, 6, 1, 2, 3, 4, 5, 6]
                                            
                                          • 切片取值
                                            解釋:串列取值的方式除了可以使用index取得單一值,也可以使用切片的方式取得多個值
                                            語法:[n,m] 取出index n~(m-1)的元素

                                            ironman_list1 = [1,2,3,4,5,6]
                                            print(list(ironman_list1[2:5]))
                                            
                                            # 輸出結果
                                            [3, 4, 5]
                                            

                                            切片取值也可設定間格,語法如下
                                            語法:[n,m,o] 取出index n~(m-1)的元素,間隔為o

                                            ironman_list1 = [1,2,3,4,5,6]
                                            print(list(ironman_list1[1:4:2]))
                                            
                                            # 輸出結果
                                            [2, 4]
                                            
                                          • 刪除元素
                                            語法:del
                                            以下示範用切片的方式刪除元素

                                            ironman_list1 = [1,2,3,4,5,6]
                                            del ironman_list1[1:4]
                                            print(list(ironman_list1))
                                            
                                            # 輸出結果
                                            [1, 5, 6]
                                            
                                          • 串列預設函數

                                            • len():串列元素數目
                                            • min():串列元素最小值
                                            • max():串列元素最大值
                                            ironman_list1 = [1,2,3,4,5,6]
                                            print('串列元素數目----->',len(ironman_list1))
                                            print('串列元素最小值--->',min(ironman_list1))
                                            print('串列元素最大值--->',max(ironman_list1))
                                            
                                            # 輸出結果
                                            串列元素數目-----> 6
                                            串列元素最小值---> 1
                                            串列元素最大值---> 6
                                            

                                            如果想取得某個元素在串列中的操作可以使用以下函數

                                            • index():元素在串列中第一次出現的index
                                            • count():元素在串列中出現的次數
                                            ironman_list1 = [1,2,3,4,3,6,3]
                                            print('元素3第一次出現的位置------>',ironman_list1.index(3))
                                            print('元素在串列中出現的次數----->',ironman_list1.count(3))
                                            
                                            # 輸出結果
                                            元素3第一次出現的位置------> 2
                                            元素在串列中出現的次數-----> 3
                                            

                                            在list中加入元素

                                            • A.append(E):在A串列中加入E元素
                                            • A.extend(B):在A串列中加入B串列的元素
                                            • A.insert(B):在A串列中加入B串列
                                              extend和insert雖然B同樣都是串列,但加入A串列的方式不一樣,可以注意一下
                                            ironman_list1 = [1,2,3,4,5,6]
                                            ironman_list1.append(8)
                                            print('將8做為元素加在list最後---->',list(ironman_list1))
                                            ironman_list1 = [1,2,3,4,5,6]
                                            ironman_list1.extend([8,9])
                                            print('將[8,9]中的元素加在list最後---->',list(ironman_list1))
                                            ironman_list1 = [1,2,3,4,5,6]
                                            ironman_list1.insert(3,8)
                                            print('在index 3的位置加入元素8---->',list(ironman_list1))
                                            
                                            # 輸出結果
                                            將8做為元素加在list最後----> [1, 2, 3, 4, 5, 6, 8]
                                            將[8,9]中的元素加在list最後----> [1, 2, 3, 4, 5, 6, 8, 9]
                                            在index 3的位置加入元素8----> [1, 2, 3, 8, 4, 5, 6]
                                            

                                            移除串列的元素

                                            • pop():取出串列中最後一個元素,並且移除元素
                                            • A.remove(E):移除A串列中第一個出現E元素
                                            ironman_list1 = [1,2,3,4,5,6]
                                            n = ironman_list1.pop()
                                            print('取出list最後一個元素並且移除---->',n)
                                            ironman_list1 = [2,3,4,5,6]
                                            ironman_list1.remove(3)
                                            print('移除第一次出現3的元素---->',list(ironman_list1))
                                            
                                            # 輸出結果
                                            取出list最後一個元素並且移除----> 6
                                            移除第一次出現3的元素----> [2, 4, 5, 6]
                                            

                                            其他串列函數操作

                                            • reverse():反轉串列的順序
                                            • sort():將串列做排序
                                            ironman_list1 = [2,5,6,3,4]
                                            ironman_list1.reverse()
                                            print('反轉list順序---->',list(ironman_list1))
                                            ironman_list1 = [3,2,5,7,4]
                                            ironman_list1.sort()
                                            print('將list從小到大排列---->',list(ironman_list1))
                                            
                                            # 輸出結果
                                            反轉list順序----> [4, 3, 6, 5, 2]
                                            將list從小到大排列----> [2, 3, 4, 5, 7]
                                            

                                          Tuple(元組):不能修改的List

                                          優點:執行速度比list快,儲存在Tuple的資料比較安全
                                          以下示範建立tuple,不同於list使用[],tuple使用()

                                          ironman_tuple = ("中信兄弟", "統一獅", "lamigo", "富邦悍將")
                                          print('總冠軍隊伍', ironman_tuple[0]) 
                                          print('亞軍隊伍', ironman_tuple[-2])
                                          
                                          # 輸出結果
                                          總冠軍隊伍 中信兄弟
                                          亞軍隊伍 lamigo
                                          
                                          • list和tuple互相轉換
                                            ironman_tuple = ("中信兄弟", "統一獅", "lamigo", "富邦悍將")
                                            ironman_list = ["brother","lion","monkey","Guardians"]
                                            tuple_to_list = list(ironman_tuple)
                                            list_to_tuple = tuple(ironman_list)
                                            print(list(tuple_to_list))
                                            print(list(list_to_tuple))
                                            
                                            # 輸出結果
                                            ['中信兄弟', '統一獅', 'lamigo', '富邦悍將']
                                            ['brother', 'lion', 'monkey', 'Guardians']
                                            

                                          Dict(字典):key-map的形式

                                          list和Tuple都是使用數字取得元素值的方式,如果想要用其他key值來取值就可以使用Dict
                                          語法:{key1:value1 [,key2:value2,…]}
                                          以下範例建立一個dict

                                          ironman_dict = {"team":"中信兄弟","year":"2017","ranking":2}
                                          print(ironman_dict["team"])
                                          
                                          # 輸出結果
                                          中信兄弟
                                          
                                          • 刪除Dict
                                            在Dict有三種刪除的動作
                                            • del A[key]:刪除元素 A[key]
                                            • A.clear():刪除A Dict所有元素
                                            • del A:刪除A Dict
                                            del ironman_dict["team"]  #刪除元素
                                            print(ironman_dict) 
                                            
                                            ironman_dict.clear() #刪除所有元素
                                            print(ironman_dict)
                                            
                                            del ironman_dict # 刪除dict
                                            
                                            # 輸出結果
                                            {'year': '2017', 'ranking': 2}
                                            {}
                                            

                                          迴圈

                                          介紹完序列結構,可以使用迴圈的方式來取值,Python有兩種迴圈(for、while),python用縮排和冒號「:」來表示區塊

                                          • for
                                            示範單一for迴圈
                                          ironman_list1 = range(0, 5)
                                          for i in ironman_list1:
                                              print(i,end=",")
                                          
                                          # 輸出結果
                                          1,2,3,8,4,5,6,
                                          

                                          巢狀的for迴圈,這邊示範9*9乘法表

                                          for i in range(1,10):
                                              for j in range(1,10):
                                                  product = i * j
                                                  print("%d*%d=%-2d " % (i,j, product), end=" ")
                                              print() #斷行
                                          
                                          # 輸出結果
                                          1*1=1   1*2=2   1*3=3   1*4=4   1*5=5   1*6=6   1*7=7   1*8=8   1*9=9   
                                          2*1=2   2*2=4   2*3=6   2*4=8   2*5=10  2*6=12  2*7=14  2*8=16  2*9=18  
                                          3*1=3   3*2=6   3*3=9   3*4=12  3*5=15  3*6=18  3*7=21  3*8=24  3*9=27  
                                          4*1=4   4*2=8   4*3=12  4*4=16  4*5=20  4*6=24  4*7=28  4*8=32  4*9=36  
                                          5*1=5   5*2=10  5*3=15  5*4=20  5*5=25  5*6=30  5*7=35  5*8=40  5*9=45  
                                          6*1=6   6*2=12  6*3=18  6*4=24  6*5=30  6*6=36  6*7=42  6*8=48  6*9=54  
                                          7*1=7   7*2=14  7*3=21  7*4=28  7*5=35  7*6=42  7*7=49  7*8=56  7*9=63  
                                          8*1=8   8*2=16  8*3=24  8*4=32  8*5=40  8*6=48  8*7=56  8*8=64  8*9=72  
                                          9*1=9   9*2=18  9*3=27  9*4=36  9*5=45  9*6=54  9*7=63  9*8=72  9*9=81  
                                          
                                          • break 及 continue
                                            在迴圈中如果要終止可以使用break、continue
                                            以下示範break(會直接離開迴圈)
                                          for i in range(1,11):
                                              if(i==6):
                                                  break
                                              print(i, end=",") 
                                          
                                          # 輸出結果,6之後就不執行
                                          1,2,3,4,5,
                                          

                                          現在使用continue看跟break有什麼不同(迴圈執行中途暫時停住不往下執行,而跳到迴圈起始處繼續執行)

                                           for i in range(1,11):
                                               if(i==6):
                                                   continue
                                               print(i, end=",")
                                          
                                          # 輸出結果,只有6不會執行,>6還會執行
                                          1,2,3,4,5,7,8,9,10,
                                          

                                          python在for中還可以使用else,用在當for中的if條件一直不滿足時最後會執行else
                                          下面示範驗證數字是否為質數

                                          n = 5
                                          if(n==2):
                                              print("2是質數!")
                                          else:
                                              for i in range(2,n):
                                                  if(n % i == 0):
                                                      print("%d 不是質數!" %n)
                                                      break
                                              else:
                                                  print("%d是質數!" %n)
                                          
                                          # 輸出結果
                                          5是質數!
                                          
                                          • while
                                          total = n = 0
                                          while(n<10):
                                              n += 1
                                              total += n
                                          print(total)
                                          
                                          # 輸出結果
                                          55
                                          
                                          ]]> Summer 2018-10-19 00:04:39 [Day03]Python 基礎語法 - 變數、運算子、運算元、判斷式 https://ithelp.ithome.com.tw/articles/10200049?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200049?sc=rss.iron 安裝環境

                                          第一天 : ...]]> 安裝環境

                                          第一天 : 安裝Anaconda
                                          第二天 : 安裝Jupyter notebook

                                          導讀

                                          本次Python30天分為幾個部分

                                          1. 基礎語法[Day3~Day5]
                                          2. 儲存資料[Day6]
                                          3. 第三方函式庫-Numpy[Day7~Day9]
                                          4. 第三方函式庫-Pandas[Day10~Day14]->比預想中還多
                                          5. 第三方函式庫-Matplotlib、Seaborn[Day15~Day19]->目前正在這裡持續中,所以可能之後的講的主題會有所改變,到時候會再回來改這篇文章
                                          6. 統計學基礎[Day20~Day22]
                                          7. 金融理論[Day22~.....]->沒意外的話,應該就是這個時間要開始demo金融

                                          因為作者本身也是第一次學習Python和寫程式文章,所以編排上會有點亂,觀念可能也會錯誤,如果有疑問可以提出一起討論,等30天完成之後有其他時間會將之前寫的文章加入一些想法。

                                          這篇文章的程式輸出結果會在程式下方顯示一樣也是用程式碼區塊的方式顯示。會再加上輸出結果。

                                          示範的程式碼放在github上面,接下來正式進入第三天課程


                                          Python基礎語法

                                          介紹python一些簡單的語法,不同於其他的語法(ex Java、C..)需要對變數做明確的宣告型別,Python宣告變數不用特別宣告型別。

                                          • 名詞解釋
                                            • 變數:隨時可以改變內容的容器名稱,當設計者使用一個變數時,應用程式就會配置一塊記憶體給此變數使用,以變數名稱作為辨識此塊記憶體的標誌,系統會依據資料型態決定配置的記憶體大小。
                                            • 運算子:指定資料做哪一種運算。 ex +,-,*....
                                            • 運算元:進行運算的資料。 ex 2 + 3 -> 2和3是運算元
                                            • 單元運算子:只有一個運算元。 ex「-100」中的「-」(負)
                                            • 二元運算子:具有兩個運算元。 ex「100 - 80」中的「-」(減)

                                          變數

                                          • 宣告變數
                                            python變數不須宣告就可以使用。例如以下程式(在Javascript中需要使用 let, const)。
                                            在Python中使用「#」當註解符號

                                            fruit = "香蕉" #fruit 的資料型態為字串
                                            

                                            同一列指定多個變數,變數之間用「,」分隔

                                            age, animal = 18, "兔子" #age的值為18, animal的值為 "兔子"
                                            
                                          • 刪除變數

                                            del fruit
                                            
                                          • 命名規則
                                            1.不可以取保留字
                                            2.變數名稱第一個字母必須是大小寫字母、_、中文(pytho3.x支援)
                                            3.只能由大小寫字母、數字、 _ 、 中文(pytho3.x支援)組成變數名稱
                                            4.英文字母大小寫視為不同的變數名稱

                                          資料型態

                                          python最簡單的資料型別
                                          1.數值(有分整數和浮點數)
                                          2.布林(Ture,False)
                                          3.字串(文字字元)
                                          以下示範這些資料型態如何宣告

                                            num1 = 34 #整數
                                            num2 = 67.83 #浮點數
                                            num3 = 1.0e8 #代表1乘以10的8次方
                                            flag1 = True #bool型態 T為大寫
                                            flag2 = False #bool型態 F為大寫
                                            str1 = "這是字串" #可使用跳脫字元「\」來包含特殊字元
                                          

                                          跳脫字元介紹
                                          跳脫字元

                                          print及type及input指令

                                          • print的語法

                                             print(項目1[,項目2, ...., sep="分隔字元", end="結束字元"])
                                            

                                            以下示範print多個數字並且使用分隔字元和結束字元

                                            print(100, 200, 300, sep="&", end="--")
                                            
                                            # 輸出結果
                                            100&200&300--
                                            
                                          • 格式化print
                                            print命令支援參數格式化功能,%s代表字串、%d代表數字、%f代表浮點數

                                            print("%s 總冠軍" % ("兄弟象"))
                                            print("最終的比數為 %d:%d" % (2,7))
                                            print("%s 贏的機率為 %8.2f%s " % ("兄弟象",99.9,"%")) #(8代表固定輸出8個字元(含小數點))
                                            
                                            # 輸出結果
                                            兄弟象 總冠軍
                                            最終的比數為 2:7
                                            兄弟象 贏的機率為    99.90% 
                                            
                                          • print也支援字串format方法列印字串和整數

                                            print("{}{}年的參加隊伍為{}".format("鐵人賽",2018,"浪流連九程式匠自然產生的佛系碼農專區"))
                                            
                                            # 輸出結果
                                            鐵人賽2018年的參加隊伍為浪流連九程式匠自然產生的佛系碼農專區
                                            
                                          • type命令會取得物件的資料型態

                                            print(type(2018))
                                            print(type("浪流連九程式匠自然產生的佛系碼農專區"))
                                            print(type(True))
                                            
                                            # 輸出結果
                                            <class 'int'>
                                            <class 'str'>
                                            <class 'bool'>
                                            
                                          • input 命令
                                            input 是讓使用者由鍵盤輸入資料,print是輸出資料

                                            winner = input("請輸入2018中華職棒總冠軍?")
                                            
                                            # 輸出結果
                                            請輸入2018中華職棒總冠軍?
                                            

                                          資料型態轉換

                                          • int() 轉換為整數
                                          • float() 轉換為浮點數
                                          • str() 轉換為字串
                                            除了以上的方法,運算也會轉換,以下示範布林值和數字的轉換
                                          num2 = 5 + True      #True 轉換為1
                                          num3 = 5 + False     #False 轉換為0
                                          print('num2---->',num2)
                                          print('num3---->',num3)
                                          
                                          # 輸出結果
                                          num2----> 6
                                          num3----> 5
                                          

                                          運算子

                                          Python有以下基礎的運算子

                                          • 算術運算子
                                            +, -, *, /, %, //(取得整除的商數), ** (次方, 7**2 = 49)

                                          • 關係運算子
                                            ==, !=, >, <, >=, <=

                                          • 邏輯運算子
                                            not, and, or

                                          • 複合指定運算子
                                            +=, -=, *=, /=, %=, //=, **=

                                          • 判斷式
                                            python 語言以冒號「:」以及縮排來表示程式區塊

                                            winner = input("請輸入2018鐵人賽參加隊伍是否有「浪流連九程式匠自然產生的佛系碼農專區」,請輸入是Yes or No?")
                                            if(winner=="Yes"):
                                              print("恭喜你答對了!!")
                                            else:
                                              print("不好意思你答錯了~><~")
                                            
                                            # 輸出結果
                                            請輸入2018鐵人賽參加隊伍是否有「浪流連九程式匠自然產生的佛系碼農專區」,請輸入是Yes or No?Yes
                                            恭喜你答對了!!
                                            
                                          • 巢狀判斷式

                                            score = int(input("請「浪流連九程式匠自然產生的佛系碼農專區」有幾人參加?"))
                                            if(score>=3):
                                                if(score==9):
                                                 print("沒錯!!剛好就是9人")
                                                elif(score>=7):
                                                 print("已經非常接近了")
                                                else:
                                                 print("不好意思,離正確答案還有一段距離")
                                            else:
                                             print("沒有這麼少人喔")
                                            
                                          ]]> Summer 2018-10-18 00:02:17
                                          [Day02]使用 Jupyter notebook來寫python https://ithelp.ithome.com.tw/articles/10200046?sc=rss.iron https://ithelp.ithome.com.tw/articles/10200046?sc=rss.iron 前一天介紹為什麼要學習Python,今天要來介紹寫Python要用什麼ide,因為目前在寫JS所以也會用VS Code撰寫,但這邊介紹用Jupyter Notebook來學習,之後文章許多範例也...]]> 前一天介紹為什麼要學習Python,今天要來介紹寫Python要用什麼ide,因為目前在寫JS所以也會用VS Code撰寫,但這邊介紹用Jupyter Notebook來學習,之後文章許多範例也都是用這個呈現。

                                          第一天介紹安裝Anaconda,現在的Anaconda版本已經預設安裝Jupyter notebook,可以直接啟動Jupyter notebook。如果沒有安裝可以使用以下語法安裝

                                          conda install jupyter
                                          

                                          或是

                                          pip install jupyter
                                          

                                          介紹

                                          Ipython: 是一種基於Python的交互式解釋器,也在語言中提供了許多好用的語法擴充。
                                          Jupyter Notebook: 是一個Ipython的Web擴充套件,能讓使用者在瀏覽器中撰寫及執行程式,Jupyter Notebook 還支援了Julia、R、以及其他程式語言的notebook。

                                          啟動Ipython Shell

                                          先開啟Anaconda Prompt,輸入ipython,看到以下畫面就代表啟動成功。
                                          https://ithelp.ithome.com.tw/upload/images/20181005/20111390gEZsIZw8D5.png

                                          啟動Jupyter Notebook

                                          先開啟Anaconda Prompt,cd 到你想要儲存python的檔案位置再輸入 Jupyter Notebook 即可在瀏覽器中開啟Jupyter Notebook編輯器(或者直接用網址輸入 localhost:8888)

                                          Jupyter Notebook
                                          

                                          Jupyter Notebook預設的port是8888,可以藉由輸入參數來改變port

                                          Jupyter Notebook -port 8787
                                          
                                          • 建立 Jupyter Notebook 檔案

                                            點選New按鈕->點選Python2
                                            https://ithelp.ithome.com.tw/upload/images/20181005/20111390xNnWbqJque.png

                                            預設檔案名稱為「Untitled」,點選名稱即可修改。圖中紅色框框的部分
                                            https://ithelp.ithome.com.tw/upload/images/20181005/20111390Gf3ljyso9X.png

                                            Jupyter Notebook是以Cell作為輸入及執行的單位,一個檔案可包含多個Cell。
                                            https://ithelp.ithome.com.tw/upload/images/20181005/20111390SMkhOiGrCb.png

                                            Jupyter Notebook支援簡易智慧輸入,使用者在Cell輸入文字後按Tab鍵,系統會列出所有的可用項目讓使用者選取。
                                            https://ithelp.ithome.com.tw/upload/images/20181005/20111390TtALqqy4GQ.png

                                          • 執行程式有以下三種方法
                                            1.按工具列的Run按鈕
                                            2.按Ctrl+Enter
                                            3.按Shift+Enter
                                            https://ithelp.ithome.com.tw/upload/images/20181005/201113908wCA3wnLjv.png

                                          • Jupyter Notebook中的快捷鍵

                                            1. 列出所有的快捷鍵 Esc+h
                                              https://ithelp.ithome.com.tw/upload/images/20181016/201113909Y3BA69M0B.png

                                            2. 取得函式的說明文件 Shift + tab
                                              https://ithelp.ithome.com.tw/upload/images/20181016/20111390thkUxORBFN.png

                                            3. 新增一個Cell Esc+b
                                              也可以使用「a」或「b」,點選Cell會變成藍色再按「a」或「b」即可,a是上方b是下方

                                            4. 刪除目前的Cell Esc + d,d(d兩次)
                                              也可以使用「x」,點選Cell會變成藍色再按「x」即可

                                            5. 取得說明文件 ?
                                              每一個Python的物件都包含一個被稱為docstring(文件字串)字串的參考。
                                              docstring會含有對此物件的內容、以及使用方法的介紹,例如執行「len?」
                                              https://ithelp.ithome.com.tw/upload/images/20181005/20111390WqXY3lDrB2.png

                                          這邊要注意的是Jupyter Notebook建立檔案的副檔名為「.ipynb」,如果要另外存成「.py」
                                          https://ithelp.ithome.com.tw/upload/images/20181005/201113908swJ4yld9q.png

                                          ]]>
                                          Summer 2018-10-17 00:00:31
                                          [Day01]學習python的行前作業 https://ithelp.ithome.com.tw/articles/10199666?sc=rss.iron https://ithelp.ithome.com.tw/articles/10199666?sc=rss.iron 前言

                                          這是鐵人第一天,主要是講述為什麼要學習Python。因為在30天之前本人的Python也是完全沒有經驗,如果有什麼地方寫得不好,或是錯的地方還請歡迎留言提出,感謝。 前言

                                          這是鐵人第一天,主要是講述為什麼要學習Python。因為在30天之前本人的Python也是完全沒有經驗,如果有什麼地方寫得不好,或是錯的地方還請歡迎留言提出,感謝。

                                          why python

                                          1. 作為數據分析是個不錯的語言
                                          2. 看到程式語言排行榜python為No3 ,前三名的語言只有Python還沒學過,好像可以學一下。
                                          3. 很多套件可以用來學習

                                          python的特色

                                          此參考(註1)
                                          1.免費且開放原始碼
                                          2.高階程式語言
                                          3.可移植性,可在多個平台上執行
                                          4.直譯式程式語言
                                          5.可嵌入性,例如可以和c語言互相嵌入運用。
                                          6.豐富且多元的套件

                                          安裝Anaconda

                                          主要是學習資料分析,所以用的是Anaconda套件組。環境是windows
                                          step1. Anaconda https://www.anaconda.com/ 下載
                                          step2. 打開Anaconda Prompt 命令視窗
                                          step3. 指令介紹

                                          • Anaconda Prompt 管理套件
                                            1.顯示anaconda 已經安裝套件的命令

                                            conda list
                                            

                                            2.更新套件

                                            conda update 套件名稱
                                            easy_install -U 套件名稱
                                            

                                            3.安裝套件有以下三種command

                                            conda install 套件名稱
                                            pip install 套件名稱
                                            easy_install 套件名稱
                                            

                                            4.移除套件

                                            conda uninstall 套件名稱
                                            easy_install -m 套件名稱
                                            
                                          • Anaconda Prompt 建立虛擬環境
                                            Python 2.x程式與 Python 3.x並不相容,如果安裝Anaconda選擇了Python 2.x環境,要如何執行Python 3.x程式呢?只要建立Anaconda虛擬環境

                                            1.建立虛擬環境

                                            conda create -n 虛擬環境名稱 python=版本 anaconda
                                            

                                            2.切換虛擬環境

                                            activate 虛擬環境名稱
                                            

                                            3.關閉虛擬環境

                                            deactivate
                                            

                                            4.複製現有Python環境

                                            conda create -n 虛擬環境名稱 --clone root
                                            

                                            5.查看目前所有的虛擬環境

                                            conda info -e
                                            

                                            6.移除虛擬環境

                                            conda remove -n 虛擬環境名稱 --all
                                            

                                          參考的文章和書本

                                          Python 初學特訓班(註1)
                                          Python 資料科學學習手冊

                                          聯絡方式

                                          github
                                          mail

                                          ]]> Summer 2018-10-16 00:00:14

paypal.me/twcctz50
http://blog.sina.com.tw/window/feed.php?ver=rss&type=entry&blog_id=48997
https://paypal.me/twcctz50?country.x=TW&locale.x=zh_TW
使用 PayPal.Me 連結付款給我: https://paypal.me/twcctz50?country.x=TW&locale.x=zh_TW 
健康是最好的禮物蛋黃油https://www.facebook.com/eggsoil  
在生寶妹之前,寶妹媽,就食用蛋黃油調養車禍後造成的心律不整等後遺症狀將近兩年,配合復健、整復治療和游泳運動等,逐漸恢復正常的心律。
直到懷寶妹後至今,感謝蛋黃油讓寶妹媽能恢復健康,給這意外來的祝福一個健康的生長環境。寶妹雖是家中最小的孩子,但也是最健康、幸福的孩子!惟有她是媽媽有豐富的母乳可供親餵。
至今,恭喜寶妹已經滿兩歲了!每天還是喜歡找媽媽喝餒餒睡覺,出生至今,身體健康,沒有感冒和生病的紀錄。祝福寶妹,能一直健康、快樂的成長、學習,成為眾人的祝福!
代工生產製造需一千斤
需用者請提早預約安排
每天補充蛋黃油可降低罹癌風險?!是的!!
昨晚,十多年老案主的弟弟周大哥說,二哥鼻咽癌治療恢復後的後遺症,又發作了!需配合醫師開的抗生素和補充細胞再生的營養素,又訂了10瓶50ml,補充瓶。
據了解個案案主,罹癌前的工作是從事印刷業。坦白說,有點醫學常識的人多半知道化學油墨對身體的傷害為何?
本科所學為設計的我,也曾在相關產業待過十幾年的時間,等到研究所和醫院合作完成論文後,才知道自己的工作:設計和教職,其實,都隱含著很高的罹癌風險!
我們都曾因此賠上過健康,但慶幸自己和案主們,都是有福之人!
感謝蛋黃油豐富的卵磷脂營養成份,除了維生素C之外,幾乎涵蓋了所有!在103學年間,我曾因超鐘點一週上課時數33小時,忙碌於家庭和工作間,哪時老二剛出生半年,做好月子後,馬上就恢復忙碌的教職工作,不出一個月,就為卵巢炎、併發盆腔炎,抗生素吃了半年,還是不見恢復的病痛所苦!
期間,幾乎每兩週跑醫院兩三趟,署基的婦科主任醫師,也建議我要跟校長請辭,調養身體為重!感謝校長體諒,讓我減課到16堂,撐到合約到期,沒有違約金的困擾,離職後,就回家照顧老二和調養自己的身體。
卵巢炎超痛的!併發盆腔炎更痛!從早痛到晚!醫師說,抗生素吃半年了,就不能再吃了!感謝醫師沒讓我繼續吃下去!
而是勸我調整工作、生活和飲食!於是,我又開始大量食用蛋黃油和自己料理三餐,就這樣子,吃了半年,某天,突然驚覺:卵巢不痛了耶!
感謝神!在恢復前的每一時刻,我被疼痛纏身,影響情緒和睡眠,每天都不知要多久,才會好?!只是一直做該做的事,好好吃飯、休息,調養身心!直到恢復時,才發覺:哇!幸好,半年就好了!
這是蛋黃油在我近十年的健康危機中,第二次救了我!感謝神創造各樣美好食物,保守、祝福我們能有機會恢復祂起初創造我們的美好!
感謝近十幾年來,因著每一次的健康危機,即時食用蛋黃油排毒、調養身體、恢復正常的心律,讓我能在身體得滋養後再懷孕生下健康的孩子們,也讓我們的生命得到延續。
為此,我才投入後來的時間、金錢、精神在服務和我一樣有需要的案主們身上。感謝大家一起陪我攜手走過已過的十幾年。祝福每個人都能有機會認知到維護健康的身體,其奧秘就在於養成正確的日常飲食習慣!
筆者:蛋黃油男
電話:02-24978169
手機:0989-422508
為資深個案自主健康管理設計師,
目前服務於品蔚養生設計事務所。
https://liker.social/invite/pRwYGraC
https://www.facebook.com/eggsoil
https://www.facebook.com/nectw721
https://www.instagram.com/0989422508mdc
https://www.pinterest.com/twcctz500
https://www.linkedin.com/in/twcctz500
https://www.twitter.com/twcctz500
https://currents.google.com/117454133619976175486
https://www.youtube.com/shorts/o8Jaud7px4w
https://www.youtube.com/channel/UC5E4_uNgGl_omnUVfL7fUyA
https://fitness-center-727.business.site/
https://g.page/r/CUt-sGCWmpP2EBM/review
https://matters.news/@twcctz500
https://pastebin.com/NgugYMZP   來自 @pastebin 
https://hardbin.com/ipfs/QmSmgLoGrr4dcJ4EFmszDXJAGbXW2oZU5nNgksdjG7bb3P/
https://zerobin.net/?b4e43b1d09b3dcbe#C4rH4SwtqM4v4BsehLtwVf2DSEhto1JRx8PzKIsmrYo=
https://zerobin.net/?4fdb0ea46d78f4a6#z7E38he/vzywjsvzxBwTBm5dvCaKc5Pei8nDkUflgOs=
https://privatebin.net/?8abe23c5b0253b66#7Vq3VyzThWp2ga7tvVBQ4om6aiYdqvma4bpfWYNKMCgG
https://medium.com/@twcctz50
https://about.me/twcctz500
paypal.me/twcctz50

ASP.NET 伺服器控制項開發 :: 2008 iT 邦幫忙鐵人賽 https://ithelp.ithome.com.tw/users/20007956/ironman zh-TW Mon, 06 Jun 2022 20:19:06 +0800 [ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題(續) https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron 接續上一文
接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是...]]>
接續上一文
接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是否不同,不同的話傳回 True,使其產生 SelectedIndexChanged 事件。

        Protected Overrides Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean
            Dim values As String()
            Dim iSelectedIndex As Integer

            Me.EnsureDataBound()
            values = postCollection.GetValues(postDataKey)

            If (Not values Is Nothing) Then
                iSelectedIndex = CInt(Me.Page.Request.Form("__EVENTARGUMENT"))
                If (Me.SelectedIndex <> iSelectedIndex) Then
                    MyBase.SetPostDataSelection(iSelectedIndex)
                    Return True
                End If
            End If
            Return False
        End Function

四、測試程式
在 TBDropDownList 的 SelectedIndexChanged 事件撰寫如下測試程式碼。

    Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged
        Dim sText As String

        sText = String.Format("TBDropDownList: Index={0} Value={1}", DropDownList2.SelectedIndex, DropDownList2.SelectedValue)
        Me.Response.Write(sText)
    End Sub

執行程式,在 TBDropDownList 選取 "王五" 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。

接下選取 Value 值相同的 "陳六" 這個選項,也會正常引發 SelectedIndexChanged ,並顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/30/5830.aspx

]]>
jeff377 2008-10-30 21:23:12
[ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題 https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無...]]> DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無法正確引發 SelectedIndexChanged 事件的問題;今天剛好在網路上看到有人在詢問此問題,所以本文將說明這個問題的源由,並修改 DropDownList 控制項來解決這個問題。
程式碼下載:ASP.NET Server Control - Day29.rar

一、DropDownList 的成員 Value 值相同產生的問題
我們先寫個測試程式來描述問題,在頁面上放置一個 DropDownList 控制項,設定 AutoPostBack=True,並加入四個 ListItem,其中 "王五" 及 "陳六" 二個 ListItem 的 Value 值相同。

    <asp:DropDownList ID="DropDownList1" runat="server" AutoPostBack="True">
            <asp:ListItem Value="0">張三</asp:ListItem>
            <asp:ListItem Value="1">李四</asp:ListItem>
            <asp:ListItem Value="2">王五</asp:ListItem>
            <asp:ListItem Value="2">陳六</asp:ListItem>
    </asp:DropDownList>

在 DropDownList 的 SelectedIndexChanged 事件,輸出 DropDownList 的 SelectedIndex 及 SelectedValue 屬性值。

    Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged
        Dim sText As String

        sText = String.Format("DropDownList: Index={0} Value={1}", DropDownList1.SelectedIndex, DropDownList1.SelectedValue)
        Me.Response.Write(sText)
    End Sub

執行程式,在 DropDownList 選取 "李四" 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。

接下來選取 "陳六" 這個選項時,竟然發生奇怪的現象,DorpDownList 竟然顯示相同 Value 值的 "王五" 這個成員的 SelectedIndex 及 SelectedValue 屬性值。

二、問題發生的原因
我們先看一下 DropDownList 輸出到用戶端的 HTML 原始碼。

<select name="DropDownList1" onchange="javascript:setTimeout('__doPostBack(\'DropDownList1\',\'\')', 0)" id="DropDownList1">
	<option selected="selected" value="0">張三</option>
	<option value="1">李四</option>
	<option value="2">王五</option>
	<option value="2">陳六</option>
</select>

DropDownList 是呼叫 __doPostBack 函式,只傳入 eventTarget參數 (對應到 __EVENTTARGET 這個 HiddenField) 為 DropDownList 的 ClientID;當 PostBack 回伺服端時,在 DropDownList 的 LoadPostData 方法中,會取得用戶端選取的 SelectedValue 值,並去尋找對應的成員的 SelectedIndex 值。可是問題來了,因為 "王五" 與 "陳六" 的 Value 是相同的值,當在尋找符合 Value 值的成員時,前面的選項 "王五" 會先符合條件而傳回該 Index 值,所以先造成取得錯誤的 SelectedIndex 。

Protected Overridable Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean
    Dim values As String() = postCollection.GetValues(postDataKey)
    Me.EnsureDataBound
    If (Not values Is Nothing) Then
        MyBase.ValidateEvent(postDataKey, values(0))
        Dim selectedIndex As Integer = Me.Items.FindByValueInternal(values(0), False)
        If (Me.SelectedIndex <> selectedIndex) Then
            MyBase.SetPostDataSelection(selectedIndex)
            Return True
        End If
    End If
    Return False
End Function

三、修改 DropDownList 控制項來解決問題
要解決這個問題最好的方式就是直接修改 DropDownList 控制項,自行處理前端呼叫 __doPostBack 的動作,將用戶端 DropDownList 選擇 SelectedIndex 一併傳回伺服端。所以我們繼承 DropDownList 命名為 TBDropDownList,覆寫 AddAttributesToRender 來自行輸出 PostBack 的用戶端指令碼,我們會用一個變數記錄 AutoPostBack 屬性,並強制將 AutoPostBack 屬性值設為 False,這是為了不要 MyBase 產生 PostBack 的指令碼;然後再自行輸出 AutoPostBack 用戶端指令碼,其中 __doPostBack 的 eventArgument 參數 (對應到 __EVENTARGUMENT 這個 HiddenField) 傳入 this.selectedIndex。

        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
            Dim bAutoPostBack As Boolean
            Dim sScript As String

            '記錄 AutoPostBack 值,並將 AutoPostBack 設為 False,不要讓 MyBase 產生 PostBack 的指令碼
            bAutoPostBack = Me.AutoPostBack
            Me.AutoPostBack = False

            MyBase.AddAttributesToRender(writer)

            If bAutoPostBack Then
                MyBase.Attributes.Remove("onchange")
                sScript = String.Format("__doPostBack('{0}',{1})", Me.ClientID, "this.selectedIndex")
                writer.AddAttribute(HtmlTextWriterAttribute.Onchange, sScript)
                Me.AutoPostBack = True
            End If
        End Sub

在頁面上放置一個 TBDropDownList 控制項,設定與上述案例相同的成員清單。

        <bee:TBDropDownList ID="DropDownList2" runat="server" AutoPostBack="True">
            <asp:ListItem Value="0">張三</asp:ListItem>
            <asp:ListItem Value="1">李四</asp:ListItem>
            <asp:ListItem Value="2">王五</asp:ListItem>
            <asp:ListItem Value="2">陳六</asp:ListItem>
        </bee:TBDropDownList>

執行程式查看 TBDropDownList 控制項的 HTML 原始碼,呼叫 __doPostBack 函式的參數已經被修改,eventArgument 參數會傳入該控制項的 selectedIndex。

<select name="DropDownList2" id="DropDownList2" onchange="__doPostBack('DropDownList2',this.selectedIndex)">
	<option selected="selected" value="0">張三</option>
	<option value="1">李四</option>
	<option value="2">王五</option>
	<option value="2">陳六</option>
</select>

[超過字數限制,下一篇接續本文]

]]>
jeff377 2008-10-30 21:15:23
[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項(續) https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron 接續一上文
二、實作圖形驗證碼控制項
雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖...]]>
接續一上文
二、實作圖形驗證碼控制項
雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖形,可是這樣只處理一半的動作,因為沒有處理「使用者輸入的驗證碼」是否與「圖形驗證碼」相符,所以我們將實作一個圖形驗證碼控制項,來處理掉所有相關動作。
即然上面的示範使用 Image 控制項來呈現驗證碼,所以圖形驗證碼控制項就繼承 Image 命名為 TBValidateCode。

    < _
    Description("圖形驗證碼控制項"), _
    ToolboxData("<{0}:TBValidateCode runat=server></{0}:TBValidateCode>") _
    > _
    Public Class TBValidateCode
        Inherits System.Web.UI.WebControls.Image
    
    End

新增 ValidateCodeUrl 屬性,設定圖形驗證碼產生頁面的網址。

        ''' <summary>
        ''' 圖形驗證碼產生頁面網址。
        ''' </summary>
        < _
        Description("圖形驗證碼產生頁面網址"), _
        DefaultValue("") _
        > _
        Public Property ValidateCodeUrl() As String
            Get
                Return FValidateCodeUrl
            End Get
            Set(ByVal value As String)
                FValidateCodeUrl = value
            End Set
        End Property

覆寫 Render 方法,若未設定 ValidateCodeUrl 屬性,則預設為 ~/Page/ValidateCode.aspx 這個頁面。另外我們在圖形的 ondbclick 加上一段用戶端指令碼,其作用是讓用戶可以滑鼠二下來重新產生一個驗證碼圖形。

        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim sUrl As String
            Dim sScript As String

            sUrl = Me.ValidateCodeUrl
            If String.IsNullOrEmpty(sUrl) Then
                sUrl = "~/Page/ValidateCode.aspx"
            End If
            If Me.BorderWidth = Unit.Empty Then
                Me.BorderWidth = Unit.Pixel(1)
            End If
            If Me.AlternateText = String.Empty Then
                Me.AlternateText = "圖形驗證碼"
            End If
            Me.ToolTip = "滑鼠點二下可重新產生驗證碼"
            Me.ImageUrl = sUrl
            If Not Me.DesignMode Then
                sScript = String.Format("this.src='{0}?flag='+Math.random();", Me.Page.ResolveClientUrl(sUrl))
                Me.Attributes("ondblclick") = sScript
            End If
            Me.Style(HtmlTextWriterStyle.Cursor) = "pointer"

            MyBase.Render(writer)
        End Sub

另外新增一個 ValidateCode 方法,用來檢查輸入驗證碼是否正確。還記得我們在產生驗證碼圖形時,同時把該驗證碼的值寫入 Session("_ValidateCode") 中吧,所以這個方法只是把用戶輸入的值與 Seesion 中的值做比對。

        ''' <summary>
        ''' 檢查輸入驗證碼是否正確。
        ''' </summary>
        ''' <param name="Code">輸入驗證碼。</param>
        ''' <returns>驗證成功傳回 True,反之傳回 False。</returns>
        Public Function ValidateCode(ByVal Code As String) As Boolean
            If Me.Page.Session(SessionKey) Is Nothing Then Return False
            If SameText(CCStr(Me.Page.Session(SessionKey)), Code) Then
                Return True
            Else
                Return False
            End If
        End Function

三、測試程式
在頁面放置一個 TBValidateCode 控制項,另外加一個文字框及按鈕,供使用者輸入驗證碼後按下「確定」鈕後到伺服端做輸入值比對的動作。

        <bee:TBValidateCode ID="TBValidateCode1" runat="server" />
        <bee:TBTextBox ID="txtCode" runat="server"></bee:TBTextBox>
        <bee:TBButton ID="TBButton1" runat="server" Text="確定" />

在「確定」鈕的 Click 事件中,我們使用 TBValidateCode 控制項的 ValidateCode 方法判斷驗證碼輸入的正確性。

    Protected Sub TBButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TBButton1.Click
        If TBValidateCode1.ValidateCode(txtCode.Text) Then
            Me.Response.Write("驗證碼輸入正確")
        Else
            Me.Response.Write("驗證碼輸入錯誤!")
        End If
    End Sub

執行程式,頁面就會隨機產生一個驗證碼圖形。

輸入正確的值按「確定」鈕,就會顯示「驗證碼輸入正確」的訊息。因為我們在同一頁面測試的關係,你會發現 PostBack 後驗證碼圖形又會重新產生,一般正常的做法是驗證正確後就導向另一個頁面。

當我們輸入錯誤的值,就會顯示「驗證碼輸入錯誤!」的訊息。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/29/5818.aspx

]]>
jeff377 2008-10-29 20:34:22
[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項 https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron 在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地...]]> 在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地在網頁上套用圖形驗證碼。
程式碼下載:ASP.NET Server Control - Day28.rar

一、產生圖形驗證碼
我們先準備一個產生圖形驗證碼的頁面 (ValidateCode.aspx),這個頁面主要是繪製驗證碼圖形,並將其寫入記憶體資料流,最後使用 Response.BinaryWrite 將圖形輸出傳遞到用戶端。當我們輸出此驗證碼圖形的同時,會使用 Session("_ValidateCode") 來記錄驗證碼的值,以便後續與使用者輸入驗證碼做比對之用。

Partial Class ValidateCode
    Inherits System.Web.UI.Page

    ''' <summary>
    ''' 產生圖形驗證碼。
    ''' </summary>
    Public Function CreateValidateCodeImage(ByRef Code As String, ByVal CodeLength As Integer, _
        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer) As Bitmap
        Dim sCode As String = String.Empty
        '顏色列表,用於驗證碼、噪線、噪點
        Dim oColors As Color() = { _
            Drawing.Color.Black, Drawing.Color.Red, Drawing.Color.Blue, Drawing.Color.Green, _
            Drawing.Color.Orange, Drawing.Color.Brown, Drawing.Color.Brown, Drawing.Color.DarkBlue}
        '字體列表,用於驗證碼
        Dim oFontNames As String() = {"Times New Roman", "MS Mincho", "Book Antiqua", _
                                      "Gungsuh", "PMingLiU", "Impact"}
        '驗證碼的字元集,去掉了一些容易混淆的字元
        Dim oCharacter As Char() = {"2"c, "3"c, "4"c, "5"c, "6"c, "8"c, _
                                    "9"c, "A"c, "B"c, "C"c, "D"c, "E"c, _
                                    "F"c, "G"c, "H"c, "J"c, "K"c, "L"c, _
                                    "M"c, "N"c, "P"c, "R"c, "S"c, "T"c, _
                                    "W"c, "X"c, "Y"c}
        Dim oRnd As New Random()
        Dim oBmp As Bitmap
        Dim oGraphics As Graphics
        Dim N1 As Integer
        Dim oPoint1 As Drawing.Point
        Dim oPoint2 As Drawing.Point
        Dim sFontName As String
        Dim oFont As Font
        Dim oColor As Color

        '生成驗證碼字串
        For N1 = 0 To CodeLength - 1
            sCode += oCharacter(oRnd.Next(oCharacter.Length))
        Next

        oBmp = New Bitmap(Width, Height)
        oGraphics = Graphics.FromImage(oBmp)
        oGraphics.Clear(Drawing.Color.White)
        Try
            For N1 = 0 To 4
                '畫噪線
                oPoint1.X = oRnd.Next(Width)
                oPoint1.Y = oRnd.Next(Height)
                oPoint2.X = oRnd.Next(Width)
                oPoint2.Y = oRnd.Next(Height)
                oColor = oColors(oRnd.Next(oColors.Length))
                oGraphics.DrawLine(New Pen(oColor), oPoint1, oPoint2)
            Next

            For N1 = 0 To sCode.Length - 1
                '畫驗證碼字串
                sFontName = oFontNames(oRnd.Next(oFontNames.Length))
                oFont = New Font(sFontName, FontSize, FontStyle.Italic)
                oColor = oColors(oRnd.Next(oColors.Length))
                oGraphics.DrawString(sCode(N1).ToString(), oFont, New SolidBrush(oColor), CSng(N1) * FontSize + 10, CSng(8))
            Next

            For i As Integer = 0 To 30
                '畫噪點
                Dim x As Integer = oRnd.Next(oBmp.Width)
                Dim y As Integer = oRnd.Next(oBmp.Height)
                Dim clr As Color = oColors(oRnd.Next(oColors.Length))
                oBmp.SetPixel(x, y, clr)
            Next

            Code = sCode
            Return oBmp
        Finally
            oGraphics.Dispose()
        End Try
    End Function

    ''' <summary>
    ''' 產生圖形驗證碼。
    ''' </summary>
    Public Sub CreateValidateCodeImage(ByRef MemoryStream As MemoryStream, _
        ByRef Code As String, ByVal CodeLength As Integer, _
        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer)
        Dim oBmp As Bitmap

        oBmp = CreateValidateCodeImage(Code, CodeLength, Width, Height, FontSize)
        Try
            oBmp.Save(MemoryStream, ImageFormat.Png)
        Finally
            oBmp.Dispose()
        End Try
    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Dim sCode As String = String.Empty
        '清除該頁輸出緩存,設置該頁無緩存
        Response.Buffer = True
        Response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0)
        Response.Expires = 0
        Response.CacheControl = "no-cache"
        Response.AppendHeader("Pragma", "No-Cache")
        '將驗證碼圖片寫入記憶體流,並將其以 "image/Png" 格式輸出
        Dim oStream As New MemoryStream()
        Try
            CreateValidateCodeImage(oStream, sCode, 4, 100, 40, 18)
            Me.Session("_ValidateCode") = sCode
            Response.ClearContent()
            Response.ContentType = "image/Png"
            Response.BinaryWrite(oStream.ToArray())
        Finally
            '釋放資源
            oStream.Dispose()
        End Try
    End Sub
End Class

我們將此頁面置於 ~/Page/ValidateCode.aspx,當要使用此頁面的圖形驗證碼,只需要在使用 Image 控制項,設定 ImageUrl 為此頁面即可。

<asp:Image ID="imgValidateCode" runat="server" ImageUrl="~/Page/ValidateCode.aspx" />

[超過字數限制,下一篇接續本文]

]]>
jeff377 2008-10-29 20:31:45
[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續2) https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron 接續上一文
接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate ...]]>
接續上一文
接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate 需要同時具有「新增」、「更新」、「取消」三個按鈕。其中 ProductID 為主索引欄位,所以我們使用 TBTextBox 來繫結 ProductID 欄位,設定 FormViewModeState.InsertMode="Enable" 使控制項在新增模式時為可編輯,設定 FormViewModeState.EditMode="Disable" 使控制項在修改模式是唯讀的。

        <bee:TBFormView ID="TBFormView1" runat="server" DataKeyNames="ProductID" DataSourceID="SqlDataSource1"
            DefaultMode="Edit" SingleTemplate="EditItemTemplate" BackColor="White" BorderColor="#CCCCCC"
            BorderStyle="None" BorderWidth="1px" CellPadding="3" GridLines="Both" Visible="False">
            <FooterStyle BackColor="White" ForeColor="#000066" />
            <RowStyle ForeColor="#000066" />
            <EditItemTemplate>
                ProductID:
                <bee:TBTextBox ID="TextBox1" runat="server" Text='<%# Bind("ProductID") %>'> 
                  <FormViewModeState EditMode="Disable" InsertMode="Enable">
                  </FormViewModeState>
                </bee:TBTextBox>

                '省略

                <asp:LinkButton ID="LinkButton1" runat="server" CausesValidation="True" CommandName="Insert"
                    Text="新增" />
                 <asp:LinkButton ID="UpdateButton" runat="server" CausesValidation="True" CommandName="Update"
                    Text="更新" />
                 <asp:LinkButton ID="UpdateCancelButton" runat="server" CausesValidation="False"
                    CommandName="Cancel" Text="取消" />
            </EditItemTemplate>
        </bee:TBFormView>

2. 測試新增模式
接下來執行程式,一開始為瀏覽模式,以 TBGridView 來呈現資料。

按下 Header 的「新增」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的新增模式。其中繫結 ProductID 欄位的 TBTextBox 為可編輯模式,而下方的按鈕只會顯示「新增」及「取消」鈕。

在新增模式輸入完畢後,按下「新增」鈕,資料錄就會被寫入資料庫。

3. 測試修改模式
接下來測試修改模式,按下「編輯」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的修改模式。其中繫結 ProductID 欄位的 TBTextBox 為唯讀模式,而下方的按鈕只會顯示「更新」及「取消」鈕。

在修改模式輸入完畢後,按下「更新」鈕,資料錄就會被寫入資料庫。

4. 頁面程式碼
示範了上述的操作後,接下來我們回頭看一下頁面的程式碼。你沒看錯,筆者也沒貼錯,真的是一行程式碼都沒有,因為所有相關動作都由控制項處理掉了。

Partial Class Day27
    Inherits System.Web.UI.Page

End Class

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx

]]>
jeff377 2008-10-28 13:57:23
[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續1) https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron 接續上一文
二、讓 TextBox 控制項可自行維護狀態
接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBText...]]>
接續上一文
二、讓 TextBox 控制項可自行維護狀態
接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBTextBox。新增 FormViewModeState 屬性 (TBFormViewModeState 型別),依 FormView Mode 來設定控制項狀。並覆寫 PreRender 方法,在此方法中呼叫 DoFormViewModeStatus 私有方法,依 FormView 的模式來處理控制項狀態。

    ''' <summary>
    ''' 文字框控制項。
    ''' </summary>
    < _
    Description("文字框控制項。"), _
    ToolboxData("<{0}:TBTextBox runat=server></{0}:TBTextBox>") _
    > _
    Public Class TBTextBox
        Inherits TextBox
        Private FFormViewModeState As TBFormViewModeState

        ''' <summary>
        ''' 依 FormViewMode 來設定控制項狀態。
        ''' </summary>
        < _
        Category(WebCommon.Category.Behavior), _
        NotifyParentProperty(True), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DefaultValue("") _
        > _
        Public ReadOnly Property FormViewModeState() As TBFormViewModeState
            Get
                If FFormViewModeState Is Nothing Then
                    FFormViewModeState = New TBFormViewModeState
                End If
                Return FFormViewModeState
            End Get
        End Property

        ''' <summary>
        ''' 處理控制項狀態。
        ''' </summary>
        Private Sub DoControlStatus(ByVal ControlStatus As EControlState)
            Select Case ControlStatus
                Case EControlState.Enable
                    Me.Enabled = True
                Case EControlState.Disable
                    Me.Enabled = False
                Case EControlState.Hide
                    Me.Visible = False
            End Select
        End Sub

        ''' <summary>
        ''' 依 FormView 的模式來處理控制項狀態。
        ''' </summary>
        Private Sub DoFormViewModeStatus()
            Dim oFormView As FormView

            '若控制項置於 FormView 中,則依 FormView 的模式來處理控制項狀態
            If TypeOf Me.BindingContainer Is FormView Then
                oFormView = DirectCast(Me.BindingContainer, FormView)
                Select Case oFormView.CurrentMode
                    Case FormViewMode.Insert
                        DoControlStatus(Me.FormViewModeState.InsertMode)
                    Case FormViewMode.Edit
                        DoControlStatus(Me.FormViewModeState.EditMode)
                    Case FormViewMode.ReadOnly
                        DoControlStatus(Me.FormViewModeState.BrowseMode)
                End Select
            End If
        End Sub

        ''' <summary>
        ''' 覆寫。引發 PreRender 事件。
        ''' </summary>
        Protected Overrides Sub OnPreRender(ByVal e As EventArgs)
            MyBase.OnPreRender(e)
            '依 FormView 的模式來處理控制項狀態
            DoFormViewModeStatus()
        End Sub

    End Class

三、測試程式
1. 設定控制項相關屬性
我們使用 Northwnd 資料庫的 Products資料表為例,以 GridView+FormView 示範資料「新增/修改/刪除」的操作。在頁面拖曳 SqlDataSource 控制項後,在頁面上的使用 TBGridView 來顯示瀏覽資料。TBGridView 的 FormViewID 設為關連的 TBFormVIew 控制項;另外有使用到 TBCommandField,設定 ShowHeaderNewButton=True,讓命令列具有「新增」鈕。

        <bee:TBGridView ID="TBGridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ProductID"
            DataSourceID="SqlDataSource1" FormViewID="TBFormView1">
            <Columns>
                <bee:TBCommandField ShowDeleteButton="True" ShowEditButton="True" 
                    ShowHeaderNewButton="True" >
                </bee:TBCommandField>
                
                '省略
                
            </Columns>
        </bee:TBGridView>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx

]]>
jeff377 2008-10-28 13:53:32
[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態 https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron GridV...]]> GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項) 一文中,示範了擴展 GridView 及 FormView 控制項,讓 GridView 可以透過屬性與 FormView 做關連來處理資料的「新增/修改/刪除」的動作。因為在該案例中,只使用 FormView 的 EditTemplate 同時處理「新增」及「修改」的動作,所以還需要自行撰寫部分程式碼去判斷控制項在新增或修改的啟用狀態,例如編號欄位在新增時為啟用,修改時就不啟用。在該文最後也提及其實有辨法讓這個案例達到零程式碼的目標,那就是讓控制項 (如 TextBox) 自行判斷所在的 FormView 的 CurrentMode,自行決定本身是否要「啟用/不啟用」、「顯示/隱藏」等狀態。本文以 TextBox 為例,說明如何修改 TextBox 讓它可以達到上述的需求。
程式碼下載:ASP.NET Server Control - Day27.rar
Northwnd 資料庫下載:NORTHWND.rar

一、TBFormViewModeState 類別

我們先定義 EControlState (控制項狀態) 列舉,描述控制項在特定模式的狀態為何。

    ''' <summary>
    ''' 控制項狀態列舉。
    ''' </summary>
    Public Enum EControlState
        ''' <summary>
        ''' 不設定。
        ''' </summary>
        NotSet = 0
        ''' <summary>
        ''' 啟用。
        ''' </summary>
        Enable = 1
        ''' <summary>
        ''' 不啟用。
        ''' </summary>
        Disable = 2
        ''' <summary>
        ''' 隱藏。
        ''' </summary>
        Hide = 3
    End Enum

再來定義 TBFormViewModeState 類別,用來設定控制項在各種 FormView 模式 (瀏覽、新增、修改) 中的控制項狀態。

''' <summary>
''' 依 FormViewMode 來設定控制項狀態。
''' </summary>
< _
Serializable(), _
TypeConverter(GetType(ExpandableObjectConverter)) _
> _
Public Class TBFormViewModeState
    Private FInsertMode As EControlState = EControlState.NotSet
    Private FEditMode As EControlState = EControlState.NotSet
    Private FBrowseMode As EControlState = EControlState.NotSet

    ''' <summary>
    ''' 在新增模式(FormViewMode=Insert)的控制項狀態。
    ''' </summary>
    < _
    NotifyParentProperty(True), _
    DefaultValue(GetType(EControlState), "NotSet") _
    > _
    Public Property InsertMode() As EControlState
        Get
            Return FInsertMode
        End Get
        Set(ByVal value As EControlState)
            FInsertMode = value
        End Set
    End Property

    ''' <summary>
    ''' 在編輯模式(FormViewMode=Edit)的控制項狀態。
    ''' </summary>
    < _
    NotifyParentProperty(True), _
    DefaultValue(GetType(EControlState), "NotSet") _
    > _
    Public Property EditMode() As EControlState
        Get
            Return FEditMode
        End Get
        Set(ByVal value As EControlState)
            FEditMode = value
        End Set
    End Property

    ''' <summary>
    ''' 在瀏覽模式(FormViewMode=ReadOnly)的控制項狀態。
    ''' </summary>
    < _
    NotifyParentProperty(True), _
    DefaultValue(GetType(EControlState), "NotSet") _
    > _
    Public Property BrowseMode() As EControlState
        Get
            Return FBrowseMode
        End Get
        Set(ByVal value As EControlState)
            FBrowseMode = value
        End Set
    End Property
End Class

定義為 TBFormViewModeState 型別的屬性是屬於複雜屬性,要套用 TypeConverter(GetType(ExpandableObjectConverter)),讓該屬性可在屬性視窗 (PropertyGrid) 擴展以便設定屬性值,如下圖所示。

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx

]]> jeff377 2008-10-28 13:45:43
[ASP.NET 控制項實作 Day26] 讓你的 GridView 與眾不同 https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron 在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文...]]> 在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文將這些關於擴展 GridView 控制項功能及欄位類別的相關文章做一整理簡介,若需要擴展 GridView 相關功能時可以做為參考。
1. 擴展 GridView 控制項 - 無資料時顯示標題列
摘要:當 GridView 繫結的 DataSource 資料筆數為 0 時,會依 EmptyDataTemplate 及 EmptyDataText 的設定來顯示無資料的狀態。若我們希望 GridView 在無資料時,可以顯示欄位標題,有一種作法是在 EmptyDataTemplate 中手動在設定一個標題列,不過這種作法很麻煩。本文擴展 GridView 控制項,直接透過屬性設定就可以在無資料顯示欄位標題。

2. 擴展 GridView 控制項 - 支援 Excel 及 Word 匯出
摘要:GridView 匯出 Excel 及 Word 文件是蠻常使用的需求,此篇文章將擴展 GridView 控制項提供匯出 Excel 及 Word 文件的方法。一般在 GridView 匯出的常見下列問題也會在此一併被解決。

3. GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項)
摘要:擴展 GridView 及 FormView 控制項,在 GridView 控制項中新增 FormViewID 屬性,關連至指定的 FormView 控制項 ID,就可以讓 GridView 結合 FormView 來做資料異動的動作。

4. 擴展 CommandField 類別 - 刪除提示訊息
摘要:新增 DeleteConfirmMessage 屬性,設定刪除提示確認訊息。

5. 擴展 CommandField 類別 - 刪除提示訊息含欄位值
摘要:設定刪除提示確認訊息中可包含指定 DataField 欄位值,明確提示要刪除的資料列。

6. 讓 CheckBoxField 繫結非布林值(0 或 1)欄位
摘要:CheckBoxField 若繫結的欄位值為 0 或 1 時 (非布林值) 會發生錯誤,本文擴展 CheckBoxField 類別,讓 CheckBoxField 有辨法繫結 0 或 1 的欄位值。

7. 擴展 CheckBoxField 類別 - 支援非布林值的雙向繫結
摘要:CheckBoxField 繫結的欄位值並無法直接使用 CBool 轉型為布林值,例如 "T/F"、"是/否" 之類的資料,若希望使用 CheckBoxField 來顯示就比較麻煩,一般的作法都是轉為 TemplateField,自行撰寫資料繫結的函式,而且只能支援單向繫結。在本文直接改寫 CheckBoxField 類別,讓 CheckBoxField 可以直接雙向繫結 "T/F" 或 "是/否" 之類的資料。

8. 擴展 CommandField 類別 - Header 加入新增鈕
摘要:支援在 CommandField 的 Header 的部分加入「新增」鈕,執行新增鈕會引發 RowCommand 事件。

9. GridView 自動編號欄位 - TBSerialNumberField
摘要:繼承 DataControlField 來撰寫自動編號欄位,若 GridView 需要自動編號欄位時只需加入欄位即可。

10. 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別
摘要:支援在 GridView 中顯示下拉清單的欄位類別。

11. 自訂 GridView 欄位 - 日期欄位
摘要:支援在 GridView 中顯示日期下拉選單編輯的欄位類別。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/27/5793.aspx

]]>
jeff377 2008-10-27 22:37:14
[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位(續) https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron 接續上一文
四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值
當用戶端使用 GridView 編輯後執...]]>
接續上一文
四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值
當用戶端使用 GridView 編輯後執行更新動作時,會呼叫 ExtractValuesFromCell 方法,來取得儲存格的欄位值,以便寫入資料來源。所以我們要覆寫 ExtractValuesFromCell 方法,將 Cell 或 TDateEdit 控制項的值取出填入具 IOrderedDictionary 介面的物件。

        ''' <summary>
        ''' 使用指定 DataControlFieldCell 的值填入指定的 IDictionary 物件。 
        ''' </summary>
        ''' <param name="Dictionary">用於儲存指定儲存格的值。</param>
        ''' <param name="Cell">包含要擷取值的儲存格。</param>
        ''' <param name="RowState">資料列的狀態。</param>
        ''' <param name="IncludeReadOnly">true 表示包含唯讀欄位的值,否則為 false。</param>
        Public Overrides Sub ExtractValuesFromCell( _
            ByVal Dictionary As IOrderedDictionary, _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState, _
            ByVal IncludeReadOnly As Boolean)

            Dim oControl As Control = Nothing
            Dim sDataField As String = Me.DataField
            Dim oValue As Object = Nothing
            Dim sNullDisplayText As String = Me.NullDisplayText
            Dim oDateEdit As TBDateEdit

            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then
                If (Cell.Controls.Count > 0) Then
                    oControl = Cell.Controls.Item(0)
                    oDateEdit = TryCast(oControl, TBDateEdit)
                    If (Not oDateEdit Is Nothing) Then
                        oValue = oDateEdit.Text
                    End If
                ElseIf IncludeReadOnly Then
                    Dim s As String = Cell.Text
                    If (s = " ") Then
                        oValue = String.Empty
                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then
                        oValue = HttpUtility.HtmlDecode(s)
                    Else
                        oValue = s
                    End If
                End If

                If (Not oValue Is Nothing) Then
                    If TypeOf oValue Is String Then
                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then
                            oValue = Nothing
                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length > 0) Then
                            oValue = Nothing
                        End If
                    End If

                    If Dictionary.Contains(sDataField) Then
                        Dictionary.Item(sDataField) = oValue
                    Else
                        Dictionary.Add(sDataField, oValue)
                    End If
                End If
            End If
        End Sub

五、測試程式
我們使用 Northwnd 資料庫的 Employees 資料表為例,在 GridView 加入自訂的 TBDateField 欄位繫結 BirthDate 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 BirthDate 欄位來做比較。

            <bee:TBDateField DataField="BirthDate" HeaderText="BirthDate" 
                SortExpression="BirthDate" DataFormatString="{0:d}" 
                ApplyFormatInEditMode="True" CalendarStyle="Winter" />            
            <asp:BoundField DataField="BirthDate" HeaderText="BirthDate" 
                SortExpression="BirthDate" DataFormatString="{0:d}" 
                ApplyFormatInEditMode="True" ReadOnly="true" />

執行程式,在編輯資料列時,TBDateField 就會以 TDateEdit 控制項來進行編輯。

使用 TDateEdit 編輯欄位值後,按「更新」鈕,資料就會被寫回資料庫。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx

]]>
jeff377 2008-10-26 17:04:50
[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位 https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron 前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中...]]> 前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中輸入日期也常蠻常見的需求,在本文將再實作一個 GridView 使用的日期欄位,在欄位儲存格使用 TBDateEdit 控制項來編輯資料。
程式碼下載:ASP.NET Server Control - Day25.rar
Northwnd 資料庫下載:NORTHWND.rar

一、繼承 TBBaseBoundField 實作 TDateField
GridView 的日期欄位需要繫結資料,一般的作法是由 BoundField 繼承下來改寫;不過我們之前已經有繼承 BoundField 製作一個 TBBaseBoundField 的自訂欄位基底類別 (詳見「 [ASP.NET 控制項實作 Day23] 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別」 一文),所以我們要實作的日期欄位直接繼承 TBBaseBoundField 命名為 TDateField,並覆寫 CreateField 方法,傳回 TDateField 物件。

    ''' <summary>
    ''' 日期欄位。
    ''' </summary>
    Public Class TBDateField
        Inherits TBBaseBoundField

        Protected Overrides Function CreateField() As DataControlField
            Return New TBDateField()
        End Function
    End Class

自訂欄位類別主要是要覆寫 InitializeDataCell 方法做資料儲存格初始化、覆寫 OnDataBindField 方法將欄位值繫結至 BoundField 物件、覆寫 ExtractValuesFromCell 方法來擷取儲存格的欄位值,下面我們將針對這幾個需要覆寫的方法做一說明。

二、覆寫 InitializeDataCell 方法 - 資料儲存格初始化
首先覆寫 InitializeDataCell 方法處理資料儲存格初始化,當唯讀狀態時使用 Cell 來呈現資料;若為編輯狀態時,則在 Cell 中加入 TBDateEdit 控制項,並將 TBDateField 的屬性設定給 TBDateEdit 控制項的相關屬性。然後將儲存格 (DataControlFieldCell) 或日期控制項 (TDateEdit) 的 DataBinding 事件導向 OnDataBindField 事件處理方法。

        ''' <summary>
        ''' 資料儲存格初始化。
        ''' </summary>
        ''' <param name="Cell">要初始化的儲存格。</param>
        ''' <param name="RowState">資料列狀態。</param>
        Protected Overrides Sub InitializeDataCell(ByVal Cell As DataControlFieldCell, ByVal RowState As DataControlRowState)
            Dim oDateEdit As TBDateEdit
            Dim oControl As Control

            If Me.CellIsEdit(RowState) Then
                '編輯狀態在儲存格加入 TBDateEdit 控制項
                oDateEdit = New TBDateEdit()
                oDateEdit.FirstDayOfWeek = Me.FirstDayOfWeek
                oDateEdit.ShowWeekNumbers = Me.ShowWeekNumbers
                oDateEdit.CalendarStyle = Me.CalendarStyle
                oDateEdit.Lang = Me.Lang
                oDateEdit.ShowTime = Me.ShowTime
                oControl = oDateEdit
                Cell.Controls.Add(oControl)
            Else
                oControl = Cell
            End If

            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then
                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)
            End If
        End Sub

TDateEdit 控制項為筆者自行撰寫的日期控制項,TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明。
日期控制項實作教學(1) - 結合 JavaScript
日期控制項實作教學(2) - PostBack 與 事件
TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)

三、覆寫 OnDataBindField 方法 - 將欄位值繫結至 BoundField 物件
當 GridView 執行 DataBind 時,每個儲存格的 DataBinding 事件都會被導向 OnDataBindField 方法,此方法中我們會由資料來源取得指定欄位值,處理此欄位值的格式化時,將欄位值呈現在 Cell 或 TDateEdit 控制項上。

        ''' <summary>
        ''' 將欄位值繫結至 BoundField 物件。 
        ''' </summary>
        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)
            Dim oControl As Control
            Dim oDateEdit As TBDateEdit
            Dim oNamingContainer As Control
            Dim oDataValue As Object            '欄位值
            Dim bEncode As Boolean              '是否編碼
            Dim sText As String                 '格式化字串

            oControl = DirectCast(sender, Control)
            oNamingContainer = oControl.NamingContainer
            oDataValue = Me.GetValue(oNamingContainer)
            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)
            sText = Me.FormatDataValue(oDataValue, bEncode)

            If TypeOf oControl Is TableCell Then
                If (sText.Length = 0) Then
                    sText = " "
                End If
                DirectCast(oControl, TableCell).Text = sText
            Else
                If Not TypeOf oControl Is TBDateEdit Then
                    Throw New HttpException(String.Format("{0}: Wrong Control Type", Me.DataField))
                End If

                oDateEdit = DirectCast(oControl, TBDateEdit)
                If Me.ApplyFormatInEditMode Then
                    oDateEdit.Text = sText
                ElseIf (Not oDataValue Is Nothing) Then
                    oDateEdit.Text = oDataValue.ToString
                End If
            End If
        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx

]]>
jeff377 2008-10-26 16:56:36
[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結(續) https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron 接續上一文
三、由關連的資料來源擷取資料
再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員...]]>
接續上一文
三、由關連的資料來源擷取資料
再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員清單內容。PerformSelect 方法的作用是去尋找頁面上的具 IDataSource 介面的控制項,並執行此資料來源的 Select 方法,以取得資料來設定 Items 的清單內容。
step1. 尋找資料來源控制項
PerformSelect 方法中有使用 FindControlEx 方法,它是自訂援尋控制項的多載方法,是取代 FindControl 進階方法。程式碼中使用 FindControlEx 去是頁面中以遞迴方式尋找具有 IDataSource 介面的控制項,且 ID 屬性值為 TBDropDownList.ID 的屬性值。
step2. 執行資料來源控制項的 Select 方法
當找到資料來源控制項後 (如 SqlDataSource、ObjectDataSource ...等等),執行其 DataSourceView.Select 方法,此方法需入一個 DataSourceViewSelectCallback 函式當作參數,當資料來源控制項取得資料後回呼我們指定的 OnDataSourceViewSelectCallback 函式中做後序處理。
step3. 將取得的資料來設定生 Items 的清單內容
在 OnDataSourceViewSelectCallback 函式中接到回傳的具 IEnumerable 介面的資料,有可能是 DataView、DataTable ...等型別的資料。利用 DataBinder.GetPropertyValue 來取得 DataTextField 及 DataValueField 設定的欄位值,逐一建立 ListItem 項目,並加入 Items 集合屬性中。

        ''' <summary>
        ''' 從關聯的資料來源擷取資料。
        ''' </summary>
        Private Sub PerformSelect()
            Dim oControl As Control
            Dim oDataSource As IDataSource
            Dim oDataSourceView As DataSourceView

            '若未設定 DataSourceID 屬性則離開
            If StrIsEmpty(Me.DataSourceID) Then Exit Sub
            '找到具 IDataSource 介面的控制項
            oControl = FindControlEx(Me.Control.Page, GetType(IDataSource), "ID", Me.DataSourceID)
            If oControl Is Nothing Then Exit Sub

            oDataSource = DirectCast(oControl, IDataSource)
            oDataSourceView = oDataSource.GetView(String.Empty)
            oDataSourceView.Select(DataSourceSelectArguments.Empty, _
                        New DataSourceViewSelectCallback(AddressOf Me.OnDataSourceViewSelectCallback))
        End Sub

        ''' <summary>
        ''' 擷取資料的回呼函式。
        ''' </summary>
        ''' <param name="data">取得的資料。</param>
        Private Sub OnDataSourceViewSelectCallback(ByVal data As IEnumerable)
            Dim oCollection As ICollection
            Dim oValue As Object
            Dim oItem As ListItem

            Me.Items.Clear()
            If data Is Nothing Then Exit Sub

            oCollection = TryCast(data, ICollection)
            Me.Items.Capacity = oCollection.Count

            For Each oValue In data
                oItem = New ListItem()
                If StrIsNotEmpty(Me.DataTextField) Then
                    oItem.Text = DataBinder.GetPropertyValue(oValue, DataTextField, Nothing)
                End If
                If StrIsNotEmpty(Me.DataValueField) Then
                    oItem.Value = DataBinder.GetPropertyValue(oValue, DataValueField, Nothing)
                End If
                Me.Items.Add(oItem)
            Next
        End Sub

四、測試程式
使用上篇中同一個案例做測試,同樣以 Northwnd 資料庫的 Products 資料表為例。在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,並設定 DataSourceID、DataTextField、DataValueField 屬性;另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。

                <bee:TBDropDownField  HeaderText="CategoryID"  
                    SortExpression="CategoryID" DataField="CategoryID" 
                    DataTextField="CategoryName" DataValueField="CategoryID" 

DataSourceID="SqlDataSource2">
                </bee:TBDropDownField>
                <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
                    SortExpression="CategoryID"  ReadOnly="true" />

執行程式,在 GridView 瀏覽的模式時,TBDropDownField 的儲存格已經會呈現 Items 對應成員的顯示文字。

執行資料列編輯時,也可以正常顯示下拉清單的內容。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx

]]>
jeff377 2008-10-25 18:11:28
[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結 https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron 上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDro...]]> 上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDropDownList 控制項相關屬性 (DataSourceID、DataTextField、DataValueField 屬性) 後,就由 TDropDownList 控制項自行處理 Items 屬性的資料繫結。當 GridView 的資料列是編輯狀態時,下拉清單會顯示出 Items 的文字內容;可是瀏覽狀態的資料列,卻是顯示欄位原始值,無法呈現 Items 的文字內容。本文將說明如何自行處理 TBDropDownField 的 Items 屬性的資料繫結動作,並使唯讀狀態的資料列也可以呈現 Items 的文字內容。

程式碼下載:ASP.NET Server Control - Day24.rar
Northwnd 資料庫下載:NORTHWND.rar

一、Items 屬性的問題
我們重新看一次原本 TBDropDownField 類別在處理 Items 屬性的資料繫結取得清單內容的程式碼,在覆寫 InitializeDataCell 方法中,當儲存格為編輯模式時,會呈現 TBDropDownList 控制項並設定取得 Items 清單內容的相關屬性,讓 TBDropDownList 自行去處理它的 Items 屬性的清單內容。

'由資料來源控制項取得清單項目
oDropDownList.DataSourceID = Me.DataSourceID
oDropDownList.DataTextField = Me.DataTextField
oDropDownList.DataValueField = Me.DataValueField

不知你有沒有發覺,我們無論在 InitializeDataCell 及 OnDataBindField 方法中,都沒有針對 TBDropDownList 控制項做任何 DataBind 動作,那它是怎麼從 DataSourceID 關聯的資料來源擷取資料呢?因為 GridView 在執行 DataBind 時,就會要求所有的子控制項做 DataBind,所以我們只要設定好 BDropDownList 控制項相關屬性後,當 TBDropDownList 自動被要求資料繫結時就會取得 Items 的清單內容。
當然使用 TBDropDownList 控制項去處理 Items 的資料繫結動作最簡單,可是這樣唯讀的儲存格只能顯示原始欄位值,無法呈現 Items 中對應成員的文字;除非無論唯讀或編輯狀態,都要建立 TBDropDownList 控制項去取得 Items 清單內容,而唯讀欄位使用 TBDropDownList.Items 去找到對應成員的顯示文字,不過這樣的作法會怪怪的,而且沒有執行效能率。所以比較好的辨法,就是由 TBDropDownField 類別自行處理 Items 的資料繫結,同時提供給唯讀狀態的
DataControlFieldCell 及編輯狀態的 TBDropDownList 使用。

二、由 TBDropDownField 類別處理 Items 屬性的資料繫結
我們要自行處理 Items 屬性來取得成員清單,在 InitializeDataCell 方法中無須處理 Items 屬性,只需產生儲存格需要的子控制項,未來在執行子控制項的 DataBinding 時的 OnDataBindField 方法中再來處理 Items 屬性。

        Protected Overrides Sub InitializeDataCell( _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState)

            Dim oDropDownList As TBDropDownList
            Dim oControl As Control

            If Me.CellIsEdit(RowState) Then
                oDropDownList = New TBDropDownList()
                oControl = oDropDownList
                Cell.Controls.Add(oControl)
            Else
                oControl = Cell
            End If

            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then
                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)
            End If
        End Sub

在 OnDataBindField 方法中,我們加上一段處理 Items 屬性的程式碼如下,會利用 PerformSelecrt 私有方法,由關聯的資料來源 (即 DataSrouceID 指定的資料來源控制項) 擷取資料並產生 Items 的成員清單,在後面會詳細講解 PerformSelecrt 方法處理擷取資料的細節。因為 TBDropDownField 每個資料儲存格都會執行 OnDataBindField 方法,但 Items 取得成員清單的動作只需做一次即可,所以會以 FIsPerformSelect 區域變數來判斷是否已取得 Items 的成員清單,若已取過就不重新取得,這樣比較有執行效能。

            If Not Me.DesignMode Then
                If Not FIsPerformSelect Then
                    '從關聯的資料來源擷取資料
                    PerformSelect()
                    FIsPerformSelect = True
                End If
            End If

當取得儲存儲的對應的欄位值時,依此欄位值由 Items 集合去取得對應的 ListItem 成員,並以此 ListItem.Text 的文字內容來做顯示。

            '由 Items 去取得對應成員的顯示內容
            oListItem = Me.Items.FindByValue(CCStr(sText))
            If oListItem IsNot Nothing Then
                sText = oListItem.Text
            End If

若是由 TBDropDownList 所引發的 OnDataBindField 方法時,使用 SetItems 私有方法將 TBDropDownField.Items 屬性複製給 TBDropDownList.Item 屬性。

                ODropDownList = DirectCast(oControl, TBDropDownList)
                SetItems(ODropDownList)

SetItems 私有方法的程式碼如下。

        Private Sub SetItems(ByVal DropDownList As TBDropDownList)
            Dim oItems() As ListItem

            If Not Me.DesignMode Then
                ReDim oItems(Me.Items.Count - 1)
                Me.Items.CopyTo(oItems, 0)
                DropDownList.Items.AddRange(oItems)
            End If
        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx

]]>
jeff377 2008-10-25 18:09:12
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續3) https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron 接續上一文
四、測試程式
辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料...]]>
接續上一文
四、測試程式
辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料庫的 Products 資料表為例,將 TBDropDownList .DataField 設為 CategoryID 欄位來做測試。首先我們測試沒有 DataSoruceID 的情況,在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。

                <bee:TBDropDownField  HeaderText="CategoryID"  
                    SortExpression="CategoryID" DataField="CategoryID" >
                    <Items>
                    <asp:ListItem Value="">未對應</asp:ListItem>
                    <asp:ListItem Value="2">Condiments</asp:ListItem>
                    <asp:ListItem Value="3">Confections</asp:ListItem>
                    </Items>
                </bee:TBDropDownField>
                <asp:BoundField DataField="CategoryID" HeaderText="CategoryID" 
                    SortExpression="CategoryID"  ReadOnly="true" />

執行程式,在 GridView 在唯讀模式,TBDropDownFIeld 可以正確的繫結 CategoryID 欄位值。

編輯某筆資料列進入編輯狀態,就會顯示 TBDropDownList 控制項,清單成員為我們在 Items 設定的內容。

使用 TBDropDownList 來做編輯欄位值,按下更新鈕,這時會執行 TBDropDownField.ExtractValuesFromCell 方法,取得儲存格中的值;最後由資料來源控制項將欄位值寫回資料庫。

接下來測試設定 TBDropDownField.DataSourceID 的情況,把 DataSourcID 指向含 Categories 資料表內容的 SqlDataSoruce 控制項。

                <bee:TBDropDownField  HeaderText="CategoryID"  
                    SortExpression="CategoryID" DataField="CategoryID" 
                    DataTextField="CategoryName" DataValueField="CategoryID" DataSourceID="SqlDataSource2">
                </bee:TBDropDownField>

執行程式查看結果,可以發現 TBDropDownList 控制項的清單內容也可以正常顯示 SqlDataSoruce 控制項取得資料。

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:32:30
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續2) https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron 接續上一文
step4. 處理資料繫結
當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事...]]>
接續上一文
step4. 處理資料繫結
當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事件,而這些事件會被導向 OnDataBindField 方法來統一處理儲存格中控制項的繫結動作。

       ''' <summary>
        ''' 將欄位值繫結至 BoundField 物件。 
        ''' </summary>
        ''' <param name="sender">控制項。</param>
        ''' <param name="e">事件引數。</param>
        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)
            Dim oControl As Control
            Dim ODropDownList As TBDropDownList
            Dim oNamingContainer As Control
            Dim oDataValue As Object            '欄位值
            Dim bEncode As Boolean              '是否編碼
            Dim sText As String                 '格式化字串

            oControl = DirectCast(sender, Control)
            oNamingContainer = oControl.NamingContainer
            oDataValue = Me.GetValue(oNamingContainer)
            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)
            sText = Me.FormatDataValue(oDataValue, bEncode)

            If TypeOf oControl Is TableCell Then
                If (sText.Length = 0) Then
                    sText = " "
                End If
                DirectCast(oControl, TableCell).Text = sText
            Else
                If Not TypeOf oControl Is TBDropDownList Then
                    Throw New HttpException(String.Format("{0}: Wrong Control Type", Me.DataField))
                End If

                ODropDownList = DirectCast(oControl, TBDropDownList)

                If Me.ApplyFormatInEditMode Then
                    ODropDownList.Text = sText
                ElseIf (Not oDataValue Is Nothing) Then
                    ODropDownList.Text = oDataValue.ToString
                End If
            End If
        End Sub

step5. 取得儲存格中的值
另外我們還需要覆寫 ExtractValuesFromCell 方法,取得儲存格中的值。這個方法是當 GridView 的編輯資料要準備寫入資料庫時,會經由 ExtractValuesFromCell 方法此來取得每個儲存格的值,並將這些欄位值加入 Dictionary 參數中,這個準備寫入的欄位值集合,可以在 DataSource 控制項的寫入資料庫的相關方法中取得使用。

        ''' <summary>
        ''' 使用指定 DataControlFieldCell 物件的值填入指定的 System.Collections.IDictionary 物件。 
        ''' </summary>
        ''' <param name="Dictionary">用於儲存指定儲存格的值。</param>
        ''' <param name="Cell">包含要擷取值的儲存格。</param>
        ''' <param name="RowState">資料列的狀態。</param>
        ''' <param name="IncludeReadOnly">true 表示包含唯讀欄位的值,否則為 false。</param>
        Public Overrides Sub ExtractValuesFromCell( _
            ByVal Dictionary As IOrderedDictionary, _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState, _
            ByVal IncludeReadOnly As Boolean)

            Dim oControl As Control = Nothing
            Dim sDataField As String = Me.DataField
            Dim oValue As Object = Nothing
            Dim sNullDisplayText As String = Me.NullDisplayText
            Dim oDropDownList As TBDropDownList

            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then
                If (Cell.Controls.Count > 0) Then
                    oControl = Cell.Controls.Item(0)
                    oDropDownList = TryCast(oControl, TBDropDownList)
                    If (Not oDropDownList Is Nothing) Then
                        oValue = oDropDownList.Text
                    End If
                ElseIf IncludeReadOnly Then
                    Dim s As String = Cell.Text
                    If (s = " ") Then
                        oValue = String.Empty
                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then
                        oValue = HttpUtility.HtmlDecode(s)
                    Else
                        oValue = s
                    End If
                End If

                If (Not oValue Is Nothing) Then
                    If TypeOf oValue Is String Then
                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then
                            oValue = Nothing
                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length > 0) Then
                            oValue = Nothing
                        End If
                    End If

                    If Dictionary.Contains(sDataField) Then
                        Dictionary.Item(sDataField) = oValue
                    Else
                        Dictionary.Add(sDataField, oValue)
                    End If
                End If
            End If
        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:31:32
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續1) https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron 接續上一文
step2. 加入 TBBaseBoundField 的屬性
TBBaseBoundField 類別會內含 DropDown...]]>
接續上一文
step2. 加入 TBBaseBoundField 的屬性
TBBaseBoundField 類別會內含 DropDownList 控制項,所以加入設定 DropDownList 控制項的對應屬性;我們在 TBBaseBoundField 類別加入了 Items 、DataSourceID、DataTextField、DataValueField 屬性。其中 Items 屬性的型別與 DropDownList.Items 屬性相同,都是 ListItemCollection 集合類別,且 Items 屬性會儲存於 ViewState 中。

        ''' <summary>
        ''' 清單項目集合。
        ''' </summary>
        < _
        Description("清單項目集合。"), _
        DefaultValue(CStr(Nothing)), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        Editor(GetType(ListItemsCollectionEditor), GetType(UITypeEditor)), _
        MergableProperty(False), _
        Category("Default")> _
        Public Overridable ReadOnly Property Items() As ListItemCollection
            Get
                If (FItems Is Nothing) Then
                    FItems = New ListItemCollection()
                    If MyBase.IsTrackingViewState Then
                        CType(FItems, IStateManager).TrackViewState()
                    End If
                End If
                Return FItems
            End Get
        End Property

        ''' <summary>
        ''' 資料來源控制項的 ID 屬性。
        ''' </summary>
        Public Property DataSourceID() As String
            Get
                Return FDataSourceID
            End Get
            Set(ByVal value As String)
                FDataSourceID = value
            End Set
        End Property

        ''' <summary>
        ''' 提供清單項目文字內容的資料來源的欄位。
        ''' </summary>
        < _
        Description("提供清單項目文字內容的資料來源的欄位。"), _
        DefaultValue("") _
        > _
        Public Property DataTextField() As String
            Get
                Return FDataTextField
            End Get
            Set(ByVal value As String)
                FDataTextField = value
            End Set
        End Property

        ''' <summary>
        ''' 提供清單項目值的資料來源的欄位。
        ''' </summary>
        Public Property DataValueField() As String
            Get
                Return FDataValueField
            End Get
            Set(ByVal value As String)
                FDataValueField = value
            End Set
        End Property

step3.建立儲存格內含的控制項
GridView 是以儲存格 (DataControlFieldCell) 為單位,我們要覆寫 InitializeDataCell 方法來建立儲存格中的控制項;當儲存格為可編輯狀態時,就建立 DropDownList 控制項並加入儲存格中,在此使用上篇文章提及的 TBDropDownList 控制項來取代,以解決清單成員不存在造成錯誤的問題。若未設定 DataSourceID 屬性時,則由 Items 屬性取得自訂的清單項目;若有設定 DataSourceID 屬性,則由資料來源控制項 (如 SqlDataSource、ObjectDataSource 控制項) 來取得清單項目。
當建立儲存格中的控制項後,需要以 AddHeadler 的方法,將此控制項的 DataBinding 事件導向 OnDataBindField 這個事件處理方法,我們要在 OnDataBindField 處理資料繫結的動作。

        ''' <summary>
        ''' 資料儲存格初始化。
        ''' </summary>
        ''' <param name="Cell">要初始化的儲存格。</param>
        ''' <param name="RowState">資料列狀態。</param>
        Protected Overrides Sub InitializeDataCell( _
            ByVal Cell As DataControlFieldCell, _
            ByVal RowState As DataControlRowState)

            Dim oDropDownList As TBDropDownList
            Dim oItems() As ListItem
            Dim oControl As Control

            If Me.CellIsEdit(RowState) Then
                oDropDownList = New TBDropDownList()
                oControl = oDropDownList
                Cell.Controls.Add(oControl)

                If Not Me.DesignMode Then
                    If StrIsEmpty(Me.DataSourceID) Then
                        '自訂清單項目
                        ReDim oItems(Me.Items.Count - 1)
                        Me.Items.CopyTo(oItems, 0)
                        oDropDownList.Items.AddRange(oItems)
                    Else
                        '由資料來源控制項取得清單項目
                        oDropDownList.DataSourceID = Me.DataSourceID
                        oDropDownList.DataTextField = Me.DataTextField
                        oDropDownList.DataValueField = Me.DataValueField
                    End If
                End If
            Else
                oControl = Cell
            End If

            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then
                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)
            End If

        End Sub

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:25:02
[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位 https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField...]]> GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField ... 等不同型別的欄位,可是偏偏沒有提供在 GridView 中可呈現 DropDownList 的欄位型別;遇到這類需求時,一般的作法都是使用 TemplateField 來處理。雖然 TemplateField 具有相當好的設計彈性。可是在當 GridView 需要動態產生欄位的需求時,TemplateField 就相當麻煩,要寫一堆程式碼自行去處理資料繫結的動作。相互比較起來,BoundField、CheckBoxField ...等這類事先定義類型的欄位,在 GridView 要動態產生這些欄位就相當方便。如果我們可以把一些常用的 GridView 的欄位,都做成類似 BoundField 一樣,只要設定欄位的屬性就好,這樣使用上就會方便許多,所以在本文將以實作 DropDownList 欄位為例,讓大家了解如何去自訂 GridView 的欄位類別。
程式碼下載:ASP.NET Server Control - Day23.rar
Northwnd 資料庫下載:NORTHWND.rar

一、選擇合適的父類別
一般自訂 GridView 的欄位類別時,大都是由 DataControlField 或 BoundField 繼承下來改寫。若是欄位不需繫結資料(如 CommandFIeld),可以由 DataControlFIeld 繼承下來,若是欄位需要做資料繫結時(如 CheckBoxFIld,可以直接由 BoundField 繼承下來改寫比較方便。
DataControlField 類別是所有類型欄位的基底類別,BoundField 類別也是由 DataControlField 類別繼承下來擴展了資料繫結部分的功能,所以我們要實作含 DropDownList 的欄位,也是由 BoundField 繼承下來改寫。

二、自訂欄位基底類別
在此我們不直接繼承 BoundFIeld,而是先撰寫一個繼承 BoundField 命名為 TBBaseBoundField 的基底類別,此類別提供一些通用的屬性及方法,使我們更方便去撰寫自訂的欄位類別。

    ''' <summary>
    ''' 資料欄位基礎類別。
    ''' </summary>
    Public MustInherit Class TBBaseBoundField
        Inherits BoundField

        Private FRowIndex As Integer = 0

        ''' <summary>
        ''' 資料列是否為編輯模式。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Public Function RowStateIsEdit(ByVal RowState As DataControlRowState) As Boolean
            Return (RowState And DataControlRowState.Edit) <> DataControlRowState.Normal
        End Function

        ''' <summary>
        ''' 資料列是否為新增模式。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Public Function RowStateIsInsert(ByVal RowState As DataControlRowState) As Boolean
            Return (RowState And DataControlRowState.Insert) <> DataControlRowState.Normal
        End Function

        ''' <summary>
        ''' 資料列是否為編輯或新增模式。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Public Function RowStateIsEditOrInsert(ByVal RowState As DataControlRowState) As Boolean
            Return RowStateIsEdit(RowState) OrElse RowStateIsInsert(RowState)
        End Function

        ''' <summary>
        ''' 判斷儲存格是否可編輯(新增/修改)。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Friend Function CellIsEdit(ByVal RowState As DataControlRowState) As Boolean
            Return (Not Me.ReadOnly) AndAlso RowStateIsEditOrInsert(RowState)
        End Function

        ''' <summary>
        ''' 資料列索引。
        ''' </summary>
        Friend ReadOnly Property RowIndex() As Integer
            Get
                Return FRowIndex
            End Get
        End Property

        ''' <summary>
        ''' 儲存格初始化。
        ''' </summary>
        ''' <param name="Cell">要初始化的儲存格。</param>
        ''' <param name="CellType">儲存格類型。</param>
        ''' <param name="RowState">資料列狀態。</param>
        ''' <param name="RowIndex">資料列之以零起始的索引。</param>
        Public Overrides Sub InitializeCell(ByVal Cell As DataControlFieldCell, ByVal CellType As DataControlCellType, _
            ByVal RowState As DataControlRowState, ByVal RowIndex As Integer)

            FRowIndex = RowIndex
            MyBase.InitializeCell(Cell, CellType, RowState, RowIndex)
        End Sub

        ''' <summary>
        ''' 是否需要執行資料繫結。
        ''' </summary>
        ''' <param name="RowState">資料列狀態。</param>
        Friend Function RequiresDataBinding(ByVal RowState As DataControlRowState) As Boolean
            If MyBase.Visible AndAlso StrIsNotEmpty(MyBase.DataField) AndAlso RowStateIsEdit(RowState) Then
                Return True
            Else
                Return False
            End If
        End Function
    End Class

三、實作 TBDropDownField 欄位類別
step1. 繼承 TBBaseBoundField 類別
首先新增一個類別,繼承 TBBaseBoundField 命名為 TBDropDownFIeld 類別,覆寫 CreateField 方法,傳回 TBDropDownFIeld 物件。

    Public Class TBDropDownField
        Inherits TBBaseBoundField

        Protected Overrides Function CreateField() As System.Web.UI.WebControls.DataControlField
            Return New TBDropDownField()
        End Function
    End Class

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx

]]>
jeff377 2008-10-24 00:19:12
[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤(續) https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron 接續上篇文章內容
三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題
要解決上述 TBDro...]]>
接續上篇文章內容
三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題
要解決上述 TBDropDownList 設定 DataSourceID 問題,需在設定 SelectedValue 屬性時,若 Items.Count=0 先用一個 FCachedSelectedValue 變數將正確的值先暫存下來,然後覆寫 PerformDataBinding 方法,當 DorpDownList 取得 DataSoruceID 所對應的項目清單內容後,因為這時 Items 的內容才會完整取回,再去設定一次 SelectedValue 屬性就可以正確的繫結資料。

    Public Class TBDropDownList
        Inherits DropDownList

        Private FCachedSelectedValue As String

        ''' <summary>
        ''' 覆寫 SelectedValue 屬性。
        ''' </summary>
        Public Overrides Property SelectedValue() As String
            Get
                Return MyBase.SelectedValue
            End Get
            Set(ByVal value As String)
                If Me.Items.Count <> 0 Then
                    Dim oItem As ListItem = Me.Items.FindByValue(value)
                    If (oItem Is Nothing) Then
                        Me.SelectedIndex = -1 '當 Items 不存在時 
                    Else
                        MyBase.SelectedValue = value
                    End If
                Else
                    FCachedSelectedValue = value
                End If
            End Set
        End Property

        Protected Overrides Sub PerformDataBinding(ByVal data As System.Collections.IEnumerable)
            MyBase.PerformDataBinding(data)

            'DataSoruceID 資料繫結後再設定 SelectedValue 屬性值
            If (Not FCachedSelectedValue Is Nothing) Then
                Me.SelectedValue = FCachedSelectedValue
            End If
        End Sub

    End Class

重新執行程式,切換到編輯模式時,TBDropDownList 就可以正確的繫結欄位值了。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-23 07:03:54
[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤 https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面...]]> DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面的程式碼也無法使用 Try ... Catch 方式來略過錯誤。其實最簡單的方式就去直接去修改 DropDownList 控制項,讓 DropDownList 控制項繫結資料時,就算欄位值不存在清單項目中也不要釋出錯誤,本文就要說明如何繼承 DorpDownList 下來修改,來有效解決這個問題。

程式碼下載:ASP.NET Server Control - Day22.rar
Northwnd 資料庫下載:NORTHWND.rar

一、覆寫 SelectedValue 屬性解決資料繫結的問題
DropDownList 控制項繫結錯誤的原因,可以由上圖的錯誤訊息可以大概得知是寫入 SelectedValue 屬性時發生的錯誤;所以我們繼承 DorpDownList 下來命名為 TBDropDownList,並覆寫 SelectedValue 屬性來解決這個問題。解決方式是在寫入 SelectedValue 屬性時,先判斷準備寫入的值是否存在項目清單中,存在的話才寫入 SelectedValue 屬性,若不存在則直接設定 SelectedIndex 屬性為 -1。

    Public Class TBDropDownList
        Inherits DropDownList

        ''' <summary>
        ''' 覆寫 SelectedValue 屬性。
        ''' </summary>
        Public Overrides Property SelectedValue() As String
            Get
                Return MyBase.SelectedValue
            End Get
            Set(ByVal value As String)
                Dim oItem As ListItem = Me.Items.FindByValue(value)
                If (oItem Is Nothing) Then
                    Me.SelectedIndex = -1 '當 Items 不存在時 
                    Exit Property
                Else
                    MyBase.SelectedValue = value
                End If
            End Set
        End Property

    End Class

我們以 Northwnd 資料庫的 Products 資料表做為測試資料,事先定義 DropDownList 的 Items 內容,其中第一個加入 "未對應" 的項目,將 SelectedValue 屬性繫結至 CategoryID 欄位。

                <bee:TBDropDownList ID="DropDownList1" runat="server" 
                    SelectedValue='<%# Bind("CategoryID") %>'>
                    <asp:ListItem Value="">未對應</asp:ListItem>
                    <asp:ListItem Value="2">Condiments</asp:ListItem>
                    <asp:ListItem Value="3">Confections</asp:ListItem>
                </bee:TBDropDownList>

當資料的 CategoryID 欄位值不存在於 DropDownList 的 Items 集合屬性中時,就會顯示第一個 "未對應" 的項目。

二、TBDropDownList 設定 DataSoruceID 產生的問題
上述的解決方法在筆者的「讓 DropDownList DataBind 不再發生錯誤」一文中已經有提及,不過有讀者發現另一個問題,就是當 DropDownList 設定 DataSourceID 時卻會發生資料無法正常繫結,以下就來解決這個問題。
我們設定 TBDropDownList 的 DataSoruceID 來取得項目清單的內容,將 DataSourceID 設定為另一個取得 Categories 資料表內容的 SqlDataSource 控制項。

                <bee:TBDropDownList ID="DropDownList1" runat="server" 
                    SelectedValue='<%# Bind("CategoryID") %>' DataSourceID="SqlDataSource2" 
                    DataTextField="CategoryName" DataValueField="CategoryID">
                </bee:TBDropDownList>
                <asp:SqlDataSource ID="SqlDataSource2" runat="server" 
                    ConnectionString="<%$ ConnectionStrings:Northwnd %>" 
                    SelectCommand="SELECT CategoryID, CategoryName, Description, Picture FROM Categories" 
                    ProviderName="<%$ ConnectionStrings:Northwnd.ProviderName %>" >
                </asp:SqlDataSource>

當執行程式時,FormView 原本在瀏覽模式時的 CategoryID 欄位值為 7 (CategoryName 應為 Product)。

當按下「編輯」時切換到 EditItemTemplate 時,改用 TBDropDownList 繫結 CategoryID 欄位值,可以這時欲無法繫結正確的值。

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-23 06:59:56
[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤(續) https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron 接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文
step2. 在智慧標籤面板加入屬性項目
DesignerA...]]>
接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文
step2. 在智慧標籤面板加入屬性項目
DesignerActionPropertyItem 類別是設定智慧標籤面上的屬性項目,DesignerActionPropertyItem 建構函式的第一個參數(memberName) 為屬性名稱,這個屬性指的是 TBDateEditActionList 類別中的屬性,所以要在 TBDateEditActionList 新增一個對應的屬性。
例如在智慧標籤中加入 AutoPostBack 屬性項目,則在 TBDateEditActionList 類別需有一個對應 AutoPostBack 屬性。

            oItems.Add(New DesignerActionPropertyItem("AutoPostBack", _
                "AutoPostBack", "Behavior", "是否引發 PostBack 動作。"))

TBDateEditActionList.AutoPostBack 屬性如下,其中 Me.Component 指的是目前的 TDateEdit 控制項,透過 GetPropertyValue 及 SetPropertyValue 方法來存取控制項的指定屬性。

        ''' <summary>
        ''' 是否引發 PostBack 動作。
        ''' </summary>
        Public Property AutoPostBack() As Boolean
            Get
                Return CType(GetPropertyValue(Me.Component, "AutoPostBack"), Boolean)
            End Get
            Set(ByVal value As Boolean)
                SetPropertyValue(Me.Component, "AutoPostBack", value)
            End Set
        End Property

    ''' <summary>
    ''' 設定物件的屬性值。
    ''' </summary>
    ''' <param name="Component">屬性值將要設定的物件。</param>
    ''' <param name="PropertyName">屬性名稱。</param>
    ''' <param name="Value">新值。</param>
    Public Shared Sub SetPropertyValue(ByVal Component As Object, ByVal PropertyName As String, ByVal Value As Object)
        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)
        Prop.SetValue(Component, Value)
    End Sub

    ''' <summary>
    ''' 取得物件的屬性值。
    ''' </summary>
    ''' <param name="Component">具有要擷取屬性的物件。</param>
    ''' <param name="PropertyName">屬性名稱。</param>
    Public Shared Function GetPropertyValue(ByVal Component As Object, ByVal PropertyName As String) As Object
        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)
        Return Prop.GetValue(Component)
    End Function

step3. 在智慧標籤面板加入方法項目
DesignerActionMethodItem 類別是設定智慧標籤面上的方法項目,DesignerActionPropertyItem 建構函式的第二個參數(memberName) 為方法名稱,這個方法指的是 TBDateEditActionList 類別中的方法,所以要在 TBDateEditActionList 新增一個對應的方法。
例如在智慧標籤中加入 About 方法項目,則在 TBDateEditActionList 類別需有一個對應 About 方法。

            oItems.Add(New DesignerActionMethodItem(Me, "About", _
                "關於 TDateEdit 控制項", "About", _
                "關於 TDateEdit 控制項。", True))

TBDateEditActionList 的 About 方法只是單純顯示一個訊息視窗,一般你可以在這方法加入任何想在設計階段處理的動作。例如自動產生 GridView 的欄位、在 FormView 加入控制項並自動排版,這些都可以在此實現的。

        Public Sub About()
            MsgBox("TDateEdit 是結合 The Coolest DHTML Calendar 日期選擇器實作的控制項")
        End Sub

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-22 18:02:28
[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron 控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯...]]> 控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯示的項目,在本文將以 TDateEdit 控制項為例,進一步說明控制項的智慧標籤的實作方式。

程式碼下載:ASP.NET Server Control - Day21.rar

一、TDateEdit 控制項介紹
TDateEdit 控制項是筆者之前在部落格中實作的一個日期控制項,如下圖所示。它是結合 JavaScript 的 The Coolest DHTML Calendar 日期選擇器實作的控制項,我已將 TDateEdit 控制項的相關程式碼含入 Bee.Web.dll 組件中。TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明,本文將以 TDateEdit 控制項為例,只針對實作智慧標籤的部分做進一步說明。
日期控制項實作教學(1) - 結合 JavaScript
日期控制項實作教學(2) - PostBack 與 事件
TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)

二、控制項加入智慧標籤
控制項要加入智慧標籤要實作控制項的 Designer,我們繼承 ControlDesigner 命名為 TBDateEditDesigner,然後覆寫 ActionLists 屬性,此屬性即是傳回智慧標籤中所包含的項目清單集合。在 ActionLists 屬性中一般會先加入父類別的 ActionLists 屬性,再加入自訂的 ActionList 類別,這樣才可以保留原父類別中智慧標籤的項目清單。

    ''' <summary>
    ''' TBDateEdit 控制項的設計模式行為。
    ''' </summary>
    Public Class TBDateEditDesigner
        Inherits System.Web.UI.Design.ControlDesigner

        ''' <summary>
        ''' 取得控制項設計工具的動作清單集合。
        ''' </summary>
        Public Overrides ReadOnly Property ActionLists() As DesignerActionListCollection
            Get
                Dim oActionLists As New DesignerActionListCollection()
                oActionLists.AddRange(MyBase.ActionLists)
                oActionLists.Add(New TBDateEditActionList(Me))
                Return oActionLists
            End Get
        End Property

    End Class

我們自訂的 ActionList 為 TBDateEditActionList 類別,它在智慧標籤呈現的項目清單如下圖所示,接下去我們會說明 TBDateEditActionList 類別的內容。

三、自訂智慧標籤面板的項目清單集合
DesignerActionList 類別定義用於建立智慧標籤面板的項目清單的基底類別,所以我們首先繼承 DesignerActionList 命名為 TBDateEditActionList。

    ''' <summary>
    ''' 定義 TBDateEdit 控制項智慧標籤面板的項目清單集合。
    ''' </summary>
    Public Class TBDateEditActionList
        Inherits DesignerActionList

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        Public Sub New(ByVal owner As ControlDesigner)
            MyBase.New(owner.Component)
        End Sub

    End Class

接下來要覆寫 GetSortedActionItems 方法,它會回傳 DesignerActionItemCollection 集合型別,此集合中會傳回要顯示在智慧標籤面板的項目清單集合,所以我們要在 DesignerActionItemCollection 集合中加入我們要呈現的項目清單內容。

        ''' <summary>
        ''' 傳回要顯示在智慧標籤面板的項目清單集合。
        ''' </summary>
        Public Overrides Function GetSortedActionItems() As System.ComponentModel.Design.DesignerActionItemCollection
            Dim oItems As New DesignerActionItemCollection()

            '在此加入智慧標籤面板的項目清單	           

            Return oItems
        End Function

step1. 在智慧標籤面板加入靜態標題項目
首先介紹 DesignerActionHeaderItem 類別,它是設定靜態標題項目,例如我們在 TDateEdit 的智慧標籤中加入「行為」、「外觀」二個標題項目,其中 DesignerActionHeaderItem 建構函式的 category 參數是群組名稱,我們可以將相關的項目歸類到同一個群組。

Dim oItems As New DesignerActionItemCollection()

oItems.Add(New DesignerActionHeaderItem("行為", "Behavior"))
oItems.Add(New DesignerActionHeaderItem("外觀", "Appearance"))

[超過字數限制,下一篇接續本文]

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx

]]>
jeff377 2008-10-22 18:01:29
[ASP.NET 控制項實作 Day20] 偵錯設計階段的程式碼 https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron 上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Des...]]> 上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Designer 相關類別,所以你在 Designer 類別中下的中斷點完全無效;當然不可能這樣寫程式碼而用感覺去偵錯,本文將告訴你如何去偵錯設計階段的程式碼。
一、設計階段程式碼的錯誤
如果撰寫 Designer、Editor、ActionList 等設計階段的程式碼,當這些設計階段的程式碼發生錯誤,可能會發生設計頁面中控制項的錯誤情形,如下圖所示。因為控制項專案本身非啟動專案,在測試網站的設計頁面若控制項發生異常時會直接釋出錯誤,無法偵錯設計階段的程式碼;若真得要偵錯誤設計階段的問題,就要使用另一個 VS2008 來偵錯。

二、設定起始外部程式
要偵錯控制項設計階段的程式碼,要先將控制項專案(Bee.Web)設定為啟時專案。然後設定控制項專案的「屬性」,在「偵錯」頁籤中的起始動作選擇「起始外部程式」,選擇 VS2008 的執行檔位置,預設為 C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe。

三、開始偵錯設計階段程式碼
step1. 控制項專案開始偵錯
在設計階要偵錯的程式碼下中斷點,在控制項專案按下 F5 開始偵錯,這時會啟動另一個新的 VS2008 執行檔。

step2. 在新的 VS2008 的工具箱加入控制項
在新的 VS2008 中新增一個測試網站,在工具箱按右鍵執行「選擇項目」開啟「選擇工具箱項目」視窗,然後按「瀏覽」鈕按選擇控制項組件(Bee.Web.dll),將要偵錯的控制項加入工具箱中。


step3. 將控制項拖曳至頁面做設計動作
在新的 VS2008 中,將控制項拖曳至頁面,就會開始執行設計階段的程式碼,特定的設計動作就會執行到相對的設計階段程式碼,當執行到之前下的中斷點時就可以開始偵錯了。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/21/5741.aspx

]]>
jeff377 2008-10-21 00:28:45
[ASP.NET 控制項實作 Day19] 控制項設計階段的外觀 https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron 有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁...]]> 有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁面是如何呈現出來呢?本文將針對控制項設計階段的外觀做進一步的說明。
程式碼下載:ASP.NET Server Control - Day19.rar
一、控制項設計階段的 HTML 碼
Web 伺服器控制項的設計模式行為都是透過 ControlDesigner 來處理,連設計階段時控制項的外觀也是如此;控制項在設計階段與執行執行時呈現的外觀不一定相同,當然大部分會儘量一致,使其能所見即所得。
控制項在設計階段的 HTML 碼是透 ControlDesigner.GetDesignTimeHtml 方法來處理,在 ControlDesigner.GetDesignTimeHtml 預設會執行控制項的 RenderControl 方法,所以大部分的情況下設計階段與執行階段輸出的 HTML 碼會相同。當控制項的 Visible=False 時,執行階段是完全不會輸出 HTML 碼,可是在設計階段時會特別將控制項設定 Visible=True,使控制項能完整呈現。

ControlDesigner.GetDesignTimeHtml 方法

Public Overridable Function GetDesignTimeHtml() As String
    Dim writer As New StringWriter(CultureInfo.InvariantCulture)
    Dim writer2 As New DesignTimeHtmlTextWriter(writer)
    Dim errorDesignTimeHtml As String = Nothing
    Dim flag As Boolean = False
    Dim visible As Boolean = True
    Dim viewControl As Control = Nothing
    Try 
        viewControl = Me.ViewControl
        visible = viewControl.Visible
        If Not visible Then
            viewControl.Visible = True
            flag = Not Me.UsePreviewControl
        End If
        viewControl.RenderControl(writer2)
        errorDesignTimeHtml = writer.ToString
    Catch exception As Exception
        errorDesignTimeHtml = Me.GetErrorDesignTimeHtml(exception)
    Finally
        If flag Then
            viewControl.Visible = visible
        End If
    End Try
    If ((Not errorDesignTimeHtml Is Nothing) AndAlso (errorDesignTimeHtml.Length <> 0)) Then
        Return errorDesignTimeHtml
    End If
    Return Me.GetEmptyDesignTimeHtml
End Function

二、自訂控制項的 Designer
以 TBToolbar 為例,若我們在 RenderContents 方法未針對 Items.Count=0 做輸出 HTML 的處理,會發現未設定 Items 屬性時,在設計頁面上完全看不到 TBToolbar 控制項;像這種控制項設計階段的 HTML 碼,就可以自訂控制項的 Designer 來處理。

繼承 ControlDesigner 命名為 TBToolbarDesigner,這個類別是用來擴充 TBToolbar 控制項的設計模式行為。我們可以覆寫 GetDesignTimeHtml 方法,處理設計階段表示控制項的 HTML 標記,此方法回傳的 HTML 原始碼就是控制項呈現在設計頁面的外觀。所以我們可以在 TBToolbar.Items.Count=0 時,輸出一段提示的 HTML 碼,這樣當 TBToolbar 未設定 Items 屬性時一樣可以在設計頁面上呈現控制項。

    ''' <summary>
    ''' 擴充 TBToolbar 控制項的設計模式行為。
    ''' </summary>
    Public Class TBToolbarDesigner
        Inherits System.Web.UI.Design.ControlDesigner

        ''' <summary>
        ''' 用來在設計階段表示控制項的 HTML 標記。
        ''' </summary>
        Public Overrides Function GetDesignTimeHtml() As String
            Dim sHTML As String
            Dim oControl As TBToolbar

            oControl = CType(ViewControl, TBToolbar)
            If oControl.Items.Count = 0 Then
                sHTML = "<div style=""background-color: #C0C0C0; border:solid 1px; width:200px"">請設定 Items 屬性</div>"
            Else
                sHTML = MyBase.GetDesignTimeHtml()
            End If
            Return sHTML
        End Function

    End Class

在 TBToolbar 控制項套用 DesignerAttribute 設定自訂的 TBToolbarDesigner 類別。

    <Designer(GetType(TBToolbarDesigner))> _
    Public Class TBToolbar
        Inherits WebControl

    End Class

重建控制項組件,切換到設計頁面上的看 TBToolbar 控制項未設定 Items 屬性時的外觀,就是我們在 TBToolbarDesigner.GetDesignTimeHtml 方法回傳的 HTML 碼。

如果你覺得上述設計階段的控制項有點太陽春,我們也可以輸出類似 SqlDataSource 控制項的外觀,將未設定 Items 屬性時輸出 HTML 改呼叫 CreatePlaceHolderDesignTimeHtml 方法。

            If oControl.Items.Count = 0 Then
                sHTML = MyBase.CreatePlaceHolderDesignTimeHtml("請設定 Items 屬性")
            Else
                sHTML = MyBase.GetDesignTimeHtml()
            End If

來看一下這樣修改後的結果,是不是比較專業一點了呢。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/20/5726.aspx

]]>
jeff377 2008-10-20 02:25:29
[ASP.NET 控制項實作 Day18] 修改集合屬性編輯器 https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron 上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合...]]> 上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合成員。是不是只能在 aspx 程式碼中手動去輸入呢?當然不需要這樣人工作業,只要改掉集合屬性編輯器就可以達到我們的需求,本文將介紹修改集合屬性編輯器的相關作法。
程式碼下載:ASP.NET Server Control - Day18.rar

一、自訂集合屬性編輯器
我們先看一下 TBToolbar.Items 屬性套用的 EditorAttribute,它是使用 CollectionEditor 類別來當作屬性編輯器,所以我們就是要繼承 CollectionEditor 類別下來修改成自訂的屬性編輯器。

< _
Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _
> _
Public ReadOnly Property Items() As TBToolbarItemCollection

新增一個繼承 CollectionEditor 的 TBToolbarItemCollectionEditor 類別,並加入建構函式。此類別屬於 Bee.WebControls.Design 命名空間,通常我們會把設計階段使用的類別歸類到特別的命名空間便於管理及使用。

Namespace WebControls.Design
    Public Class TBToolbarItemCollectionEditor
        Inherits CollectionEditor

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        ''' <param name="Type">型別。</param>
        Public Sub New(ByVal Type As Type)
            MyBase.New(Type)
        End Sub

    End Class
End Namespace

我們可以先修改 Items 屬性的 EditorAttribute,看看我們自訂的 TBToolbarItemCollectionEditor 是否能正常運作。不過這個屬性編輯器跟原本的沒什麼差異,因為我們只是單純繼承下來沒做任何異動,接下去我們就要開始來修改這個屬性編輯器。

< _
Editor(GetType(TBToolbarItemCollectionEditor), GetType(UITypeEditor)) _
> _
Public ReadOnly Property Items() As TBToolbarItemCollection

二、加入不同型別的集合成員
再來我們就要著手修改集合屬性編輯器,讓它可以加入不同型別的集合成員。覆寫 CollectionEditor 的 CanSelectMultipleInstances 方法傳回 True,這個方法是設定 CollectionEditor 是否允許加入多種不同型別的集合成員。

        Protected Overrides Function CanSelectMultipleInstances() As Boolean
            Return True
        End Function

再來覆寫 CreateNewItemTypes 方法,這個方法是取得這個集合編輯器可包含的資料型別,將集合可包含的資料型別以陣列傳回。

        ''' <summary>
        ''' 取得這個集合編輯器可包含的資料型別。
        ''' </summary>
        ''' <returns>這個集合可包含的資料型別陣列。</returns>
        Protected Overrides Function CreateNewItemTypes() As System.Type()
            Dim ItemTypes(2) As System.Type
            ItemTypes(0) = GetType(TBToolbarButton)
            ItemTypes(1) = GetType(TBToolbarTextbox)
            ItemTypes(2) = GetType(TBToolbarLabel)
            Return ItemTypes
        End Function

重建控制項組件,使用 Items 的集合屬性編輯器,就可以發現「加入」鈕的下拉清單就會出現我們所定義的三種型別的集合成員,如此可以加入不同型別的成員了。

三、設定清單項目的顯示文字
在成員清單項目中預設會顯示成員含命名空間的型別,若我們要修改成比較有識別的顯示文字,例如 TBToolbarButton(Key=Add) 可以顯示「按鈕-Add」,這時可以覆寫 GetDisplayText 方法來設定清單項目的顯示文字。

        ''' <summary>
        ''' 取出指定清單項目的顯示文字。
        ''' </summary>
        Protected Overrides Function GetDisplayText(ByVal value As Object) As String
            If TypeOf value Is TBToolbarButton Then
                Return String.Format("按鈕 - {0}", CType(value, TBToolbarButton).Key)
            ElseIf TypeOf value Is TBToolbarTextbox Then
                Return "文字框"
            ElseIf TypeOf value Is TBToolbarLabel Then
                Return String.Format("標籤 - {0}", CType(value, TBToolbarLabel).Text)
            Else
                Return value.GetType.Name
            End If
        End Function

四、集合編輯器的屬性視窗的屬性描述
一般屬性視窗下面都會有屬性描述,可以集合屬性編輯器中的屬性視窗下面竟沒有屬性描述。若我們要讓它的屬性描述可以顯示,可以覆寫 CreateCollectionForm 方法,取得集合屬性編輯表單,再去設定表單上的 PropertyGrid.HelpVisible
= True 即可。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/19/5721.aspx

]]>
jeff377 2008-10-19 00:13:21
[ASP.NET 控制項實作 Day17] 集合屬性包含不同型別的成員 https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron 我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。...]]> 我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。如果我們希望工具列中不只包含按鈕,可以包含其他不同類型的子控制項,那該怎麼做呢?本文就以上篇中的 TBToolbar 控制項為案例,讓 Items 集合屬性可以加入 Button、TextBox、Label ...等不同的子控制項。
程式碼下載:ASP.NET Server Control - Day17.rar
一、不同型別的集合成員
我們的需求是讓工具列可以加入 Button、TextBox、Label 三種子控制項,所以繼承原來的 TBToolbarItem (只保留 Enabled 屬性),新增了 TBToolbarButton、TBToolbarTextbox、TBToolbarLabel 三個類別。

這些新增的成員類別都是繼承至 TBToolbarItem,所以在 aspx 程式碼中,手動輸入 Items 的成員時,就會列出這幾種定義的成員型別。

二、建立不同型別集合成員的子控制項
因為 Items 屬性的成員具不同型別,所以我們要改寫 RenderContents 方法,判斷成員型別來建立對應類型的子控制項。若為 TBToolbarButton 型別建立 Button 控制項、若為 TBToolbarTextbox 型別則建立 TextBox 控制項、若為 TBToolbarLabel 型別則建立 Label 控制項。其中 TBToolbarButton 建立的控制項為 TBButton,這個控制項是我們在「 [ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能」一文中實作的具詢問訊息的按鈕控制項。

        ''' <summary>
        ''' 覆寫 RenderContents 方法。
        ''' </summary>
        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim oItem As TBToolbarItem
            Dim oControl As Control

            For Each oItem In Me.Items
                If TypeOf oItem Is TBToolbarButton Then
                    '建立 Button 控制項
                    oControl = CreateToolbarButton(CType(oItem, TBToolbarButton))
                ElseIf TypeOf oItem Is TBToolbarTextbox Then
                    '建立 Textbox 控制項
                    oControl = CreateToolbarTextbox(CType(oItem, TBToolbarTextbox))
                Else
                    '建立 Label 控制項
                    oControl = CreateToolbarLabel(CType(oItem, TBToolbarLabel))
                End If
                Me.Controls.Add(oControl)
            Next

            MyBase.RenderContents(writer)
        End Sub

        ''' <summary>
        ''' 建立工具列按鈕。
        ''' </summary>
        Private Function CreateToolbarButton(ByVal Item As TBToolbarButton) As Control
            Dim oButton As TBButton
            Dim sScript As String

            oButton = New TBButton()
            oButton.Text = Item.Text
            oButton.Enabled = Item.Enabled
            oButton.ID = Item.Key
            oButton.ConfirmMessage = Item.ConfirmMessage
            sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, Item.Key)
            oButton.OnClientClick = sScript

            Return oButton
        End Function

        ''' <summary>
        ''' 建立工具列文字框。
        ''' </summary>
        Private Function CreateToolbarTextbox(ByVal Item As TBToolbarTextbox) As Control
            Dim oTextBox As TextBox

            oTextBox = New TextBox
            Return oTextBox
        End Function

        ''' <summary>
        ''' 建立工具列標籤。
        ''' </summary>
        Private Function CreateToolbarLabel(ByVal Item As TBToolbarLabel) As Control
            Dim oLabel As Label

            oLabel = New Label()
            oLabel.Text = Item.Text
            Return oLabel
        End Function

我們手動在 aspx 程式碼中輸入不同型別的成員,TBToolbar 控制項就會呈現對應的子控制項。

三、執行程式
執行程式,就可以在瀏覽器看到呈現的工具列,當按下「刪除」時也會出現我們定義的詢問訊息。

輸出的 HTML 碼如下

<span id="TBToolbar1">
<input type="submit" name="TBToolbar1$Add" value="新增" onclick="__doPostBack('TBToolbar1','Add');" id="TBToolbar1_Add" />
<input type="submit" name="TBToolbar1$Edit" value="修改" onclick="__doPostBack('TBToolbar1','Edit');" id="TBToolbar1_Edit" />
<input type="submit" name="TBToolbar1$Delete" value="刪除" onclick="if (confirm('確定刪除嗎?')==false) {return false;}__doPostBack('TBToolbar1','Delete');" id="TBToolbar1_Delete" />
<span>關鍵字</span>
<input name="TBToolbar1$ctl01" type="text" />
<input type="submit" name="TBToolbar1$Search" value="搜尋" onclick="__doPostBack('TBToolbar1','Search');" id="TBToolbar1_Search" />
</span>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/18/5718.aspx

]]>
jeff377 2008-10-18 00:05:57
[ASP.NET 控制項實作 Day16] 繼承 WebControl 實作 Toolbar 控制項 https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron 前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項...]]> 前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項,進而比較二者之間的差異。
程式碼下載:ASP.NET Server Control - Day16.rar

一、繼承 WebControl 實作 TBToolbar 控制項
step1. 新增繼承 WebControl 的 TBToolbar 控制項
新增繼承 WebControl 的 TBToolbar 控制項,你也可以直接原修改原 TBToolbar 控制項,繼承對象由 CompositeControl 更改為 WebControl即可。跟之前一樣在 TBToolbar 控制項加入 Items 屬性及 Click 事件。
另外 TBToolbar 控制項需實作 INamingContainer 界面,此界面很特殊沒有任何屬性或方法,INamingContainer 界面的作用是子控制項的 ClientID 會在前面加上父控制項的 ClickID,使每個子控制項有唯一的 ClientID。

step2. 建立工具列按鈕集合
覆寫 RenderContents 方法,將原本 TBToolbar (複合控制項) 的 CreateChildControls 方法中建立工具列按鈕程式碼,搬移至 RenderContents 方法即可。

        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)
            Dim oButton As Button
            Dim oEventArgs As ClickEventArgs

            oButton = CType(sender, Button)
            oEventArgs = New ClickEventArgs()
            oEventArgs.Key = oButton.ID
            OnClick(oEventArgs)
        End Sub

        ''' <summary>
        ''' 覆寫 RenderContents 方法。
        ''' </summary>
        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim oItem As TBToolbarItem
            Dim oButton As Button

            For Each oItem In Me.Items
                oButton = New Button()
                oButton.Text = oItem.Text
                oButton.Enabled = oItem.Enabled
                oButton.ID = oItem.Key
                AddHandler oButton.Click, AddressOf ButtonClickEventHandler
                Me.Controls.Add(oButton)
            Next

            If Me.Items.Count = 0 AndAlso Me.DesignMode Then
                oButton = New Button()
                oButton.Text = "請設定 Items 屬性。"
                Me.Controls.Add(oButton)
            End If

            MyBase.RenderContents(writer)
        End Sub

上述的直接搬移過來的程式碼還有個問題,就是原來的使用 AddHandler 來處理按鈕事件的方式變成沒有作用了?因為現在不是複合式控制項,當前端的按鈕 PostBack 傳回伺服端時,TBToolbar 不會事先建立子控制槓,所以機制會找不到原來產生的按鈕,也就無法使用 AddHandler 來處理事件了。

AddHandler oButton.Click, AddressOf ButtonClickEventHandler

step3. 處理 Click 事件
因為不能使用 AddHandler 來處理按鈕事件,所以我們就自行使用 Page.ClientScript.GetPostBackEventReference 方法來產生 PostBack 動作的用戶端指令碼,按鈕的 OnClientClick 去執行 PostBack 的動作。

            For Each oItem In Me.Items
                oButton = New Button()
                oButton.Text = oItem.Text
                oButton.Enabled = oItem.Enabled
                oButton.ID = oItem.Key
                sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, oItem.Key)
                oButton.OnClientClick = sScript
                Me.Controls.Add(oButton)
            Next

TBToolar 控制項輸出的 HTML 碼如下

<span id="TBToolbar1">
<input type="submit" name="TBToolbar1$Add" value="新增" onclick="__doPostBack('TBToolbar1','Add');" 

id="TBToolbar1_Add" />
<input type="submit" name="TBToolbar1$Edit" value="修改" onclick="__doPostBack('TBToolbar1','Edit');" 

id="TBToolbar1_Edit" />
<input type="submit" name="TBToolbar1$Delete" value="刪除" onclick="__doPostBack('TBToolbar1','Delete');" 

id="TBToolbar1_Delete" />
</span>

要自行處理 PostBack 的事件,需實作 IPostBackEventHandler 介面,在 RaisePostBackEvent 方法來引發 TBToolbar 的 Click 事件。

    Public Class TBToolbar
        Inherits WebControl
        Implements INamingContainer
        Implements IPostBackEventHandler

        Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements 

System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
            Dim oEventArgs As ClickEventArgs

            oEventArgs = New ClickEventArgs()
            oEventArgs.Key = eventArgument
            Me.OnClick(oEventArgs)
        End Sub

    End Class

二、測試程式
在測試頁面上放置 TBToolbar 控制項,在 Click 事件撰寫測試程式碼。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/17/5706.aspx

]]>
jeff377 2008-10-17 00:05:40
[ASP.NET 控制項實作 Day15] 複合控制項隱藏的問題 https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron 上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。
程...]]>
上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。
程式碼下載:ASP.NET Server Control - Day15.rar
一、複合控制項建立子控制項的時機
還記得我們之前介紹複合控制項時有談到 CompositeControl 類別會確保我們存取子控制項時,它的子控制項一定會事先建立;也就是當我們使用 Controls 屬性去存取子控制項時,一定會執行 CreateChildControls 方法,以確保子控制項事先被建立。我們看一下 CompositeControl 類別的 Controls 屬性的寫法就可以了解其中的原由,在存取 CompositeControl.Controls 屬性時,它會先執行 Control.EnsureChildControls 方法;而 EnsureChildControls 方法會去判斷子控制項是否已建立,若未建立會去執行 CreateChildControls 方法,這也就是為什麼 CompositeControl 有辨法確保子控制項事先被建立的原因。

CompositeControl.Controls 屬性如下

Public Overrides ReadOnly Property Controls As ControlCollection
    Get
        Me.EnsureChildControls
        Return MyBase.Controls
    End Get
End Property

Control.EnsureChildControls 方法如下

Protected Overridable Sub EnsureChildControls()
    If (Not Me.ChildControlsCreated AndAlso Not Me.flags.Item(&H100)) Then
        Me.flags.Set(&H100)
        Try 
            Me.ResolveAdapter
            If (Not Me._adapter Is Nothing) Then
                Me._adapter.CreateChildControls
            Else
                Me.CreateChildControls
            End If
            Me.ChildControlsCreated = True
        Finally
            Me.flags.Clear(&H100)
        End Try
    End If
End Sub

二、複合控制項隱藏的問題
我們以上篇的 TBToolbar 控制項為例,撰寫一些測試案例來說明複合控制項的問題。在撰寫測試案例之前,我們先修改一下 TBToolbar 控制項,覆寫 LoadViewState 及 SaveViewState 方法,將 Items 屬性儲存於 ViewState 中以維持狀態。

在測試頁面上放置「測試一」、「測試二」、「PostBack」三個按鈕,這三個按鈕的動作如下。
「測試一」按鈕:在工具列直接新增一個按鈕。
「測試二」按鈕:先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。
「PostBack」按鈕:單純執行 PostBack,不撰寫程式碼。

三個按鈕的程式碼如下所示。

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles 

Button1.Click
        Dim oItem As TBToolbarItem

        '加入新按鈕
        oItem = New TBToolbarItem()
        oItem.Text = "新按鈕"
        oItem.Key = "NewButton"
        TBToolbar1.Items.Add(oItem)
        Me.Response.Write("「測試一」按鈕")
    End Sub

    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles 

Button2.Click
        Dim oItem As TBToolbarItem
        Dim oButton As Button

        '先執行 FindControl 去取得 ID="Add" 的按鈕
        oButton = TBToolbar1.FindControl("Add")

        '再加入新按鈕
        oItem = New TBToolbarItem()
        oItem.Text = "新按鈕"
        oItem.Key = "NewButton"
        TBToolbar1.Items.Add(oItem)
        Me.Response.Write("「測試二」按鈕")
    End Sub

    Protected Sub Button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles 

Button3.Click
        '單純 PostBack,無程式碼
        Me.Response.Write("「PostBack」按鈕")
    End Sub

案例一:執行「測試一」按鈕,在工具列直接新增一個按鈕。
當按下「測試一」按鈕時,工具列可以正常加入我們新增的按鈕。

案例二:執行「測試二」按鈕,先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。
重新執行程式,當按下「測試二」按鈕時,你會發現奇怪的現象,工具列竟然沒有加入我們新增的按鈕?

此時再按下「PostBack」按鈕,工具列才會出現我們剛剛加入的按鈕。

為什麼會發生這種怪現象呢?其實原因很簡單,因為 FindControl 時會去存取 Controls 屬性,而這時子控制項已經被建立了;而之前再用 Items 屬性加入新按鈕,它已經不會在重建子控制項,導致第一時間沒有加入新按鈕。不過 Items 屬性會被存在 ViewState 中,所以當執行「PostBack」按鈕時,就會出現我們剛剛新增的按鈕。

三、解決方式
要解決上述「測試二」的問題,只要覆寫 TBToolbar 控制項的 Render 方法,在 Render 前執行 RecreateChildControls 方法,強制重建子控制項。

        ''' <summary>
        ''' 覆寫 Render 方法。
        ''' </summary>
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Me.RecreateChildControls()
            MyBase.Render(writer)
        End Sub

再一次執行「測試二」的動作,就會發現執行結果就會正常了。

四、結語
在複合控制項的 Render 前執行 RecreateChildControls 方法可以強制重建子控制項,可是這樣又會引發另一個問題,那就是當直接存取子控制項去修改子控制項的屬性後,一旦在 Render 又重建子控制項,那之前設定子控制項狀態又被全部重建了,所以需特別注意有這樣的情形。另外複合控制項有可能重覆執行建立子控制的動作,在執行效能上也比較不佳。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/16/5695.aspx

]]>
jeff377 2008-10-16 00:14:12
[ASP.NET 控制項實作 Day14] 繼承 CompositeControl 實作 Toolbar 控制項 https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron 之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 To...]]> 之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 Toolbar 控制項,此工具列控制項包含 Items 屬性來描述工具列項目集合,依 Items 屬性的設定來建立工具列按鈕,另外包含 Click 事件可以得知使用按了那個按鈕。
程式碼下載:ASP.NET Server Control - Day14.rar
一、工具列項目集合類別
工具列包含多個按鈕,新增 TBToolbarItem 類別來描述工具列項目,TBToolbarItem 類別包含 Key、Text、Enabled 三個屬性;而 TBToolbarItemCollection 為 TBToolbarItem 的集合類別來描述工具列按鈕集合。

二、實作 TBToolbar 控制項
step1. 新增繼承 CompositeControl 的 TBToolbar 控制項

    < _
    Description("工具列控制項。"), _
    ParseChildren(True, "Items"), _
    ToolboxData("<{0}:TBToolbar runat=server ></{0}:TBToolbar>") _
    > _
    Public Class TBToolbar
        Inherits CompositeControl
    End Class 

step2. 新增 Items 屬性,描述工具列項目集合

        ''' <summary>
        ''' 工具列項目集合。
        ''' </summary>
        < _
        Description("工具列項目集合。"), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _
        > _
        Public ReadOnly Property Items() As TBToolbarItemCollection
            Get
                If FItems Is Nothing Then
                    FItems = New TBToolbarItemCollection()
                End If
                Return FItems
            End Get
        End Property

step3. 新增 Click 事件
TBToolbar 類別新增 Click 事件,當按下按鈕時會引發 Click 事件,由 Click 的事件引數 e.Key 可以得知使用者按了那個按鈕。

        ''' <summary>
        ''' Click 事件引數。
        ''' </summary>
        Public Class ClickEventArgs
            Inherits System.EventArgs
            Private FKey As String = String.Empty

            ''' <summary>
            ''' 項目鍵值。
            ''' </summary>
            Public Property Key() As String
                Get
                    Return FKey
                End Get
                Set(ByVal value As String)
                    FKey = value
                End Set
            End Property
        End Class

        ''' <summary>
        ''' 按下工具列按鈕所引發的事件。
        ''' </summary>
        < _
        Description("按下工具列按鈕所引發的事件。") _
        > _
        Public Event Click(ByVal sender As Object, ByVal e As ClickEventArgs)

        ''' <summary>
        ''' 引發 Click 事件。
        ''' </summary>
        Protected Overridable Sub OnClick(ByVal e As ClickEventArgs)
            RaiseEvent Click(Me, e)
        End Sub

step4. 建立工具列按鈕集合
覆寫 CreateChildControls 方法,依 Items 屬性的設定,來建立工具列中的按鈕集合。每個按鈕的 Click 事件都導向 ButtonClickEventHandler 方法,來處理所有按鈕的 Click 動作,並引發 TBToolbar 的 Click 事件。

        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)
            Dim oButton As Button
            Dim oEventArgs As ClickEventArgs

            oButton = CType(sender, Button)
            oEventArgs = New ClickEventArgs()
            oEventArgs.Key = oButton.ID
            OnClick(oEventArgs)
        End Sub

        ''' <summary>
        ''' 建立子控制項。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim oItem As TBToolbarItem
            Dim oButton As Button

            For Each oItem In Me.Items
                oButton = New Button()
                oButton.Text = oItem.Text
                oButton.Enabled = oItem.Enabled
                oButton.ID = oItem.Key
                AddHandler oButton.Click, AddressOf ButtonClickEventHandler
                Me.Controls.Add(oButton)
            Next
            MyBase.CreateChildControls()
        End Sub

三、測試程式
在頁面拖曳 TBToolbar 控制項,並設定 Items 屬性,如入新增、修改、刪除三個按鈕。

在 TBToolbar 控制項的 Click 事件加入測試程式碼,輸出引發 Click 事件的 e.Key。

    Protected Sub TBToolbar1_Click(ByVal sender As Object, ByVal e As Bee.Web.WebControls.TBToolbar.ClickEventArgs) Handles TBToolbar1.Click
        Me.Response.Write(String.Format("您按了 {0}", e.Key))
    End Sub

執行程式,當按了工具列上的按鈕時,就會引發 Click 事件,並輸出該按鈕對應的 Key。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/15/5687.aspx

]]>
jeff377 2008-10-15 00:13:50
[ASP.NET 控制項實作 Day13] Flash 控制項 https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。
程式碼下...]]>
Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。
程式碼下載:ASP.NET Server Control - Day13.rar

一、網頁 Flash 的原始 HTML 碼
我們先觀查在網頁中套用 Flash 插件的原始 HTML 碼,以點部落首頁抬頭的 Flash 原始碼為例如下,其中 <object> tag 的 codebase attribute 是指 Flash 插件的下載位置及版本。

<object id="ShockwaveFlash2" height="90" width="728" 
  codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0" 
  classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000">
<param value="http://files.dotblogs.com.tw/dotjum/ad/debug.swf" name="movie"/>
<param value="high" name="quality"/>
<param value="#000000" name="bgcolor"/>
<embed height="90" width="728" type="application/x-shockwave-flash" 
  pluginspage="http://www.macromedia.com/go/getflashplayer" quality="high" 
  src="http://files.dotblogs.com.tw/dotjum/ad/debug.swf"/>
</object>

在 <object> tag 中必要的 attribute 為 classid、codebase、movie、width、height,而 <embed> tag 的必要 attribute 為 src、pluginspage、width、height,其他選擇性的 attribute 可參閱以下網頁。

Flash OBJECT and EMBED tag attributes
http://kb.adobe.com/selfservice/viewContent.do?externalId=tn\_12701

二、實作 TFlash 控制項
了解 Flash 的原始 HTML 碼後,我們就可以開始著手撰寫 TBFlash 控制項,想辨法來輸出所需要的 HTML 碼。

step1. 新增 TBFlash 控制項繼承至 TBActiveX
我們先在 TBActiveX 控制項新增一個 CodeBase 屬性,用來設定 ActiveX 插入的下載位置及版本,然後新增 TBFlash 控制項繼承至 TBActiveX,並在建構函式中設定 MyBase.ClassId 及 MyBase.CodeBase 屬性。

    Public Class TBFlash
        Inherits TBActiveX

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        Sub New()
            MyBase.ClassId = "D27CDB6E-AE6D-11CF-96B8-444553540000"
            MyBase.CodeBase = "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0"
        End Sub
    End Class 

step2. 加入相關屬性
在 TBFlash 加入 MovieUrl 及 Quality 屬性,MovieUrl 為 Flash 檔案來源,Quality 為影音品質。

step3. 輸出 Flash 相關參數
覆寫 CreateChildControls 方法,輸出 MovieUrl 及 Quality 屬性對應的參數,以及在 Params 集合屬性設定的參數。

        ''' <summary>
        ''' 加入 MediaPlayer 參數。
        ''' </summary>
        ''' <param name="Name">參數名稱。</param>
        ''' <param name="Value">參數值。</param>
        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As TBActiveXParam

            oParam = New TBActiveXParam(Name, Value)
            Me.Params.Add(oParam)
        End Sub

        ''' <summary>
        ''' 建立 Embed 標記。
        ''' </summary>
        Private Function CreateEmbed() As HtmlControls.HtmlGenericControl
            Dim oEmbed As HtmlControls.HtmlGenericControl
            Dim oParam As TBActiveXParam

            oEmbed = New HtmlControls.HtmlGenericControl()
            oEmbed.TagName = "embed"
            oEmbed.Attributes("src") = Me.ResolveClientUrl(Me.MovieUrl)
            oEmbed.Attributes("pluginspage") = "http://www.macromedia.com/go/getflashplayer"
            oEmbed.Attributes("height") = Me.Height.ToString
            oEmbed.Attributes("width") = Me.Width.ToString

            'Embed 的 Attributes 加入 Params 集合屬性的設定
            For Each oParam In Me.Params
                If oParam.Name <> "movie" Then
                    oEmbed.Attributes(oParam.Name) = oParam.Value
                End If
            Next
            Return oEmbed
        End Function

        ''' <summary>
        ''' 建立子控制項。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim oEmbed As HtmlControls.HtmlGenericControl

            '加入 movie 參數
            AddParam("movie", Me.ResolveClientUrl(Me.MovieUrl))

            '加入 quality 參數
            If Me.Quality <> EQuality.NotSet Then
                AddParam("quality", Me.Quality.ToString.ToLower)
            End If

            MyBase.CreateChildControls()

            oEmbed = CreateEmbed()
            Me.Controls.Add(oEmbed)
        End Sub

三、測試程式
在頁面拖曳 TBFlash 控制項,設定 MovieUrl 及 Quality 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Flash。

        <bee:TBFlash ID="TBFlash1" runat="server" Height="90px" 
            MovieUrl="http://files.dotblogs.com.tw/dotjum/ad/debug.swf" Quality="High" 
            Width="728px">
        </bee:TBFlash>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/14/5674.aspx

]]>
jeff377 2008-10-14 00:16:30
[ASP.NET 控制項實作 Day12] 繼承 TBActiveX 重新改寫 TBMediaPlayer 控制項 https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron 上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX ...]]> 上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX 通用屬性,所以 TBMediaPlayer 基本上是可以由 TBActiveX 繼承下來,再加入 Media Player 特有的屬性即可。本文將原來的 TBMediaPlayer 控制項,繼承的父類別由 WebControl 改為 TBActiveX 類別,重新改寫 TBMediaPlayer 控制項。
程式碼下載:ASP.NET Server Control - Day12.rar

一、改寫 TBMediaPlayer 控制項
TBMediaPlayer 控制項原本是繼承 WebControl,現改繼承對象為 TBActiveX,來重新改寫 TBMediaPlayer 控制項。

step1. TBMediaPlayer 繼承至 TBActiveX
新增 TBMediaPlayer 控制項,繼承至 TBActiveX,並在建構函式設定 Media Player ActiveX 的 ClassId。

    Public Class TBMediaPlayer
        Inherits TBActiveX

        ''' <summary>
        ''' 建構函式。
        ''' </summary>
        Sub New()
            MyBase.ClassId = "6BF52A52-394A-11D3-B153-00C04F79FAA6"
        End Sub
    End Class

step2. 加入相關屬性
跟原來的 TBMediaPlayer 控制項一樣,加入 Url、AutoStart、UIMode 三個屬性,可視情形加入需要設定的屬性。

step3. 加入 Media Player 參數
覆寫 CreateChildControls 方法,動態依屬性設定在 Params 集合屬性加入參數。雖然 TBMediaPlayer 控制項目前只有 Url、AutoStart、UIMode 三個屬性,但是父類別 TBActiveX 具有 Params 集合屬性,所以開發人員可以視需求加入其他未定義的參數。

        ''' <summary>
        ''' 加入 MediaPlayer 參數。
        ''' </summary>
        ''' <param name="Name">參數名稱。</param>
        ''' <param name="Value">參數值。</param>
        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As TBActiveXParam

            oParam = New TBActiveXParam(Name, Value)
            Me.Params.Add(oParam)
        End Sub

        ''' <summary>
        ''' 覆寫 CreateChildControls 方法。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            '加入 Url 參數
            If Me.Url <> String.Empty Then
                AddParam("URL", Me.ResolveClientUrl(Me.Url))
            End If
            '加入 autoStart 參數
            If Me.AutoStart Then
                AddParam("autoStart", "true")
            End If
            '加入 uiMode 參數
            If Me.UIMode <> EUIMode.NotSet Then
                AddParam("uiMode", Me.UIMode.ToString)
            End If
            MyBase.CreateChildControls()
        End Sub

二、執行程式
在頁面拖曳 TBMediaPlayer 控制項,設定 Url、AutoStart、UIMode 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Media Player。

        <bee:TBMediaPlayer ID="TBMediaPlayer1" runat="server" AutoStart="True" 
            Height="249px" Url="D:\Movie_01.wmv" Width="250px">
        </bee:TBMediaPlayer>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/13/5663.aspx

]]>
jeff377 2008-10-13 00:13:29
[ASP.NET 控制項實作 Day11] ActiveX 伺服器控制項 https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX...]]> Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX 使用的參數(相當於 ActiveX 控制項的屬性)以 Param Tag 來表示。本文標題命名為「ActiveX 伺服器控制項」就是避免誤解為 ActiveX 控制項,而是在 ASP.NET 中輸出 ActiveX 相關 HTML 碼的伺服器控制項;我們可透過 ActiveX 伺服器控制項可以用來輸出網頁上引用 ActiveX 的通用 HTML 碼,另外 ActiveX 的參數會以集合屬性來呈現,所以也會一併學習到集合屬性的撰寫方式。
程式碼下載:ASP.NET Server Control - Day11.rar

一、集合屬性
ActiveX 的 Param 參數是集合屬性,所以我們定義了 TBActiveParam 類別描述 ActiveX 參數,包含 Name 及 Value 屬性;而 TBActiveXParamCollection 為 TBActiveParam 的集合類別,用來描述 ActiveX 參數集合。TBActiveXParamCollection 繼承 CollectionBase,加入操作集合的 Add、Insert、Remove、IndexOf、Contains 等方法,關於集合屬性的用法可以參閱筆者在部落格的「撰寫伺服器控制項的集合屬性 (CollectionBase)」一文中有詳細說明。

二、實作 ActiveX 伺服器控制項
step1. 新增繼承 WebControl 的 TBActiveX

step2. 覆寫 TagKey 屬性,傳回 object 的 Tag

        Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag
            Get
                Return HtmlTextWriterTag.Object
            End Get
        End Property

step3. 新增 ClassId 屬性,描述 ActiveX 的 ClassId
定義 ClassId 屬性,並覆寫 AddAttributesToRender 來輸出此屬性。

        ''' <summary>
        ''' 覆寫 AddAttributesToRender 方法。
        ''' </summary>
        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
            '加入 MediaPlayer ActiveX 元件的 classid
            writer.AddAttribute("classid", String.Format("clsid:{0}", Me.ClassId))
            MyBase.AddAttributesToRender(writer)
        End Sub

step4. 新增 Params 屬性,描述 ActiveX 的參數集合
定義 Params 屬性,型別為 TBActiveXParamCollection 類別,套用 EditorAttribute 設定 CollectionEditor 為集合編輯器。

        ''' <summary>
        ''' ActiveX 控制項參數集合。
        ''' </summary>
        < _
        Description("控制項參數集合。"), _
        PersistenceMode(PersistenceMode.InnerProperty), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _
        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _
        > _
        Public ReadOnly Property Params() As TBActiveXParamCollection
            Get
                If FParams Is Nothing Then
                    FParams = New TBActiveXParamCollection()
                End If
                Return FParams
            End Get
        End Property

當編輯 Params 時,會使用的 CollectionEditor 集合編輯器。

step5. 輸出 ActiveX 參數
覆寫 CreateChildControls 方法,在此方法依 Params 集合屬性定義依序來輸出 ActiveX 的參數集合。

        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As HtmlControls.HtmlGenericControl

            oParam = New HtmlControls.HtmlGenericControl("param")
            oParam.Attributes.Add("name", Name)
            oParam.Attributes.Add("value", Value)
            Me.Controls.Add(oParam)
        End Sub

        ''' <summary>
        ''' 建立子控制項。
        ''' </summary>
        Protected Overrides Sub CreateChildControls()
            Dim oParam As TBActiveXParam

            '加入 ActiveX 參數集合
            For Each oParam In Me.Params
                AddParam(oParam.Name, oParam.Value)
            Next
            MyBase.CreateChildControls()
        End Sub

三、執行程式
上一篇我們使用 TBMediaPlayer 控制項來設定 Media Player,在此我們改用 TBActiveX 控制項來設定 Media Player,一樣可以呈現相同的結果。

        <bee:TBActiveX ID="TBActiveX1" runat="server" 
            ClassId="6BF52A52-394A-11D3-B153-00C04F79FAA6" Height="250px" Width="250px">
            <Params>
                <bee:TBActiveXParam Name="URL" Value="d:/Movie_01.wmv" />
                <bee:TBActiveXParam Name="autoStart" Value="true" />
            </Params>
        </bee:TBActiveX>

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/12/5659.aspx

]]>
jeff377 2008-10-12 04:20:27
[ASP.NET 控制項實作 Day10] Media Player 控制項 https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron 我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控...]]> 我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控制項來處理 Media Player,只能在 aspx 中加入 Media Player 相關的程式碼;本文將示範如何製作一個 Media Player 控制項,讓我們在 ASP.NET 中更方便的使用 Media Player。
程式碼下載:ASP.NET Server Control - Day10.rar

一、Media Player 原始 HTML 碼
在製作 Media Player 控制項之前,你需要先了解 Media Player 原本的 HTML 碼,控制項的作用就是想辨法把這些寫在 aspx 中的 HTML 碼交由控制項來輸出而已,以下為網頁中加入 Media Player 的 HTML 碼範例。

<OBJECT id="VIDEO" width="320" height="240" 
	style="position:absolute; left:0;top:0;"
	CLASSID="CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6"
	type="application/x-oleobject">
	
	<PARAM NAME="URL" VALUE="your file or url">
	<PARAM NAME="SendPlayStateChangeEvents" VALUE="True">
	<PARAM NAME="AutoStart" VALUE="True">
	<PARAM name="uiMode" value="none">
	<PARAM name="PlayCount" value="9999">
</OBJECT>

在下面的網頁有 Media Player 相關參數說明。
http://www.mioplanet.com/rsc/embed\_mediaplayer.htm

二、實作 Media Player 控制項
step1.首先新增由 WebControl 繼承下來的 TBMediaPlayer 類別。

    Public Class TBMediaPlayer
        Inherits WebControl

    End Class

step2.覆寫 TagKey 屬性,傳回 object 的 Tag。

        Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag
            Get
                Return HtmlTextWriterTag.Object
            End Get
        End Property

step3.輸出 HTML Tag 的 Attribute
在 object Tag 中包含 style、classid、type 二個 Attribute,WebControl 本身會處理 style,所以我們只需覆寫 AddAttributesToRender 方法,處理 classid 及 type 二個 Attribute,記得覆寫 AddAttributesToRender 方法時最後要加入 MyBase.AddAttributesToRender(writer),才會執行父類別的 AddAttributesToRender 方法。

        ''' <summary>
        ''' 覆寫 AddAttributesToRender 方法。
        ''' </summary>
        Protected Overrides Sub AddAttributesToRender(ByVal writer As System.Web.UI.HtmlTextWriter)
            '加入 MediaPlayer ActiveX 元件的 classid
            writer.AddAttribute("classid", "clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6")
            writer.AddAttribute("type", "application/x-oleobject")
            MyBase.AddAttributesToRender(writer)
        End Sub

step4.加入 Url 屬性
加入指定播放檔案來源的 Url 屬性,其中套用 EditorAttribute 設定 UrlEditor,使用 Url 專用的編輯器來設定屬性。

        ''' <summary>
        ''' 播放檔案來源。
        ''' </summary>
        < _
        Description("播放檔案來源"), _
        Bindable(True), _
        Category("Appearance"), _
        Editor(GetType(UrlEditor), GetType(UITypeEditor)), _
        UrlProperty(), _
        DefaultValue("") _
        > _
        Public Property Url() As String
            Get
                Return FUrl
            End Get
            Set(ByVal value As String)
                FUrl = value
            End Set
        End Property

step5.輸出 Url 參數
接下來覆寫 CreateChildControls 方法,輸出 Url 參數。

        ''' <summary>
        ''' 加入參數。
        ''' </summary>
        ''' <param name="Name">參數名稱。</param>
        ''' <param name="Value">參數值。</param>
        Private Sub AddParam(ByVal Name As String, ByVal Value As String)
            Dim oParam As HtmlControls.HtmlGenericControl

            oParam = New HtmlControls.HtmlGenericControl("param")
            oParam.Attributes.Add("name", Name)
            oParam.Attributes.Add("value", Value)
            Me.Controls.Add(oParam)
        End Sub

        Protected Overrides Sub CreateChildControls()
            '加入 Url 參數
            AddParam("url", Me.ResolveClientUrl(Me.Url))
            MyBase.CreateChildControls()
        End Sub

step6.輸出 Media Player 其他參數
你可以將 Media Player 的參數設定皆使用相對應的屬性來設定,然後使用 step5 的方式來輸出所有設定的參數值。

三、Media Player 控制項程式碼
Media Player 控制項的完整程式碼如下,此控制項只加入 URL、AutoStart、UIMode 三個參數,你可以視需求情形將使用到的參數定義為屬性來做設定即可。
因為此處有字元數限制,完整的程式碼請參閱筆者部落格相同文章
http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx

四、執行程式
把 TBMediaPlayer 控制項拖曳至頁面,設定好屬性後,執行程式就可以在頁面上看到呈現出來的 Media Player。

        <bee:TBMediaPlayer ID="TBMediaPlayer1" runat="server" Height="250px" 
            Width="250px" Url="~/test.wmv" />

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx

]]>
jeff377 2008-10-11 19:08:27
[ASP.NET 控制項實作 Day9] 控制項常用 Attribute 介紹(2) https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron 接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。
六、ToolboxDataAttribute 類別<...]]>
接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。
六、ToolboxDataAttribute 類別
作用:指定當自訂控制項從工具箱拖曳到頁面時,為此自訂控制項產生的預設標記。
當我們新增一個伺服器控制項,它就會預設在控制項類別套用 ToolboxDataAttribute,定義在控制項在 aspx 程式碼中的標記。

<ToolboxData("<{0}:TBButton runat=server ></{0}:TBButton>")> _
Public Class TBButton
    Inherits System.Web.UI.WebControls.Button
End Class

七、DefaultPropertyAttribute 類別
作用:指定類別的預設屬性。
下面的範例中,MyTextbox 類別套用 DefaultPropertyAttribute,設定 Text 屬性為預設屬性。

<DefaultProperty("Text")> _
Public Class MyTextbox
    Inherits WebControl

    Public Property Text() As String
        Get
            Return CType(Me.ViewState("Text"), String)
        End Get

        Set(ByVal value As String)
            Me.ViewState("Text") = value
        End Set
    End Property
End Class

八、DefaultEventAttribute 類別
作用:指定控制項的預設事件。
下面的範例中,MyTextbox 類別套用 DefaultEventAttribute,設定 TextChanged 為預設屬性。

<DefaultEvent("TextChanged")> _
Public Class MyTextbox
    Inherits WebControl

    ''' <summary>
    ''' TextChanged 事件。
    ''' </summary>
    Public Event TextChanged As EventHandler
End Class

當設計階段在頁面上的 MyTextbox 控制項點二下時,就會產生預設事件的處理函式。

    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox3.TextChanged

    End Sub

九、LocalizableAttribute 類別
作用:指定屬性是否應該當地語系化。
當屬性套用設為為 true 的 LocalizableAttribute 時,其屬性值會儲存在資源檔中,未來不需修改程式碼就可以將這些資源檔當地語系化。

        <Localizable(True)> _
        Public Property Text() As String
            Get
                Return CType(Me.ViewState("Text"), String)
            End Get

            Set(ByVal value As String)
                Me.ViewState("Text") = value
            End Set
        End Property

十、DesignerAttribute 類別
作用:設定控制項在設計階段服務的類別。
指定一個設計階段的服務類別,來管理控制項在設計階段的行為,例如控制項的設計階段外觀、智慧標籤內容。例如下面範例的 TBGridView 控制項就定義了 TBGridViewDesigner 來實作設計階段的行為,未來的章節中也會介紹如何實作控制項的 Designer。

    < Designer(GetType(TBGridViewDesigner)) > _
    Public Class TBGridView
        Inherits GridView
    End Class

十一、EditorAttribute 類別
作用:指定在屬性視窗中編輯屬性值的編輯器。
例如 ListBox 控制項的 Items 屬性,在屬性視窗編輯 Items 屬性時,會彈出 Items 集合屬性的編輯器。以下範例就是定義 Items 屬性的編輯器類別為 TBListItemsCollectionEditor,未來的章節中也會介紹如何實作屬性的 Editor。

        <Editor(GetType(TBListItemsCollectionEditor), GetType(System.Drawing.Design.UITypeEditor))> _
        Public Overrides ReadOnly Property Items() As ListItemCollection

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/10/5653.aspx

]]>
jeff377 2008-10-10 11:27:02
[ASP.NET 控制項實作 Day8] 控制項常用 Attribute 介紹(1) https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在...]]> Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在 .NET 中 Property 與 Attribute 的意義及用法不同,不過微軟線上文件也將它翻譯為「屬性」,這可能讓人發生困擾及誤解;筆者比較喜歡的方式就是 Property 是屬性,Attribute 就維持原文。在 .NET 中類別或屬性上可以套用上不同的 Attribute,使類別或屬性具有不同的特性,本文將介紹一些在伺服器控制項常使用到的 Attribute。
一、DescriptionAttribute 類別
作用:指定控制項或屬性的描述。
當 DescriptionAttribute 套用至控制項的類別時,設定的描述內容就會出現在工具箱中控制項的提示。

<Description("按鈕控制項")> _
Public Class TBButton
    Inherits System.Web.UI.WebControls.Button
End Class


當 DescriptionAttribute 套用至控制項的屬性時,在屬性視窗下面就會出現設定的屬性描述內容。

<Description("詢問訊息")> _
Public Property ConfirmMessage() As String

二、DefaultValueAttribute 類別
作用:指定屬性的預設值。
使用 DefaultValueAttribute 設定屬性的預設值,若設定的屬性值與預設值相同時,此屬性值就不會出現在 aspx 程式碼中;筆者強烈建議屬性一定套用 DefaultValueAttribute,一來在 aspx 中的程式碼會比較少,另外一個重點是若因為某些因素需要修改屬性的預設值時,所有已開發頁面的控制項屬性值會一併變更;因為當初屬性值是預設值,沒有被寫入 aspx 程式碼中,所以一但控制項的屬性預設值變更,頁面已佈屬的控制項的屬性值就會全面適用。

        Private FConfirmMessage As String = String.Empty

        <DefaultValue("")> _
        Public Property ConfirmMessage() As String
            Get
                Return FConfirmMessage
            End Get
            Set(ByVal value As String)
                FConfirmMessage = value
            End Set
        End Property

三、CategoryAttribute 類別
作用:指定屬性或事件的分類名稱,當屬性視窗設定為 [分類] 模式時,以群組方式來顯示屬性或事件。
例如設定 ConfirmMessage 屬性在 "Behavior" 分類,則 ConfirmMessage 屬性會被歸類到「行為」分類。

        <Category("Behavior")> _
        Public Property ConfirmMessage() As String

四、BindableAttribute 類別
作用:指定成員是否通常使用於繫結。
在資料繫結設定視窗中中,指定屬性是否預設會出現在屬性清單中。

<Bindable(True)> _
Public Property ConfirmMessage() As String

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/09/5647.aspx

]]>
jeff377 2008-10-09 21:11:43
[ASP.NET 控制項實作 Day7] 設定工具箱的控制項圖示 https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron 當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。...]]> 當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。
一、加入控制項圖示檔
首先要準備一個 16 x 16 的點陣圖(bmp),如下所示。

將此圖檔加入至「伺服器控制項專案」中,可以如下圖所示,用一個特定的資料夾來儲存所有工具箱的圖示。

然後在圖檔的屬性視窗中,設定建置動作為「內嵌資源」。

二、設定控制項的圖示
首先定義一個 TBResource 類別,此為一個空的類別,其命名空間需與根命名空間相同,做為引用資源檔時使用。並加上控制項圖示的 WebResource 定義,因為根命名空間是 Bee.Web,而圖檔名稱為 TBButton.bmp,所以定義如下所示。

假設我們要設定 TBButton 的工具箱圖示,則在 TBButton 類別套用 ToolboxBitmapAttribute 如下,其中第一個參數為 GetType(TBResource),第二個參數為圖檔檔名。

重新編輯伺服器控制項專案,再將 Bee.Web.dll 組件的控制項加入工具箱中,你就可以發現 TBButton 的圖示已經變成設定的圖示了。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/08/5624.aspx

]]>
jeff377 2008-10-08 22:26:13
[ASP.NET 控制項實作 Day6] 事件與 PostBack https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron 一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用...]]> 一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用戶端使用者的操作透過 PostBack 來產生相對應的事件。例如前端使用者按鈕後會引發伺服端 Button 的 Click 事件,當前端使用者輸入文字框完畢後離開後會引發伺服端 TextBox 的 TextChanged 事件,在本文中就是要說明如何透過 PostBack 來產生與使用者互動的事件。
一、IPostBackEventHandler 與 IPostBackDataHandler 介面
控制項要處理 PostBack 產生的事件,必須實作 IPostBackEventHandler 或 IPostBackDataHandler 介面,這二個介面有什麼差別呢?例如 Button 是實作IPostBackEventHandler 介面,由控制項產生的 PostBack 直接引發事件,如 Button 的 Click 事件。例如 TextBox 是實作 IPostBackDataHandler 介面,當頁面產生 PostBack 時,會傳用戶端輸入的新值給控制項,由控制項本身去決定是否引發該事件;以 TextBox 舉例來說,它會判斷新值與舊值不同時才會引發 TextChanged 事件。

二、IPostBackEventHandler 介面實作
首先介紹 IPostBackEventHandler 介面,它包含 RaisePostBackEvent 方法,控制項在此方法中需處理要引發那些事件。我們繼承 WebControl 撰寫 MyButton 類別來說明 IPostBackEventHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入按鈕的 HTML 原始碼,並定義一個 Click 事件。實作 IPostBackEventHandler 介面的 RaisePostBackEvent 方法,在此方法中直接引發 Click 事件。

Public Class MyButton
    Inherits WebControl
    Implements IPostBackEventHandler

    ''' <summary>
    ''' Click 事件。
    ''' </summary>
    Public Event Click As EventHandler

    ''' <summary>
    ''' 引發 Click 事件。
    ''' </summary>
    Private Sub OnClick(ByVal e As EventArgs)
        RaiseEvent Click(Me, e)
    End Sub

    Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
        Dim e As New EventArgs()
        OnClick(e) '引發 Click 事件
    End Sub

    ''' <summary>
    ''' 覆寫 Render 方法。
    ''' </summary>
    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
        Dim sHTML As String

        sHTML = String.Format("<INPUT TYPE=Submit Name={0} Value = '按鈕'/>", Me.UniqueID)
        writer.Write(sHTML)
    End Sub

End Class

在頁面上拖曳 MyButton 控制項,在屬性視窗找到 Click 事件,點二下產生 Click 事件處理函式,並撰寫測試程式碼如下。

    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click
        Me.Page.Response.Write("MyButton Click 事件")
    End Sub

執行程式,當按下 MyButton 按鈕時,就會執行它的 RaisePostBackEvent 方法,進而引發 Click 事件,也就會執行 Click 事件處理函式的程式碼。

三、IPostBackDataHandler 介面實作
IPostBackDataHandler 介面包含 LoadPostData 及 RaisePostDataChangedEvent 方法,當頁面 PostBack 時,會尋找具 IPostBackDataHandler 介面的控制項,先執行LoadPostData 方法,控制項在 LoadPostData 方法中會判斷用戶端傳入值決定是否引發事件,若 LoadPostData 傳回 True 表示要引發事件,此時會執行RaisePostDataChangedEvent 方法去決定要引發那些事件,反之傳回 False 表示不引發事件。

我們繼承 WebControl 撰寫 MyText 類別來說明 IPostBackDataHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入文字框的 HTML 原始碼,並定義一個 TextChanged 事件。在 LoadPostData 方法中我們會判斷用戶端傳入值與目前的值是否不相同,不相同時才會傳回 True,此時才會執行 RaisePostDataChangedEvent 方法,進而引發 TextChanged 事件。

Public Class MyTextbox
    Inherits WebControl
    Implements IPostBackDataHandler

    Public Property Text() As String
        Get
            Return CType(Me.ViewState("Text"), String)
        End Get
        Set(ByVal value As String)
            Me.ViewState("Text") = value
        End Set
    End Property

    ''' <summary>
    ''' TextChanged 事件。
    ''' </summary>
    Public Event TextChanged As EventHandler

    ''' <summary>
    ''' 引發 TextChanged 事件。
    ''' </summary>
    Private Sub OnTextChanged(ByVal e As EventArgs)
        RaiseEvent TextChanged(Me, e)
    End Sub

    Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData
        '前端使用者輸入值
        Dim oNewValue As String = postCollection.Item(postDataKey)
        If oNewValue <> Me.Text Then
            Me.Text = oNewValue
            Return True
        Else
            Return False
        End If
    End Function

    Public Sub RaisePostDataChangedEvent() Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent
        Dim e As New EventArgs()
        '引發 TextChanged 事件
        OnTextChanged(e)
    End Sub

    ''' <summary>
    ''' 覆寫 Render 方法。
    ''' </summary>
    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
        Dim sHTML As String

        sHTML = String.Format("<INPUT Type=text Name={0} Value={1} >", Me.UniqueID, Me.Text)
        writer.Write(sHTML)
    End Sub

End Class

在頁面上拖曳 MyTextbox 及 MyButton 控制項,在 MyButton 的 Click 及 MyTextbox 的 TextChanged 事件撰寫如下測試程式碼。

    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click
        Me.Page.Response.Write("MyButton Click 事件")
        Me.Page.Response.Write("<br/>")
    End Sub

    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox1.TextChanged
        Me.Page.Response.Write("MyTextbox TextChanged 事件")
        Me.Page.Response.Write("<br/>")
    End Sub

執行程式,在 MyTextbox 輸入 "A",再按下 MyButton,因為 MyTextbox 的值「空字串->"A"」,所以會引發 MyTextbox 的 TextChanged 事件及 MyButton 的 Click 事件。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格

]]>
jeff377 2008-10-07 23:30:19
[ASP.NET 控制項實作 Day5] 屬性與 ViewState https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron 在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性...]]> 在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性如何存取 ViewState 是比較有效率的方式。
當你加入一個「ASP.NET 伺服器控制項」時,類別中預設會有一個 Text 屬性寫法的範例如下所示,屬性的讀寫都是直接存取 ViewState,這是一般常見的控制項屬性寫法。可是這種屬性的寫法是沒有效率的,因為 ViewState 的成員是 Object 型別,每次讀取屬性時都是由 ViewState 取出指定鍵值的成員再轉型為屬性的型別,寫入屬性的動作也是直接寫入 ViewState 中。

    Property Text() As String
        Get
            Dim s As String = CStr(ViewState("Text"))
            If s Is Nothing Then
                Return String.Empty
            Else
                Return s
            End If
        End Get

        Set(ByVal Value As String)
            ViewState("Text") = Value
        End Set
    End Property

比較好的方式應該是讀取 ViewState 成員只做一次型別轉換的動作,而寫入 ViewState 的動作可以在 Render 前做批次寫入的動作即可。為了達到這樣的需求,我們可以覆寫 LoadViewState 及 SaveViewState 方法來處理屬性與 ViewState 的存取動作;當控制項初始化後會執行 LoadViewState 方法,來載入 ViewState 還原的控制項狀態,當控制項 Render 之前,會執行 SaveViewState 方法,將控制項的最終狀態存入 ViewState 中,也就是在此方法之後對控制項所做的任何變更都將會被忽略。

我們改寫屬性的寫法,不直接用 ViewState 來存取屬性,而是改用「屬性區域變數」來存取屬性,在 LoadViewState 時載入 ViewState 到屬性區域變數,而 SaveViewState 時再將屬性區域變數寫入 ViewState 中。我們依此方式將 Text 屬性改寫如下。

    Private FText As String

    Property Text() As String
        Get
            Return FText
        End Get
        Set(ByVal Value As String)
            FText = Value
        End Set
    End Property

    ''' <summary>
    ''' 載入 ViewState 還原控制項狀態。
    ''' </summary>
    Protected Overrides Sub LoadViewState(ByVal savedState As Object)
        If Not (savedState Is Nothing) Then
            ' Load State from the array of objects that was saved at vedViewState.
            Dim myState As Object() = CType(savedState, Object())

            If Not (myState(0) Is Nothing) Then
                MyBase.LoadViewState(myState(0))
            End If

            If Not (myState(1) Is Nothing) Then
                FText = CType(myState(1), String)
            End If
        End If
    End Sub

    ''' <summary>
    ''' 將控制項狀態寫入 ViewState 中。
    ''' </summary>
    Protected Overrides Function SaveViewState() As Object
        Dim baseState As Object = MyBase.SaveViewState()
        Dim myState(1) As Object
        myState(0) = baseState
        myState(1) = FText
        Return myState
    End Function

利用上述的方式,我們可以在 LoadViewState 批次載入所有屬性值,而在 SaveViewState 批次寫入屬性值,如此在讀取屬性就不用一直做型別轉換的動作以改善效率。

結語
雖然屬性一般都是儲存於 ViewState 中,不過若是一些無關緊要的屬性或是完全不會執行階段就變更的屬性,可以考慮不需要將這些屬性儲存於 ViewState 中;因為 ViewState 是個兩面刃,ViewState 可以很輕易幫我們維護屬性值,不過相對的也增加了面頁的傳輸量,所以可以視實際情形來決定屬性是否要儲存於 ViewState 中。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/07/5601.aspx

]]>
jeff377 2008-10-06 21:17:20
[ASP.NET 控制項實作 Day4] 複合控制項 https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron 複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬...]]> 複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬於複合控制項。我們常在網頁上常看到一種輸入日期的方式是年月日三個下拉清單,本文將利用複合控制項來實作這個年月日下拉清單控制項,示範如何實作複合控制項。
一、CompositeControl 類別的特性
CompositeControl 類別是抽象類別,它會實作 INamingContaner 介面,INamingContaner 介面會在子控制項的 ClinetID 加上父控制項的 ID,以確保頁面上控制項的 ClientID 是唯一的。繼承 CompositeControl 類別一般都是覆寫 CreateChildControls 方法,在此方法中將建立子控制項並加入 Controls 集合屬性中;當存取其子控制項時,若子控制項未建立,會執行 CreateChildControls 方法,以會確保所有子控制項皆已在存取 ControlCollection 之前建立。
二、日期下拉清單輸入器
我們繼承 CompositeControl 類別,命名為 TBDropDownDate。這個控制項會包含年月日三個下拉清單(DropDownList),所以我們只要在 CreateChildControls 方法中依序建立年月日的 DropDownList 子控制項,並加入 Controls 集合屬性中即可。

''' <summary>
''' 日期下拉清單輸入器。
''' </summary>
< _
ToolboxData("<{0}:TBDropDownDate runat=server></{0}:TBDropDownDate>") _
> _
Public Class TBDropDownDate
    Inherits System.Web.UI.WebControls.CompositeControl

    Protected Overrides Sub CreateChildControls()
        Dim oYear As DropDownList
        Dim oMonth As DropDownList
        Dim oDay As DropDownList
        Dim N1 As Integer

        '年下拉清單區間為 1950-2010 (年區間可以用屬性來設定)
        oYear = New DropDownList
        oYear.ID = "Year"
        For N1 = 1950 To 2010
            oYear.Items.Add(N1.ToString)
        Next
        Me.Controls.Add(oYear) '加入子控制項
        Me.Controls.Add(New LiteralControl("年"))

        '月下拉清單區間為 1-12
        oMonth = New DropDownList
        oMonth.ID = "Month"
        For N1 = 1 To 12
            oMonth.Items.Add(N1.ToString)
        Next
        Me.Controls.Add(oMonth) '加入子控制項
        Me.Controls.Add(New LiteralControl("月"))

        '日下拉清單區為為 1-31
        oDay = New DropDownList
        oDay.ID = "Day"
        For N1 = 1 To 12
            oDay.Items.Add(N1.ToString)
        Next
        Me.Controls.Add(oDay) '加入子控制項
        Me.Controls.Add(New LiteralControl("日"))

    End Sub
End Class

在設定階段拖曳 TBDropDownDate 到頁面上,就可以看到我們在 CreateChildControls 方法中所加入的子控制項。

執行程式,檢視它的 HTML 原始碼,會發現年月日的子控制項的 ClientID 都會在原 ID 前加上父控制項的 ID,這樣命名規則可以確保所有的控制項的 ClinetID 都是唯一值。

<span id="TBDropDownDate1">
<select name="TBDropDownDate1$Year" id="TBDropDownDate1_Year">

....省略

<select name="TBDropDownDate1$Month" id="TBDropDownDate1_Month">

....省略

<select name="TBDropDownDate1$Day" id="TBDropDownDate1_Day">
</span>

三、結語
我們已經看過三類伺服器控制項的簡單案例,不過這三個案例都只是簡單說明控制項 UI 的部分,一個完整的控制項需具備屬性、方法、事件、設計階段支援...等,在後面的文章中,我們將陸續針對這些部分做詳細的介紹。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/05/5583.aspx

]]>
jeff377 2008-10-05 22:22:25
[ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能 https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron 相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息...]]> 相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息」、「TextBox 設為 ReadOnly 時,可以取得前端傳回的 Text 屬性」這類需求,都可以直接繼承原控制項下來,加上我們需要的功能即可。以下我們就以一個簡單的案例來說明如何繼承現有伺服器下來擴展功能。
一、擴展 Button 控制項:按鈕加上詢問訊息
按下按鈕執行某些動作前,有時會詢問使用者是否執行該動作;例如按下刪除鈕,會詢問使用者是否確定要執行刪除的動作。當然這只需要簡單的 JavaScript 就可以完成,不過相對於 .NET 的程式語言,JavaScript 是非常不易維護的用戶端指令碼,如果能讓開發人員完全用不到 JavaScript,那何樂不為呢? 那就由 Button 控制項本身提供加上詢問訊息的功能就可以,相關的 JavaScript 由控制項去處理。
一般要在 Button 加上詢問訊息,只要在 OnClientClick 屬性設定如下的 JavaScript 即可。我們的目的只是讓開發人員連設定 OnClientClick 屬性的 JavaScript 都省略,直接設定要詢問的訊息即可,接下來我們就要開始實作這個控制項。

<asp:Button ID="Button1" runat="server" Text="Button"  OnClientClick="if (confirm('確定執行嗎?')==false) {return false;}" />   

在 Bee.Web 專案中,加入「ASP.NET 伺服器控制項」,此控制項繼承 Button 下來命名為 TBButton (命名空間為 Bee.Web.WebControls)。在 TBButton 類別中加入 ConfirmMessage 屬性,用來設定詢問訊息的內容。然後在 Render 方法將詢問詢息的 JavaScript 設定到 OnClientClick 屬性即可。

Namespace WebControls
    < _
    Description("按鈕控制項"), _
    ToolboxData("<{0}:TBButton runat=server></{0}:TBButton>") _
    > _
    Public Class TBButton
        Inherits System.Web.UI.WebControls.Button

        <Description("詢問訊息")> _
        Public Property ConfirmMessage() As String
            Get
                Dim sConfirmMessage As String
                sConfirmMessage = CStr(ViewState("ConfirmMessage"))
                If sConfirmMessage Is Nothing Then
                    Return String.Empty
                Else
                    Return sConfirmMessage
                End If
            End Get
            Set(ByVal value As String)
                ViewState("ConfirmMessage") = value
            End Set
        End Property

        ''' <summary>
        ''' 覆寫 Render 方法。
        ''' </summary>
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            Dim sScript As String
            Dim sConfirm As String

            '若有設定 ConfirmMessage 屬性,則在 OnClientClick 加入詢問訊息的 JavaScript
            If Me.ConfirmMessage <> String.Empty Then
                sScript = Me.OnClientClick
                '詢問訊息的 JavaScript
                sConfirm = String.Format("if (confirm('{0}')==false) {{return false;}}", Me.ConfirmMessage)
                If sScript = String.Empty Then
                    Me.OnClientClick = sConfirm
                Else
                    Me.OnClientClick = sConfirm & sScript
                End If
            End If
            MyBase.Render(writer)
        End Sub

    End Class
End Namespace

將 TBButton 拖曳到測試頁面,設定 ConfirmMessage 屬性。

<bee:TBButton ID="TBButton1" runat="server" ConfirmMessage="確定刪除此筆資料嗎?" Text="刪除" />

執行結果如下。

二、結語
筆者在開發 ASP.NET 的應用程式過程中,通常會習慣把所有現有控制項繼承下來,無論目前需不需要擴展控制項功能。這種方式對於開發大型系統是相當有幫助的,因為無法預期在系統開發的過程中會不會因為某些狀況,而臨時需要擴展控制項的功能,所以就先全部繼承下來以備不時之需,也為未來保留修改的彈性。

三、相關連結
擴展 CommandField 類別 - 刪除提示訊息
按鈕加上詢問訊息

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx

]]>
jeff377 2008-10-04 21:24:49
[ASP.NET 控制項實作 Day2] 建立第一個伺服器控制項 https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron 上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。
撰寫伺服器控制項大致分為下列三種方式
1.由無到有建立全新的控制項,一般...]]>
上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。
撰寫伺服器控制項大致分為下列三種方式
1.由無到有建立全新的控制項,一般會繼承至 System.Web.UI.Control 或 System.Web.UI.WebControls.WebControl 類別。
2.繼承現有控制項,擴展原有控制項的功能,如繼承原有 TextBox 來擴展功能。
3.複合式控制項,將多個現有的控制項組合成為一個新的控制項,例如 TextBox 右邊加個 Button 整合成一個控制項,一般會繼承至 System.Web.UI.WebControls.CompositeControl 類別。

本文將先介紹第1種方式,由無到有來建立控制項,後面的文章中會陸續介紹第2、3種方式的控制項。要建立全新的控制項會繼承至 Control 或 WebControl,沒有 UI 的控制項可由 Control 繼承下來 (如 SqlDataSource),具 UI 的控制項會由 WebControl 繼承下來。接下來的範例中,我們將繼承 WebControl 來建立第一個 MyTextBox 控制項。

一、新增 MyTextBox 控制項
在 Bee.Web 專案按右鍵選單,執行「加入\新增項目」,選擇「ASP.NET 伺服器控制項」,在名稱文字框中輸入 MyTextbox,按下「確定」鈕,就會在專案中加入 MyTextbox 控制項類別。

新加入的控制項預設有一個 Text 屬性,以及覆寫 Render 方法。Render 方法是「將控制項呈現在指定的 HTML 寫入器中」,簡單的說就是在 Render 方法會將控制項對應的 HTML 碼輸出,用來呈現在用戶端的瀏覽器上。假設我們要撰寫一個網頁上的文字框,那就先去看一下文字框在網頁中對應的 HTML 碼,然後在 Render 方法中想辨法輸出這些 HTML 碼即可。

二、輸出控制項的 HTML 碼
你可以使用 FrontPage 之類的 HTML 編輯器,先編輯出控制項的呈現方式,進而去觀查它的 HTML 碼,再回頭去思考如何去撰寫這個伺服器控制項。假設 MyTextbox 控制項包含一個文字框及一個按鈕,那最終輸出的 HTML 碼應該如下。

<input id="Text1" type="text" />
<input id="Button1" type="button" value="button" />

我們在 MyTextbox 的 RenderContents 方法中輸出上述的 HTML 碼。

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim sHTML As String

        sHTML = "<input id=""Text1"" type=""text"" />" & _
                "<input id=""Button1"" type=""button"" value=""button"" />"
        writer.Write(sHTML)
    End Sub

建置控制項專案,然後拖曳 MyTextbox 在測試頁面上,設計階段就會呈現出我們期望的結果。

執行程式,在瀏覽器看一下 MyTextbox 控制項輸出的結果,是不是跟我們預期的一樣呢。

三、屬性套用到控制項 HTML 碼
控制項不可能單純這樣輸出 HTML 碼而已,控制項的相關屬性設定,一般都影響到輸出的 HTML 碼。假設 MyTextbox 有 Text 及 ButtonText 二個屬性,分別對應到 文字框的內容及按鈕的文字,MyTextbox 本來就有 Text 屬性,依像畫蘆葫新增 ButtonText 屬性。

    < _
    Bindable(True), _
    Category("Appearance"), _
    DefaultValue(""), _
    Localizable(True)> _
    Property ButtonText() As String
        Get
            Dim s As String = CStr(ViewState("ButtonText"))
            If s Is Nothing Then
                Return String.Empty
            Else
                Return s
            End If
        End Get

        Set(ByVal Value As String)
            ViewState("ButtonText") = Value
        End Set
    End Property

RenderContents 方法改寫如下。

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim sHTML As String

        sHTML = "<input id=""Text1"" type=""text"" value=""{0}""/>" & _
                "<input id=""Button1"" type=""button"" value=""{1}"" />"
        sHTML = String.Format(sHTML, Me.Text, Me.ButtonText)
        writer.Write(sHTML)
    End Sub

重新建置控制項專案,在頁面上測試 MyTextbox 的 Text 及 ButtonText 屬性。

四、使 ClientID (HTML 原始碼控制項的 ID) 是唯一值
在頁面上放置二個 MyTextbox 控制項,執行程式,在瀏覽器中檢查 MyTextbox 的 HTML 原始碼。你會發現 MyTextbox 會以一個 span 包住控制項的內容,而每個控制項的輸出的 ClientID 是唯一的。不過 MyTextbox 內含的文字框及按鈕卻會重覆,所以一般子控制項的 ClientID 會在前面包含父控制項的 ID。

<span id="MyTextbox1">
<input id="Text1" type="text" value="這是文字"/>
<input id="Button1" type="button" value="這是按鈕" />
</span>

<br />

<span id="MyTextbox2">
<input id="Text1" type="text" value="這是文字"/>
<input id="Button1" type="button" value="這是按鈕" />
</span>

所以我們再次修改 RenderContents 方法的程式碼

    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)
        Dim sHTML As String

        sHTML = "<input id=""{0}_Text"" type=""text"" value=""{1}""/>" & _
                "<input id=""{0}_Button"" type=""button"" value=""{2}"" />"
        sHTML = String.Format(sHTML, Me.ID, Me.Text, Me.ButtonText)
        writer.Write(sHTML)
    End Sub

執行程式,再次檢視 HTML 原始碼,所有的 ClinetID 都會是唯一的。

<span id="MyTextbox1">
<input id="MyTextbox1_Text" type="text" value="這是文字"/>
<input id="MyTextbox1_Button" type="button" value="這是按鈕" />
</span>

<br />

<span id="MyTextbox2">
<input id="MyTextbox2_Text" type="text" value="這是文字"/>
<input id="MyTextbox2_Button" type="button" value="這是按鈕" />
</span>

五、控制項前置詞
自訂控制項的預設前置詞是 cc1,不過這是可以修改的,在專案中的 AssemblyInfo.vb 檔案中,加入如下定義即可。詳細的作法請參考筆者部落格中的「自訂伺服器控制項前置詞」一本有詳細介紹,在此不再累述。

'設定控制項的標記前置詞
<Assembly: TagPrefix("Bee.Web.WebControls", "bee")>

六、結語
本文中是用土法鍊鋼的方法在撰寫伺服器控制項,一般在實作控制項時會有更好的方式、更易維護的寫法,後續的文章中會陸續介紹相關作法。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/03/5573.aspx

]]>
jeff377 2008-10-03 23:51:35
[ASP.NET 控制項實作 Day1] 建立 ASP.NET 伺服器控制項專案 https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron 在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如...]]> 在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如何建立「伺服器控制項」專案,以及如何測試開發階段的的伺服器控制項。
一、建立「ASP.NET 伺服器控制項」專案
首先執行功能表「檔案\新增專案」,在專案類型中選擇 Visual Basic -> Web,選取「ASP.NET 伺服器控制項」範本,在名稱文字框中輸入專案名稱,也就是組件的檔案名稱,我們輸入 Bee.Web 為專案名稱,組件檔案為 Bee.Web.dll,按下「確定」鈕即會建立新的「ASP.NET 伺服器控制項」專案。

在新建立「ASP.NET 伺服器控制項」專案中,會預設加入一個伺服器控制項類別(ServerControl1.vb),這個伺服器控制項已經事件幫我們加入一些控制項的程式碼。目前暫不做任何修改,直接使用此控制項來做測試說明。

接下來執行功能表「專案\Bee.Web 屬性」,設定此組件的根命名空間,一般慣用的根命名空間都會與組件名稱相同,以方便加入參考時可以快速找到相關組件。

我們先儲存這個「ASP.NET 伺服器控制項」專案,指定儲存位置,按下「儲存」鈕。整個專案相關檔案,會儲存在以專案名稱的資料夾中。

二、加入測試網站
不要關閉目前「ASP.NET 伺服器控制項」專案,執行功能表「檔案\加入\新網站」,選擇「ASP.NET 網站」,會在方案中加入一個網站,來測試開發階段的伺服器控制項使用。

在測試網站加入參考,選擇「專案」頁籤,此頁籤中會列出該方案中其他可加入參考的專案,選取 Bee.Web 專案,按下「確定」鈕。

先在 Bee.Web 專案中執行「建置」動作,然後切換到測試網站的頁面設計,工具箱中就會出現 ServerControl1 伺服器控制項。這個控制項就可以直接拖曳至頁面中使用,這個控制項只是單純 Render 出 Text 屬性值,你可以在控制項屬性視窗中,更改 Text 屬性值為 "測試文字",就會看到這個控制項顯示 "測試文字"。將測試網站設為啟動專案,按下「F5」執行程式,就會看到該控制在執行階段的結果。

備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格
http://www.dotblogs.com.tw/jeff377/archive/2008/10/02/5562.aspx

]]>
jeff377 2008-10-02 23:16:29


https://matters.news/@twcctz500/undefined-健康是最好的禮物蛋黃油https-www-facebook-com-eggsoil-bafyreifu3vznb6tdpc5axcoyjkxkyoqxzodprbhhg67b4pofzvsilba5be

<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"     xmlns:media="http://search.yahoo.com/mrss/">    <channel>        <title>ASP.NET 伺服器控制項開發 :: 2008 iT 邦幫忙鐵人賽</title>        <link>https://ithelp.ithome.com.tw/users/20007956/ironman</link>        <description><![CDATA[ASP.NET 是目前相當熱門的網站開發程式語言,市面上也有一大卡車的書籍在介紹 ASP.NET,不過卻非常少介紹「ASP.NET 伺服器控制項」方面的書籍。在此將透過一系列...]]></description>        <atom:link href="https://ithelp.ithome.com.tw/users/20007956/ironman" rel="self"></atom:link>                <language>zh-TW</language>        <lastBuildDate>Mon, 06 Jun 2022 20:19:06 +0800</lastBuildDate>                    <item>                <title>[ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013458?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> 接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> 接下來還要覆寫 LoadPostData 方法,取得 __EVENTARGUMENT 這個 HiddenField 的值,並判斷與原 SelectedIndex 屬性值是否不同,不同的話傳回 True,使其產生 SelectedIndexChanged 事件。</p> <pre><code>        Protected Overrides Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean            Dim values As String()            Dim iSelectedIndex As Integer            Me.EnsureDataBound()            values = postCollection.GetValues(postDataKey)            If (Not values Is Nothing) Then                iSelectedIndex = CInt(Me.Page.Request.Form(&quot;__EVENTARGUMENT&quot;))                If (Me.SelectedIndex &lt;&gt; iSelectedIndex) Then                    MyBase.SetPostDataSelection(iSelectedIndex)                    Return True                End If            End If            Return False        End Function </code></pre> <p><strong>四、測試程式</strong><br /> 在 TBDropDownList 的 SelectedIndexChanged 事件撰寫如下測試程式碼。</p> <pre><code>    Protected Sub DropDownList2_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList2.SelectedIndexChanged        Dim sText As String        sText = String.Format(&quot;TBDropDownList: Index={0} Value={1}&quot;, DropDownList2.SelectedIndex, DropDownList2.SelectedValue)        Me.Response.Write(sText)    End Sub </code></pre> <p>執行程式,在 TBDropDownList 選取 &quot;王五&quot; 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_10.png" alt="" /></p> <p>接下選取 Value 值相同的 &quot;陳六&quot; 這個選項,也會正常引發 SelectedIndexChanged ,並顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_11.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/30/5830.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/30/5830.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-30 21:23:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day29] 解決 DropDownList 成員 Value 值相同產生的問題</title>                <link>https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013457?sc=rss.iron</guid>                <description><![CDATA[<p>DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無...]]></description>                                    <content:encoded><![CDATA[<p>DropDownList 控制頁的成員清單中,若有 ListItem 的 Value 值是相同的情形時,會造成 DropDownList 無法取得正確的 SelectedIndex 屬性值、且無法正確引發 SelectedIndexChanged 事件的問題;今天剛好在網路上看到有人在詢問此問題,所以本文將說明這個問題的源由,並修改 DropDownList 控制項來解決這個問題。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008103021737321.rar" target="_blank">ASP.NET Server Control - Day29.rar</a></p> <p><strong>一、DropDownList 的成員 Value 值相同產生的問題</strong><br /> 我們先寫個測試程式來描述問題,在頁面上放置一個 DropDownList 控制項,設定 AutoPostBack=True,並加入四個 ListItem,其中 &quot;王五&quot; 及 &quot;陳六&quot; 二個 ListItem 的 Value 值相同。</p> <pre><code>    &lt;asp:DropDownList ID=&quot;DropDownList1&quot; runat=&quot;server&quot; AutoPostBack=&quot;True&quot;&gt;            &lt;asp:ListItem Value=&quot;0&quot;&gt;張三&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;1&quot;&gt;李四&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;王五&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;陳六&lt;/asp:ListItem&gt;    &lt;/asp:DropDownList&gt; </code></pre> <p>在 DropDownList 的 SelectedIndexChanged 事件,輸出 DropDownList 的 SelectedIndex 及 SelectedValue 屬性值。</p> <pre><code>    Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles DropDownList1.SelectedIndexChanged        Dim sText As String        sText = String.Format(&quot;DropDownList: Index={0} Value={1}&quot;, DropDownList1.SelectedIndex, DropDownList1.SelectedValue)        Me.Response.Write(sText)    End Sub </code></pre> <p>執行程式,在 DropDownList 選取 &quot;李四&quot; 這個選項時,會正常顯示該成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb.png" alt="" /></p> <p>接下來選取 &quot;陳六&quot; 這個選項時,竟然發生奇怪的現象,DorpDownList 竟然顯示相同 Value 值的 &quot;王五&quot; 這個成員的 SelectedIndex 及 SelectedValue 屬性值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_6.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay29DropDownListValue_112DF/image_thumb_7.png" alt="" /></p> <p><strong>二、問題發生的原因</strong><br /> 我們先看一下 DropDownList 輸出到用戶端的 HTML 原始碼。</p> <pre><code>&lt;select name=&quot;DropDownList1&quot; onchange=&quot;javascript:setTimeout('__doPostBack(\'DropDownList1\',\'\')', 0)&quot; id=&quot;DropDownList1&quot;&gt; &lt;option selected=&quot;selected&quot; value=&quot;0&quot;&gt;張三&lt;/option&gt; &lt;option value=&quot;1&quot;&gt;李四&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;王五&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;陳六&lt;/option&gt; &lt;/select&gt; </code></pre> <p>DropDownList 是呼叫 __doPostBack 函式,只傳入 eventTarget參數 (對應到 __EVENTTARGET 這個 HiddenField) 為 DropDownList 的 ClientID;當 PostBack 回伺服端時,在 DropDownList 的 LoadPostData 方法中,會取得用戶端選取的 SelectedValue 值,並去尋找對應的成員的 SelectedIndex 值。可是問題來了,因為 &quot;王五&quot; 與 &quot;陳六&quot; 的 Value 是相同的值,當在尋找符合 Value 值的成員時,前面的選項 &quot;王五&quot; 會先符合條件而傳回該 Index 值,所以先造成取得錯誤的 SelectedIndex 。</p> <pre><code>Protected Overridable Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As NameValueCollection) As Boolean    Dim values As String() = postCollection.GetValues(postDataKey)    Me.EnsureDataBound    If (Not values Is Nothing) Then        MyBase.ValidateEvent(postDataKey, values(0))        Dim selectedIndex As Integer = Me.Items.FindByValueInternal(values(0), False)        If (Me.SelectedIndex &lt;&gt; selectedIndex) Then            MyBase.SetPostDataSelection(selectedIndex)            Return True        End If    End If    Return False End Function </code></pre> <p><strong>三、修改 DropDownList 控制項來解決問題</strong><br /> 要解決這個問題最好的方式就是直接修改 DropDownList 控制項,自行處理前端呼叫 __doPostBack 的動作,將用戶端 DropDownList 選擇 SelectedIndex 一併傳回伺服端。所以我們繼承 DropDownList 命名為 TBDropDownList,覆寫 AddAttributesToRender 來自行輸出 PostBack 的用戶端指令碼,我們會用一個變數記錄 AutoPostBack 屬性,並強制將 AutoPostBack 屬性值設為 False,這是為了不要 MyBase 產生 PostBack 的指令碼;然後再自行輸出 AutoPostBack 用戶端指令碼,其中 __doPostBack 的 eventArgument 參數 (對應到 __EVENTARGUMENT 這個 HiddenField) 傳入 this.selectedIndex。</p> <pre><code>        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)            Dim bAutoPostBack As Boolean            Dim sScript As String            '記錄 AutoPostBack 值,並將 AutoPostBack 設為 False,不要讓 MyBase 產生 PostBack 的指令碼            bAutoPostBack = Me.AutoPostBack            Me.AutoPostBack = False            MyBase.AddAttributesToRender(writer)            If bAutoPostBack Then                MyBase.Attributes.Remove(&quot;onchange&quot;)                sScript = String.Format(&quot;__doPostBack('{0}',{1})&quot;, Me.ClientID, &quot;this.selectedIndex&quot;)                writer.AddAttribute(HtmlTextWriterAttribute.Onchange, sScript)                Me.AutoPostBack = True            End If        End Sub </code></pre> <p>在頁面上放置一個 TBDropDownList 控制項,設定與上述案例相同的成員清單。</p> <pre><code>        &lt;bee:TBDropDownList ID=&quot;DropDownList2&quot; runat=&quot;server&quot; AutoPostBack=&quot;True&quot;&gt;            &lt;asp:ListItem Value=&quot;0&quot;&gt;張三&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;1&quot;&gt;李四&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;王五&lt;/asp:ListItem&gt;            &lt;asp:ListItem Value=&quot;2&quot;&gt;陳六&lt;/asp:ListItem&gt;        &lt;/bee:TBDropDownList&gt; </code></pre> <p>執行程式查看 TBDropDownList 控制項的 HTML 原始碼,呼叫 __doPostBack 函式的參數已經被修改,eventArgument 參數會傳入該控制項的 selectedIndex。</p> <pre><code>&lt;select name=&quot;DropDownList2&quot; id=&quot;DropDownList2&quot; onchange=&quot;__doPostBack('DropDownList2',this.selectedIndex)&quot;&gt; &lt;option selected=&quot;selected&quot; value=&quot;0&quot;&gt;張三&lt;/option&gt; &lt;option value=&quot;1&quot;&gt;李四&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;王五&lt;/option&gt; &lt;option value=&quot;2&quot;&gt;陳六&lt;/option&gt; &lt;/select&gt; </code></pre> <p>[超過字數限制,下一篇接續本文]</p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-30 21:15:23</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013365?sc=rss.iron</guid>                <description><![CDATA[<p>接續一上文<br /> <strong>二、實作圖形驗證碼控制項</strong><br /> 雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖...]]></description>                                    <content:encoded><![CDATA[<p>接續一上文<br /> <strong>二、實作圖形驗證碼控制項</strong><br /> 雖然我們可以使用 Image 控制項來呈現 ValidateCode.aspx 頁面產生的驗證碼圖形,可是這樣只處理一半的動作,因為沒有處理「使用者輸入的驗證碼」是否與「圖形驗證碼」相符,所以我們將實作一個圖形驗證碼控制項,來處理掉所有相關動作。<br /> 即然上面的示範使用 Image 控制項來呈現驗證碼,所以圖形驗證碼控制項就繼承 Image 命名為 TBValidateCode。</p> <pre><code>    &lt; _    Description(&quot;圖形驗證碼控制項&quot;), _    ToolboxData(&quot;&lt;{0}:TBValidateCode runat=server&gt;&lt;/{0}:TBValidateCode&gt;&quot;) _    &gt; _    Public Class TBValidateCode        Inherits System.Web.UI.WebControls.Image        End </code></pre> <p>新增 ValidateCodeUrl 屬性,設定圖形驗證碼產生頁面的網址。</p> <pre><code>        ''' &lt;summary&gt;        ''' 圖形驗證碼產生頁面網址。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;圖形驗證碼產生頁面網址&quot;), _        DefaultValue(&quot;&quot;) _        &gt; _        Public Property ValidateCodeUrl() As String            Get                Return FValidateCodeUrl            End Get            Set(ByVal value As String)                FValidateCodeUrl = value            End Set        End Property </code></pre> <p>覆寫 Render 方法,若未設定 ValidateCodeUrl 屬性,則預設為 ~/Page/ValidateCode.aspx 這個頁面。另外我們在圖形的 ondbclick 加上一段用戶端指令碼,其作用是讓用戶可以滑鼠二下來重新產生一個驗證碼圖形。</p> <pre><code>        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim sUrl As String            Dim sScript As String            sUrl = Me.ValidateCodeUrl            If String.IsNullOrEmpty(sUrl) Then                sUrl = &quot;~/Page/ValidateCode.aspx&quot;            End If            If Me.BorderWidth = Unit.Empty Then                Me.BorderWidth = Unit.Pixel(1)            End If            If Me.AlternateText = String.Empty Then                Me.AlternateText = &quot;圖形驗證碼&quot;            End If            Me.ToolTip = &quot;滑鼠點二下可重新產生驗證碼&quot;            Me.ImageUrl = sUrl            If Not Me.DesignMode Then                sScript = String.Format(&quot;this.src='{0}?flag='+Math.random();&quot;, Me.Page.ResolveClientUrl(sUrl))                Me.Attributes(&quot;ondblclick&quot;) = sScript            End If            Me.Style(HtmlTextWriterStyle.Cursor) = &quot;pointer&quot;            MyBase.Render(writer)        End Sub </code></pre> <p>另外新增一個 ValidateCode 方法,用來檢查輸入驗證碼是否正確。還記得我們在產生驗證碼圖形時,同時把該驗證碼的值寫入 Session(&quot;_ValidateCode&quot;) 中吧,所以這個方法只是把用戶輸入的值與 Seesion 中的值做比對。</p> <pre><code>        ''' &lt;summary&gt;        ''' 檢查輸入驗證碼是否正確。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Code&quot;&gt;輸入驗證碼。&lt;/param&gt;        ''' &lt;returns&gt;驗證成功傳回 True,反之傳回 False。&lt;/returns&gt;        Public Function ValidateCode(ByVal Code As String) As Boolean            If Me.Page.Session(SessionKey) Is Nothing Then Return False            If SameText(CCStr(Me.Page.Session(SessionKey)), Code) Then                Return True            Else                Return False            End If        End Function </code></pre> <p><strong>三、測試程式</strong><br /> 在頁面放置一個 TBValidateCode 控制項,另外加一個文字框及按鈕,供使用者輸入驗證碼後按下「確定」鈕後到伺服端做輸入值比對的動作。</p> <pre><code>        &lt;bee:TBValidateCode ID=&quot;TBValidateCode1&quot; runat=&quot;server&quot; /&gt;        &lt;bee:TBTextBox ID=&quot;txtCode&quot; runat=&quot;server&quot;&gt;&lt;/bee:TBTextBox&gt;        &lt;bee:TBButton ID=&quot;TBButton1&quot; runat=&quot;server&quot; Text=&quot;確定&quot; /&gt; </code></pre> <p>在「確定」鈕的 Click 事件中,我們使用 TBValidateCode 控制項的 ValidateCode 方法判斷驗證碼輸入的正確性。</p> <pre><code>    Protected Sub TBButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles TBButton1.Click        If TBValidateCode1.ValidateCode(txtCode.Text) Then            Me.Response.Write(&quot;驗證碼輸入正確&quot;)        Else            Me.Response.Write(&quot;驗證碼輸入錯誤!&quot;)        End If    End Sub </code></pre> <p>執行程式,頁面就會隨機產生一個驗證碼圖形。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay28_1B7F/image_thumb.png" alt="" /></p> <p>輸入正確的值按「確定」鈕,就會顯示「驗證碼輸入正確」的訊息。因為我們在同一頁面測試的關係,你會發現 PostBack 後驗證碼圖形又會重新產生,一般正常的做法是驗證正確後就導向另一個頁面。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay28_1B7F/image_thumb_1.png" alt="" /></p> <p>當我們輸入錯誤的值,就會顯示「驗證碼輸入錯誤!」的訊息。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay28_1B7F/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/29/5818.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/29/5818.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-29 20:34:22</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day28] 圖形驗證碼控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013361?sc=rss.iron</guid>                <description><![CDATA[<p>在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地...]]></description>                                    <content:encoded><![CDATA[<p>在網頁上常把圖形驗證碼應用在登入或貼文的頁面中,因為圖形驗證碼具有機器不易識別的特性,可以防止機器人程式惡意的存取網頁。在本文中將實作一個圖形驗證碼的伺服器控制項,透過簡單的屬性設定就可以輕易地在網頁上套用圖形驗證碼。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081029202355938.rar" target="_blank">ASP.NET Server Control - Day28.rar</a></p> <p><strong>一、產生圖形驗證碼</strong><br /> 我們先準備一個產生圖形驗證碼的頁面 (ValidateCode.aspx),這個頁面主要是繪製驗證碼圖形,並將其寫入記憶體資料流,最後使用 Response.BinaryWrite 將圖形輸出傳遞到用戶端。當我們輸出此驗證碼圖形的同時,會使用 Session(&quot;_ValidateCode&quot;) 來記錄驗證碼的值,以便後續與使用者輸入驗證碼做比對之用。</p> <pre><code>Partial Class ValidateCode    Inherits System.Web.UI.Page    ''' &lt;summary&gt;    ''' 產生圖形驗證碼。    ''' &lt;/summary&gt;    Public Function CreateValidateCodeImage(ByRef Code As String, ByVal CodeLength As Integer, _        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer) As Bitmap        Dim sCode As String = String.Empty        '顏色列表,用於驗證碼、噪線、噪點        Dim oColors As Color() = { _            Drawing.Color.Black, Drawing.Color.Red, Drawing.Color.Blue, Drawing.Color.Green, _            Drawing.Color.Orange, Drawing.Color.Brown, Drawing.Color.Brown, Drawing.Color.DarkBlue}        '字體列表,用於驗證碼        Dim oFontNames As String() = {&quot;Times New Roman&quot;, &quot;MS Mincho&quot;, &quot;Book Antiqua&quot;, _                                      &quot;Gungsuh&quot;, &quot;PMingLiU&quot;, &quot;Impact&quot;}        '驗證碼的字元集,去掉了一些容易混淆的字元        Dim oCharacter As Char() = {&quot;2&quot;c, &quot;3&quot;c, &quot;4&quot;c, &quot;5&quot;c, &quot;6&quot;c, &quot;8&quot;c, _                                    &quot;9&quot;c, &quot;A&quot;c, &quot;B&quot;c, &quot;C&quot;c, &quot;D&quot;c, &quot;E&quot;c, _                                    &quot;F&quot;c, &quot;G&quot;c, &quot;H&quot;c, &quot;J&quot;c, &quot;K&quot;c, &quot;L&quot;c, _                                    &quot;M&quot;c, &quot;N&quot;c, &quot;P&quot;c, &quot;R&quot;c, &quot;S&quot;c, &quot;T&quot;c, _                                    &quot;W&quot;c, &quot;X&quot;c, &quot;Y&quot;c}        Dim oRnd As New Random()        Dim oBmp As Bitmap        Dim oGraphics As Graphics        Dim N1 As Integer        Dim oPoint1 As Drawing.Point        Dim oPoint2 As Drawing.Point        Dim sFontName As String        Dim oFont As Font        Dim oColor As Color        '生成驗證碼字串        For N1 = 0 To CodeLength - 1            sCode += oCharacter(oRnd.Next(oCharacter.Length))        Next        oBmp = New Bitmap(Width, Height)        oGraphics = Graphics.FromImage(oBmp)        oGraphics.Clear(Drawing.Color.White)        Try            For N1 = 0 To 4                '畫噪線                oPoint1.X = oRnd.Next(Width)                oPoint1.Y = oRnd.Next(Height)                oPoint2.X = oRnd.Next(Width)                oPoint2.Y = oRnd.Next(Height)                oColor = oColors(oRnd.Next(oColors.Length))                oGraphics.DrawLine(New Pen(oColor), oPoint1, oPoint2)            Next            For N1 = 0 To sCode.Length - 1                '畫驗證碼字串                sFontName = oFontNames(oRnd.Next(oFontNames.Length))                oFont = New Font(sFontName, FontSize, FontStyle.Italic)                oColor = oColors(oRnd.Next(oColors.Length))                oGraphics.DrawString(sCode(N1).ToString(), oFont, New SolidBrush(oColor), CSng(N1) * FontSize + 10, CSng(8))            Next            For i As Integer = 0 To 30                '畫噪點                Dim x As Integer = oRnd.Next(oBmp.Width)                Dim y As Integer = oRnd.Next(oBmp.Height)                Dim clr As Color = oColors(oRnd.Next(oColors.Length))                oBmp.SetPixel(x, y, clr)            Next            Code = sCode            Return oBmp        Finally            oGraphics.Dispose()        End Try    End Function    ''' &lt;summary&gt;    ''' 產生圖形驗證碼。    ''' &lt;/summary&gt;    Public Sub CreateValidateCodeImage(ByRef MemoryStream As MemoryStream, _        ByRef Code As String, ByVal CodeLength As Integer, _        ByVal Width As Integer, ByVal Height As Integer, ByVal FontSize As Integer)        Dim oBmp As Bitmap        oBmp = CreateValidateCodeImage(Code, CodeLength, Width, Height, FontSize)        Try            oBmp.Save(MemoryStream, ImageFormat.Png)        Finally            oBmp.Dispose()        End Try    End Sub    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load        Dim sCode As String = String.Empty        '清除該頁輸出緩存,設置該頁無緩存        Response.Buffer = True        Response.ExpiresAbsolute = System.DateTime.Now.AddMilliseconds(0)        Response.Expires = 0        Response.CacheControl = &quot;no-cache&quot;        Response.AppendHeader(&quot;Pragma&quot;, &quot;No-Cache&quot;)        '將驗證碼圖片寫入記憶體流,並將其以 &quot;image/Png&quot; 格式輸出        Dim oStream As New MemoryStream()        Try            CreateValidateCodeImage(oStream, sCode, 4, 100, 40, 18)            Me.Session(&quot;_ValidateCode&quot;) = sCode            Response.ClearContent()            Response.ContentType = &quot;image/Png&quot;            Response.BinaryWrite(oStream.ToArray())        Finally            '釋放資源            oStream.Dispose()        End Try    End Sub End Class </code></pre> <p>我們將此頁面置於 ~/Page/ValidateCode.aspx,當要使用此頁面的圖形驗證碼,只需要在使用 Image 控制項,設定 ImageUrl 為此頁面即可。</p> <pre><code>&lt;asp:Image ID=&quot;imgValidateCode&quot; runat=&quot;server&quot; ImageUrl=&quot;~/Page/ValidateCode.aspx&quot; /&gt; </code></pre> <p>[超過字數限制,下一篇接續本文]</p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-29 20:31:45</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續2)</title>                <link>https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013241?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> 接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate ...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> 接下來設定做為新增、編輯使用的 TBFormView 控制項,我們只使用 EditItemTemplate 來同時處理新增、刪除,所以 EditItemTemplate 需要同時具有「新增」、「更新」、「取消」三個按鈕。其中 ProductID 為主索引欄位,所以我們使用 TBTextBox 來繫結 ProductID 欄位,設定 FormViewModeState.InsertMode=&quot;Enable&quot; 使控制項在新增模式時為可編輯,設定 FormViewModeState.EditMode=&quot;Disable&quot; 使控制項在修改模式是唯讀的。</p> <pre><code>        &lt;bee:TBFormView ID=&quot;TBFormView1&quot; runat=&quot;server&quot; DataKeyNames=&quot;ProductID&quot; DataSourceID=&quot;SqlDataSource1&quot;            DefaultMode=&quot;Edit&quot; SingleTemplate=&quot;EditItemTemplate&quot; BackColor=&quot;White&quot; BorderColor=&quot;#CCCCCC&quot;            BorderStyle=&quot;None&quot; BorderWidth=&quot;1px&quot; CellPadding=&quot;3&quot; GridLines=&quot;Both&quot; Visible=&quot;False&quot;&gt;            &lt;FooterStyle BackColor=&quot;White&quot; ForeColor=&quot;#000066&quot; /&gt;            &lt;RowStyle ForeColor=&quot;#000066&quot; /&gt;            &lt;EditItemTemplate&gt;                ProductID:                &lt;bee:TBTextBox ID=&quot;TextBox1&quot; runat=&quot;server&quot; Text='&lt;%# Bind(&quot;ProductID&quot;) %&gt;'&gt;                  &lt;FormViewModeState EditMode=&quot;Disable&quot; InsertMode=&quot;Enable&quot;&gt;                  &lt;/FormViewModeState&gt;                &lt;/bee:TBTextBox&gt;                '省略                &lt;asp:LinkButton ID=&quot;LinkButton1&quot; runat=&quot;server&quot; CausesValidation=&quot;True&quot; CommandName=&quot;Insert&quot;                    Text=&quot;新增&quot; /&gt;                 &lt;asp:LinkButton ID=&quot;UpdateButton&quot; runat=&quot;server&quot; CausesValidation=&quot;True&quot; CommandName=&quot;Update&quot;                    Text=&quot;更新&quot; /&gt;                 &lt;asp:LinkButton ID=&quot;UpdateCancelButton&quot; runat=&quot;server&quot; CausesValidation=&quot;False&quot;                    CommandName=&quot;Cancel&quot; Text=&quot;取消&quot; /&gt;            &lt;/EditItemTemplate&gt;        &lt;/bee:TBFormView&gt; </code></pre> <p><strong>2. 測試新增模式</strong><br /> 接下來執行程式,一開始為瀏覽模式,以 TBGridView 來呈現資料。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_2.png" alt="" /></p> <p>按下 Header 的「新增」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的新增模式。其中繫結 ProductID 欄位的 TBTextBox 為可編輯模式,而下方的按鈕只會顯示「新增」及「取消」鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_3.png" alt="" /></p> <p>在新增模式輸入完畢後,按下「新增」鈕,資料錄就會被寫入資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_4.png" alt="" /></p> <p><strong>3. 測試修改模式</strong><br /> 接下來測試修改模式,按下「編輯」鈕,就會隱藏 TBGridView,而切換到 TBFormView 的修改模式。其中繫結 ProductID 欄位的 TBTextBox 為唯讀模式,而下方的按鈕只會顯示「更新」及「取消」鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_5.png" alt="" /></p> <p>在修改模式輸入完畢後,按下「更新」鈕,資料錄就會被寫入資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_6.png" alt="" /></p> <p><strong>4. 頁面程式碼</strong><br /> 示範了上述的操作後,接下來我們回頭看一下頁面的程式碼。你沒看錯,筆者也沒貼錯,真的是一行程式碼都沒有,因為所有相關動作都由控制項處理掉了。</p> <pre><code>Partial Class Day27    Inherits System.Web.UI.Page End Class </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-28 13:57:23</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態(續1)</title>                <link>https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013239?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>二、讓 TextBox 控制項可自行維護狀態</strong><br /> 接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBText...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>二、讓 TextBox 控制項可自行維護狀態</strong><br /> 接下來擴展 TextBox 控制項,繼承 TextBox 命名為 TBTextBox。新增 FormViewModeState 屬性 (TBFormViewModeState 型別),依 FormView Mode 來設定控制項狀。並覆寫 PreRender 方法,在此方法中呼叫 DoFormViewModeStatus 私有方法,依 FormView 的模式來處理控制項狀態。</p> <pre><code>    ''' &lt;summary&gt;    ''' 文字框控制項。    ''' &lt;/summary&gt;    &lt; _    Description(&quot;文字框控制項。&quot;), _    ToolboxData(&quot;&lt;{0}:TBTextBox runat=server&gt;&lt;/{0}:TBTextBox&gt;&quot;) _    &gt; _    Public Class TBTextBox        Inherits TextBox        Private FFormViewModeState As TBFormViewModeState        ''' &lt;summary&gt;        ''' 依 FormViewMode 來設定控制項狀態。        ''' &lt;/summary&gt;        &lt; _        Category(WebCommon.Category.Behavior), _        NotifyParentProperty(True), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        PersistenceMode(PersistenceMode.InnerProperty), _        DefaultValue(&quot;&quot;) _        &gt; _        Public ReadOnly Property FormViewModeState() As TBFormViewModeState            Get                If FFormViewModeState Is Nothing Then                    FFormViewModeState = New TBFormViewModeState                End If                Return FFormViewModeState            End Get        End Property        ''' &lt;summary&gt;        ''' 處理控制項狀態。        ''' &lt;/summary&gt;        Private Sub DoControlStatus(ByVal ControlStatus As EControlState)            Select Case ControlStatus                Case EControlState.Enable                    Me.Enabled = True                Case EControlState.Disable                    Me.Enabled = False                Case EControlState.Hide                    Me.Visible = False            End Select        End Sub        ''' &lt;summary&gt;        ''' 依 FormView 的模式來處理控制項狀態。        ''' &lt;/summary&gt;        Private Sub DoFormViewModeStatus()            Dim oFormView As FormView            '若控制項置於 FormView 中,則依 FormView 的模式來處理控制項狀態            If TypeOf Me.BindingContainer Is FormView Then                oFormView = DirectCast(Me.BindingContainer, FormView)                Select Case oFormView.CurrentMode                    Case FormViewMode.Insert                        DoControlStatus(Me.FormViewModeState.InsertMode)                    Case FormViewMode.Edit                        DoControlStatus(Me.FormViewModeState.EditMode)                    Case FormViewMode.ReadOnly                        DoControlStatus(Me.FormViewModeState.BrowseMode)                End Select            End If        End Sub        ''' &lt;summary&gt;        ''' 覆寫。引發 PreRender 事件。        ''' &lt;/summary&gt;        Protected Overrides Sub OnPreRender(ByVal e As EventArgs)            MyBase.OnPreRender(e)            '依 FormView 的模式來處理控制項狀態            DoFormViewModeStatus()        End Sub    End Class </code></pre> <p><strong>三、測試程式</strong><br /> <strong>1. 設定控制項相關屬性</strong><br /> 我們使用 Northwnd 資料庫的 Products資料表為例,以 GridView+FormView 示範資料「新增/修改/刪除」的操作。在頁面拖曳 SqlDataSource 控制項後,在頁面上的使用 TBGridView 來顯示瀏覽資料。TBGridView 的 FormViewID 設為關連的 TBFormVIew 控制項;另外有使用到 TBCommandField,設定 ShowHeaderNewButton=True,讓命令列具有「新增」鈕。</p> <pre><code>        &lt;bee:TBGridView ID=&quot;TBGridView1&quot; runat=&quot;server&quot; AutoGenerateColumns=&quot;False&quot; DataKeyNames=&quot;ProductID&quot;            DataSourceID=&quot;SqlDataSource1&quot; FormViewID=&quot;TBFormView1&quot;&gt;            &lt;Columns&gt;                &lt;bee:TBCommandField ShowDeleteButton=&quot;True&quot; ShowEditButton=&quot;True&quot;                    ShowHeaderNewButton=&quot;True&quot; &gt;                &lt;/bee:TBCommandField&gt;                                '省略                            &lt;/Columns&gt;        &lt;/bee:TBGridView&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-28 13:53:32</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day27] 控制項依 FormView CurrentMode 自行設定狀態</title>                <link>https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013233?sc=rss.iron</guid>                <description><![CDATA[<p>在 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/12/3936.aspx" target="_blank">GridV...]]></description>                                    <content:encoded><![CDATA[<p>在 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/12/3936.aspx" target="_blank">GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項)</a> 一文中,示範了擴展 GridView 及 FormView 控制項,讓 GridView 可以透過屬性與 FormView 做關連來處理資料的「新增/修改/刪除」的動作。因為在該案例中,只使用 FormView 的 EditTemplate 同時處理「新增」及「修改」的動作,所以還需要自行撰寫部分程式碼去判斷控制項在新增或修改的啟用狀態,例如編號欄位在新增時為啟用,修改時就不啟用。在該文最後也提及其實有辨法讓這個案例達到零程式碼的目標,那就是讓控制項 (如 TextBox) 自行判斷所在的 FormView 的 CurrentMode,自行決定本身是否要「啟用/不啟用」、「顯示/隱藏」等狀態。本文以 TextBox 為例,說明如何修改 TextBox 讓它可以達到上述的需求。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081028133154164.rar" target="_blank">ASP.NET Server Control - Day27.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、TBFormViewModeState 類別</strong><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb.png" alt="" /><br /> 我們先定義 EControlState (控制項狀態) 列舉,描述控制項在特定模式的狀態為何。</p> <pre><code>    ''' &lt;summary&gt;    ''' 控制項狀態列舉。    ''' &lt;/summary&gt;    Public Enum EControlState        ''' &lt;summary&gt;        ''' 不設定。        ''' &lt;/summary&gt;        NotSet = 0        ''' &lt;summary&gt;        ''' 啟用。        ''' &lt;/summary&gt;        Enable = 1        ''' &lt;summary&gt;        ''' 不啟用。        ''' &lt;/summary&gt;        Disable = 2        ''' &lt;summary&gt;        ''' 隱藏。        ''' &lt;/summary&gt;        Hide = 3    End Enum </code></pre> <p>再來定義 TBFormViewModeState 類別,用來設定控制項在各種 FormView 模式 (瀏覽、新增、修改) 中的控制項狀態。</p> <pre><code>''' &lt;summary&gt; ''' 依 FormViewMode 來設定控制項狀態。 ''' &lt;/summary&gt; &lt; _ Serializable(), _ TypeConverter(GetType(ExpandableObjectConverter)) _ &gt; _ Public Class TBFormViewModeState    Private FInsertMode As EControlState = EControlState.NotSet    Private FEditMode As EControlState = EControlState.NotSet    Private FBrowseMode As EControlState = EControlState.NotSet    ''' &lt;summary&gt;    ''' 在新增模式(FormViewMode=Insert)的控制項狀態。    ''' &lt;/summary&gt;    &lt; _    NotifyParentProperty(True), _    DefaultValue(GetType(EControlState), &quot;NotSet&quot;) _    &gt; _    Public Property InsertMode() As EControlState        Get            Return FInsertMode        End Get        Set(ByVal value As EControlState)            FInsertMode = value        End Set    End Property    ''' &lt;summary&gt;    ''' 在編輯模式(FormViewMode=Edit)的控制項狀態。    ''' &lt;/summary&gt;    &lt; _    NotifyParentProperty(True), _    DefaultValue(GetType(EControlState), &quot;NotSet&quot;) _    &gt; _    Public Property EditMode() As EControlState        Get            Return FEditMode        End Get        Set(ByVal value As EControlState)            FEditMode = value        End Set    End Property    ''' &lt;summary&gt;    ''' 在瀏覽模式(FormViewMode=ReadOnly)的控制項狀態。    ''' &lt;/summary&gt;    &lt; _    NotifyParentProperty(True), _    DefaultValue(GetType(EControlState), &quot;NotSet&quot;) _    &gt; _    Public Property BrowseMode() As EControlState        Get            Return FBrowseMode        End Get        Set(ByVal value As EControlState)            FBrowseMode = value        End Set    End Property End Class </code></pre> <p>定義為 TBFormViewModeState 型別的屬性是屬於複雜屬性,要套用 TypeConverter(GetType(ExpandableObjectConverter)),讓該屬性可在屬性視窗 (PropertyGrid) 擴展以便設定屬性值,如下圖所示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay27FormViewCurrentMode_E5A/image_thumb_1.png" alt="" /></p> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/28/5806.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-28 13:45:43</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day26] 讓你的 GridView 與眾不同</title>                <link>https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013209?sc=rss.iron</guid>                <description><![CDATA[<p>在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文...]]></description>                                    <content:encoded><![CDATA[<p>在網路上可以找到相當多擴展 GridView 控制項功能的文章,在筆者的部落格中也有多篇提及擴展 GridView、DataControlField、BoundFIeld 功能的相關文章,在本文將這些關於擴展 GridView 控制項功能及欄位類別的相關文章做一整理簡介,若需要擴展 GridView 相關功能時可以做為參考。<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/22/4105.aspx" target="_blank"><strong>1. 擴展 GridView 控制項 - 無資料時顯示標題列</strong></a><br /> 摘要:當 GridView 繫結的 DataSource 資料筆數為 0 時,會依 EmptyDataTemplate 及 EmptyDataText 的設定來顯示無資料的狀態。若我們希望 GridView 在無資料時,可以顯示欄位標題,有一種作法是在 EmptyDataTemplate 中手動在設定一個標題列,不過這種作法很麻煩。本文擴展 GridView 控制項,直接透過屬性設定就可以在無資料顯示欄位標題。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridView_DAA6/image_thumb.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/17/4028.aspx" target="_blank"><strong>2. 擴展 GridView 控制項 - 支援 Excel 及 Word 匯出</strong></a><br /> 摘要:GridView 匯出 Excel 及 Word 文件是蠻常使用的需求,此篇文章將擴展 GridView 控制項提供匯出 Excel 及 Word 文件的方法。一般在 GridView 匯出的常見下列問題也會在此一併被解決。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridViewExcelWord_14C22/image_thumb_1.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridViewExcelWord_14C22/image_thumb_2.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/12/3936.aspx" target="_blank"><strong>3. GridView+FormView 示範資料 新增/修改/刪除(進階篇:伺服器控制項)</strong></a><br /> 摘要:擴展 GridView 及 FormView 控制項,在 GridView 控制項中新增 FormViewID 屬性,關連至指定的 FormView 控制項 ID,就可以讓 GridView 結合 FormView 來做資料異動的動作。</p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1766.aspx" target="_blank"><strong>4. 擴展 CommandField 類別 - 刪除提示訊息</strong></a><br /> 摘要:新增 DeleteConfirmMessage 屬性,設定刪除提示確認訊息。<br /> <img src="http://blog.blueshop.com.tw/images/blog_blueshop_com_tw/jeff377/1525/r_Ex19.1.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1767.aspx" target="_blank"><strong>5. 擴展 CommandField 類別 - 刪除提示訊息含欄位值</strong></a><br /> 摘要:設定刪除提示確認訊息中可包含指定 DataField 欄位值,明確提示要刪除的資料列。<br /> <img src="http://blog.blueshop.com.tw/images/blog_blueshop_com_tw/jeff377/1525/r_Ex20.1.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1710.aspx" target="_blank"><strong>6. 讓 CheckBoxField 繫結非布林值(0 或 1)欄位</strong></a><br /> 摘要:CheckBoxField 若繫結的欄位值為 0 或 1 時 (非布林值) 會發生錯誤,本文擴展 CheckBoxField 類別,讓 CheckBoxField 有辨法繫結 0 或 1 的欄位值。</p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/21/4093.aspx" target="_blank"><strong>7. 擴展 CheckBoxField 類別 - 支援非布林值的雙向繫結</strong></a><br /> 摘要:CheckBoxField 繫結的欄位值並無法直接使用 CBool 轉型為布林值,例如 &quot;T/F&quot;、&quot;是/否&quot; 之類的資料,若希望使用 CheckBoxField 來顯示就比較麻煩,一般的作法都是轉為 TemplateField,自行撰寫資料繫結的函式,而且只能支援單向繫結。在本文直接改寫 CheckBoxField 類別,讓 CheckBoxField 可以直接雙向繫結 &quot;T/F&quot; 或 &quot;是/否&quot; 之類的資料。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/CheckBoxField_1471C/image_thumb.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/13/3969.aspx" target="_blank"><strong>8. 擴展 CommandField 類別 - Header 加入新增鈕</strong></a><br /> 摘要:支援在 CommandField 的 Header 的部分加入「新增」鈕,執行新增鈕會引發 RowCommand 事件。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/CommandFieldHeader_13B3E/image_4.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/05/29/4169.aspx" target="_blank"><strong>9. GridView 自動編號欄位 - TBSerialNumberField</strong></a><br /> 摘要:繼承 DataControlField 來撰寫自動編號欄位,若 GridView 需要自動編號欄位時只需加入欄位即可。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0805/GridViewTBSerialNumberField_13347/image_2.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank"><strong>10. 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別</strong></a><br /> 摘要:支援在 GridView 中顯示下拉清單的欄位類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_1.png" alt="" /></p> <p><a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx" target="_blank"><strong>11. 自訂 GridView 欄位 - 日期欄位</strong></a><br /> 摘要:支援在 GridView 中顯示日期下拉選單編輯的欄位類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/27/5793.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/27/5793.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-27 22:37:14</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013091?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值</strong><br /> 當用戶端使用 GridView 編輯後執...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>四、覆寫 ExtractValuesFromCell 方法 - 擷取儲存格的欄位值</strong><br /> 當用戶端使用 GridView 編輯後執行更新動作時,會呼叫 ExtractValuesFromCell 方法,來取得儲存格的欄位值,以便寫入資料來源。所以我們要覆寫 ExtractValuesFromCell 方法,將 Cell 或 TDateEdit 控制項的值取出填入具 IOrderedDictionary 介面的物件。</p> <pre><code>        ''' &lt;summary&gt;        ''' 使用指定 DataControlFieldCell 的值填入指定的 IDictionary 物件。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Dictionary&quot;&gt;用於儲存指定儲存格的值。&lt;/param&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;包含要擷取值的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列的狀態。&lt;/param&gt;        ''' &lt;param name=&quot;IncludeReadOnly&quot;&gt;true 表示包含唯讀欄位的值,否則為 false。&lt;/param&gt;        Public Overrides Sub ExtractValuesFromCell( _            ByVal Dictionary As IOrderedDictionary, _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState, _            ByVal IncludeReadOnly As Boolean)            Dim oControl As Control = Nothing            Dim sDataField As String = Me.DataField            Dim oValue As Object = Nothing            Dim sNullDisplayText As String = Me.NullDisplayText            Dim oDateEdit As TBDateEdit            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then                If (Cell.Controls.Count &gt; 0) Then                    oControl = Cell.Controls.Item(0)                    oDateEdit = TryCast(oControl, TBDateEdit)                    If (Not oDateEdit Is Nothing) Then                        oValue = oDateEdit.Text                    End If                ElseIf IncludeReadOnly Then                    Dim s As String = Cell.Text                    If (s = &quot; &quot;) Then                        oValue = String.Empty                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then                        oValue = HttpUtility.HtmlDecode(s)                    Else                        oValue = s                    End If                End If                If (Not oValue Is Nothing) Then                    If TypeOf oValue Is String Then                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then                            oValue = Nothing                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length &gt; 0) Then                            oValue = Nothing                        End If                    End If                    If Dictionary.Contains(sDataField) Then                        Dictionary.Item(sDataField) = oValue                    Else                        Dictionary.Add(sDataField, oValue)                    End If                End If            End If        End Sub </code></pre> <p><strong>五、測試程式</strong><br /> 我們使用 Northwnd 資料庫的 Employees 資料表為例,在 GridView 加入自訂的 TBDateField 欄位繫結 BirthDate 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 BirthDate 欄位來做比較。</p> <pre><code>            &lt;bee:TBDateField DataField=&quot;BirthDate&quot; HeaderText=&quot;BirthDate&quot;                SortExpression=&quot;BirthDate&quot; DataFormatString=&quot;{0:d}&quot;                ApplyFormatInEditMode=&quot;True&quot; CalendarStyle=&quot;Winter&quot; /&gt;                        &lt;asp:BoundField DataField=&quot;BirthDate&quot; HeaderText=&quot;BirthDate&quot;                SortExpression=&quot;BirthDate&quot; DataFormatString=&quot;{0:d}&quot;                ApplyFormatInEditMode=&quot;True&quot; ReadOnly=&quot;true&quot; /&gt; </code></pre> <p>執行程式,在編輯資料列時,TBDateField 就會以 TDateEdit 控制項來進行編輯。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb_2.png" alt="" /></p> <p>使用 TDateEdit 編輯欄位值後,按「更新」鈕,資料就會被寫回資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb_3.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/26/5777.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-26 17:04:50</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day25] 自訂 GridView 欄位 - 日期欄位</title>                <link>https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013083?sc=rss.iron</guid>                <description><![CDATA[<p>前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中...]]></description>                                    <content:encoded><![CDATA[<p>前二篇文章介紹了自訂 GridView 使用的下拉清單欄位 (TBDropDownField),對如何繼承 BoundField 類別下來改寫自訂欄位應該有進一步的了解。在 GridView 中輸入日期也常蠻常見的需求,在本文將再實作一個 GridView 使用的日期欄位,在欄位儲存格使用 TBDateEdit 控制項來編輯資料。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081026164615771.rar" target="_blank">ASP.NET Server Control - Day25.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、繼承 TBBaseBoundField 實作 TDateField</strong><br /> GridView 的日期欄位需要繫結資料,一般的作法是由 BoundField 繼承下來改寫;不過我們之前已經有繼承 BoundField 製作一個 TBBaseBoundField 的自訂欄位基底類別 (詳見「 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">[ASP.NET 控制項實作 Day23] 自訂 GridVie 欄位類別 - 實作 TBDropDownField 欄位類別</a>」 一文),所以我們要實作的日期欄位直接繼承 TBBaseBoundField 命名為 TDateField,並覆寫 CreateField 方法,傳回 TDateField 物件。</p> <pre><code>    ''' &lt;summary&gt;    ''' 日期欄位。    ''' &lt;/summary&gt;    Public Class TBDateField        Inherits TBBaseBoundField        Protected Overrides Function CreateField() As DataControlField            Return New TBDateField()        End Function    End Class </code></pre> <p>自訂欄位類別主要是要覆寫 InitializeDataCell 方法做資料儲存格初始化、覆寫 OnDataBindField 方法將欄位值繫結至 BoundField 物件、覆寫 ExtractValuesFromCell 方法來擷取儲存格的欄位值,下面我們將針對這幾個需要覆寫的方法做一說明。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay25TBDropDownFieldItems_6B94/image_thumb.png" alt="" /></p> <p><strong>二、覆寫 InitializeDataCell 方法 - 資料儲存格初始化</strong><br /> 首先覆寫 InitializeDataCell 方法處理資料儲存格初始化,當唯讀狀態時使用 Cell 來呈現資料;若為編輯狀態時,則在 Cell 中加入 TBDateEdit 控制項,並將 TBDateField 的屬性設定給 TBDateEdit 控制項的相關屬性。然後將儲存格 (DataControlFieldCell) 或日期控制項 (TDateEdit) 的 DataBinding 事件導向 OnDataBindField 事件處理方法。</p> <pre><code>        ''' &lt;summary&gt;        ''' 資料儲存格初始化。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;要初始化的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Protected Overrides Sub InitializeDataCell(ByVal Cell As DataControlFieldCell, ByVal RowState As DataControlRowState)            Dim oDateEdit As TBDateEdit            Dim oControl As Control            If Me.CellIsEdit(RowState) Then                '編輯狀態在儲存格加入 TBDateEdit 控制項                oDateEdit = New TBDateEdit()                oDateEdit.FirstDayOfWeek = Me.FirstDayOfWeek                oDateEdit.ShowWeekNumbers = Me.ShowWeekNumbers                oDateEdit.CalendarStyle = Me.CalendarStyle                oDateEdit.Lang = Me.Lang                oDateEdit.ShowTime = Me.ShowTime                oControl = oDateEdit                Cell.Controls.Add(oControl)            Else                oControl = Cell            End If            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)            End If        End Sub </code></pre> <p>TDateEdit 控制項為筆者自行撰寫的日期控制項,TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明。<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1742.aspx" target="_blank">日期控制項實作教學(1) - 結合 JavaScript</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1743.aspx" target="_blank">日期控制項實作教學(2) - PostBack 與 事件</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1746.aspx" target="_blank">TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)</a><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb_1.png" alt="" /></p> <p><strong>三、覆寫 OnDataBindField 方法 - 將欄位值繫結至 BoundField 物件</strong><br /> 當 GridView 執行 DataBind 時,每個儲存格的 DataBinding 事件都會被導向 OnDataBindField 方法,此方法中我們會由資料來源取得指定欄位值,處理此欄位值的格式化時,將欄位值呈現在 Cell 或 TDateEdit 控制項上。</p> <pre><code>        ''' &lt;summary&gt;        ''' 將欄位值繫結至 BoundField 物件。        ''' &lt;/summary&gt;        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)            Dim oControl As Control            Dim oDateEdit As TBDateEdit            Dim oNamingContainer As Control            Dim oDataValue As Object            '欄位值            Dim bEncode As Boolean              '是否編碼            Dim sText As String                 '格式化字串            oControl = DirectCast(sender, Control)            oNamingContainer = oControl.NamingContainer            oDataValue = Me.GetValue(oNamingContainer)            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)            sText = Me.FormatDataValue(oDataValue, bEncode)            If TypeOf oControl Is TableCell Then                If (sText.Length = 0) Then                    sText = &quot; &quot;                End If                DirectCast(oControl, TableCell).Text = sText            Else                If Not TypeOf oControl Is TBDateEdit Then                    Throw New HttpException(String.Format(&quot;{0}: Wrong Control Type&quot;, Me.DataField))                End If                oDateEdit = DirectCast(oControl, TBDateEdit)                If Me.ApplyFormatInEditMode Then                    oDateEdit.Text = sText                ElseIf (Not oDataValue Is Nothing) Then                    oDateEdit.Text = oDataValue.ToString                End If            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-26 16:56:36</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013047?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>三、由關連的資料來源擷取資料</strong><br /> 再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>三、由關連的資料來源擷取資料</strong><br /> 再來就是重點就是要處理 PerformSelecrt 私有方法,來取得 Items 屬性的成員清單內容。PerformSelect 方法的作用是去尋找頁面上的具 IDataSource 介面的控制項,並執行此資料來源的 Select 方法,以取得資料來設定 Items 的清單內容。<br /> <strong>step1. 尋找資料來源控制項</strong><br /> PerformSelect 方法中有使用 FindControlEx 方法,它是自訂援尋控制項的多載方法,是取代 FindControl 進階方法。程式碼中使用 FindControlEx 去是頁面中以遞迴方式尋找具有 IDataSource 介面的控制項,且 ID 屬性值為 TBDropDownList.ID 的屬性值。<br /> <strong>step2. 執行資料來源控制項的 Select 方法</strong><br /> 當找到資料來源控制項後 (如 SqlDataSource、ObjectDataSource ...等等),執行其 DataSourceView.Select 方法,此方法需入一個 DataSourceViewSelectCallback 函式當作參數,當資料來源控制項取得資料後回呼我們指定的 OnDataSourceViewSelectCallback 函式中做後序處理。<br /> <strong>step3. 將取得的資料來設定生 Items 的清單內容</strong><br /> 在 OnDataSourceViewSelectCallback 函式中接到回傳的具 IEnumerable 介面的資料,有可能是 DataView、DataTable ...等型別的資料。利用 DataBinder.GetPropertyValue 來取得 DataTextField 及 DataValueField 設定的欄位值,逐一建立 ListItem 項目,並加入 Items 集合屬性中。</p> <pre><code>        ''' &lt;summary&gt;        ''' 從關聯的資料來源擷取資料。        ''' &lt;/summary&gt;        Private Sub PerformSelect()            Dim oControl As Control            Dim oDataSource As IDataSource            Dim oDataSourceView As DataSourceView            '若未設定 DataSourceID 屬性則離開            If StrIsEmpty(Me.DataSourceID) Then Exit Sub            '找到具 IDataSource 介面的控制項            oControl = FindControlEx(Me.Control.Page, GetType(IDataSource), &quot;ID&quot;, Me.DataSourceID)            If oControl Is Nothing Then Exit Sub            oDataSource = DirectCast(oControl, IDataSource)            oDataSourceView = oDataSource.GetView(String.Empty)            oDataSourceView.Select(DataSourceSelectArguments.Empty, _                        New DataSourceViewSelectCallback(AddressOf Me.OnDataSourceViewSelectCallback))        End Sub        ''' &lt;summary&gt;        ''' 擷取資料的回呼函式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;data&quot;&gt;取得的資料。&lt;/param&gt;        Private Sub OnDataSourceViewSelectCallback(ByVal data As IEnumerable)            Dim oCollection As ICollection            Dim oValue As Object            Dim oItem As ListItem            Me.Items.Clear()            If data Is Nothing Then Exit Sub            oCollection = TryCast(data, ICollection)            Me.Items.Capacity = oCollection.Count            For Each oValue In data                oItem = New ListItem()                If StrIsNotEmpty(Me.DataTextField) Then                    oItem.Text = DataBinder.GetPropertyValue(oValue, DataTextField, Nothing)                End If                If StrIsNotEmpty(Me.DataValueField) Then                    oItem.Value = DataBinder.GetPropertyValue(oValue, DataValueField, Nothing)                End If                Me.Items.Add(oItem)            Next        End Sub </code></pre> <p><strong>四、測試程式</strong><br /> 使用上篇中同一個案例做測試,同樣以 Northwnd 資料庫的 Products 資料表為例。在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,並設定 DataSourceID、DataTextField、DataValueField 屬性;另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。</p> <pre><code>                &lt;bee:TBDropDownField  HeaderText=&quot;CategoryID&quot;                      SortExpression=&quot;CategoryID&quot; DataField=&quot;CategoryID&quot;                    DataTextField=&quot;CategoryName&quot; DataValueField=&quot;CategoryID&quot; DataSourceID=&quot;SqlDataSource2&quot;&gt;                &lt;/bee:TBDropDownField&gt;                &lt;asp:BoundField DataField=&quot;CategoryID&quot; HeaderText=&quot;CategoryID&quot;                    SortExpression=&quot;CategoryID&quot;  ReadOnly=&quot;true&quot; /&gt; </code></pre> <p>執行程式,在 GridView 瀏覽的模式時,TBDropDownField 的儲存格已經會呈現 Items 對應成員的顯示文字。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay24TBDropDownField_73B8/image_thumb_1.png" alt="" /></p> <p>執行資料列編輯時,也可以正常顯示下拉清單的內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay24TBDropDownField_73B8/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-25 18:11:28</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day24] TBDropDownField 的 Items 屬性的資料繫結</title>                <link>https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10013041?sc=rss.iron</guid>                <description><![CDATA[<p>上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDro...]]></description>                                    <content:encoded><![CDATA[<p>上篇中我們實作了 GridView 的 TBDropDownField 欄位類別,不過眼尖的讀者不知有沒有發覺我們並處理 Items 屬性取得成員清單的動作,而是直接設定儲存格內含的 TBDropDownList 控制項相關屬性 (DataSourceID、DataTextField、DataValueField 屬性) 後,就由 TDropDownList 控制項自行處理 Items 屬性的資料繫結。當 GridView 的資料列是編輯狀態時,下拉清單會顯示出 Items 的文字內容;可是瀏覽狀態的資料列,卻是顯示欄位原始值,無法呈現 Items 的文字內容。本文將說明如何自行處理 TBDropDownField 的 Items 屬性的資料繫結動作,並使唯讀狀態的資料列也可以呈現 Items 的文字內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay24TBDropDownField_73B8/image_thumb.png" alt="" /><br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081025163920497.rar" target="_blank">ASP.NET Server Control - Day24.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、Items 屬性的問題</strong><br /> 我們重新看一次原本 TBDropDownField 類別在處理 Items 屬性的資料繫結取得清單內容的程式碼,在覆寫 InitializeDataCell 方法中,當儲存格為編輯模式時,會呈現 TBDropDownList 控制項並設定取得 Items 清單內容的相關屬性,讓 TBDropDownList 自行去處理它的 Items 屬性的清單內容。</p> <pre><code>'由資料來源控制項取得清單項目 oDropDownList.DataSourceID = Me.DataSourceID oDropDownList.DataTextField = Me.DataTextField oDropDownList.DataValueField = Me.DataValueField </code></pre> <p>不知你有沒有發覺,我們無論在 InitializeDataCell 及 OnDataBindField 方法中,都沒有針對 TBDropDownList 控制項做任何 DataBind 動作,那它是怎麼從 DataSourceID 關聯的資料來源擷取資料呢?因為 GridView 在執行 DataBind 時,就會要求所有的子控制項做 DataBind,所以我們只要設定好 BDropDownList 控制項相關屬性後,當 TBDropDownList 自動被要求資料繫結時就會取得 Items 的清單內容。<br /> 當然使用 TBDropDownList 控制項去處理 Items 的資料繫結動作最簡單,可是這樣唯讀的儲存格只能顯示原始欄位值,無法呈現 Items 中對應成員的文字;除非無論唯讀或編輯狀態,都要建立 TBDropDownList 控制項去取得 Items 清單內容,而唯讀欄位使用 TBDropDownList.Items 去找到對應成員的顯示文字,不過這樣的作法會怪怪的,而且沒有執行效能率。所以比較好的辨法,就是由 TBDropDownField 類別自行處理 Items 的資料繫結,同時提供給唯讀狀態的<br /> DataControlFieldCell 及編輯狀態的 TBDropDownList 使用。</p> <p><strong>二、由 TBDropDownField 類別處理 Items 屬性的資料繫結</strong><br /> 我們要自行處理 Items 屬性來取得成員清單,在 InitializeDataCell 方法中無須處理 Items 屬性,只需產生儲存格需要的子控制項,未來在執行子控制項的 DataBinding 時的 OnDataBindField 方法中再來處理 Items 屬性。</p> <pre><code>        Protected Overrides Sub InitializeDataCell( _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState)            Dim oDropDownList As TBDropDownList            Dim oControl As Control            If Me.CellIsEdit(RowState) Then                oDropDownList = New TBDropDownList()                oControl = oDropDownList                Cell.Controls.Add(oControl)            Else                oControl = Cell            End If            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)            End If        End Sub </code></pre> <p>在 OnDataBindField 方法中,我們加上一段處理 Items 屬性的程式碼如下,會利用 PerformSelecrt 私有方法,由關聯的資料來源 (即 DataSrouceID 指定的資料來源控制項) 擷取資料並產生 Items 的成員清單,在後面會詳細講解 PerformSelecrt 方法處理擷取資料的細節。因為 TBDropDownField 每個資料儲存格都會執行 OnDataBindField 方法,但 Items 取得成員清單的動作只需做一次即可,所以會以 FIsPerformSelect 區域變數來判斷是否已取得 Items 的成員清單,若已取過就不重新取得,這樣比較有執行效能。</p> <pre><code>            If Not Me.DesignMode Then                If Not FIsPerformSelect Then                    '從關聯的資料來源擷取資料                    PerformSelect()                    FIsPerformSelect = True                End If            End If </code></pre> <p>當取得儲存儲的對應的欄位值時,依此欄位值由 Items 集合去取得對應的 ListItem 成員,並以此 ListItem.Text 的文字內容來做顯示。</p> <pre><code>            '由 Items 去取得對應成員的顯示內容            oListItem = Me.Items.FindByValue(CCStr(sText))            If oListItem IsNot Nothing Then                sText = oListItem.Text            End If </code></pre> <p>若是由 TBDropDownList 所引發的 OnDataBindField 方法時,使用 SetItems 私有方法將 TBDropDownField.Items 屬性複製給 TBDropDownList.Item 屬性。</p> <pre><code>                ODropDownList = DirectCast(oControl, TBDropDownList)                SetItems(ODropDownList) </code></pre> <p>SetItems 私有方法的程式碼如下。</p> <pre><code>        Private Sub SetItems(ByVal DropDownList As TBDropDownList)            Dim oItems() As ListItem            If Not Me.DesignMode Then                ReDim oItems(Me.Items.Count - 1)                Me.Items.CopyTo(oItems, 0)                DropDownList.Items.AddRange(oItems)            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/25/5772.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-25 18:09:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續3)</title>                <link>https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012977?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>四、測試程式</strong><br /> 辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>四、測試程式</strong><br /> 辛苦寫好 TBDropDownField 欄位類別時,接下來就是驗收成果的時候。我們以 Northwnd 資料庫的 Products 資料表為例,將 TBDropDownList .DataField 設為 CategoryID 欄位來做測試。首先我們測試沒有 DataSoruceID 的情況,在 GridView 加入自訂的 TBDropDownField 欄位繫結 CategoryID 欄位,另外加入另一個 BoundField 的唯讀欄位,也同樣繫結 CategoryID 欄位來做比較。</p> <pre><code>                &lt;bee:TBDropDownField  HeaderText=&quot;CategoryID&quot;                      SortExpression=&quot;CategoryID&quot; DataField=&quot;CategoryID&quot; &gt;                    &lt;Items&gt;                    &lt;asp:ListItem Value=&quot;&quot;&gt;未對應&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;2&quot;&gt;Condiments&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;3&quot;&gt;Confections&lt;/asp:ListItem&gt;                    &lt;/Items&gt;                &lt;/bee:TBDropDownField&gt;                &lt;asp:BoundField DataField=&quot;CategoryID&quot; HeaderText=&quot;CategoryID&quot;                    SortExpression=&quot;CategoryID&quot;  ReadOnly=&quot;true&quot; /&gt; </code></pre> <p>執行程式,在 GridView 在唯讀模式,TBDropDownFIeld 可以正確的繫結 CategoryID 欄位值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb.png" alt="" /></p> <p>編輯某筆資料列進入編輯狀態,就會顯示 TBDropDownList 控制項,清單成員為我們在 Items 設定的內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_1.png" alt="" /></p> <p>使用 TBDropDownList 來做編輯欄位值,按下更新鈕,這時會執行 TBDropDownField.ExtractValuesFromCell 方法,取得儲存格中的值;最後由資料來源控制項將欄位值寫回資料庫。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_2.png" alt="" /></p> <p>接下來測試設定 TBDropDownField.DataSourceID 的情況,把 DataSourcID 指向含 Categories 資料表內容的 SqlDataSoruce 控制項。</p> <pre><code>                &lt;bee:TBDropDownField  HeaderText=&quot;CategoryID&quot;                      SortExpression=&quot;CategoryID&quot; DataField=&quot;CategoryID&quot;                    DataTextField=&quot;CategoryName&quot; DataValueField=&quot;CategoryID&quot; DataSourceID=&quot;SqlDataSource2&quot;&gt;                &lt;/bee:TBDropDownField&gt; </code></pre> <p>執行程式查看結果,可以發現 TBDropDownList 控制項的清單內容也可以正常顯示 SqlDataSoruce 控制項取得資料。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay23DataControlField_67E8/image_thumb_3.png" alt="" /></p> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:32:30</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續2)</title>                <link>https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012973?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>step4. 處理資料繫結</strong><br /> 當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>step4. 處理資料繫結</strong><br /> 當 GridView 控制項在執行資料繫結時,儲存格的控制項就會引發 DataBinding 事件,而這些事件會被導向 OnDataBindField 方法來統一處理儲存格中控制項的繫結動作。</p> <pre><code>       ''' &lt;summary&gt;        ''' 將欄位值繫結至 BoundField 物件。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;sender&quot;&gt;控制項。&lt;/param&gt;        ''' &lt;param name=&quot;e&quot;&gt;事件引數。&lt;/param&gt;        Protected Overrides Sub OnDataBindField(ByVal sender As Object, ByVal e As EventArgs)            Dim oControl As Control            Dim ODropDownList As TBDropDownList            Dim oNamingContainer As Control            Dim oDataValue As Object            '欄位值            Dim bEncode As Boolean              '是否編碼            Dim sText As String                 '格式化字串            oControl = DirectCast(sender, Control)            oNamingContainer = oControl.NamingContainer            oDataValue = Me.GetValue(oNamingContainer)            bEncode = ((Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) AndAlso TypeOf oControl Is TableCell)            sText = Me.FormatDataValue(oDataValue, bEncode)            If TypeOf oControl Is TableCell Then                If (sText.Length = 0) Then                    sText = &quot; &quot;                End If                DirectCast(oControl, TableCell).Text = sText            Else                If Not TypeOf oControl Is TBDropDownList Then                    Throw New HttpException(String.Format(&quot;{0}: Wrong Control Type&quot;, Me.DataField))                End If                ODropDownList = DirectCast(oControl, TBDropDownList)                If Me.ApplyFormatInEditMode Then                    ODropDownList.Text = sText                ElseIf (Not oDataValue Is Nothing) Then                    ODropDownList.Text = oDataValue.ToString                End If            End If        End Sub </code></pre> <p><strong>step5. 取得儲存格中的值</strong><br /> 另外我們還需要覆寫 ExtractValuesFromCell 方法,取得儲存格中的值。這個方法是當 GridView 的編輯資料要準備寫入資料庫時,會經由 ExtractValuesFromCell 方法此來取得每個儲存格的值,並將這些欄位值加入 Dictionary 參數中,這個準備寫入的欄位值集合,可以在 DataSource 控制項的寫入資料庫的相關方法中取得使用。</p> <pre><code>        ''' &lt;summary&gt;        ''' 使用指定 DataControlFieldCell 物件的值填入指定的 System.Collections.IDictionary 物件。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Dictionary&quot;&gt;用於儲存指定儲存格的值。&lt;/param&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;包含要擷取值的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列的狀態。&lt;/param&gt;        ''' &lt;param name=&quot;IncludeReadOnly&quot;&gt;true 表示包含唯讀欄位的值,否則為 false。&lt;/param&gt;        Public Overrides Sub ExtractValuesFromCell( _            ByVal Dictionary As IOrderedDictionary, _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState, _            ByVal IncludeReadOnly As Boolean)            Dim oControl As Control = Nothing            Dim sDataField As String = Me.DataField            Dim oValue As Object = Nothing            Dim sNullDisplayText As String = Me.NullDisplayText            Dim oDropDownList As TBDropDownList            If (((RowState And DataControlRowState.Insert) = DataControlRowState.Normal) OrElse Me.InsertVisible) Then                If (Cell.Controls.Count &gt; 0) Then                    oControl = Cell.Controls.Item(0)                    oDropDownList = TryCast(oControl, TBDropDownList)                    If (Not oDropDownList Is Nothing) Then                        oValue = oDropDownList.Text                    End If                ElseIf IncludeReadOnly Then                    Dim s As String = Cell.Text                    If (s = &quot; &quot;) Then                        oValue = String.Empty                    ElseIf (Me.SupportsHtmlEncode AndAlso Me.HtmlEncode) Then                        oValue = HttpUtility.HtmlDecode(s)                    Else                        oValue = s                    End If                End If                If (Not oValue Is Nothing) Then                    If TypeOf oValue Is String Then                        If (CStr(oValue).Length = 0) AndAlso Me.ConvertEmptyStringToNull Then                            oValue = Nothing                        ElseIf (CStr(oValue) = sNullDisplayText) AndAlso (sNullDisplayText.Length &gt; 0) Then                            oValue = Nothing                        End If                    End If                    If Dictionary.Contains(sDataField) Then                        Dictionary.Item(sDataField) = oValue                    Else                        Dictionary.Add(sDataField, oValue)                    End If                End If            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:31:32</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位(續1)</title>                <link>https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012971?sc=rss.iron</guid>                <description><![CDATA[<p>接續上一文<br /> <strong>step2. 加入 TBBaseBoundField 的屬性</strong><br /> TBBaseBoundField 類別會內含 DropDown...]]></description>                                    <content:encoded><![CDATA[<p>接續上一文<br /> <strong>step2. 加入 TBBaseBoundField 的屬性</strong><br /> TBBaseBoundField 類別會內含 DropDownList 控制項,所以加入設定 DropDownList 控制項的對應屬性;我們在 TBBaseBoundField 類別加入了 Items 、DataSourceID、DataTextField、DataValueField 屬性。其中 Items 屬性的型別與 DropDownList.Items 屬性相同,都是 ListItemCollection 集合類別,且 Items 屬性會儲存於 ViewState 中。</p> <pre><code>        ''' &lt;summary&gt;        ''' 清單項目集合。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;清單項目集合。&quot;), _        DefaultValue(CStr(Nothing)), _        PersistenceMode(PersistenceMode.InnerProperty), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        Editor(GetType(ListItemsCollectionEditor), GetType(UITypeEditor)), _        MergableProperty(False), _        Category(&quot;Default&quot;)&gt; _        Public Overridable ReadOnly Property Items() As ListItemCollection            Get                If (FItems Is Nothing) Then                    FItems = New ListItemCollection()                    If MyBase.IsTrackingViewState Then                        CType(FItems, IStateManager).TrackViewState()                    End If                End If                Return FItems            End Get        End Property        ''' &lt;summary&gt;        ''' 資料來源控制項的 ID 屬性。        ''' &lt;/summary&gt;        Public Property DataSourceID() As String            Get                Return FDataSourceID            End Get            Set(ByVal value As String)                FDataSourceID = value            End Set        End Property        ''' &lt;summary&gt;        ''' 提供清單項目文字內容的資料來源的欄位。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;提供清單項目文字內容的資料來源的欄位。&quot;), _        DefaultValue(&quot;&quot;) _        &gt; _        Public Property DataTextField() As String            Get                Return FDataTextField            End Get            Set(ByVal value As String)                FDataTextField = value            End Set        End Property        ''' &lt;summary&gt;        ''' 提供清單項目值的資料來源的欄位。        ''' &lt;/summary&gt;        Public Property DataValueField() As String            Get                Return FDataValueField            End Get            Set(ByVal value As String)                FDataValueField = value            End Set        End Property </code></pre> <p><strong>step3.建立儲存格內含的控制項</strong><br /> GridView 是以儲存格 (DataControlFieldCell) 為單位,我們要覆寫 InitializeDataCell 方法來建立儲存格中的控制項;當儲存格為可編輯狀態時,就建立 DropDownList 控制項並加入儲存格中,在此使用上篇文章提及的 TBDropDownList 控制項來取代,以解決清單成員不存在造成錯誤的問題。若未設定 DataSourceID 屬性時,則由 Items 屬性取得自訂的清單項目;若有設定 DataSourceID 屬性,則由資料來源控制項 (如 SqlDataSource、ObjectDataSource 控制項) 來取得清單項目。<br /> 當建立儲存格中的控制項後,需要以 AddHeadler 的方法,將此控制項的 DataBinding 事件導向 OnDataBindField 這個事件處理方法,我們要在 OnDataBindField 處理資料繫結的動作。</p> <pre><code>        ''' &lt;summary&gt;        ''' 資料儲存格初始化。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;要初始化的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Protected Overrides Sub InitializeDataCell( _            ByVal Cell As DataControlFieldCell, _            ByVal RowState As DataControlRowState)            Dim oDropDownList As TBDropDownList            Dim oItems() As ListItem            Dim oControl As Control            If Me.CellIsEdit(RowState) Then                oDropDownList = New TBDropDownList()                oControl = oDropDownList                Cell.Controls.Add(oControl)                If Not Me.DesignMode Then                    If StrIsEmpty(Me.DataSourceID) Then                        '自訂清單項目                        ReDim oItems(Me.Items.Count - 1)                        Me.Items.CopyTo(oItems, 0)                        oDropDownList.Items.AddRange(oItems)                    Else                        '由資料來源控制項取得清單項目                        oDropDownList.DataSourceID = Me.DataSourceID                        oDropDownList.DataTextField = Me.DataTextField                        oDropDownList.DataValueField = Me.DataValueField                    End If                End If            Else                oControl = Cell            End If            If (oControl IsNot Nothing) AndAlso MyBase.Visible Then                AddHandler oControl.DataBinding, New EventHandler(AddressOf Me.OnDataBindField)            End If        End Sub </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:25:02</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day23] 自訂GridVie欄位-實作TBDropDownField欄位</title>                <link>https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012965?sc=rss.iron</guid>                <description><![CDATA[<p>GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField...]]></description>                                    <content:encoded><![CDATA[<p>GridView 是 ASP.NET 中一個相當常用的控制項,在 GridView 可加入 BoundField、CheckBoxField、CommandField、TemplateField ... 等不同型別的欄位,可是偏偏沒有提供在 GridView 中可呈現 DropDownList 的欄位型別;遇到這類需求時,一般的作法都是使用 TemplateField 來處理。雖然 TemplateField 具有相當好的設計彈性。可是在當 GridView 需要動態產生欄位的需求時,TemplateField 就相當麻煩,要寫一堆程式碼自行去處理資料繫結的動作。相互比較起來,BoundField、CheckBoxField ...等這類事先定義類型的欄位,在 GridView 要動態產生這些欄位就相當方便。如果我們可以把一些常用的 GridView 的欄位,都做成類似 BoundField 一樣,只要設定欄位的屬性就好,這樣使用上就會方便許多,所以在本文將以實作 DropDownList 欄位為例,讓大家了解如何去自訂 GridView 的欄位類別。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102321355642.rar" target="_blank">ASP.NET Server Control - Day23.rar </a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、選擇合適的父類別</strong><br /> 一般自訂 GridView 的欄位類別時,大都是由 DataControlField 或 BoundField 繼承下來改寫。若是欄位不需繫結資料(如 CommandFIeld),可以由 DataControlFIeld 繼承下來,若是欄位需要做資料繫結時(如 CheckBoxFIld,可以直接由 BoundField 繼承下來改寫比較方便。<br /> DataControlField 類別是所有類型欄位的基底類別,BoundField 類別也是由 DataControlField 類別繼承下來擴展了資料繫結部分的功能,所以我們要實作含 DropDownList 的欄位,也是由 BoundField 繼承下來改寫。</p> <p><strong>二、自訂欄位基底類別</strong><br /> 在此我們不直接繼承 BoundFIeld,而是先撰寫一個繼承 BoundField 命名為 TBBaseBoundField 的基底類別,此類別提供一些通用的屬性及方法,使我們更方便去撰寫自訂的欄位類別。</p> <pre><code>    ''' &lt;summary&gt;    ''' 資料欄位基礎類別。    ''' &lt;/summary&gt;    Public MustInherit Class TBBaseBoundField        Inherits BoundField        Private FRowIndex As Integer = 0        ''' &lt;summary&gt;        ''' 資料列是否為編輯模式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Public Function RowStateIsEdit(ByVal RowState As DataControlRowState) As Boolean            Return (RowState And DataControlRowState.Edit) &lt;&gt; DataControlRowState.Normal        End Function        ''' &lt;summary&gt;        ''' 資料列是否為新增模式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Public Function RowStateIsInsert(ByVal RowState As DataControlRowState) As Boolean            Return (RowState And DataControlRowState.Insert) &lt;&gt; DataControlRowState.Normal        End Function        ''' &lt;summary&gt;        ''' 資料列是否為編輯或新增模式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Public Function RowStateIsEditOrInsert(ByVal RowState As DataControlRowState) As Boolean            Return RowStateIsEdit(RowState) OrElse RowStateIsInsert(RowState)        End Function        ''' &lt;summary&gt;        ''' 判斷儲存格是否可編輯(新增/修改)。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Friend Function CellIsEdit(ByVal RowState As DataControlRowState) As Boolean            Return (Not Me.ReadOnly) AndAlso RowStateIsEditOrInsert(RowState)        End Function        ''' &lt;summary&gt;        ''' 資料列索引。        ''' &lt;/summary&gt;        Friend ReadOnly Property RowIndex() As Integer            Get                Return FRowIndex            End Get        End Property        ''' &lt;summary&gt;        ''' 儲存格初始化。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Cell&quot;&gt;要初始化的儲存格。&lt;/param&gt;        ''' &lt;param name=&quot;CellType&quot;&gt;儲存格類型。&lt;/param&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        ''' &lt;param name=&quot;RowIndex&quot;&gt;資料列之以零起始的索引。&lt;/param&gt;        Public Overrides Sub InitializeCell(ByVal Cell As DataControlFieldCell, ByVal CellType As DataControlCellType, _            ByVal RowState As DataControlRowState, ByVal RowIndex As Integer)            FRowIndex = RowIndex            MyBase.InitializeCell(Cell, CellType, RowState, RowIndex)        End Sub        ''' &lt;summary&gt;        ''' 是否需要執行資料繫結。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;RowState&quot;&gt;資料列狀態。&lt;/param&gt;        Friend Function RequiresDataBinding(ByVal RowState As DataControlRowState) As Boolean            If MyBase.Visible AndAlso StrIsNotEmpty(MyBase.DataField) AndAlso RowStateIsEdit(RowState) Then                Return True            Else                Return False            End If        End Function    End Class </code></pre> <p><strong>三、實作 TBDropDownField 欄位類別</strong><br /> <strong>step1. 繼承 TBBaseBoundField 類別</strong><br /> 首先新增一個類別,繼承 TBBaseBoundField 命名為 TBDropDownFIeld 類別,覆寫 CreateField 方法,傳回 TBDropDownFIeld 物件。</p> <pre><code>    Public Class TBDropDownField        Inherits TBBaseBoundField        Protected Overrides Function CreateField() As System.Web.UI.WebControls.DataControlField            Return New TBDropDownField()        End Function    End Class </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/24/5762.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-24 00:19:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012915?sc=rss.iron</guid>                <description><![CDATA[<p>接續上篇文章內容<br /> <strong>三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題</strong><br /> 要解決上述 TBDro...]]></description>                                    <content:encoded><![CDATA[<p>接續上篇文章內容<br /> <strong>三、解決 TBDropDownList 設定 DataSourceID 造成資料無法繫結的問題</strong><br /> 要解決上述 TBDropDownList 設定 DataSourceID 問題,需在設定 SelectedValue 屬性時,若 Items.Count=0 先用一個 FCachedSelectedValue 變數將正確的值先暫存下來,然後覆寫 PerformDataBinding 方法,當 DorpDownList 取得 DataSoruceID 所對應的項目清單內容後,因為這時 Items 的內容才會完整取回,再去設定一次 SelectedValue 屬性就可以正確的繫結資料。</p> <pre><code>    Public Class TBDropDownList        Inherits DropDownList        Private FCachedSelectedValue As String        ''' &lt;summary&gt;        ''' 覆寫 SelectedValue 屬性。        ''' &lt;/summary&gt;        Public Overrides Property SelectedValue() As String            Get                Return MyBase.SelectedValue            End Get            Set(ByVal value As String)                If Me.Items.Count &lt;&gt; 0 Then                    Dim oItem As ListItem = Me.Items.FindByValue(value)                    If (oItem Is Nothing) Then                        Me.SelectedIndex = -1 '當 Items 不存在時                    Else                        MyBase.SelectedValue = value                    End If                Else                    FCachedSelectedValue = value                End If            End Set        End Property        Protected Overrides Sub PerformDataBinding(ByVal data As System.Collections.IEnumerable)            MyBase.PerformDataBinding(data)            'DataSoruceID 資料繫結後再設定 SelectedValue 屬性值            If (Not FCachedSelectedValue Is Nothing) Then                Me.SelectedValue = FCachedSelectedValue            End If        End Sub    End Class </code></pre> <p>重新執行程式,切換到編輯模式時,TBDropDownList 就可以正確的繫結欄位值了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_5.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-23 07:03:54</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day22] 讓 DropDownList 不再因項目清單不存在而造成錯誤</title>                <link>https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012909?sc=rss.iron</guid>                <description><![CDATA[<p>DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面...]]></description>                                    <content:encoded><![CDATA[<p>DropDownList 控制項常常會因為項目清單中不存在繫結的欄位,而發生以下的錯誤訊息。因為繫結資料的不完整或異常就會造成這樣的異常錯誤,在設計上實在是相當困擾,而且最麻煩的是這個錯誤在頁面的程式碼也無法使用 Try ... Catch 方式來略過錯誤。其實最簡單的方式就去直接去修改 DropDownList 控制項,讓 DropDownList 控制項繫結資料時,就算欄位值不存在清單項目中也不要釋出錯誤,本文就要說明如何繼承 DorpDownList 下來修改,來有效解決這個問題。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb.png" alt="" /><br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102365119295.rar" target="_blank">ASP.NET Server Control - Day22.rar</a><br /> Northwnd 資料庫下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102364857537.rar" target="_blank">NORTHWND.rar</a></p> <p><strong>一、覆寫 SelectedValue 屬性解決資料繫結的問題</strong><br /> DropDownList 控制項繫結錯誤的原因,可以由上圖的錯誤訊息可以大概得知是寫入 SelectedValue 屬性時發生的錯誤;所以我們繼承 DorpDownList 下來命名為 TBDropDownList,並覆寫 SelectedValue 屬性來解決這個問題。解決方式是在寫入 SelectedValue 屬性時,先判斷準備寫入的值是否存在項目清單中,存在的話才寫入 SelectedValue 屬性,若不存在則直接設定 SelectedIndex 屬性為 -1。</p> <pre><code>    Public Class TBDropDownList        Inherits DropDownList        ''' &lt;summary&gt;        ''' 覆寫 SelectedValue 屬性。        ''' &lt;/summary&gt;        Public Overrides Property SelectedValue() As String            Get                Return MyBase.SelectedValue            End Get            Set(ByVal value As String)                Dim oItem As ListItem = Me.Items.FindByValue(value)                If (oItem Is Nothing) Then                    Me.SelectedIndex = -1 '當 Items 不存在時                    Exit Property                Else                    MyBase.SelectedValue = value                End If            End Set        End Property    End Class </code></pre> <p>我們以 Northwnd 資料庫的 Products 資料表做為測試資料,事先定義 DropDownList 的 Items 內容,其中第一個加入 &quot;未對應&quot; 的項目,將 SelectedValue 屬性繫結至 CategoryID 欄位。</p> <pre><code>                &lt;bee:TBDropDownList ID=&quot;DropDownList1&quot; runat=&quot;server&quot;                    SelectedValue='&lt;%# Bind(&quot;CategoryID&quot;) %&gt;'&gt;                    &lt;asp:ListItem Value=&quot;&quot;&gt;未對應&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;2&quot;&gt;Condiments&lt;/asp:ListItem&gt;                    &lt;asp:ListItem Value=&quot;3&quot;&gt;Confections&lt;/asp:ListItem&gt;                &lt;/bee:TBDropDownList&gt; </code></pre> <p>當資料的 CategoryID 欄位值不存在於 DropDownList 的 Items 集合屬性中時,就會顯示第一個 &quot;未對應&quot; 的項目。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_1.png" alt="" /></p> <p><strong>二、TBDropDownList 設定 DataSoruceID 產生的問題</strong><br /> 上述的解決方法在筆者的「讓 DropDownList DataBind 不再發生錯誤」一文中已經有提及,不過有讀者發現另一個問題,就是當 DropDownList 設定 DataSourceID 時卻會發生資料無法正常繫結,以下就來解決這個問題。<br /> 我們設定 TBDropDownList 的 DataSoruceID 來取得項目清單的內容,將 DataSourceID 設定為另一個取得 Categories 資料表內容的 SqlDataSource 控制項。</p> <pre><code>                &lt;bee:TBDropDownList ID=&quot;DropDownList1&quot; runat=&quot;server&quot;                    SelectedValue='&lt;%# Bind(&quot;CategoryID&quot;) %&gt;' DataSourceID=&quot;SqlDataSource2&quot;                    DataTextField=&quot;CategoryName&quot; DataValueField=&quot;CategoryID&quot;&gt;                &lt;/bee:TBDropDownList&gt;                &lt;asp:SqlDataSource ID=&quot;SqlDataSource2&quot; runat=&quot;server&quot;                    ConnectionString=&quot;&lt;%$ ConnectionStrings:Northwnd %&gt;&quot;                    SelectCommand=&quot;SELECT CategoryID, CategoryName, Description, Picture FROM Categories&quot;                    ProviderName=&quot;&lt;%$ ConnectionStrings:Northwnd.ProviderName %&gt;&quot; &gt;                &lt;/asp:SqlDataSource&gt; </code></pre> <p>當執行程式時,FormView 原本在瀏覽模式時的 CategoryID 欄位值為 7 (CategoryName 應為 Product)。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_3.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_2.png" alt="" /></p> <p>當按下「編輯」時切換到 EditItemTemplate 時,改用 TBDropDownList 繫結 CategoryID 欄位值,可以這時欲無法繫結正確的值。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay22DropDownList_14F78/image_thumb_4.png" alt="" /></p> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-23 06:59:56</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤(續)</title>                <link>https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012897?sc=rss.iron</guid>                <description><![CDATA[<p>接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文<br /> <strong>step2. 在智慧標籤面板加入屬性項目</strong><br /> DesignerA...]]></description>                                    <content:encoded><![CDATA[<p>接續 [ASP.NET 控制項實作 Day21] 實作控制項智慧標籤 一文<br /> <strong>step2. 在智慧標籤面板加入屬性項目</strong><br /> DesignerActionPropertyItem 類別是設定智慧標籤面上的屬性項目,DesignerActionPropertyItem 建構函式的第一個參數(memberName) 為屬性名稱,這個屬性指的是 TBDateEditActionList 類別中的屬性,所以要在 TBDateEditActionList 新增一個對應的屬性。<br /> 例如在智慧標籤中加入 AutoPostBack 屬性項目,則在 TBDateEditActionList 類別需有一個對應 AutoPostBack 屬性。</p> <pre><code>            oItems.Add(New DesignerActionPropertyItem(&quot;AutoPostBack&quot;, _                &quot;AutoPostBack&quot;, &quot;Behavior&quot;, &quot;是否引發 PostBack 動作。&quot;)) </code></pre> <p>TBDateEditActionList.AutoPostBack 屬性如下,其中 Me.Component 指的是目前的 TDateEdit 控制項,透過 GetPropertyValue 及 SetPropertyValue 方法來存取控制項的指定屬性。</p> <pre><code>        ''' &lt;summary&gt;        ''' 是否引發 PostBack 動作。        ''' &lt;/summary&gt;        Public Property AutoPostBack() As Boolean            Get                Return CType(GetPropertyValue(Me.Component, &quot;AutoPostBack&quot;), Boolean)            End Get            Set(ByVal value As Boolean)                SetPropertyValue(Me.Component, &quot;AutoPostBack&quot;, value)            End Set        End Property    ''' &lt;summary&gt;    ''' 設定物件的屬性值。    ''' &lt;/summary&gt;    ''' &lt;param name=&quot;Component&quot;&gt;屬性值將要設定的物件。&lt;/param&gt;    ''' &lt;param name=&quot;PropertyName&quot;&gt;屬性名稱。&lt;/param&gt;    ''' &lt;param name=&quot;Value&quot;&gt;新值。&lt;/param&gt;    Public Shared Sub SetPropertyValue(ByVal Component As Object, ByVal PropertyName As String, ByVal Value As Object)        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)        Prop.SetValue(Component, Value)    End Sub    ''' &lt;summary&gt;    ''' 取得物件的屬性值。    ''' &lt;/summary&gt;    ''' &lt;param name=&quot;Component&quot;&gt;具有要擷取屬性的物件。&lt;/param&gt;    ''' &lt;param name=&quot;PropertyName&quot;&gt;屬性名稱。&lt;/param&gt;    Public Shared Function GetPropertyValue(ByVal Component As Object, ByVal PropertyName As String) As Object        Dim Prop As PropertyDescriptor = TypeDescriptor.GetProperties(Component).Item(PropertyName)        Return Prop.GetValue(Component)    End Function </code></pre> <p><strong>step3. 在智慧標籤面板加入方法項目</strong><br /> DesignerActionMethodItem 類別是設定智慧標籤面上的方法項目,DesignerActionPropertyItem 建構函式的第二個參數(memberName) 為方法名稱,這個方法指的是 TBDateEditActionList 類別中的方法,所以要在 TBDateEditActionList 新增一個對應的方法。<br /> 例如在智慧標籤中加入 About 方法項目,則在 TBDateEditActionList 類別需有一個對應 About 方法。</p> <pre><code>            oItems.Add(New DesignerActionMethodItem(Me, &quot;About&quot;, _                &quot;關於 TDateEdit 控制項&quot;, &quot;About&quot;, _                &quot;關於 TDateEdit 控制項。&quot;, True)) </code></pre> <p>TBDateEditActionList 的 About 方法只是單純顯示一個訊息視窗,一般你可以在這方法加入任何想在設計階段處理的動作。例如自動產生 GridView 的欄位、在 FormView 加入控制項並自動排版,這些都可以在此實現的。</p> <pre><code>        Public Sub About()            MsgBox(&quot;TDateEdit 是結合 The Coolest DHTML Calendar 日期選擇器實作的控制項&quot;)        End Sub </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-22 18:02:28</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day21] 實作控制項智慧標籤</title>                <link>https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012896?sc=rss.iron</guid>                <description><![CDATA[<p>控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯...]]></description>                                    <content:encoded><![CDATA[<p>控制項通常會把常用屬性或功能顯示在智慧標籤中,提供使用者更簡便的快速設定,例如下圖為 GridView 的智慧。若要製作控制項的智慧標籤,需實作控制項的 ActionList 加入智慧標籤中要顯示的項目,在本文將以 TDateEdit 控制項為例,進一步說明控制項的智慧標籤的實作方式。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb.png" alt="" /><br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008102217453969.rar" target="_blank">ASP.NET Server Control - Day21.rar</a></p> <p><strong>一、TDateEdit 控制項介紹</strong><br /> TDateEdit 控制項是筆者之前在部落格中實作的一個日期控制項,如下圖所示。它是結合 JavaScript 的 The Coolest DHTML Calendar 日期選擇器實作的控制項,我已將 TDateEdit 控制項的相關程式碼含入 Bee.Web.dll 組件中。TDateEdit 控制項的相關細節可以參考筆者部落格下面幾篇文章有進一步說明,本文將以 TDateEdit 控制項為例,只針對實作智慧標籤的部分做進一步說明。<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1742.aspx" target="_blank">日期控制項實作教學(1) - 結合 JavaScript</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1743.aspx" target="_blank">日期控制項實作教學(2) - PostBack 與 事件</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1746.aspx" target="_blank">TBDateEdit 日期控制項 - 1.0.0.0 版 (Open Source)</a><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb_1.png" alt="" /></p> <p><strong>二、控制項加入智慧標籤</strong><br /> 控制項要加入智慧標籤要實作控制項的 Designer,我們繼承 ControlDesigner 命名為 TBDateEditDesigner,然後覆寫 ActionLists 屬性,此屬性即是傳回智慧標籤中所包含的項目清單集合。在 ActionLists 屬性中一般會先加入父類別的 ActionLists 屬性,再加入自訂的 ActionList 類別,這樣才可以保留原父類別中智慧標籤的項目清單。</p> <pre><code>    ''' &lt;summary&gt;    ''' TBDateEdit 控制項的設計模式行為。    ''' &lt;/summary&gt;    Public Class TBDateEditDesigner        Inherits System.Web.UI.Design.ControlDesigner        ''' &lt;summary&gt;        ''' 取得控制項設計工具的動作清單集合。        ''' &lt;/summary&gt;        Public Overrides ReadOnly Property ActionLists() As DesignerActionListCollection            Get                Dim oActionLists As New DesignerActionListCollection()                oActionLists.AddRange(MyBase.ActionLists)                oActionLists.Add(New TBDateEditActionList(Me))                Return oActionLists            End Get        End Property    End Class </code></pre> <p>我們自訂的 ActionList 為 TBDateEditActionList 類別,它在智慧標籤呈現的項目清單如下圖所示,接下去我們會說明 TBDateEditActionList 類別的內容。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay21_6429/image_thumb_2.png" alt="" /></p> <p><strong>三、自訂智慧標籤面板的項目清單集合</strong><br /> DesignerActionList 類別定義用於建立智慧標籤面板的項目清單的基底類別,所以我們首先繼承 DesignerActionList 命名為 TBDateEditActionList。</p> <pre><code>    ''' &lt;summary&gt;    ''' 定義 TBDateEdit 控制項智慧標籤面板的項目清單集合。    ''' &lt;/summary&gt;    Public Class TBDateEditActionList        Inherits DesignerActionList        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        Public Sub New(ByVal owner As ControlDesigner)            MyBase.New(owner.Component)        End Sub    End Class </code></pre> <p>接下來要覆寫 GetSortedActionItems 方法,它會回傳 DesignerActionItemCollection 集合型別,此集合中會傳回要顯示在智慧標籤面板的項目清單集合,所以我們要在 DesignerActionItemCollection 集合中加入我們要呈現的項目清單內容。</p> <pre><code>        ''' &lt;summary&gt;        ''' 傳回要顯示在智慧標籤面板的項目清單集合。        ''' &lt;/summary&gt;        Public Overrides Function GetSortedActionItems() As System.ComponentModel.Design.DesignerActionItemCollection            Dim oItems As New DesignerActionItemCollection()            '在此加入智慧標籤面板的項目清單                      Return oItems        End Function </code></pre> <p><strong>step1. 在智慧標籤面板加入靜態標題項目</strong><br /> 首先介紹 DesignerActionHeaderItem 類別,它是設定靜態標題項目,例如我們在 TDateEdit 的智慧標籤中加入「行為」、「外觀」二個標題項目,其中 DesignerActionHeaderItem 建構函式的 category 參數是群組名稱,我們可以將相關的項目歸類到同一個群組。</p> <pre><code>Dim oItems As New DesignerActionItemCollection() oItems.Add(New DesignerActionHeaderItem(&quot;行為&quot;, &quot;Behavior&quot;)) oItems.Add(New DesignerActionHeaderItem(&quot;外觀&quot;, &quot;Appearance&quot;)) </code></pre> <p>[超過字數限制,下一篇接續本文]</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/22/5749.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-22 18:01:29</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day20] 偵錯設計階段的程式碼</title>                <link>https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012807?sc=rss.iron</guid>                <description><![CDATA[<p>上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Des...]]></description>                                    <content:encoded><![CDATA[<p>上篇我們介紹了自訂 Designer 來輸出控制項設計階段的 HTML 碼,可是若你去對針 Designer 的程式碼下中斷點,你會發覺根本無法偵錯。因為程式在執行階段時期,根本不會執行 Designer 相關類別,所以你在 Designer 類別中下的中斷點完全無效;當然不可能這樣寫程式碼而用感覺去偵錯,本文將告訴你如何去偵錯設計階段的程式碼。<br /> <strong>一、設計階段程式碼的錯誤</strong><br /> 如果撰寫 Designer、Editor、ActionList 等設計階段的程式碼,當這些設計階段的程式碼發生錯誤,可能會發生設計頁面中控制項的錯誤情形,如下圖所示。因為控制項專案本身非啟動專案,在測試網站的設計頁面若控制項發生異常時會直接釋出錯誤,無法偵錯設計階段的程式碼;若真得要偵錯誤設計階段的問題,就要使用另一個 VS2008 來偵錯。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_1.png" alt="" /></p> <p><strong>二、設定起始外部程式</strong><br /> 要偵錯控制項設計階段的程式碼,要先將控制項專案(Bee.Web)設定為啟時專案。然後設定控制項專案的「屬性」,在「偵錯」頁籤中的起始動作選擇「起始外部程式」,選擇 VS2008 的執行檔位置,預設為 C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb.png" alt="" /></p> <p><strong>三、開始偵錯設計階段程式碼</strong><br /> <strong>step1. 控制項專案開始偵錯</strong><br /> 在設計階要偵錯的程式碼下中斷點,在控制項專案按下 F5 開始偵錯,這時會啟動另一個新的 VS2008 執行檔。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_2.png" alt="" /></p> <p><strong>step2. 在新的 VS2008 的工具箱加入控制項</strong><br /> 在新的 VS2008 中新增一個測試網站,在工具箱按右鍵執行「選擇項目」開啟「選擇工具箱項目」視窗,然後按「瀏覽」鈕按選擇控制項組件(Bee.Web.dll),將要偵錯的控制項加入工具箱中。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_4.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_5.png" alt="" /><br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_6.png" alt="" /></p> <p><strong>step3. 將控制項拖曳至頁面做設計動作</strong><br /> 在新的 VS2008 中,將控制項拖曳至頁面,就會開始執行設計階段的程式碼,特定的設計動作就會執行到相對的設計階段程式碼,當執行到之前下的中斷點時就可以開始偵錯了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay20_E7AA/image_thumb_7.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/21/5741.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/21/5741.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-21 00:28:45</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day19] 控制項設計階段的外觀</title>                <link>https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012682?sc=rss.iron</guid>                <description><![CDATA[<p>有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁...]]></description>                                    <content:encoded><![CDATA[<p>有一些控制項在執行階段是不會呈現,也就是說控制項本身在執行階段不會 Render 出 HTML 碼,例如 SqlDataSoruce、ScriptManager 這類控制項;那它們在設計階段的頁面是如何呈現出來呢?本文將針對控制項設計階段的外觀做進一步的說明。<br /> 程式碼下載:ASP.NET Server Control - Day19.rar<br /> <strong>一、控制項設計階段的 HTML 碼</strong><br /> Web 伺服器控制項的設計模式行為都是透過 ControlDesigner 來處理,連設計階段時控制項的外觀也是如此;控制項在設計階段與執行執行時呈現的外觀不一定相同,當然大部分會儘量一致,使其能所見即所得。<br /> 控制項在設計階段的 HTML 碼是透 ControlDesigner.GetDesignTimeHtml 方法來處理,在 ControlDesigner.GetDesignTimeHtml 預設會執行控制項的 RenderControl 方法,所以大部分的情況下設計階段與執行階段輸出的 HTML 碼會相同。當控制項的 Visible=False 時,執行階段是完全不會輸出 HTML 碼,可是在設計階段時會特別將控制項設定 Visible=True,使控制項能完整呈現。</p> <p>ControlDesigner.GetDesignTimeHtml 方法</p> <pre><code>Public Overridable Function GetDesignTimeHtml() As String    Dim writer As New StringWriter(CultureInfo.InvariantCulture)    Dim writer2 As New DesignTimeHtmlTextWriter(writer)    Dim errorDesignTimeHtml As String = Nothing    Dim flag As Boolean = False    Dim visible As Boolean = True    Dim viewControl As Control = Nothing    Try        viewControl = Me.ViewControl        visible = viewControl.Visible        If Not visible Then            viewControl.Visible = True            flag = Not Me.UsePreviewControl        End If        viewControl.RenderControl(writer2)        errorDesignTimeHtml = writer.ToString    Catch exception As Exception        errorDesignTimeHtml = Me.GetErrorDesignTimeHtml(exception)    Finally        If flag Then            viewControl.Visible = visible        End If    End Try    If ((Not errorDesignTimeHtml Is Nothing) AndAlso (errorDesignTimeHtml.Length &lt;&gt; 0)) Then        Return errorDesignTimeHtml    End If    Return Me.GetEmptyDesignTimeHtml End Function </code></pre> <p><strong>二、自訂控制項的 Designer</strong><br /> 以 TBToolbar 為例,若我們在 RenderContents 方法未針對 Items.Count=0 做輸出 HTML 的處理,會發現未設定 Items 屬性時,在設計頁面上完全看不到 TBToolbar 控制項;像這種控制項設計階段的 HTML 碼,就可以自訂控制項的 Designer 來處理。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay19_1ACC/image_thumb.png" alt="" /></p> <p>繼承 ControlDesigner 命名為 TBToolbarDesigner,這個類別是用來擴充 TBToolbar 控制項的設計模式行為。我們可以覆寫 GetDesignTimeHtml 方法,處理設計階段表示控制項的 HTML 標記,此方法回傳的 HTML 原始碼就是控制項呈現在設計頁面的外觀。所以我們可以在 TBToolbar.Items.Count=0 時,輸出一段提示的 HTML 碼,這樣當 TBToolbar 未設定 Items 屬性時一樣可以在設計頁面上呈現控制項。</p> <pre><code>    ''' &lt;summary&gt;    ''' 擴充 TBToolbar 控制項的設計模式行為。    ''' &lt;/summary&gt;    Public Class TBToolbarDesigner        Inherits System.Web.UI.Design.ControlDesigner        ''' &lt;summary&gt;        ''' 用來在設計階段表示控制項的 HTML 標記。        ''' &lt;/summary&gt;        Public Overrides Function GetDesignTimeHtml() As String            Dim sHTML As String            Dim oControl As TBToolbar            oControl = CType(ViewControl, TBToolbar)            If oControl.Items.Count = 0 Then                sHTML = &quot;&lt;div style=&quot;&quot;background-color: #C0C0C0; border:solid 1px; width:200px&quot;&quot;&gt;請設定 Items 屬性&lt;/div&gt;&quot;            Else                sHTML = MyBase.GetDesignTimeHtml()            End If            Return sHTML        End Function    End Class </code></pre> <p>在 TBToolbar 控制項套用 DesignerAttribute 設定自訂的 TBToolbarDesigner 類別。</p> <pre><code>    &lt;Designer(GetType(TBToolbarDesigner))&gt; _    Public Class TBToolbar        Inherits WebControl    End Class </code></pre> <p>重建控制項組件,切換到設計頁面上的看 TBToolbar 控制項未設定 Items 屬性時的外觀,就是我們在 TBToolbarDesigner.GetDesignTimeHtml 方法回傳的 HTML 碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay19_1ACC/image_thumb_1.png" alt="" /></p> <p>如果你覺得上述設計階段的控制項有點太陽春,我們也可以輸出類似 SqlDataSource 控制項的外觀,將未設定 Items 屬性時輸出 HTML 改呼叫 CreatePlaceHolderDesignTimeHtml 方法。</p> <pre><code>            If oControl.Items.Count = 0 Then                sHTML = MyBase.CreatePlaceHolderDesignTimeHtml(&quot;請設定 Items 屬性&quot;)            Else                sHTML = MyBase.GetDesignTimeHtml()            End If </code></pre> <p>來看一下這樣修改後的結果,是不是比較專業一點了呢。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay19_1ACC/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/20/5726.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/20/5726.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-20 02:25:29</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day18] 修改集合屬性編輯器</title>                <link>https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012636?sc=rss.iron</guid>                <description><![CDATA[<p>上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合...]]></description>                                    <content:encoded><![CDATA[<p>上篇我們實作了「集合屬性包含不同型別的成員」,不過若有去使用屬性視窗編輯 TBToolbar 的 Items 屬性,你會發覺這個集合屬性編輯器無法加入我們定義不同型別的成員,只能加入最原始的集合成員。是不是只能在 aspx 程式碼中手動去輸入呢?當然不需要這樣人工作業,只要改掉集合屬性編輯器就可以達到我們的需求,本文將介紹修改集合屬性編輯器的相關作法。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/200810190138285.rar" target="_blank">ASP.NET Server Control - Day18.rar</a></p> <p><strong>一、自訂集合屬性編輯器</strong><br /> 我們先看一下 TBToolbar.Items 屬性套用的 EditorAttribute,它是使用 CollectionEditor 類別來當作屬性編輯器,所以我們就是要繼承 CollectionEditor 類別下來修改成自訂的屬性編輯器。</p> <pre><code>&lt; _ Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _ &gt; _ Public ReadOnly Property Items() As TBToolbarItemCollection </code></pre> <p>新增一個繼承 CollectionEditor 的 TBToolbarItemCollectionEditor 類別,並加入建構函式。此類別屬於 Bee.WebControls.Design 命名空間,通常我們會把設計階段使用的類別歸類到特別的命名空間便於管理及使用。</p> <pre><code>Namespace WebControls.Design    Public Class TBToolbarItemCollectionEditor        Inherits CollectionEditor        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Type&quot;&gt;型別。&lt;/param&gt;        Public Sub New(ByVal Type As Type)            MyBase.New(Type)        End Sub    End Class End Namespace </code></pre> <p>我們可以先修改 Items 屬性的 EditorAttribute,看看我們自訂的 TBToolbarItemCollectionEditor 是否能正常運作。不過這個屬性編輯器跟原本的沒什麼差異,因為我們只是單純繼承下來沒做任何異動,接下去我們就要開始來修改這個屬性編輯器。</p> <pre><code>&lt; _ Editor(GetType(TBToolbarItemCollectionEditor), GetType(UITypeEditor)) _ &gt; _ Public ReadOnly Property Items() As TBToolbarItemCollection </code></pre> <p><strong>二、加入不同型別的集合成員</strong><br /> 再來我們就要著手修改集合屬性編輯器,讓它可以加入不同型別的集合成員。覆寫 CollectionEditor 的 CanSelectMultipleInstances 方法傳回 True,這個方法是設定 CollectionEditor 是否允許加入多種不同型別的集合成員。</p> <pre><code>        Protected Overrides Function CanSelectMultipleInstances() As Boolean            Return True        End Function </code></pre> <p>再來覆寫 CreateNewItemTypes 方法,這個方法是取得這個集合編輯器可包含的資料型別,將集合可包含的資料型別以陣列傳回。</p> <pre><code>        ''' &lt;summary&gt;        ''' 取得這個集合編輯器可包含的資料型別。        ''' &lt;/summary&gt;        ''' &lt;returns&gt;這個集合可包含的資料型別陣列。&lt;/returns&gt;        Protected Overrides Function CreateNewItemTypes() As System.Type()            Dim ItemTypes(2) As System.Type            ItemTypes(0) = GetType(TBToolbarButton)            ItemTypes(1) = GetType(TBToolbarTextbox)            ItemTypes(2) = GetType(TBToolbarLabel)            Return ItemTypes        End Function </code></pre> <p>重建控制項組件,使用 Items 的集合屬性編輯器,就可以發現「加入」鈕的下拉清單就會出現我們所定義的三種型別的集合成員,如此可以加入不同型別的成員了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay18_6E6C/image_2.png" alt="" /></p> <p><strong>三、設定清單項目的顯示文字</strong><br /> 在成員清單項目中預設會顯示成員含命名空間的型別,若我們要修改成比較有識別的顯示文字,例如 TBToolbarButton(Key=Add) 可以顯示「按鈕-Add」,這時可以覆寫 GetDisplayText 方法來設定清單項目的顯示文字。</p> <pre><code>        ''' &lt;summary&gt;        ''' 取出指定清單項目的顯示文字。        ''' &lt;/summary&gt;        Protected Overrides Function GetDisplayText(ByVal value As Object) As String            If TypeOf value Is TBToolbarButton Then                Return String.Format(&quot;按鈕 - {0}&quot;, CType(value, TBToolbarButton).Key)            ElseIf TypeOf value Is TBToolbarTextbox Then                Return &quot;文字框&quot;            ElseIf TypeOf value Is TBToolbarLabel Then                Return String.Format(&quot;標籤 - {0}&quot;, CType(value, TBToolbarLabel).Text)            Else                Return value.GetType.Name            End If        End Function </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay18_6E6C/image_4.png" alt="" /></p> <p><strong>四、集合編輯器的屬性視窗的屬性描述</strong><br /> 一般屬性視窗下面都會有屬性描述,可以集合屬性編輯器中的屬性視窗下面竟沒有屬性描述。若我們要讓它的屬性描述可以顯示,可以覆寫 CreateCollectionForm 方法,取得集合屬性編輯表單,再去設定表單上的 PropertyGrid.HelpVisible<br /> = True 即可。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay18_6E6C/image_6.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/19/5721.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/19/5721.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-19 00:13:21</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day17] 集合屬性包含不同型別的成員</title>                <link>https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012600?sc=rss.iron</guid>                <description><![CDATA[<p>我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。...]]></description>                                    <content:encoded><![CDATA[<p>我們知道在 GridView 的 Columns 集合屬性中,可以包含不同型別的欄位,如 BoundFIeld、CheckBoxField、HyperLinkField ...等不同型別的欄位。如果我們希望工具列中不只包含按鈕,可以包含其他不同類型的子控制項,那該怎麼做呢?本文就以上篇中的 TBToolbar 控制項為案例,讓 Items 集合屬性可以加入 Button、TextBox、Label ...等不同的子控制項。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081017235034673.rar" target="_blank">ASP.NET Server Control - Day17.rar</a><br /> <strong>一、不同型別的集合成員</strong><br /> 我們的需求是讓工具列可以加入 Button、TextBox、Label 三種子控制項,所以繼承原來的 TBToolbarItem (只保留 Enabled 屬性),新增了 TBToolbarButton、TBToolbarTextbox、TBToolbarLabel 三個類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_6.png" alt="" /></p> <p>這些新增的成員類別都是繼承至 TBToolbarItem,所以在 aspx 程式碼中,手動輸入 Items 的成員時,就會列出這幾種定義的成員型別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_2.png" alt="" /></p> <p><strong>二、建立不同型別集合成員的子控制項</strong><br /> 因為 Items 屬性的成員具不同型別,所以我們要改寫 RenderContents 方法,判斷成員型別來建立對應類型的子控制項。若為 TBToolbarButton 型別建立 Button 控制項、若為 TBToolbarTextbox 型別則建立 TextBox 控制項、若為 TBToolbarLabel 型別則建立 Label 控制項。其中 TBToolbarButton 建立的控制項為 TBButton,這個控制項是我們在「 <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx" target="_blank">[ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能</a>」一文中實作的具詢問訊息的按鈕控制項。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 RenderContents 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim oItem As TBToolbarItem            Dim oControl As Control            For Each oItem In Me.Items                If TypeOf oItem Is TBToolbarButton Then                    '建立 Button 控制項                    oControl = CreateToolbarButton(CType(oItem, TBToolbarButton))                ElseIf TypeOf oItem Is TBToolbarTextbox Then                    '建立 Textbox 控制項                    oControl = CreateToolbarTextbox(CType(oItem, TBToolbarTextbox))                Else                    '建立 Label 控制項                    oControl = CreateToolbarLabel(CType(oItem, TBToolbarLabel))                End If                Me.Controls.Add(oControl)            Next            MyBase.RenderContents(writer)        End Sub        ''' &lt;summary&gt;        ''' 建立工具列按鈕。        ''' &lt;/summary&gt;        Private Function CreateToolbarButton(ByVal Item As TBToolbarButton) As Control            Dim oButton As TBButton            Dim sScript As String            oButton = New TBButton()            oButton.Text = Item.Text            oButton.Enabled = Item.Enabled            oButton.ID = Item.Key            oButton.ConfirmMessage = Item.ConfirmMessage            sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, Item.Key)            oButton.OnClientClick = sScript            Return oButton        End Function        ''' &lt;summary&gt;        ''' 建立工具列文字框。        ''' &lt;/summary&gt;        Private Function CreateToolbarTextbox(ByVal Item As TBToolbarTextbox) As Control            Dim oTextBox As TextBox            oTextBox = New TextBox            Return oTextBox        End Function        ''' &lt;summary&gt;        ''' 建立工具列標籤。        ''' &lt;/summary&gt;        Private Function CreateToolbarLabel(ByVal Item As TBToolbarLabel) As Control            Dim oLabel As Label            oLabel = New Label()            oLabel.Text = Item.Text            Return oLabel        End Function </code></pre> <p>我們手動在 aspx 程式碼中輸入不同型別的成員,TBToolbar 控制項就會呈現對應的子控制項。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_8.png" alt="" /></p> <p><strong>三、執行程式</strong><br /> 執行程式,就可以在瀏覽器看到呈現的工具列,當按下「刪除」時也會出現我們定義的詢問訊息。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay17_1430A/image_4.png" alt="" /></p> <p>輸出的 HTML 碼如下</p> <pre><code>&lt;span id=&quot;TBToolbar1&quot;&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Add&quot; value=&quot;新增&quot; onclick=&quot;__doPostBack('TBToolbar1','Add');&quot; id=&quot;TBToolbar1_Add&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Edit&quot; value=&quot;修改&quot; onclick=&quot;__doPostBack('TBToolbar1','Edit');&quot; id=&quot;TBToolbar1_Edit&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Delete&quot; value=&quot;刪除&quot; onclick=&quot;if (confirm('確定刪除嗎?')==false) {return false;}__doPostBack('TBToolbar1','Delete');&quot; id=&quot;TBToolbar1_Delete&quot; /&gt; &lt;span&gt;關鍵字&lt;/span&gt; &lt;input name=&quot;TBToolbar1$ctl01&quot; type=&quot;text&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Search&quot; value=&quot;搜尋&quot; onclick=&quot;__doPostBack('TBToolbar1','Search');&quot; id=&quot;TBToolbar1_Search&quot; /&gt; &lt;/span&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/18/5718.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/18/5718.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-18 00:05:57</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day16] 繼承 WebControl 實作 Toolbar 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012507?sc=rss.iron</guid>                <description><![CDATA[<p>前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項...]]></description>                                    <content:encoded><![CDATA[<p>前面我們討論過「繼承 CompositeControl 實作 Toolbar 控制項」,本文將繼承 WebControl 來實作同樣功能的 Toolbar 控制項,用不同的方式來實作同一個控制項,進而比較二者之間的差異。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081016225639603.rar" target="_blank">ASP.NET Server Control - Day16.rar</a></p> <p><strong>一、繼承 WebControl 實作 TBToolbar 控制項</strong><br /> <strong>step1. 新增繼承 WebControl 的 TBToolbar 控制項</strong><br /> 新增繼承 WebControl 的 TBToolbar 控制項,你也可以直接原修改原 TBToolbar 控制項,繼承對象由 CompositeControl 更改為 WebControl即可。跟之前一樣在 TBToolbar 控制項加入 Items 屬性及 Click 事件。<br /> 另外 TBToolbar 控制項需實作 INamingContainer 界面,此界面很特殊沒有任何屬性或方法,INamingContainer 界面的作用是子控制項的 ClientID 會在前面加上父控制項的 ClickID,使每個子控制項有唯一的 ClientID。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15WebControlToolbar_12C6D/image_thumb_3.png" alt="" /></p> <p><strong>step2. 建立工具列按鈕集合</strong><br /> 覆寫 RenderContents 方法,將原本 TBToolbar (複合控制項) 的 CreateChildControls 方法中建立工具列按鈕程式碼,搬移至 RenderContents 方法即可。</p> <pre><code>        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)            Dim oButton As Button            Dim oEventArgs As ClickEventArgs            oButton = CType(sender, Button)            oEventArgs = New ClickEventArgs()            oEventArgs.Key = oButton.ID            OnClick(oEventArgs)        End Sub        ''' &lt;summary&gt;        ''' 覆寫 RenderContents 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub RenderContents(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim oItem As TBToolbarItem            Dim oButton As Button            For Each oItem In Me.Items                oButton = New Button()                oButton.Text = oItem.Text                oButton.Enabled = oItem.Enabled                oButton.ID = oItem.Key                AddHandler oButton.Click, AddressOf ButtonClickEventHandler                Me.Controls.Add(oButton)            Next            If Me.Items.Count = 0 AndAlso Me.DesignMode Then                oButton = New Button()                oButton.Text = &quot;請設定 Items 屬性。&quot;                Me.Controls.Add(oButton)            End If            MyBase.RenderContents(writer)        End Sub </code></pre> <p>上述的直接搬移過來的程式碼還有個問題,就是原來的使用 AddHandler 來處理按鈕事件的方式變成沒有作用了?因為現在不是複合式控制項,當前端的按鈕 PostBack 傳回伺服端時,TBToolbar 不會事先建立子控制槓,所以機制會找不到原來產生的按鈕,也就無法使用 AddHandler 來處理事件了。</p> <pre><code>AddHandler oButton.Click, AddressOf ButtonClickEventHandler </code></pre> <p><strong>step3. 處理 Click 事件</strong><br /> 因為不能使用 AddHandler 來處理按鈕事件,所以我們就自行使用 Page.ClientScript.GetPostBackEventReference 方法來產生 PostBack 動作的用戶端指令碼,按鈕的 OnClientClick 去執行 PostBack 的動作。</p> <pre><code>            For Each oItem In Me.Items                oButton = New Button()                oButton.Text = oItem.Text                oButton.Enabled = oItem.Enabled                oButton.ID = oItem.Key                sScript = Me.Page.ClientScript.GetPostBackEventReference(Me, oItem.Key)                oButton.OnClientClick = sScript                Me.Controls.Add(oButton)            Next </code></pre> <p>TBToolar 控制項輸出的 HTML 碼如下</p> <pre><code>&lt;span id=&quot;TBToolbar1&quot;&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Add&quot; value=&quot;新增&quot; onclick=&quot;__doPostBack('TBToolbar1','Add');&quot; id=&quot;TBToolbar1_Add&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Edit&quot; value=&quot;修改&quot; onclick=&quot;__doPostBack('TBToolbar1','Edit');&quot; id=&quot;TBToolbar1_Edit&quot; /&gt; &lt;input type=&quot;submit&quot; name=&quot;TBToolbar1$Delete&quot; value=&quot;刪除&quot; onclick=&quot;__doPostBack('TBToolbar1','Delete');&quot; id=&quot;TBToolbar1_Delete&quot; /&gt; &lt;/span&gt; </code></pre> <p>要自行處理 PostBack 的事件,需實作 IPostBackEventHandler 介面,在 RaisePostBackEvent 方法來引發 TBToolbar 的 Click 事件。</p> <pre><code>    Public Class TBToolbar        Inherits WebControl        Implements INamingContainer        Implements IPostBackEventHandler        Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent            Dim oEventArgs As ClickEventArgs            oEventArgs = New ClickEventArgs()            oEventArgs.Key = eventArgument            Me.OnClick(oEventArgs)        End Sub    End Class </code></pre> <p><strong>二、測試程式</strong><br /> 在測試頁面上放置 TBToolbar 控制項,在 Click 事件撰寫測試程式碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15WebControlToolbar_12C6D/image_thumb.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/17/5706.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/17/5706.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-17 00:05:40</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day15] 複合控制項隱藏的問題</title>                <link>https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012425?sc=rss.iron</guid>                <description><![CDATA[<p>上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。<br /> 程...]]></description>                                    <content:encoded><![CDATA[<p>上一篇我們使用複合控制項(繼承 CompositeControl)的方式來實作 TBToolbar 控制項,本文將針對複合控制項做一些測試,說明在使用複合控制項要注意的一些問題。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008101612954661.rar" target="_blank">ASP.NET Server Control - Day15.rar</a><br /> <strong>一、複合控制項建立子控制項的時機</strong><br /> 還記得我們之前介紹複合控制項時有談到 CompositeControl 類別會確保我們存取子控制項時,它的子控制項一定會事先建立;也就是當我們使用 Controls 屬性去存取子控制項時,一定會執行 CreateChildControls 方法,以確保子控制項事先被建立。我們看一下 CompositeControl 類別的 Controls 屬性的寫法就可以了解其中的原由,在存取 CompositeControl.Controls 屬性時,它會先執行 Control.EnsureChildControls 方法;而 EnsureChildControls 方法會去判斷子控制項是否已建立,若未建立會去執行 CreateChildControls 方法,這也就是為什麼 CompositeControl 有辨法確保子控制項事先被建立的原因。</p> <p>CompositeControl.Controls 屬性如下</p> <pre><code>Public Overrides ReadOnly Property Controls As ControlCollection    Get        Me.EnsureChildControls        Return MyBase.Controls    End Get End Property </code></pre> <p>Control.EnsureChildControls 方法如下</p> <pre><code>Protected Overridable Sub EnsureChildControls()    If (Not Me.ChildControlsCreated AndAlso Not Me.flags.Item(&amp;H100)) Then        Me.flags.Set(&amp;H100)        Try            Me.ResolveAdapter            If (Not Me._adapter Is Nothing) Then                Me._adapter.CreateChildControls            Else                Me.CreateChildControls            End If            Me.ChildControlsCreated = True        Finally            Me.flags.Clear(&amp;H100)        End Try    End If End Sub </code></pre> <p><strong>二、複合控制項隱藏的問題</strong><br /> 我們以上篇的 TBToolbar 控制項為例,撰寫一些測試案例來說明複合控制項的問題。在撰寫測試案例之前,我們先修改一下 TBToolbar 控制項,覆寫 LoadViewState 及 SaveViewState 方法,將 Items 屬性儲存於 ViewState 中以維持狀態。</p> <p>在測試頁面上放置「測試一」、「測試二」、「PostBack」三個按鈕,這三個按鈕的動作如下。<br /> 「測試一」按鈕:在工具列直接新增一個按鈕。<br /> 「測試二」按鈕:先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。<br /> 「PostBack」按鈕:單純執行 PostBack,不撰寫程式碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb.png" alt="" /></p> <p>三個按鈕的程式碼如下所示。</p> <pre><code>    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click        Dim oItem As TBToolbarItem        '加入新按鈕        oItem = New TBToolbarItem()        oItem.Text = &quot;新按鈕&quot;        oItem.Key = &quot;NewButton&quot;        TBToolbar1.Items.Add(oItem)        Me.Response.Write(&quot;「測試一」按鈕&quot;)    End Sub    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click        Dim oItem As TBToolbarItem        Dim oButton As Button        '先執行 FindControl 去取得 ID=&quot;Add&quot; 的按鈕        oButton = TBToolbar1.FindControl(&quot;Add&quot;)        '再加入新按鈕        oItem = New TBToolbarItem()        oItem.Text = &quot;新按鈕&quot;        oItem.Key = &quot;NewButton&quot;        TBToolbar1.Items.Add(oItem)        Me.Response.Write(&quot;「測試二」按鈕&quot;)    End Sub    Protected Sub Button3_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button3.Click        '單純 PostBack,無程式碼        Me.Response.Write(&quot;「PostBack」按鈕&quot;)    End Sub </code></pre> <p>案例一:執行「測試一」按鈕,在工具列直接新增一個按鈕。<br /> 當按下「測試一」按鈕時,工具列可以正常加入我們新增的按鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_1.png" alt="" /></p> <p>案例二:執行「測試二」按鈕,先使用 FindControl 取得工具列的按鈕,然後在在工具列再新增一個按鈕。<br /> 重新執行程式,當按下「測試二」按鈕時,你會發現奇怪的現象,工具列竟然沒有加入我們新增的按鈕?<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_2.png" alt="" /></p> <p>此時再按下「PostBack」按鈕,工具列才會出現我們剛剛加入的按鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_3.png" alt="" /></p> <p>為什麼會發生這種怪現象呢?其實原因很簡單,因為 FindControl 時會去存取 Controls 屬性,而這時子控制項已經被建立了;而之前再用 Items 屬性加入新按鈕,它已經不會在重建子控制項,導致第一時間沒有加入新按鈕。不過 Items 屬性會被存在 ViewState 中,所以當執行「PostBack」按鈕時,就會出現我們剛剛新增的按鈕。</p> <p><strong>三、解決方式</strong><br /> 要解決上述「測試二」的問題,只要覆寫 TBToolbar 控制項的 Render 方法,在 Render 前執行 RecreateChildControls 方法,強制重建子控制項。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 Render 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)            Me.RecreateChildControls()            MyBase.Render(writer)        End Sub </code></pre> <p>再一次執行「測試二」的動作,就會發現執行結果就會正常了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay15_E8C/image_thumb_5.png" alt="" /></p> <p><strong>四、結語</strong><br /> 在複合控制項的 Render 前執行 RecreateChildControls 方法可以強制重建子控制項,可是這樣又會引發另一個問題,那就是當直接存取子控制項去修改子控制項的屬性後,一旦在 Render 又重建子控制項,那之前設定子控制項狀態又被全部重建了,所以需特別注意有這樣的情形。另外複合控制項有可能重覆執行建立子控制的動作,在執行效能上也比較不佳。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/16/5695.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/16/5695.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-16 00:14:12</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day14] 繼承 CompositeControl 實作 Toolbar 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012339?sc=rss.iron</guid>                <description><![CDATA[<p>之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 To...]]></description>                                    <content:encoded><![CDATA[<p>之前我們簡單介紹過繼承 CompositeControl 來實作複合控制項,在本文我們將以 Toolbar 控制項為例,以複合控制項的作法(繼承 CompositeControl )來實作 Toolbar 控制項,此工具列控制項包含 Items 屬性來描述工具列項目集合,依 Items 屬性的設定來建立工具列按鈕,另外包含 Click 事件可以得知使用按了那個按鈕。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/20081014234924792.rar" target="_blank">ASP.NET Server Control - Day14.rar</a><br /> <strong>一、工具列項目集合類別</strong><br /> 工具列包含多個按鈕,新增 TBToolbarItem 類別來描述工具列項目,TBToolbarItem 類別包含 Key、Text、Enabled 三個屬性;而 TBToolbarItemCollection 為 TBToolbarItem 的集合類別來描述工具列按鈕集合。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay14CompositeControlToolbar_12481/image_thumb.png" alt="" /></p> <p><strong>二、實作 TBToolbar 控制項</strong><br /> <strong>step1. 新增繼承 CompositeControl 的 TBToolbar 控制項</strong></p> <pre><code>    &lt; _    Description(&quot;工具列控制項。&quot;), _    ParseChildren(True, &quot;Items&quot;), _    ToolboxData(&quot;&lt;{0}:TBToolbar runat=server &gt;&lt;/{0}:TBToolbar&gt;&quot;) _    &gt; _    Public Class TBToolbar        Inherits CompositeControl    End Class </code></pre> <p><strong>step2. 新增 Items 屬性,描述工具列項目集合</strong></p> <pre><code>        ''' &lt;summary&gt;        ''' 工具列項目集合。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;工具列項目集合。&quot;), _        PersistenceMode(PersistenceMode.InnerProperty), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _        &gt; _        Public ReadOnly Property Items() As TBToolbarItemCollection            Get                If FItems Is Nothing Then                    FItems = New TBToolbarItemCollection()                End If                Return FItems            End Get        End Property </code></pre> <p><strong>step3. 新增 Click 事件</strong><br /> TBToolbar 類別新增 Click 事件,當按下按鈕時會引發 Click 事件,由 Click 的事件引數 e.Key 可以得知使用者按了那個按鈕。</p> <pre><code>        ''' &lt;summary&gt;        ''' Click 事件引數。        ''' &lt;/summary&gt;        Public Class ClickEventArgs            Inherits System.EventArgs            Private FKey As String = String.Empty            ''' &lt;summary&gt;            ''' 項目鍵值。            ''' &lt;/summary&gt;            Public Property Key() As String                Get                    Return FKey                End Get                Set(ByVal value As String)                    FKey = value                End Set            End Property        End Class        ''' &lt;summary&gt;        ''' 按下工具列按鈕所引發的事件。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;按下工具列按鈕所引發的事件。&quot;) _        &gt; _        Public Event Click(ByVal sender As Object, ByVal e As ClickEventArgs)        ''' &lt;summary&gt;        ''' 引發 Click 事件。        ''' &lt;/summary&gt;        Protected Overridable Sub OnClick(ByVal e As ClickEventArgs)            RaiseEvent Click(Me, e)        End Sub </code></pre> <p><strong>step4. 建立工具列按鈕集合</strong><br /> 覆寫 CreateChildControls 方法,依 Items 屬性的設定,來建立工具列中的按鈕集合。每個按鈕的 Click 事件都導向 ButtonClickEventHandler 方法,來處理所有按鈕的 Click 動作,並引發 TBToolbar 的 Click 事件。</p> <pre><code>        Private Sub ButtonClickEventHandler(ByVal sender As Object, ByVal e As EventArgs)            Dim oButton As Button            Dim oEventArgs As ClickEventArgs            oButton = CType(sender, Button)            oEventArgs = New ClickEventArgs()            oEventArgs.Key = oButton.ID            OnClick(oEventArgs)        End Sub        ''' &lt;summary&gt;        ''' 建立子控制項。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            Dim oItem As TBToolbarItem            Dim oButton As Button            For Each oItem In Me.Items                oButton = New Button()                oButton.Text = oItem.Text                oButton.Enabled = oItem.Enabled                oButton.ID = oItem.Key                AddHandler oButton.Click, AddressOf ButtonClickEventHandler                Me.Controls.Add(oButton)            Next            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>三、測試程式</strong><br /> 在頁面拖曳 TBToolbar 控制項,並設定 Items 屬性,如入新增、修改、刪除三個按鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay14CompositeControlToolbar_12481/image_thumb_1.png" alt="" /></p> <p>在 TBToolbar 控制項的 Click 事件加入測試程式碼,輸出引發 Click 事件的 e.Key。</p> <pre><code>    Protected Sub TBToolbar1_Click(ByVal sender As Object, ByVal e As Bee.Web.WebControls.TBToolbar.ClickEventArgs) Handles TBToolbar1.Click        Me.Response.Write(String.Format(&quot;您按了 {0}&quot;, e.Key))    End Sub </code></pre> <p>執行程式,當按了工具列上的按鈕時,就會引發 Click 事件,並輸出該按鈕對應的 Key。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay14CompositeControlToolbar_12481/image_thumb_2.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/15/5687.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/15/5687.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-15 00:13:50</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day13] Flash 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012267?sc=rss.iron</guid>                <description><![CDATA[<p>Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。<br /> 程式碼下...]]></description>                                    <content:encoded><![CDATA[<p>Flash 也是網頁常用的 ActiveX 插件,在本文中將繼承 TBActiveX 下來撰寫 TBFlash 控制項,用來輸出網頁套用 Flash 的相關 HTML 碼。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/2008101323519608.rar" target="_blank">ASP.NET Server Control - Day13.rar</a></p> <p><strong>一、網頁 Flash 的原始 HTML 碼</strong><br /> 我們先觀查在網頁中套用 Flash 插件的原始 HTML 碼,以點部落首頁抬頭的 Flash 原始碼為例如下,其中 &lt;object&gt; tag 的 codebase attribute 是指 Flash 插件的下載位置及版本。</p> <pre><code>&lt;object id=&quot;ShockwaveFlash2&quot; height=&quot;90&quot; width=&quot;728&quot;  codebase=&quot;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0&quot;  classid=&quot;clsid:D27CDB6E-AE6D-11cf-96B8-444553540000&quot;&gt; &lt;param value=&quot;http://files.dotblogs.com.tw/dotjum/ad/debug.swf&quot; name=&quot;movie&quot;/&gt; &lt;param value=&quot;high&quot; name=&quot;quality&quot;/&gt; &lt;param value=&quot;#000000&quot; name=&quot;bgcolor&quot;/&gt; &lt;embed height=&quot;90&quot; width=&quot;728&quot; type=&quot;application/x-shockwave-flash&quot;  pluginspage=&quot;http://www.macromedia.com/go/getflashplayer&quot; quality=&quot;high&quot;  src=&quot;http://files.dotblogs.com.tw/dotjum/ad/debug.swf&quot;/&gt; &lt;/object&gt; </code></pre> <p>在 &lt;object&gt; tag 中必要的 attribute 為 classid、codebase、movie、width、height,而 &lt;embed&gt; tag 的必要 attribute 為 src、pluginspage、width、height,其他選擇性的 attribute 可參閱以下網頁。</p> <p>Flash OBJECT and EMBED tag attributes<br /> <a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701" target="_blank"><a href="http://kb.adobe.com/selfservice/viewContent.do?externalId=tn%5C_12701" target="_blank">http://kb.adobe.com/selfservice/viewContent.do?externalId=tn\_12701</a></a></p> <p><strong>二、實作 TFlash 控制項</strong><br /> 了解 Flash 的原始 HTML 碼後,我們就可以開始著手撰寫 TBFlash 控制項,想辨法來輸出所需要的 HTML 碼。</p> <p><strong>step1. 新增 TBFlash 控制項繼承至 TBActiveX</strong><br /> 我們先在 TBActiveX 控制項新增一個 CodeBase 屬性,用來設定 ActiveX 插入的下載位置及版本,然後新增 TBFlash 控制項繼承至 TBActiveX,並在建構函式中設定 MyBase.ClassId 及 MyBase.CodeBase 屬性。</p> <pre><code>    Public Class TBFlash        Inherits TBActiveX        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        Sub New()            MyBase.ClassId = &quot;D27CDB6E-AE6D-11CF-96B8-444553540000&quot;            MyBase.CodeBase = &quot;http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,29,0&quot;        End Sub    End Class </code></pre> <p><strong>step2. 加入相關屬性</strong><br /> 在 TBFlash 加入 MovieUrl 及 Quality 屬性,MovieUrl 為 Flash 檔案來源,Quality 為影音品質。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay13Flash_13AEC/image_2.png" alt="" /></p> <p><strong>step3. 輸出 Flash 相關參數</strong><br /> 覆寫 CreateChildControls 方法,輸出 MovieUrl 及 Quality 屬性對應的參數,以及在 Params 集合屬性設定的參數。</p> <pre><code>        ''' &lt;summary&gt;        ''' 加入 MediaPlayer 參數。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Name&quot;&gt;參數名稱。&lt;/param&gt;        ''' &lt;param name=&quot;Value&quot;&gt;參數值。&lt;/param&gt;        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As TBActiveXParam            oParam = New TBActiveXParam(Name, Value)            Me.Params.Add(oParam)        End Sub        ''' &lt;summary&gt;        ''' 建立 Embed 標記。        ''' &lt;/summary&gt;        Private Function CreateEmbed() As HtmlControls.HtmlGenericControl            Dim oEmbed As HtmlControls.HtmlGenericControl            Dim oParam As TBActiveXParam            oEmbed = New HtmlControls.HtmlGenericControl()            oEmbed.TagName = &quot;embed&quot;            oEmbed.Attributes(&quot;src&quot;) = Me.ResolveClientUrl(Me.MovieUrl)            oEmbed.Attributes(&quot;pluginspage&quot;) = &quot;http://www.macromedia.com/go/getflashplayer&quot;            oEmbed.Attributes(&quot;height&quot;) = Me.Height.ToString            oEmbed.Attributes(&quot;width&quot;) = Me.Width.ToString            'Embed 的 Attributes 加入 Params 集合屬性的設定            For Each oParam In Me.Params                If oParam.Name &lt;&gt; &quot;movie&quot; Then                    oEmbed.Attributes(oParam.Name) = oParam.Value                End If            Next            Return oEmbed        End Function        ''' &lt;summary&gt;        ''' 建立子控制項。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            Dim oEmbed As HtmlControls.HtmlGenericControl            '加入 movie 參數            AddParam(&quot;movie&quot;, Me.ResolveClientUrl(Me.MovieUrl))            '加入 quality 參數            If Me.Quality &lt;&gt; EQuality.NotSet Then                AddParam(&quot;quality&quot;, Me.Quality.ToString.ToLower)            End If            MyBase.CreateChildControls()            oEmbed = CreateEmbed()            Me.Controls.Add(oEmbed)        End Sub </code></pre> <p><strong>三、測試程式</strong><br /> 在頁面拖曳 TBFlash 控制項,設定 MovieUrl 及 Quality 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Flash。</p> <pre><code>        &lt;bee:TBFlash ID=&quot;TBFlash1&quot; runat=&quot;server&quot; Height=&quot;90px&quot;            MovieUrl=&quot;http://files.dotblogs.com.tw/dotjum/ad/debug.swf&quot; Quality=&quot;High&quot;            Width=&quot;728px&quot;&gt;        &lt;/bee:TBFlash&gt; </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay13Flash_13AEC/image_4.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/14/5674.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/14/5674.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-14 00:16:30</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day12] 繼承 TBActiveX 重新改寫 TBMediaPlayer 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012196?sc=rss.iron</guid>                <description><![CDATA[<p>上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX ...]]></description>                                    <content:encoded><![CDATA[<p>上篇介紹的 TBActiveX 控制項,它可以支援網頁 Media Player 的設定,這跟前面提及的 TBMediaPlayer 功能相同。TBActiveX 具有網頁設定 ActiveX 通用屬性,所以 TBMediaPlayer 基本上是可以由 TBActiveX 繼承下來,再加入 Media Player 特有的屬性即可。本文將原來的 TBMediaPlayer 控制項,繼承的父類別由 WebControl 改為 TBActiveX 類別,重新改寫 TBMediaPlayer 控制項。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/200810130325962.rar" target="_blank">ASP.NET Server Control - Day12.rar</a></p> <p><strong>一、改寫 TBMediaPlayer 控制項</strong><br /> TBMediaPlayer 控制項原本是繼承 WebControl,現改繼承對象為 TBActiveX,來重新改寫 TBMediaPlayer 控制項。</p> <p><strong>step1. TBMediaPlayer 繼承至 TBActiveX</strong><br /> 新增 TBMediaPlayer 控制項,繼承至 TBActiveX,並在建構函式設定 Media Player ActiveX 的 ClassId。</p> <pre><code>    Public Class TBMediaPlayer        Inherits TBActiveX        ''' &lt;summary&gt;        ''' 建構函式。        ''' &lt;/summary&gt;        Sub New()            MyBase.ClassId = &quot;6BF52A52-394A-11D3-B153-00C04F79FAA6&quot;        End Sub    End Class </code></pre> <p><strong>step2. 加入相關屬性</strong><br /> 跟原來的 TBMediaPlayer 控制項一樣,加入 Url、AutoStart、UIMode 三個屬性,可視情形加入需要設定的屬性。</p> <p><strong>step3. 加入 Media Player 參數</strong><br /> 覆寫 CreateChildControls 方法,動態依屬性設定在 Params 集合屬性加入參數。雖然 TBMediaPlayer 控制項目前只有 Url、AutoStart、UIMode 三個屬性,但是父類別 TBActiveX 具有 Params 集合屬性,所以開發人員可以視需求加入其他未定義的參數。</p> <pre><code>        ''' &lt;summary&gt;        ''' 加入 MediaPlayer 參數。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Name&quot;&gt;參數名稱。&lt;/param&gt;        ''' &lt;param name=&quot;Value&quot;&gt;參數值。&lt;/param&gt;        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As TBActiveXParam            oParam = New TBActiveXParam(Name, Value)            Me.Params.Add(oParam)        End Sub        ''' &lt;summary&gt;        ''' 覆寫 CreateChildControls 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            '加入 Url 參數            If Me.Url &lt;&gt; String.Empty Then                AddParam(&quot;URL&quot;, Me.ResolveClientUrl(Me.Url))            End If            '加入 autoStart 參數            If Me.AutoStart Then                AddParam(&quot;autoStart&quot;, &quot;true&quot;)            End If            '加入 uiMode 參數            If Me.UIMode &lt;&gt; EUIMode.NotSet Then                AddParam(&quot;uiMode&quot;, Me.UIMode.ToString)            End If            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>二、執行程式</strong><br /> 在頁面拖曳 TBMediaPlayer 控制項,設定 Url、AutoStart、UIMode 屬性,若有需要加入其他參數,可自行設定 Params 集合屬性。執行程式就可以在頁面上看到呈現出來的 Media Player。</p> <pre><code>        &lt;bee:TBMediaPlayer ID=&quot;TBMediaPlayer1&quot; runat=&quot;server&quot; AutoStart=&quot;True&quot;            Height=&quot;249px&quot; Url=&quot;D:\Movie_01.wmv&quot; Width=&quot;250px&quot;&gt;        &lt;/bee:TBMediaPlayer&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/13/5663.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/13/5663.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-13 00:13:29</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day11] ActiveX 伺服器控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012159?sc=rss.iron</guid>                <description><![CDATA[<p>Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX...]]></description>                                    <content:encoded><![CDATA[<p>Media Player 與 Flash 之類在網頁上執行的外掛控制項,都是屬於 ActiveX 控制項,它們套用在 HTML 碼中的方式差不多,除了要指定 ClassID 以外,ActiveX 使用的參數(相當於 ActiveX 控制項的屬性)以 Param Tag 來表示。本文標題命名為「ActiveX 伺服器控制項」就是避免誤解為 ActiveX 控制項,而是在 ASP.NET 中輸出 ActiveX 相關 HTML 碼的伺服器控制項;我們可透過 ActiveX 伺服器控制項可以用來輸出網頁上引用 ActiveX 的通用 HTML 碼,另外 ActiveX 的參數會以集合屬性來呈現,所以也會一併學習到集合屬性的撰寫方式。<br /> 程式碼下載:<a href="http://files.dotblogs.com.tw/jeff377/0810/200810124846794.rar" target="_blank">ASP.NET Server Control - Day11.rar</a></p> <p><strong>一、集合屬性</strong><br /> ActiveX 的 Param 參數是集合屬性,所以我們定義了 TBActiveParam 類別描述 ActiveX 參數,包含 Name 及 Value 屬性;而 TBActiveXParamCollection 為 TBActiveParam 的集合類別,用來描述 ActiveX 參數集合。TBActiveXParamCollection 繼承 CollectionBase,加入操作集合的 Add、Insert、Remove、IndexOf、Contains 等方法,關於集合屬性的用法可以參閱筆者在部落格的「<a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1747.aspx" target="_blank">撰寫伺服器控制項的集合屬性 (CollectionBase)</a>」一文中有詳細說明。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay11ActiveX_166/image_thumb_1.png" alt="" /></p> <p><strong>二、實作 ActiveX 伺服器控制項</strong><br /> <strong>step1. 新增繼承 WebControl 的 TBActiveX</strong></p> <p><strong>step2. 覆寫 TagKey 屬性,傳回 object 的 Tag</strong></p> <pre><code>        Protected Overrides ReadOnly Property TagKey() As HtmlTextWriterTag            Get                Return HtmlTextWriterTag.Object            End Get        End Property </code></pre> <p><strong>step3. 新增 ClassId 屬性,描述 ActiveX 的 ClassId</strong><br /> 定義 ClassId 屬性,並覆寫 AddAttributesToRender 來輸出此屬性。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 AddAttributesToRender 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)            '加入 MediaPlayer ActiveX 元件的 classid            writer.AddAttribute(&quot;classid&quot;, String.Format(&quot;clsid:{0}&quot;, Me.ClassId))            MyBase.AddAttributesToRender(writer)        End Sub </code></pre> <p><strong>step4. 新增 Params 屬性,描述 ActiveX 的參數集合</strong><br /> 定義 Params 屬性,型別為 TBActiveXParamCollection 類別,套用 EditorAttribute 設定 CollectionEditor 為集合編輯器。</p> <pre><code>        ''' &lt;summary&gt;        ''' ActiveX 控制項參數集合。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;控制項參數集合。&quot;), _        PersistenceMode(PersistenceMode.InnerProperty), _        DesignerSerializationVisibility(DesignerSerializationVisibility.Content), _        Editor(GetType(CollectionEditor), GetType(UITypeEditor)) _        &gt; _        Public ReadOnly Property Params() As TBActiveXParamCollection            Get                If FParams Is Nothing Then                    FParams = New TBActiveXParamCollection()                End If                Return FParams            End Get        End Property </code></pre> <p>當編輯 Params 時,會使用的 CollectionEditor 集合編輯器。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay11ActiveX_166/image_thumb_2.png" alt="" /></p> <p><strong>step5. 輸出 ActiveX 參數</strong><br /> 覆寫 CreateChildControls 方法,在此方法依 Params 集合屬性定義依序來輸出 ActiveX 的參數集合。</p> <pre><code>        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As HtmlControls.HtmlGenericControl            oParam = New HtmlControls.HtmlGenericControl(&quot;param&quot;)            oParam.Attributes.Add(&quot;name&quot;, Name)            oParam.Attributes.Add(&quot;value&quot;, Value)            Me.Controls.Add(oParam)        End Sub        ''' &lt;summary&gt;        ''' 建立子控制項。        ''' &lt;/summary&gt;        Protected Overrides Sub CreateChildControls()            Dim oParam As TBActiveXParam            '加入 ActiveX 參數集合            For Each oParam In Me.Params                AddParam(oParam.Name, oParam.Value)            Next            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>三、執行程式</strong><br /> 上一篇我們使用 TBMediaPlayer 控制項來設定 Media Player,在此我們改用 TBActiveX 控制項來設定 Media Player,一樣可以呈現相同的結果。</p> <pre><code>        &lt;bee:TBActiveX ID=&quot;TBActiveX1&quot; runat=&quot;server&quot;            ClassId=&quot;6BF52A52-394A-11D3-B153-00C04F79FAA6&quot; Height=&quot;250px&quot; Width=&quot;250px&quot;&gt;            &lt;Params&gt;                &lt;bee:TBActiveXParam Name=&quot;URL&quot; Value=&quot;d:/Movie_01.wmv&quot; /&gt;                &lt;bee:TBActiveXParam Name=&quot;autoStart&quot; Value=&quot;true&quot; /&gt;            &lt;/Params&gt;        &lt;/bee:TBActiveX&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/12/5659.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/12/5659.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-12 04:20:27</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day10] Media Player 控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012142?sc=rss.iron</guid>                <description><![CDATA[<p>我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控...]]></description>                                    <content:encoded><![CDATA[<p>我們在前面幾篇文章中,已經簡要的對伺服器控制項做了基本介紹,接下來的幾篇文章中我們要開始實作伺服器控制項。在網頁上常使用 Media Player 來撥放影片,在 ASP.NET 中沒有現成的控制項來處理 Media Player,只能在 aspx 中加入 Media Player 相關的程式碼;本文將示範如何製作一個 Media Player 控制項,讓我們在 ASP.NET 中更方便的使用 Media Player。<br /> 程式碼下載:<a href="http://Files.Dotblogs.com.tw/jeff377%5C0810%5C2008101122230798.rar" target="_blank">ASP.NET Server Control - Day10.rar</a></p> <p><strong>一、Media Player 原始 HTML 碼</strong><br /> 在製作 Media Player 控制項之前,你需要先了解 Media Player 原本的 HTML 碼,控制項的作用就是想辨法把這些寫在 aspx 中的 HTML 碼交由控制項來輸出而已,以下為網頁中加入 Media Player 的 HTML 碼範例。</p> <pre><code>&lt;OBJECT id=&quot;VIDEO&quot; width=&quot;320&quot; height=&quot;240&quot; style=&quot;position:absolute; left:0;top:0;&quot; CLASSID=&quot;CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6&quot; type=&quot;application/x-oleobject&quot;&gt; &lt;PARAM NAME=&quot;URL&quot; VALUE=&quot;your file or url&quot;&gt; &lt;PARAM NAME=&quot;SendPlayStateChangeEvents&quot; VALUE=&quot;True&quot;&gt; &lt;PARAM NAME=&quot;AutoStart&quot; VALUE=&quot;True&quot;&gt; &lt;PARAM name=&quot;uiMode&quot; value=&quot;none&quot;&gt; &lt;PARAM name=&quot;PlayCount&quot; value=&quot;9999&quot;&gt; &lt;/OBJECT&gt; </code></pre> <p>在下面的網頁有 Media Player 相關參數說明。<br /> <a href="http://www.mioplanet.com/rsc/embed_mediaplayer.htm" target="_blank"><a href="http://www.mioplanet.com/rsc/embed%5C_mediaplayer.htm" target="_blank">http://www.mioplanet.com/rsc/embed\_mediaplayer.htm</a></a></p> <p><strong>二、實作 Media Player 控制項</strong><br /> <strong>step1.首先新增由 WebControl 繼承下來的 TBMediaPlayer 類別。</strong></p> <pre><code>    Public Class TBMediaPlayer        Inherits WebControl    End Class </code></pre> <p><strong>step2.覆寫 TagKey 屬性,傳回 object 的 Tag。</strong></p> <pre><code>        Protected Overrides ReadOnly Property TagKey() As System.Web.UI.HtmlTextWriterTag            Get                Return HtmlTextWriterTag.Object            End Get        End Property </code></pre> <p><strong>step3.輸出 HTML Tag 的 Attribute</strong><br /> 在 object Tag 中包含 style、classid、type 二個 Attribute,WebControl 本身會處理 style,所以我們只需覆寫 AddAttributesToRender 方法,處理 classid 及 type 二個 Attribute,記得覆寫 AddAttributesToRender 方法時最後要加入 MyBase.AddAttributesToRender(writer),才會執行父類別的 AddAttributesToRender 方法。</p> <pre><code>        ''' &lt;summary&gt;        ''' 覆寫 AddAttributesToRender 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub AddAttributesToRender(ByVal writer As System.Web.UI.HtmlTextWriter)            '加入 MediaPlayer ActiveX 元件的 classid            writer.AddAttribute(&quot;classid&quot;, &quot;clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6&quot;)            writer.AddAttribute(&quot;type&quot;, &quot;application/x-oleobject&quot;)            MyBase.AddAttributesToRender(writer)        End Sub </code></pre> <p><strong>step4.加入 Url 屬性</strong><br /> 加入指定播放檔案來源的 Url 屬性,其中套用 EditorAttribute 設定 UrlEditor,使用 Url 專用的編輯器來設定屬性。</p> <pre><code>        ''' &lt;summary&gt;        ''' 播放檔案來源。        ''' &lt;/summary&gt;        &lt; _        Description(&quot;播放檔案來源&quot;), _        Bindable(True), _        Category(&quot;Appearance&quot;), _        Editor(GetType(UrlEditor), GetType(UITypeEditor)), _        UrlProperty(), _        DefaultValue(&quot;&quot;) _        &gt; _        Public Property Url() As String            Get                Return FUrl            End Get            Set(ByVal value As String)                FUrl = value            End Set        End Property </code></pre> <p><strong>step5.輸出 Url 參數</strong><br /> 接下來覆寫 CreateChildControls 方法,輸出 Url 參數。</p> <pre><code>        ''' &lt;summary&gt;        ''' 加入參數。        ''' &lt;/summary&gt;        ''' &lt;param name=&quot;Name&quot;&gt;參數名稱。&lt;/param&gt;        ''' &lt;param name=&quot;Value&quot;&gt;參數值。&lt;/param&gt;        Private Sub AddParam(ByVal Name As String, ByVal Value As String)            Dim oParam As HtmlControls.HtmlGenericControl            oParam = New HtmlControls.HtmlGenericControl(&quot;param&quot;)            oParam.Attributes.Add(&quot;name&quot;, Name)            oParam.Attributes.Add(&quot;value&quot;, Value)            Me.Controls.Add(oParam)        End Sub        Protected Overrides Sub CreateChildControls()            '加入 Url 參數            AddParam(&quot;url&quot;, Me.ResolveClientUrl(Me.Url))            MyBase.CreateChildControls()        End Sub </code></pre> <p><strong>step6.輸出 Media Player 其他參數</strong><br /> 你可以將 Media Player 的參數設定皆使用相對應的屬性來設定,然後使用 step5 的方式來輸出所有設定的參數值。</p> <p><strong>三、Media Player 控制項程式碼</strong><br /> Media Player 控制項的完整程式碼如下,此控制項只加入 URL、AutoStart、UIMode 三個參數,你可以視需求情形將使用到的參數定義為屬性來做設定即可。<br /> 因為此處有字元數限制,完整的程式碼請參閱筆者部落格相同文章<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx</a></p> <p><strong>四、執行程式</strong><br /> 把 TBMediaPlayer 控制項拖曳至頁面,設定好屬性後,執行程式就可以在頁面上看到呈現出來的 Media Player。</p> <pre><code>        &lt;bee:TBMediaPlayer ID=&quot;TBMediaPlayer1&quot; runat=&quot;server&quot; Height=&quot;250px&quot;            Width=&quot;250px&quot; Url=&quot;~/test.wmv&quot; /&gt; </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/11/5655.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-11 19:08:27</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day9] 控制項常用 Attribute 介紹(2)</title>                <link>https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012060?sc=rss.iron</guid>                <description><![CDATA[<p>接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。<br /> <strong>六、ToolboxDataAttribute 類別</strong><...]]></description>                                    <content:encoded><![CDATA[<p>接續上篇 Attribute 的介紹,本文將再介紹一些伺服器控制項常用的 Attribute。<br /> <strong>六、ToolboxDataAttribute 類別</strong><br /> 作用:指定當自訂控制項從工具箱拖曳到頁面時,為此自訂控制項產生的預設標記。<br /> 當我們新增一個伺服器控制項,它就會預設在控制項類別套用 ToolboxDataAttribute,定義在控制項在 aspx 程式碼中的標記。</p> <pre><code>&lt;ToolboxData(&quot;&lt;{0}:TBButton runat=server &gt;&lt;/{0}:TBButton&gt;&quot;)&gt; _ Public Class TBButton    Inherits System.Web.UI.WebControls.Button End Class </code></pre> <p><strong>七、DefaultPropertyAttribute 類別</strong><br /> 作用:指定類別的預設屬性。<br /> 下面的範例中,MyTextbox 類別套用 DefaultPropertyAttribute,設定 Text 屬性為預設屬性。</p> <pre><code>&lt;DefaultProperty(&quot;Text&quot;)&gt; _ Public Class MyTextbox    Inherits WebControl    Public Property Text() As String        Get            Return CType(Me.ViewState(&quot;Text&quot;), String)        End Get        Set(ByVal value As String)            Me.ViewState(&quot;Text&quot;) = value        End Set    End Property End Class </code></pre> <p><strong>八、DefaultEventAttribute 類別</strong><br /> 作用:指定控制項的預設事件。<br /> 下面的範例中,MyTextbox 類別套用 DefaultEventAttribute,設定 TextChanged 為預設屬性。</p> <pre><code>&lt;DefaultEvent(&quot;TextChanged&quot;)&gt; _ Public Class MyTextbox    Inherits WebControl    ''' &lt;summary&gt;    ''' TextChanged 事件。    ''' &lt;/summary&gt;    Public Event TextChanged As EventHandler End Class </code></pre> <p>當設計階段在頁面上的 MyTextbox 控制項點二下時,就會產生預設事件的處理函式。</p> <pre><code>    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox3.TextChanged    End Sub </code></pre> <p><strong>九、LocalizableAttribute 類別</strong><br /> 作用:指定屬性是否應該當地語系化。<br /> 當屬性套用設為為 true 的 LocalizableAttribute 時,其屬性值會儲存在資源檔中,未來不需修改程式碼就可以將這些資源檔當地語系化。</p> <pre><code>        &lt;Localizable(True)&gt; _        Public Property Text() As String            Get                Return CType(Me.ViewState(&quot;Text&quot;), String)            End Get            Set(ByVal value As String)                Me.ViewState(&quot;Text&quot;) = value            End Set        End Property </code></pre> <p><strong>十、DesignerAttribute 類別</strong><br /> 作用:設定控制項在設計階段服務的類別。<br /> 指定一個設計階段的服務類別,來管理控制項在設計階段的行為,例如控制項的設計階段外觀、智慧標籤內容。例如下面範例的 TBGridView 控制項就定義了 TBGridViewDesigner 來實作設計階段的行為,未來的章節中也會介紹如何實作控制項的 Designer。</p> <pre><code>    &lt; Designer(GetType(TBGridViewDesigner)) &gt; _    Public Class TBGridView        Inherits GridView    End Class </code></pre> <p><strong>十一、EditorAttribute 類別</strong><br /> 作用:指定在屬性視窗中編輯屬性值的編輯器。<br /> 例如 ListBox 控制項的 Items 屬性,在屬性視窗編輯 Items 屬性時,會彈出 Items 集合屬性的編輯器。以下範例就是定義 Items 屬性的編輯器類別為 TBListItemsCollectionEditor,未來的章節中也會介紹如何實作屬性的 Editor。</p> <pre><code>        &lt;Editor(GetType(TBListItemsCollectionEditor), GetType(System.Drawing.Design.UITypeEditor))&gt; _        Public Overrides ReadOnly Property Items() As ListItemCollection </code></pre> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/10/5653.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/10/5653.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-10 11:27:02</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day8] 控制項常用 Attribute 介紹(1)</title>                <link>https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10012016?sc=rss.iron</guid>                <description><![CDATA[<p>Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在...]]></description>                                    <content:encoded><![CDATA[<p>Property 與 Attribute 二個術語一般都是翻譯成「屬性」,例如類別的屬性,是使用英文的 Property,而 HTML/XML 的元素屬性,使用的英文則是 Attribute。在 .NET 中 Property 與 Attribute 的意義及用法不同,不過微軟線上文件也將它翻譯為「屬性」,這可能讓人發生困擾及誤解;筆者比較喜歡的方式就是 Property 是屬性,Attribute 就維持原文。在 .NET 中類別或屬性上可以套用上不同的 Attribute,使類別或屬性具有不同的特性,本文將介紹一些在伺服器控制項常使用到的 Attribute。<br /> <strong>一、DescriptionAttribute 類別</strong><br /> 作用:指定控制項或屬性的描述。<br /> 當 DescriptionAttribute 套用至控制項的類別時,設定的描述內容就會出現在工具箱中控制項的提示。</p> <pre><code>&lt;Description(&quot;按鈕控制項&quot;)&gt; _ Public Class TBButton    Inherits System.Web.UI.WebControls.Button End Class </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb.png" alt="" /><br /> 當 DescriptionAttribute 套用至控制項的屬性時,在屬性視窗下面就會出現設定的屬性描述內容。</p> <pre><code>&lt;Description(&quot;詢問訊息&quot;)&gt; _ Public Property ConfirmMessage() As String </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb_1.png" alt="" /></p> <p><strong>二、DefaultValueAttribute 類別</strong><br /> 作用:指定屬性的預設值。<br /> 使用 DefaultValueAttribute 設定屬性的預設值,若設定的屬性值與預設值相同時,此屬性值就不會出現在 aspx 程式碼中;筆者強烈建議屬性一定套用 DefaultValueAttribute,一來在 aspx 中的程式碼會比較少,另外一個重點是若因為某些因素需要修改屬性的預設值時,所有已開發頁面的控制項屬性值會一併變更;因為當初屬性值是預設值,沒有被寫入 aspx 程式碼中,所以一但控制項的屬性預設值變更,頁面已佈屬的控制項的屬性值就會全面適用。</p> <pre><code>        Private FConfirmMessage As String = String.Empty        &lt;DefaultValue(&quot;&quot;)&gt; _        Public Property ConfirmMessage() As String            Get                Return FConfirmMessage            End Get            Set(ByVal value As String)                FConfirmMessage = value            End Set        End Property </code></pre> <p><strong>三、CategoryAttribute 類別</strong><br /> 作用:指定屬性或事件的分類名稱,當屬性視窗設定為 [分類] 模式時,以群組方式來顯示屬性或事件。<br /> 例如設定 ConfirmMessage 屬性在 &quot;Behavior&quot; 分類,則 ConfirmMessage 屬性會被歸類到「行為」分類。</p> <pre><code>        &lt;Category(&quot;Behavior&quot;)&gt; _        Public Property ConfirmMessage() As String </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb_2.png" alt="" /></p> <p><strong>四、BindableAttribute 類別</strong><br /> 作用:指定成員是否通常使用於繫結。<br /> 在資料繫結設定視窗中中,指定屬性是否預設會出現在屬性清單中。</p> <pre><code>&lt;Bindable(True)&gt; _ Public Property ConfirmMessage() As String </code></pre> <p><img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay7Attribute_D3/image_thumb_3.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/09/5647.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/09/5647.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-09 21:11:43</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day7] 設定工具箱的控制項圖示</title>                <link>https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011933?sc=rss.iron</guid>                <description><![CDATA[<p>當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。...]]></description>                                    <content:encoded><![CDATA[<p>當我們把自訂控制項加入到工具箱中時,你會發現所有的控制項預設都是同樣的圖示,雖然控制項的圖示不變更不會有什麼影響,不過我們還是希望為自訂控制項加上合適的外衣,本文將介紹如何設定工具箱控制項圖示。<br /> <strong>一、加入控制項圖示檔</strong><br /> 首先要準備一個 16 x 16 的點陣圖(bmp),如下所示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_1.png" alt="" /><br /> 將此圖檔加入至「伺服器控制項專案」中,可以如下圖所示,用一個特定的資料夾來儲存所有工具箱的圖示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_2.png" alt="" /><br /> 然後在圖檔的屬性視窗中,設定建置動作為「內嵌資源」。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_3.png" alt="" /><br /> <strong>二、設定控制項的圖示</strong><br /> 首先定義一個 TBResource 類別,此為一個空的類別,其命名空間需與根命名空間相同,做為引用資源檔時使用。並加上控制項圖示的 WebResource 定義,因為根命名空間是 Bee.Web,而圖檔名稱為 TBButton.bmp,所以定義如下所示。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_5.png" alt="" /><br /> 假設我們要設定 TBButton 的工具箱圖示,則在 TBButton 類別套用 ToolboxBitmapAttribute 如下,其中第一個參數為 GetType(TBResource),第二個參數為圖檔檔名。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_7.png" alt="" /><br /> 重新編輯伺服器控制項專案,再將 Bee.Web.dll 組件的控制項加入工具箱中,你就可以發現 TBButton 的圖示已經變成設定的圖示了。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay6_BA88/image_thumb_8.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/08/5624.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/08/5624.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-08 22:26:13</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day6] 事件與 PostBack</title>                <link>https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011861?sc=rss.iron</guid>                <description><![CDATA[<p>一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用...]]></description>                                    <content:encoded><![CDATA[<p>一般類別的事件撰寫很單純,不過在 ASP.NET 中與前端使用者互動產生的事件就不是那麼簡單了;在以往的 ASP 年代是沒有事件這回事的,而在 ASP.NET 把網頁程式撰寫真正的物件導向化,用戶端使用者的操作透過 PostBack 來產生相對應的事件。例如前端使用者按鈕後會引發伺服端 Button 的 Click 事件,當前端使用者輸入文字框完畢後離開後會引發伺服端 TextBox 的 TextChanged 事件,在本文中就是要說明如何透過 PostBack 來產生與使用者互動的事件。<br /> <strong>一、IPostBackEventHandler 與 IPostBackDataHandler 介面</strong><br /> 控制項要處理 PostBack 產生的事件,必須實作 IPostBackEventHandler 或 IPostBackDataHandler 介面,這二個介面有什麼差別呢?例如 Button 是實作IPostBackEventHandler 介面,由控制項產生的 PostBack 直接引發事件,如 Button 的 Click 事件。例如 TextBox 是實作 IPostBackDataHandler 介面,當頁面產生 PostBack 時,會傳用戶端輸入的新值給控制項,由控制項本身去決定是否引發該事件;以 TextBox 舉例來說,它會判斷新值與舊值不同時才會引發 TextChanged 事件。</p> <p><strong>二、IPostBackEventHandler 介面實作</strong><br /> 首先介紹 IPostBackEventHandler 介面,它包含 RaisePostBackEvent 方法,控制項在此方法中需處理要引發那些事件。我們繼承 WebControl 撰寫 MyButton 類別來說明 IPostBackEventHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入按鈕的 HTML 原始碼,並定義一個 Click 事件。實作 IPostBackEventHandler 介面的 RaisePostBackEvent 方法,在此方法中直接引發 Click 事件。</p> <pre><code>Public Class MyButton    Inherits WebControl    Implements IPostBackEventHandler    ''' &lt;summary&gt;    ''' Click 事件。    ''' &lt;/summary&gt;    Public Event Click As EventHandler    ''' &lt;summary&gt;    ''' 引發 Click 事件。    ''' &lt;/summary&gt;    Private Sub OnClick(ByVal e As EventArgs)        RaiseEvent Click(Me, e)    End Sub    Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent        Dim e As New EventArgs()        OnClick(e) '引發 Click 事件    End Sub    ''' &lt;summary&gt;    ''' 覆寫 Render 方法。    ''' &lt;/summary&gt;    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)        Dim sHTML As String        sHTML = String.Format(&quot;&lt;INPUT TYPE=Submit Name={0} Value = '按鈕'/&gt;&quot;, Me.UniqueID)        writer.Write(sHTML)    End Sub End Class </code></pre> <p>在頁面上拖曳 MyButton 控制項,在屬性視窗找到 Click 事件,點二下產生 Click 事件處理函式,並撰寫測試程式碼如下。</p> <pre><code>    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click        Me.Page.Response.Write(&quot;MyButton Click 事件&quot;)    End Sub </code></pre> <p>執行程式,當按下 MyButton 按鈕時,就會執行它的 RaisePostBackEvent 方法,進而引發 Click 事件,也就會執行 Click 事件處理函式的程式碼。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay5PostBack_143C0/image_2.png" alt="" /></p> <p><strong>三、IPostBackDataHandler 介面實作</strong><br /> IPostBackDataHandler 介面包含 LoadPostData 及 RaisePostDataChangedEvent 方法,當頁面 PostBack 時,會尋找具 IPostBackDataHandler 介面的控制項,先執行LoadPostData 方法,控制項在 LoadPostData 方法中會判斷用戶端傳入值決定是否引發事件,若 LoadPostData 傳回 True 表示要引發事件,此時會執行RaisePostDataChangedEvent 方法去決定要引發那些事件,反之傳回 False 表示不引發事件。</p> <p>我們繼承 WebControl 撰寫 MyText 類別來說明 IPostBackDataHandler 介面,我們簡化控制項程式碼直接在 Render 方法輸入文字框的 HTML 原始碼,並定義一個 TextChanged 事件。在 LoadPostData 方法中我們會判斷用戶端傳入值與目前的值是否不相同,不相同時才會傳回 True,此時才會執行 RaisePostDataChangedEvent 方法,進而引發 TextChanged 事件。</p> <pre><code>Public Class MyTextbox    Inherits WebControl    Implements IPostBackDataHandler    Public Property Text() As String        Get            Return CType(Me.ViewState(&quot;Text&quot;), String)        End Get        Set(ByVal value As String)            Me.ViewState(&quot;Text&quot;) = value        End Set    End Property    ''' &lt;summary&gt;    ''' TextChanged 事件。    ''' &lt;/summary&gt;    Public Event TextChanged As EventHandler    ''' &lt;summary&gt;    ''' 引發 TextChanged 事件。    ''' &lt;/summary&gt;    Private Sub OnTextChanged(ByVal e As EventArgs)        RaiseEvent TextChanged(Me, e)    End Sub    Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData        '前端使用者輸入值        Dim oNewValue As String = postCollection.Item(postDataKey)        If oNewValue &lt;&gt; Me.Text Then            Me.Text = oNewValue            Return True        Else            Return False        End If    End Function    Public Sub RaisePostDataChangedEvent() Implements System.Web.UI.IPostBackDataHandler.RaisePostDataChangedEvent        Dim e As New EventArgs()        '引發 TextChanged 事件        OnTextChanged(e)    End Sub    ''' &lt;summary&gt;    ''' 覆寫 Render 方法。    ''' &lt;/summary&gt;    Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)        Dim sHTML As String        sHTML = String.Format(&quot;&lt;INPUT Type=text Name={0} Value={1} &gt;&quot;, Me.UniqueID, Me.Text)        writer.Write(sHTML)    End Sub End Class </code></pre> <p>在頁面上拖曳 MyTextbox 及 MyButton 控制項,在 MyButton 的 Click 及 MyTextbox 的 TextChanged 事件撰寫如下測試程式碼。</p> <pre><code>    Protected Sub MyButton1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyButton1.Click        Me.Page.Response.Write(&quot;MyButton Click 事件&quot;)        Me.Page.Response.Write(&quot;&lt;br/&gt;&quot;)    End Sub    Protected Sub MyTextbox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextbox1.TextChanged        Me.Page.Response.Write(&quot;MyTextbox TextChanged 事件&quot;)        Me.Page.Response.Write(&quot;&lt;br/&gt;&quot;)    End Sub </code></pre> <p>執行程式,在 MyTextbox 輸入 &quot;A&quot;,再按下 MyButton,因為 MyTextbox 的值「空字串-&gt;&quot;A&quot;」,所以會引發 MyTextbox 的 TextChanged 事件及 MyButton 的 Click 事件。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay5PostBack_143C0/image_6.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格</p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-07 23:30:19</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day5] 屬性與 ViewState</title>                <link>https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011745?sc=rss.iron</guid>                <description><![CDATA[<p>在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性...]]></description>                                    <content:encoded><![CDATA[<p>在 ASP.NET 中,控制項的屬性與 ViewState 有著密不可分的關係,透過 ViewState 才有辨法維護控制項的屬性值。在本文中將介紹屬性與 ViewState 的關係,並說明屬性如何存取 ViewState 是比較有效率的方式。<br /> 當你加入一個「ASP.NET 伺服器控制項」時,類別中預設會有一個 Text 屬性寫法的範例如下所示,屬性的讀寫都是直接存取 ViewState,這是一般常見的控制項屬性寫法。可是這種屬性的寫法是沒有效率的,因為 ViewState 的成員是 Object 型別,每次讀取屬性時都是由 ViewState 取出指定鍵值的成員再轉型為屬性的型別,寫入屬性的動作也是直接寫入 ViewState 中。</p> <pre><code>    Property Text() As String        Get            Dim s As String = CStr(ViewState(&quot;Text&quot;))            If s Is Nothing Then                Return String.Empty            Else                Return s            End If        End Get        Set(ByVal Value As String)            ViewState(&quot;Text&quot;) = Value        End Set    End Property </code></pre> <p>比較好的方式應該是讀取 ViewState 成員只做一次型別轉換的動作,而寫入 ViewState 的動作可以在 Render 前做批次寫入的動作即可。為了達到這樣的需求,我們可以覆寫 LoadViewState 及 SaveViewState 方法來處理屬性與 ViewState 的存取動作;當控制項初始化後會執行 LoadViewState 方法,來載入 ViewState 還原的控制項狀態,當控制項 Render 之前,會執行 SaveViewState 方法,將控制項的最終狀態存入 ViewState 中,也就是在此方法之後對控制項所做的任何變更都將會被忽略。</p> <p>我們改寫屬性的寫法,不直接用 ViewState 來存取屬性,而是改用「屬性區域變數」來存取屬性,在 LoadViewState 時載入 ViewState 到屬性區域變數,而 SaveViewState 時再將屬性區域變數寫入 ViewState 中。我們依此方式將 Text 屬性改寫如下。</p> <pre><code>    Private FText As String    Property Text() As String        Get            Return FText        End Get        Set(ByVal Value As String)            FText = Value        End Set    End Property    ''' &lt;summary&gt;    ''' 載入 ViewState 還原控制項狀態。    ''' &lt;/summary&gt;    Protected Overrides Sub LoadViewState(ByVal savedState As Object)        If Not (savedState Is Nothing) Then            ' Load State from the array of objects that was saved at vedViewState.            Dim myState As Object() = CType(savedState, Object())            If Not (myState(0) Is Nothing) Then                MyBase.LoadViewState(myState(0))            End If            If Not (myState(1) Is Nothing) Then                FText = CType(myState(1), String)            End If        End If    End Sub    ''' &lt;summary&gt;    ''' 將控制項狀態寫入 ViewState 中。    ''' &lt;/summary&gt;    Protected Overrides Function SaveViewState() As Object        Dim baseState As Object = MyBase.SaveViewState()        Dim myState(1) As Object        myState(0) = baseState        myState(1) = FText        Return myState    End Function </code></pre> <p>利用上述的方式,我們可以在 LoadViewState 批次載入所有屬性值,而在 SaveViewState 批次寫入屬性值,如此在讀取屬性就不用一直做型別轉換的動作以改善效率。</p> <p><strong>結語</strong><br /> 雖然屬性一般都是儲存於 ViewState 中,不過若是一些無關緊要的屬性或是完全不會執行階段就變更的屬性,可以考慮不需要將這些屬性儲存於 ViewState 中;因為 ViewState 是個兩面刃,ViewState 可以很輕易幫我們維護屬性值,不過相對的也增加了面頁的傳輸量,所以可以視實際情形來決定屬性是否要儲存於 ViewState 中。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/07/5601.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/07/5601.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-06 21:17:20</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day4] 複合控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011633?sc=rss.iron</guid>                <description><![CDATA[<p>複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬...]]></description>                                    <content:encoded><![CDATA[<p>複合控制項就是控制項可包含其他子控制項,複合控制項繼承至 System.Web.UI.WebControls.CompositeControl,例如 Login 及 Wizard 等控制項就是屬於複合控制項。我們常在網頁上常看到一種輸入日期的方式是年月日三個下拉清單,本文將利用複合控制項來實作這個年月日下拉清單控制項,示範如何實作複合控制項。<br /> <strong>一、CompositeControl 類別的特性</strong><br /> CompositeControl 類別是抽象類別,它會實作 INamingContaner 介面,INamingContaner 介面會在子控制項的 ClinetID 加上父控制項的 ID,以確保頁面上控制項的 ClientID 是唯一的。繼承 CompositeControl 類別一般都是覆寫 CreateChildControls 方法,在此方法中將建立子控制項並加入 Controls 集合屬性中;當存取其子控制項時,若子控制項未建立,會執行 CreateChildControls 方法,以會確保所有子控制項皆已在存取 ControlCollection 之前建立。<br /> <strong>二、日期下拉清單輸入器</strong><br /> 我們繼承 CompositeControl 類別,命名為 TBDropDownDate。這個控制項會包含年月日三個下拉清單(DropDownList),所以我們只要在 CreateChildControls 方法中依序建立年月日的 DropDownList 子控制項,並加入 Controls 集合屬性中即可。</p> <pre><code>''' &lt;summary&gt; ''' 日期下拉清單輸入器。 ''' &lt;/summary&gt; &lt; _ ToolboxData(&quot;&lt;{0}:TBDropDownDate runat=server&gt;&lt;/{0}:TBDropDownDate&gt;&quot;) _ &gt; _ Public Class TBDropDownDate    Inherits System.Web.UI.WebControls.CompositeControl    Protected Overrides Sub CreateChildControls()        Dim oYear As DropDownList        Dim oMonth As DropDownList        Dim oDay As DropDownList        Dim N1 As Integer        '年下拉清單區間為 1950-2010 (年區間可以用屬性來設定)        oYear = New DropDownList        oYear.ID = &quot;Year&quot;        For N1 = 1950 To 2010            oYear.Items.Add(N1.ToString)        Next        Me.Controls.Add(oYear) '加入子控制項        Me.Controls.Add(New LiteralControl(&quot;年&quot;))        '月下拉清單區間為 1-12        oMonth = New DropDownList        oMonth.ID = &quot;Month&quot;        For N1 = 1 To 12            oMonth.Items.Add(N1.ToString)        Next        Me.Controls.Add(oMonth) '加入子控制項        Me.Controls.Add(New LiteralControl(&quot;月&quot;))        '日下拉清單區為為 1-31        oDay = New DropDownList        oDay.ID = &quot;Day&quot;        For N1 = 1 To 12            oDay.Items.Add(N1.ToString)        Next        Me.Controls.Add(oDay) '加入子控制項        Me.Controls.Add(New LiteralControl(&quot;日&quot;))    End Sub End Class </code></pre> <p>在設定階段拖曳 TBDropDownDate 到頁面上,就可以看到我們在 CreateChildControls 方法中所加入的子控制項。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/ASP.NETDay4_9C76/image_2.png" alt="" /></p> <p>執行程式,檢視它的 HTML 原始碼,會發現年月日的子控制項的 ClientID 都會在原 ID 前加上父控制項的 ID,這樣命名規則可以確保所有的控制項的 ClinetID 都是唯一值。</p> <pre><code>&lt;span id=&quot;TBDropDownDate1&quot;&gt; &lt;select name=&quot;TBDropDownDate1$Year&quot; id=&quot;TBDropDownDate1_Year&quot;&gt; ....省略 &lt;select name=&quot;TBDropDownDate1$Month&quot; id=&quot;TBDropDownDate1_Month&quot;&gt; ....省略 &lt;select name=&quot;TBDropDownDate1$Day&quot; id=&quot;TBDropDownDate1_Day&quot;&gt; &lt;/span&gt; </code></pre> <p><strong>三、結語</strong><br /> 我們已經看過三類伺服器控制項的簡單案例,不過這三個案例都只是簡單說明控制項 UI 的部分,一個完整的控制項需具備屬性、方法、事件、設計階段支援...等,在後面的文章中,我們將陸續針對這些部分做詳細的介紹。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/05/5583.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/05/5583.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-05 22:22:25</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day3] 擴展現有伺服器控制項功能</title>                <link>https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011562?sc=rss.iron</guid>                <description><![CDATA[<p>相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息...]]></description>                                    <content:encoded><![CDATA[<p>相對於由無到有開發控制項,繼承現有現伺服器控制項是比較簡單且實用的方式;若希望在現有的控制項增加某些屬性或功能,直接繼承該控制項下來擴展功能是最快的方式,例如「按下 Button 會彈出詢問訊息」、「TextBox 設為 ReadOnly 時,可以取得前端傳回的 Text 屬性」這類需求,都可以直接繼承原控制項下來,加上我們需要的功能即可。以下我們就以一個簡單的案例來說明如何繼承現有伺服器下來擴展功能。<br /> <strong>一、擴展 Button 控制項:按鈕加上詢問訊息</strong><br /> 按下按鈕執行某些動作前,有時會詢問使用者是否執行該動作;例如按下刪除鈕,會詢問使用者是否確定要執行刪除的動作。當然這只需要簡單的 JavaScript 就可以完成,不過相對於 .NET 的程式語言,JavaScript 是非常不易維護的用戶端指令碼,如果能讓開發人員完全用不到 JavaScript,那何樂不為呢? 那就由 Button 控制項本身提供加上詢問訊息的功能就可以,相關的 JavaScript 由控制項去處理。<br /> 一般要在 Button 加上詢問訊息,只要在 OnClientClick 屬性設定如下的 JavaScript 即可。我們的目的只是讓開發人員連設定 OnClientClick 屬性的 JavaScript 都省略,直接設定要詢問的訊息即可,接下來我們就要開始實作這個控制項。</p> <pre><code>&lt;asp:Button ID=&quot;Button1&quot; runat=&quot;server&quot; Text=&quot;Button&quot;  OnClientClick=&quot;if (confirm('確定執行嗎?')==false) {return false;}&quot; /&gt;   </code></pre> <p>在 Bee.Web 專案中,加入「ASP.NET 伺服器控制項」,此控制項繼承 Button 下來命名為 TBButton (命名空間為 Bee.Web.WebControls)。在 TBButton 類別中加入 ConfirmMessage 屬性,用來設定詢問訊息的內容。然後在 Render 方法將詢問詢息的 JavaScript 設定到 OnClientClick 屬性即可。</p> <pre><code>Namespace WebControls    &lt; _    Description(&quot;按鈕控制項&quot;), _    ToolboxData(&quot;&lt;{0}:TBButton runat=server&gt;&lt;/{0}:TBButton&gt;&quot;) _    &gt; _    Public Class TBButton        Inherits System.Web.UI.WebControls.Button        &lt;Description(&quot;詢問訊息&quot;)&gt; _        Public Property ConfirmMessage() As String            Get                Dim sConfirmMessage As String                sConfirmMessage = CStr(ViewState(&quot;ConfirmMessage&quot;))                If sConfirmMessage Is Nothing Then                    Return String.Empty                Else                    Return sConfirmMessage                End If            End Get            Set(ByVal value As String)                ViewState(&quot;ConfirmMessage&quot;) = value            End Set        End Property        ''' &lt;summary&gt;        ''' 覆寫 Render 方法。        ''' &lt;/summary&gt;        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)            Dim sScript As String            Dim sConfirm As String            '若有設定 ConfirmMessage 屬性,則在 OnClientClick 加入詢問訊息的 JavaScript            If Me.ConfirmMessage &lt;&gt; String.Empty Then                sScript = Me.OnClientClick                '詢問訊息的 JavaScript                sConfirm = String.Format(&quot;if (confirm('{0}')==false) {{return false;}}&quot;, Me.ConfirmMessage)                If sScript = String.Empty Then                    Me.OnClientClick = sConfirm                Else                    Me.OnClientClick = sConfirm &amp; sScript                End If            End If            MyBase.Render(writer)        End Sub    End Class End Namespace </code></pre> <p>將 TBButton 拖曳到測試頁面,設定 ConfirmMessage 屬性。</p> <pre><code>&lt;bee:TBButton ID=&quot;TBButton1&quot; runat=&quot;server&quot; ConfirmMessage=&quot;確定刪除此筆資料嗎?&quot; Text=&quot;刪除&quot; /&gt; </code></pre> <p>執行結果如下。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/8cea6e0221af_B473/image_2.png" alt="" /></p> <p><strong>二、結語</strong><br /> 筆者在開發 ASP.NET 的應用程式過程中,通常會習慣把所有現有控制項繼承下來,無論目前需不需要擴展控制項功能。這種方式對於開發大型系統是相當有幫助的,因為無法預期在系統開發的過程中會不會因為某些狀況,而臨時需要擴展控制項的功能,所以就先全部繼承下來以備不時之需,也為未來保留修改的彈性。</p> <p><strong>三、相關連結</strong><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1748.aspx" target="_blank">擴展 CommandField 類別 - 刪除提示訊息</a><br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1697.aspx" target="_blank">按鈕加上詢問訊息</a></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/04/5578.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-04 21:24:49</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day2] 建立第一個伺服器控制項</title>                <link>https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011523?sc=rss.iron</guid>                <description><![CDATA[<p>上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。<br /> 撰寫伺服器控制項大致分為下列三種方式<br /> 1.由無到有建立全新的控制項,一般...]]></description>                                    <content:encoded><![CDATA[<p>上一篇中已經建立「ASP.NET 伺服器控制項」專案,接下來我們將學習來撰寫第一個伺服器控制項。<br /> 撰寫伺服器控制項大致分為下列三種方式<br /> 1.由無到有建立全新的控制項,一般會繼承至 System.Web.UI.Control 或 System.Web.UI.WebControls.WebControl 類別。<br /> 2.繼承現有控制項,擴展原有控制項的功能,如繼承原有 TextBox 來擴展功能。<br /> 3.複合式控制項,將多個現有的控制項組合成為一個新的控制項,例如 TextBox 右邊加個 Button 整合成一個控制項,一般會繼承至 System.Web.UI.WebControls.CompositeControl 類別。</p> <p>本文將先介紹第1種方式,由無到有來建立控制項,後面的文章中會陸續介紹第2、3種方式的控制項。要建立全新的控制項會繼承至 Control 或 WebControl,沒有 UI 的控制項可由 Control 繼承下來 (如 SqlDataSource),具 UI 的控制項會由 WebControl 繼承下來。接下來的範例中,我們將繼承 WebControl 來建立第一個 MyTextBox 控制項。</p> <p><strong>一、新增 MyTextBox 控制項</strong><br /> 在 Bee.Web 專案按右鍵選單,執行「加入\新增項目」,選擇「ASP.NET 伺服器控制項」,在名稱文字框中輸入 MyTextbox,按下「確定」鈕,就會在專案中加入 MyTextbox 控制項類別。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_thumb.png" alt="" /><br /> 新加入的控制項預設有一個 Text 屬性,以及覆寫 Render 方法。Render 方法是「將控制項呈現在指定的 HTML 寫入器中」,簡單的說就是在 Render 方法會將控制項對應的 HTML 碼輸出,用來呈現在用戶端的瀏覽器上。假設我們要撰寫一個網頁上的文字框,那就先去看一下文字框在網頁中對應的 HTML 碼,然後在 Render 方法中想辨法輸出這些 HTML 碼即可。</p> <p><strong>二、輸出控制項的 HTML 碼</strong><br /> 你可以使用 FrontPage 之類的 HTML 編輯器,先編輯出控制項的呈現方式,進而去觀查它的 HTML 碼,再回頭去思考如何去撰寫這個伺服器控制項。假設 MyTextbox 控制項包含一個文字框及一個按鈕,那最終輸出的 HTML 碼應該如下。</p> <pre><code>&lt;input id=&quot;Text1&quot; type=&quot;text&quot; /&gt; &lt;input id=&quot;Button1&quot; type=&quot;button&quot; value=&quot;button&quot; /&gt; </code></pre> <p>我們在 MyTextbox 的 RenderContents 方法中輸出上述的 HTML 碼。</p> <pre><code>    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)        Dim sHTML As String        sHTML = &quot;&lt;input id=&quot;&quot;Text1&quot;&quot; type=&quot;&quot;text&quot;&quot; /&gt;&quot; &amp; _                &quot;&lt;input id=&quot;&quot;Button1&quot;&quot; type=&quot;&quot;button&quot;&quot; value=&quot;&quot;button&quot;&quot; /&gt;&quot;        writer.Write(sHTML)    End Sub </code></pre> <p>建置控制項專案,然後拖曳 MyTextbox 在測試頁面上,設計階段就會呈現出我們期望的結果。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_4.png" alt="" /></p> <p>執行程式,在瀏覽器看一下 MyTextbox 控制項輸出的結果,是不是跟我們預期的一樣呢。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_6.png" alt="" /></p> <p><strong>三、屬性套用到控制項 HTML 碼</strong><br /> 控制項不可能單純這樣輸出 HTML 碼而已,控制項的相關屬性設定,一般都影響到輸出的 HTML 碼。假設 MyTextbox 有 Text 及 ButtonText 二個屬性,分別對應到 文字框的內容及按鈕的文字,MyTextbox 本來就有 Text 屬性,依像畫蘆葫新增 ButtonText 屬性。</p> <pre><code>    &lt; _    Bindable(True), _    Category(&quot;Appearance&quot;), _    DefaultValue(&quot;&quot;), _    Localizable(True)&gt; _    Property ButtonText() As String        Get            Dim s As String = CStr(ViewState(&quot;ButtonText&quot;))            If s Is Nothing Then                Return String.Empty            Else                Return s            End If        End Get        Set(ByVal Value As String)            ViewState(&quot;ButtonText&quot;) = Value        End Set    End Property </code></pre> <p>RenderContents 方法改寫如下。</p> <pre><code>    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)        Dim sHTML As String        sHTML = &quot;&lt;input id=&quot;&quot;Text1&quot;&quot; type=&quot;&quot;text&quot;&quot; value=&quot;&quot;{0}&quot;&quot;/&gt;&quot; &amp; _                &quot;&lt;input id=&quot;&quot;Button1&quot;&quot; type=&quot;&quot;button&quot;&quot; value=&quot;&quot;{1}&quot;&quot; /&gt;&quot;        sHTML = String.Format(sHTML, Me.Text, Me.ButtonText)        writer.Write(sHTML)    End Sub </code></pre> <p>重新建置控制項專案,在頁面上測試 MyTextbox 的 Text 及 ButtonText 屬性。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/0e8fe1ad2977_14385/image_8.png" alt="" /></p> <p><strong>四、使 ClientID (HTML 原始碼控制項的 ID) 是唯一值</strong><br /> 在頁面上放置二個 MyTextbox 控制項,執行程式,在瀏覽器中檢查 MyTextbox 的 HTML 原始碼。你會發現 MyTextbox 會以一個 span 包住控制項的內容,而每個控制項的輸出的 ClientID 是唯一的。不過 MyTextbox 內含的文字框及按鈕卻會重覆,所以一般子控制項的 ClientID 會在前面包含父控制項的 ID。</p> <pre><code>&lt;span id=&quot;MyTextbox1&quot;&gt; &lt;input id=&quot;Text1&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;Button1&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; &lt;br /&gt; &lt;span id=&quot;MyTextbox2&quot;&gt; &lt;input id=&quot;Text1&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;Button1&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; </code></pre> <p>所以我們再次修改 RenderContents 方法的程式碼</p> <pre><code>    Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter)        Dim sHTML As String        sHTML = &quot;&lt;input id=&quot;&quot;{0}_Text&quot;&quot; type=&quot;&quot;text&quot;&quot; value=&quot;&quot;{1}&quot;&quot;/&gt;&quot; &amp; _                &quot;&lt;input id=&quot;&quot;{0}_Button&quot;&quot; type=&quot;&quot;button&quot;&quot; value=&quot;&quot;{2}&quot;&quot; /&gt;&quot;        sHTML = String.Format(sHTML, Me.ID, Me.Text, Me.ButtonText)        writer.Write(sHTML)    End Sub </code></pre> <p>執行程式,再次檢視 HTML 原始碼,所有的 ClinetID 都會是唯一的。</p> <pre><code>&lt;span id=&quot;MyTextbox1&quot;&gt; &lt;input id=&quot;MyTextbox1_Text&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;MyTextbox1_Button&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; &lt;br /&gt; &lt;span id=&quot;MyTextbox2&quot;&gt; &lt;input id=&quot;MyTextbox2_Text&quot; type=&quot;text&quot; value=&quot;這是文字&quot;/&gt; &lt;input id=&quot;MyTextbox2_Button&quot; type=&quot;button&quot; value=&quot;這是按鈕&quot; /&gt; &lt;/span&gt; </code></pre> <p><strong>五、控制項前置詞</strong><br /> 自訂控制項的預設前置詞是 cc1,不過這是可以修改的,在專案中的 AssemblyInfo.vb 檔案中,加入如下定義即可。詳細的作法請參考筆者部落格中的「<a href="http://www.dotblogs.com.tw/jeff377/archive/2008/03/17/1744.aspx" target="_blank">自訂伺服器控制項前置詞</a>」一本有詳細介紹,在此不再累述。</p> <pre><code>'設定控制項的標記前置詞 &lt;Assembly: TagPrefix(&quot;Bee.Web.WebControls&quot;, &quot;bee&quot;)&gt; </code></pre> <p><strong>六、結語</strong><br /> 本文中是用土法鍊鋼的方法在撰寫伺服器控制項,一般在實作控制項時會有更好的方式、更易維護的寫法,後續的文章中會陸續介紹相關作法。</p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/03/5573.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/03/5573.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-03 23:51:35</pubDate>                                                                                                                                            </item>                    <item>                <title>[ASP.NET 控制項實作 Day1] 建立 ASP.NET 伺服器控制項專案</title>                <link>https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron</link>                <guid isPermaLink="true">https://ithelp.ithome.com.tw/articles/10011408?sc=rss.iron</guid>                <description><![CDATA[<p>在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如...]]></description>                                    <content:encoded><![CDATA[<p>在 ASP.NET 開發環境中,我們常使用現成的控制項直接拖曳至頁面中使用,有沒有想過我們也可以開發自用的控制項呢?本文將本文以 VS2008 為開發工具,VB.NET 為開發程式語言,來說明如何建立「伺服器控制項」專案,以及如何測試開發階段的的伺服器控制項。<br /> <strong>一、建立「ASP.NET 伺服器控制項」專案</strong><br /> 首先執行功能表「檔案\新增專案」,在專案類型中選擇 Visual Basic -&gt; Web,選取「ASP.NET 伺服器控制項」範本,在名稱文字框中輸入專案名稱,也就是組件的檔案名稱,我們輸入 Bee.Web 為專案名稱,組件檔案為 Bee.Web.dll,按下「確定」鈕即會建立新的「ASP.NET 伺服器控制項」專案。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_1.png" alt="" /><br /> 在新建立「ASP.NET 伺服器控制項」專案中,會預設加入一個伺服器控制項類別(ServerControl1.vb),這個伺服器控制項已經事件幫我們加入一些控制項的程式碼。目前暫不做任何修改,直接使用此控制項來做測試說明。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_2.png" alt="" /><br /> 接下來執行功能表「專案\Bee.Web 屬性」,設定此組件的根命名空間,一般慣用的根命名空間都會與組件名稱相同,以方便加入參考時可以快速找到相關組件。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_10.png" alt="" /><br /> 我們先儲存這個「ASP.NET 伺服器控制項」專案,指定儲存位置,按下「儲存」鈕。整個專案相關檔案,會儲存在以專案名稱的資料夾中。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_3.png" alt="" /></p> <p><strong>二、加入測試網站</strong><br /> 不要關閉目前「ASP.NET 伺服器控制項」專案,執行功能表「檔案\加入\新網站」,選擇「ASP.NET 網站」,會在方案中加入一個網站,來測試開發階段的伺服器控制項使用。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_5.png" alt="" /><br /> 在測試網站加入參考,選擇「專案」頁籤,此頁籤中會列出該方案中其他可加入參考的專案,選取 Bee.Web 專案,按下「確定」鈕。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_6.png" alt="" /><br /> 先在 Bee.Web 專案中執行「建置」動作,然後切換到測試網站的頁面設計,工具箱中就會出現 ServerControl1 伺服器控制項。這個控制項就可以直接拖曳至頁面中使用,這個控制項只是單純 Render 出 Text 屬性值,你可以在控制項屬性視窗中,更改 Text 屬性值為 &quot;測試文字&quot;,就會看到這個控制項顯示 &quot;測試文字&quot;。將測試網站設為啟動專案,按下「F5」執行程式,就會看到該控制在執行階段的結果。<br /> <img src="http://files.dotblogs.com.tw/jeff377/0810/166fb54653d6_12877/image_thumb_12.png" alt="" /></p> <p>備註:本文同步發佈於筆者「ASP.NET 魔法學院」部落格<br /> <a href="http://www.dotblogs.com.tw/jeff377/archive/2008/10/02/5562.aspx" target="_blank">http://www.dotblogs.com.tw/jeff377/archive/2008/10/02/5562.aspx</a></p> ]]></content:encoded>                                <dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">jeff377</dc:creator>                <pubDate>2008-10-02 23:16:29</pubDate>                                                                                                                                            </item>            </channel> </rss>