WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Table の行のコピー作成

by WebSurfer 2010年9月9日 13:12

System.Web.UI.WebControls.Table の話です。

Table オブジェクトの、ある行のコピーを作って、その Table に追加するにはどうしたらいいでしょうか? 以下のような感じです。

オリジナルのテーブル 一行コピーして追加後のテーブル
オリジナル 一行コピー後

上のオリジナルの画像を作ったコードは以下の通りです。

<asp:Table id="Table1" 
    runat="server"
    CellPadding="10" 
    GridLines="Both"
    HorizontalAlign="Center">
    <asp:TableRow>
        <asp:TableCell>
            Item
        </asp:TableCell>
        <asp:TableCell>
            Description
        </asp:TableCell>
    </asp:TableRow>
    <asp:TableRow>
        <asp:TableCell>
            Row 0, Col 0
        </asp:TableCell>
        <asp:TableCell>
            Row 0, Col 1
        </asp:TableCell>
    </asp:TableRow>
    <asp:TableRow>
        <asp:TableCell>
            Row 1, Col 0
        </asp:TableCell>
        <asp:TableCell>
            Row 1, Col 1
        </asp:TableCell>
    </asp:TableRow>
</asp:Table>

この Table1 の一行目のコピーをプログラムで動的に作って、最終行の下に追加することを考えてみます。

以下のようにするとどうなるでしょう?

TableRow newRow = new TableRow();
newRow = Table1.Rows[0];
Table1.Rows.Add(newRow);

うまくいきそうに見えますが、これだと一行目が最下行の下に移動するだけです。

以下のように、TableRow だけでなく、その中身の TableCell のコピーも作って TableRow に設定し、それを Table に追加してやる必要があります。

TableRow newRow = new TableRow();
foreach (TableCell cell in Table1.Rows[0].Cells)
{
    TableCell newCell = new TableCell();
    newCell.Text = cell.Text;
    newRow.Cells.Add(newCell);
}
Table1.Rows.Add(newRow);

Table コンロトールはほとんど使わないし、実際にこのようなことをすることはなさそうなので、役に立たない話かもしれません。(汗) でも、昔、せっかく考えたことなので、忘れないように書いておきます。

Tags:

ASP.NET | ASP.NET

ValidationSummary の表示

by WebSurfer 2010年9月8日 12:27

TextBox への入力を RequiredFieldValidator, RegularExpressionValidator でチェックし、エラーメッセージを ValidationSummary で表示するようにしたとします。以下のような感じです。

SummartValidator の表示

上の画面を表示したコードは以下のようになります。

<asp:TextBox ID="TextBox1" runat="server" />  
<asp:RequiredFieldValidator 
  ID="RequiredFieldValidator1"    
  runat="server" 
  ErrorMessage="入力必須" 
  ControlToValidate="TextBox1" 
  Text="*" 
  ForeColor="Red" />
<asp:RegularExpressionValidator 
  ID="RegularExpressionValidator1" 
  runat="server" 
  ErrorMessage="正しい郵便番号が必要" 
  ControlToValidate="TextBox1" 
  ValidationExpression="\d{3}(-(\d{4}|\d{2}))?" 
  Text="*" 
  ForeColor="Red" />
<br />
<asp:Button ID="Button1" 
  runat="server" 
  Text="実行" />  
<asp:ValidationSummary ID="ValidationSummary1" 
  runat="server" 
  ShowMessageBox="True" 
  ShowSummary="False" />    

TextBox にフォーカスがあるとき Enter キーを押すと form が submit(ポストバック)されますが、この時に入力が不正だった場合、期待した動作をするでしょうか?

ちなみに、期待した動作とは以下のとおりです。

  1. ポストバックがキャンセルされる。
  2. TextBox1 の横に「*」が表示される。
  3. エラーメッセージが ValidationSummary に表示される。

上記のコードで試してみると、1, 2 は実行されますが、3 は実行されないという結果になりました。IE8, Firefox 3.6.8, Opera 10.61 で同じ結果でした。

なお、Button1 をクリック(もしくは Button1 にフォーカスを当てて Enter キーを押す)した場合は、上記 1. 2, 3 すべて実行されます。それが、上の画像です。

TextBox にフォーカス → Enter キーでは、自動生成される検証用の JavaScript の一部のコードがスキップされることが原因です。何故スキップされるのか理由は分かりませんが。

対応策としては、ハック的なのでお勧めはできませんが、以下のようにすると、TextBox にフォーカス → Enter キーでも ValidationSummary が表示されるようになります。

protected void Page_Load(object sender, EventArgs e)
{
  TextBox1.Attributes.Add("onkeydown", 
    "if (event.keyCode == 13) Page_ClientValidate();");
}

Page_ClientValidate メソッドは引数に validationGroup を取りますが、上記のように空にしておいても正しく処置してくれるようです(IsValidationGroupMatch は true 返すので)。

TextBox にフォーカス → Enter キーで ValidationSummary が表示されないようになっているのには何か理由があって、無理に上記のようなことをすると予期せぬ副作用が出るのかもしれませんので、注意してください。

---------- 2011/4/30 追記 ----------

やっぱり予期せぬ副作用がありました。(汗)

まず、検証対象の TextBox が複数ある場合、onkeydown 属性に上記のような小細工をすると ValidationSummary がダブって出てしまい、期待した動きになりません。例えば、以下のコードで、一方の TextBox の入力が正しくて、他方が NG のとき、正しい方の TextBox にフォーカスを当てて Enter キーを押すと不具合が出ます。

もうひとつの問題は、オートコンプリート機能を利用する際、ドロップダウンリストに表示される選択肢を Enter キーで確定できなくなることです。

実際に試せるように 実験室 にアプリケーションをアップしておきましたので、興味のある方は試してみてください。

<%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<script runat="server">
  protected void Page_Load(object sender, EventArgs e)
  {
    TextBox1.Attributes.Add("onkeydown", 
      "if (event.keyCode == 13) Page_ClientValidate();");
    TextBox2.Attributes.Add("onkeydown",
      "if (event.keyCode == 13) Page_ClientValidate();");
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
  <title></title>
</head>
<body>
  <form id="form1" runat="server">
  <div>
    郵便番号:
    <asp:TextBox id="TextBox1" runat="server" />  
    <asp:RequiredFieldValidator 
      ID="RequiredFieldValidator1"    
      runat="server" 
      ErrorMessage="入力必須" 
      ControlToValidate="TextBox1" 
      Text="*" 
      ForeColor="Red" 
      Display="Dynamic" />
    <asp:RegularExpressionValidator 
      ID="RegularExpressionValidator1" 
      runat="server" 
      ErrorMessage="正しい郵便番号が必要" 
      ControlToValidate="TextBox1" 
      ValidationExpression="\d{3}(-(\d{4}|\d{2}))?" 
      Text="*" 
      ForeColor="Red" 
      Display="Dynamic" />
    <br />
    メールアドレス:
    <asp:TextBox id="TextBox2" runat="server" />  
    <asp:RequiredFieldValidator 
      ID="RequiredFieldValidator2"    
      runat="server" 
      ErrorMessage="入力必須" 
      ControlToValidate="TextBox2" 
      Text="*" 
      ForeColor="Red" 
      Display="Dynamic" />
    <asp:RegularExpressionValidator 
      ID="RegularExpressionValidator2" 
      runat="server" 
      ErrorMessage="正しいメールアドレスが必要" 
      ControlToValidate="TextBox2" 
      ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*" 
      Text="*" 
      ForeColor="Red" 
      Display="Dynamic" />
    <br />
    <asp:Button id="Button2" 
      runat="server" 
      Text="検証" />  
    <asp:ValidationSummary id="ValidationSummary1" 
      runat="server" 
      ShowMessageBox="True" 
      ShowSummary="False" 
      HeaderText="サマリーです" />
  </div>
  </form>
</body>
</html>

Tags:

Validation

SmtpClient でメール送信

by WebSurfer 2010年9月7日 14:25

ユーザー登録の際のパスワードの連絡、ブログにコメントが書き込まれた時の通知などのために、Web アプリケーションにメールの自動送信機能を実装するケースは多いと思います。

自分の HP にも、メールの自動送信の機能を実装しています。もっとも、ユーザー登録するような人はいないし、コメントを書き込む人もいないので、ほとんど役に立っていませんが。(苦笑)

メール送信に利用しているのは .NET Framework のライブラリ SmtpClient クラスです。これを利用すると簡単にメール送信ができます。

ただし、日本語対応に問題があり注意が必要です。以下に、文字コードとしてデフォルトの UTF-8 を使った場合と、日本で広く使われている iso-2022-jp (JIS) を使った場合の注意事項を書いておきます。

(1) UTF-8 を使う場合

UTF-8 の場合は、ほとんど文字化けに悩むことはありませんが、アドレスの表示名に日本語を使う場合のみ注意が必要です。

表示名は MailAddress コンストラクタの第 2 引数で設定しますが、日本語の文字列をそのまま代入すると Q エンコードされてしまいます。

Q エンコードで問題なければいいのですが、受信するメーラによっては B エンコードでないと文字化けするかもしれません。(Outlook Express, Windows メールは問題ありませんでしたが)

B エンコードにするには、以下のサンプルコードにある EncodeMailHeader ようなメソッドを作って日本語の表示名を変換し、それを第 2 引数に代入してください。

件名、本文、添付ファイル名は、以下のサンプルのように日本語をそのまま代入して問題ありません。件名、添付ファイル名は、自動的に UTF-8 の B エンコードに変換されて、ヘッダーに設定されます。本文は、UTF-8 の base64 エンコードとなります。

private string EncodeMailHeader(string str, Encoding enc)
{
  string ret = 
    System.Convert.ToBase64String(enc.GetBytes(str));
  ret = string.Format("=?{0}?B?{1}?=", enc.BodyName, ret);
  return ret;
} 

Encoding enc = Encoding.GetEncoding("utf-8");

MailAddress to = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));
MailAddress From = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));

MailMessage mailMessage = new MailMessage(from, to);

mailMessage.Subject = "日本語の件名";
mailMessage.Body = "日本語の本文";

Attachment data = 
  new Attachment("添付ファイル名", 
    System.Net.Mime.MediaTypeNames.Application.Octet);
ContentDisposition disposition = data.ContentDisposition;
disposition.FileName = 
  EncodeMailHeader(System.IO.Path.GetFileName(file), enc);
disposition.CreationDate = 
  System.IO.File.GetCreationTime(file);
disposition.ModificationDate = 
  System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = 
  System.IO.File.GetLastAccessTime(file);

mailMessage.Attachments.Add(data);

SmtpClient smtpClient = new SmtpClient();
smtpClient.Host = "mail.abc.co.jp";
smtpClient.Port = 25;
smtpClient.Send(mailMessage);

(2) iso-2022-jp を使う場合

Encoding を iso-2022-jp にする以外は、メールアドレスを設定するところまでは上記の UTF-8 の場合と同じです。

件名は、直接 Subject プロパティに日本語を代入すると Q エンコードになってしまいます。以下のサンプルのように、EncodeMailHeader メソッドを使って変換してから Subject プロパティに代入しなければなりません。(注:.NET Framework 4.5 で件名のエンコードが変わったそうです。詳しくは下の 2013/5/10 追記を見てください)

一番の問題は本文で、Body を使用すると Content-Transfer-Encoding は 8bit エンコードになってしまいます。これを 7bit にする方法はないそうです。代わりに、AlternateView を使って 7bit エンコードにする方法があります。以下にそのサンプルを書いておきます。

添付ファイル名は、Content-Type の name は、どのよう設定にしても UTF-8 の B エンコードになってしまいます。一方、Content-Disposition の filename は、以下のように設定すれば、iso-2022-jp の B エンコードになります。

以上のように、iso-2022-jp を使うのは、特に本文を AlternateView にするところが、かなり無理やりっぽい感じです。自分的には決してお勧めではないです。

Encoding enc = Encoding.GetEncoding("iso-2022-jp");

private string EncodeMailHeader(string str, Encoding enc)
{
  string ret = 
    System.Convert.ToBase64String(enc.GetBytes(str));
  ret = string.Format("=?{0}?B?{1}?=", enc.BodyName, ret);
  return ret;
}

MailAddress to = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));
MailAddress From = 
  new MailAddress("surfer@mail.abc.co.jp", 
    EncodeMailHeader("日本語の名前", enc));

MailMessage mailMessage = new MailMessage(from, to);

mailMessage.Subject = EncodeMailHeader("日本語の件名", enc);

// プレーンテキストのAlternateViewを作成
AlternateView htmlView = 
  AlternateView.CreateAlternateViewFromString(
    "日本語の本文",
    enc,
    System.Net.Mime.MediaTypeNames.Text.Plain);

// TransferEncoding.SevenBitを指定
htmlView.TransferEncoding = 
  System.Net.Mime.TransferEncoding.SevenBit;

mailMessage.AlternateViews.Add(htmlView);

Attachment data = 
  new Attachment("添付ファイル名", 
    System.Net.Mime.MediaTypeNames.Application.Octet);
ContentDisposition disposition = data.ContentDisposition;
disposition.FileName = 
  EncodeMailHeader(System.IO.Path.GetFileName(file), enc);
disposition.CreationDate = 
  System.IO.File.GetCreationTime(file);
disposition.ModificationDate = 
  System.IO.File.GetLastWriteTime(file);
disposition.ReadDate = 
  System.IO.File.GetLastAccessTime(file);

mailMessage.Attachments.Add(data);

SmtpClient smtpClient = new SmtpClient();
smtpClient.Host = "mail.abc.co.jp";
smtpClient.Port = 25;
smtpClient.Send(mailMessage);

ということで、結局自分はどうしているかというと、デフォルトの UTF-8 を使っています。UTF-8 が使えないメーラーなんか相手にしない・・・と言いたいところですが、自分宛のメール送信ぐらいにしか使ってないから、何だっていいのです。(笑)

----- 2013/5/10 追記 -----

.NET Framework 4.5 では件名 (Subject) の実装が変わって、上記の「(2) iso-2022-jp を使う場合」に書いたサンプルコードでは Q エンコードになってしまうそうです。その理由など、詳しくは以下のページを参照してください。

.NET Framework 4.5 の System.Net.Mail で日本語の件名を ISO-2022-JP の Base64 でエンコードして送信する方法

二重にエンコードすることにより B エンコードになるそうです。検証していませんが、上記のサンプルコードでは以下のように変更すればよいはずです。

.NET Framework 4 まで(上記サンプルコードの通り)

mailMessage.Subject = EncodeMailHeader("日本語の件名", enc);

.NET Framework 4.5 では二重エンコード

mailMessage.Subject = 
  EncodeMailHeader(EncodeMailHeader("日本語の件名", enc), enc);

Tags: , ,

.NET Framework

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年4月  >>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

View posts in large calendar