by WebSurfer
2011年11月20日 15:47
リストボックスの内のアイテムのスクロール位置をポストバック前後で維持する方法です。IE9, Firefox 8.0, Safari 5.1.1, Opera 11.52 で検証してみましたが、Opera 以外は期待通りの動きをしました。(Opera では scrollTop が取得できず、うまくいきません。他の方法を検討中です)(2011/11/21 追記: IE6 もダメでした。IE7, IE8 は OK でした)
IE ではリストボックスの中の項目を選択するしないにかかわらず、ポストバックすると一番先頭に戻ってしまいます。その他のブラウザでは、リストボックスの中の項目を選択すればポストバック後に一番先頭に戻ることはありませんが、スクロール位置はポストバック前後で変わってしまいます。
今回の例では、JavaScript の scrollTop を使用して、ポストバック直前のリストボックスのスクロールバーの上端の座標を取得して隠しフィールドに保存し、ポストバックして再描画する時に隠しフィールドに保存した座標を取得してスクロール位置を設定しています。
そのサンプルコードは以下の通りです。上の画像は、このコードを実行したときのブラウザの仮面です。実際に動かして試せるよう 実験室 にアップしましたので、興味のある方は試してみてください。
隠しフィールドには ASP.NET の HiddenField サーバーコントロールを使いました。これを使うと自動的に ViewState によってポストバック前後の Value が維持されます。普通の html の <input type="hidden" ... /> を使う場合は、ポストバック前後の value の維持に一工夫必要です。
リストボックスには、これも ASP.NET の ListBox サーバーコントロールを使っていますが、スクロール位置をポストバック前後で維持する仕組みに関しては、普通の html の select でも同じです。
<%@ 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">
void SubmitBtn_Click(Object sender, EventArgs e)
{
string selectedItems = String.Empty;
int count = 0;
for (int i = 0; i < ListBox1.Items.Count; i++)
{
if (ListBox1.Items[i].Selected)
{
selectedItems += ListBox1.Items[i].Value + ", ";
count++;
}
}
if (count > 0)
{
selectedItems =
selectedItems.TrimEnd(new char[] { ',', ' ' });
Label2.Text = "You chose: " + selectedItems;
}
else
{
Label2.Text = "アイテムが選択されていません。";
}
}
protected void Page_Load(object sender, EventArgs e)
{
string value = HiddenField1.Value;
Label1.Text = "Scroll Top: " + value;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="Scripts/jquery-1.4.1.js" type="text/javascript">
</script>
<script type="text/javascript">
//<![CDATA[
// Opera 11.52 で動かない(scrollTop が取得できない)
function setScrollTopInHiddenField() {
var position =
document.getElementById("<%=ListBox1.ClientID%>").scrollTop;
document.getElementById("<%=HiddenField1.ClientID%>").value =
position;
}
window.onload = function () {
var position =
document.getElementById("<%=HiddenField1.ClientID%>").value;
if (position != "") {
document.getElementById("<%=ListBox1.ClientID%>").scrollTop =
position;
}
}
// jQuery を使っても、やはり Opera では動かない
// function setScrollTopInHiddenField() {
// var position =
// $("#<%=ListBox1.ClientID%>").scrollTop();
// $("#<%=HiddenField1.ClientID%>").val(position);
// }
// $(document).ready(function () {
// var position =
// $("#<%=HiddenField1.ClientID%>").val();
// if (position != "") {
// $("#<%=ListBox1.ClientID%>").scrollTop(position);
// }
// });
//]]>
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:HiddenField ID="HiddenField1" runat="server" />
<asp:Label ID="Label1" runat="server">
</asp:Label>
<br />
<asp:ListBox id="ListBox1"
Rows="10"
SelectionMode="Multiple"
runat="server">
<asp:ListItem>Item 1</asp:ListItem>
<asp:ListItem>Item 2</asp:ListItem>
<asp:ListItem>Item 3</asp:ListItem>
<asp:ListItem>Item 4</asp:ListItem>
<asp:ListItem>Item 5</asp:ListItem>
<asp:ListItem>Item 6</asp:ListItem>
<asp:ListItem>Item 7</asp:ListItem>
<asp:ListItem>Item 8</asp:ListItem>
<asp:ListItem>Item 9</asp:ListItem>
<asp:ListItem>Item 10</asp:ListItem>
<asp:ListItem>Item 11</asp:ListItem>
<asp:ListItem>Item 12</asp:ListItem>
<asp:ListItem>Item 13</asp:ListItem>
<asp:ListItem>Item 14</asp:ListItem>
<asp:ListItem>Item 15</asp:ListItem>
<asp:ListItem>Item 16</asp:ListItem>
<asp:ListItem>Item 17</asp:ListItem>
<asp:ListItem>Item 18</asp:ListItem>
<asp:ListItem>Item 19</asp:ListItem>
<asp:ListItem>Item 20</asp:ListItem>
<asp:ListItem>Item 21</asp:ListItem>
<asp:ListItem>Item 22</asp:ListItem>
<asp:ListItem>Item 23</asp:ListItem>
<asp:ListItem>Item 24</asp:ListItem>
<asp:ListItem>Item 25</asp:ListItem>
</asp:ListBox>
<asp:button id="Button1"
Text="Submit"
OnClick="SubmitBtn_Click"
OnClientClick="setScrollTopInHiddenField();"
runat="server" />
<br />
<asp:Label ID="Label2" runat="server">
</asp:Label>
</div>
</form>
</body>
</html>
@ Page ディレクティブに MaintainScrollPositionOnPostback 属性というものがありますが、たぶんこんな感じでポストバック前後の位置を維持しているのではないかと思います。
------ 2016/1/12 追記 ------
MaintainScrollPositionOnPostBack(ポストバック前後で上下左右のスクロール位置を維持する機能)の仕組みを調べてみました。
やはり、この記事に書いたようなスクロール位置を保存する隠しフィールドとクライアントスクリプトを使用しています。
MaintainScrollPositionOnPostBack でスクロール位置を維持する仕組みについては別の記事「ポストバック前後でスクロール位置維持」に書きましたので興味がありましたら見てください。
by WebSurfer
2011年11月18日 22:58
ASP.NET ベースの Web アプリケーションを開発する際、Visual Studio に搭載されている開発サーバーを利用されている方は多いと思います。
余談ですが、この開発サーバーは、もともと Cassini という名前のサンプル Web サーバープログラムだそうで、今でも Microsoft のサイト ASP.NET Cassini Sample Web Server からソースコードを入手できます。(2012/1/8 追記: Cassini のダウンロードページがリンク切れになっていました。それに代わるものというわけではないですが、CassiniDev - Cassini 3.5/4.0 Developers Edition という CodePlex のサイトで改良版が提供されています)
開発サーバーは手軽で便利ですが、開発マシンで IIS が使えるのであれば、開発サーバーを開発に利用するのは避けたほうがいいと思います。
理由は、開発サーバーでは問題なく動いていても、IIS を使った本番環境に移すと動かないということがあるからです。
自分が認識している問題としては、以下があります。
-
アクセス権: 開発サーバーの場合は PC にログインしているユーザーアカウントが持つアクセス権を使います。Administrator 権限でログインしていれば、ほぼ無制限に PC 内のリソースにアクセスできます。一方、実環境で IIS 上で動かす場合、リソースへのアクセス権は IIS のワーカープロセスのアカウント(IIS7 の場合、デフォルトで NETWORK SERVICE)が持つものになります。そのアクセス権には制限がありますので、ファイルが開けないとか書き込めないという問題が出てきます。
-
統合パイプラインモード: 開発サーバーには IIS7 の統合パイプラインモードの機能がありません(クラッシックモード相当になります)。また、統合パイプラインモードとクラシックモードとでは web.config での HTTP ハンドラ、HTTP モジュールの定義方法が異なりますので、定義の仕方によっては、HTTP ハンドラ、HTTP モジュールが動かない場合があります。
-
要求のフィルタリング: IIS7 で導入された 要求のフィルタリング requestFiltering は開発サーバーにはありません。それゆえ、カスタムエラーの表示などエラーハンドリングが異なります。
-
MIME の種類: ファイルを要求した際、そのファイルの拡張子が IIS に定義済みの MIME の種類ではないと "HTTP エラー 404.0 - Not Found" を返します。よくある例が .flv や .mp4 ファイルで、開発サーバーでは再生できるが、IIS では再生できないという結果になります。(2013/4/20 追記)
-
SSL: 開発サーバーには SSL 通信の機能がありません。したがって、SSL 通信下における試験ができません。(2012/1/8 追記)
-
Session 0 分離: Windows Vista, Windows Server 2008 から Session 0 分離というセキュリティ対策がとられています(詳しくは、Windows でのサービスとドライバーへの Session 0 分離の影響 というページからダウンロードできる文書を参照)。つまり、ワーカープロセスは Session 0 で動いていて、それから起動されるアプリも Session 0 で動く。Session 0 は noninteractive なので、モーダルダイアログやフォームを表示することはできません(InvalidOperationException 例外がスローされます)。一方、ASP.NET 開発サーバーを使っている場合など、ワーカープロセスがログインしたユーザーのアカウントで動いている時は話が違ってきます。(2012/2/1 追記)
-
32-bit/64-bit の問題: これは逆に ASP,NET 開発サーバーで動かないケースで、利用しているライブラリが 64-bit 用の場合が問題になります。Visual Studio は 32-bit 版しか提供されていませんので、64-bit OS 上では WOW 上で 32-bit 動作します。その状態で、Visual Studio で Web アプリケーションを開発して ASP.NET 開発サーバで動作させた場合、Web アプリケーションは 32-bit で動作します。従って、x64 版の dll は動きません。例えば、Sharepoint の dll など x64 版 しかないものがあるそうですが、そのような場合は ASP.NET 開発サーバーでは動きません。(2013/9/5 追記)
-
その他: IIS 用に用意された 拡張モジュール が利用できない。特に使えないと困るのが、SEO 対策に有効な URL Rewrite Module でしょうか。(2012/1/8 追記)
IIS 上で開発すれば、上記のようなトラブルは未然に防ぐことができます。(Visual Studio 2010 SP1 でリリースされた IIS Express を使うという選択肢があるようですが、自分は使ったことがないので、上記の問題の有無はわかりません。)(2012/10/27 追記:今さらながらですが IIS Express を使ってみました。1 項の「アクセス権」の問題は IIS Express では解決できません。その他にも問題があります。詳しくは IIS Express をインストールしました を見てください。)
XP, Vista, 7 など、サーバー OS でなくても、あるグレード以上なら IIS が標準で実装されています。ちょっと設定に時間はかかりますが、IIS をインストールして IIS 上で開発できます。
ただし、OS のグレードによっては IIS がインストールされていても機能に制限があり、デバッグができないものがあるので注意してください。
------ 2013/9/6 追記 ------
ASP.NET 開発サーバー、IIS Express、IIS の比較を書いた Microsoft の公式文書(MSDN ライブラリ)を紹介しておきます(URL 下記)。Visual Studio で IIS を使用して開発したい方は、そのページの下の方の「IIS をインストールして Visual Studio と連動するように構成」というセクションに設定方法のページへのリンクがありますので参考にしてください。
ASP.NET Web プロジェクト用の Visual Studio の Web サーバー
ASP.NET 開発サーバーの実態は何かは、以下のページが参考になると思います。
VS 2005やIISを使用せずにWebアプリケーションを実行するには?[VS 2005のみ]
WebDev.WebServer.EXE がある場所は Visual Studio のバージョンによって異なるので注意してください。上のページに書いてある場所は Visual Stidio 2005 の場合です。
Visual Studio 2008, 2010 の場合は以下のようになります。64-bit OS の場合は Program Files ⇒ Program Files (x86) になるはずです。
VS2008:
C:\Program Files\Common Files\microsoft shared\DevServer\9.0
VS2010:
C:\Program Files\Common Files\microsoft shared\DevServer\10.0
by WebSurfer
2011年11月15日 22:35
SSL (Secure Sockets Layer) 通信を行っているページで、"セキュリティで保護されているコンテンツのみ表示されます。"(IE9 の場合。他のバージョン、ブラウザではメッセージが異なります。)という警告が表示されることがあります。
原因は、https:// で接続したページ内に https (SSL) と、http (非SSL) の接続が混在しているからです。例えば、そのページで使用している画像、CSS、JavaScript などのリソースへの参照を http で始まる URL で行っているケースです。
解決方法は、参照するファイルの URL を次のいずれかの形式に設定することです。
-
httpsで始まる絶対 URL パスを使う。
-
/ (スラッシュ)で始まるサイトルート相対パスを使う。
-
相対パスを使う。
上記のパスの具体的な例はそれぞれ以下のとおりです。
<img src="https://www.aaa.co.jp/images/sample.jpg" />
<img src="/images/sample.jpg" />
<img src="../images/sample.jpg" />
上記のようなケースは、html ソースを見れば気がつくので容易に対処できると思います。
気がつきにくいのが iframe の src 属性です。src を省略したり、http で始まる URL に設定したりすると警告が出ます。実は、自分は、これが問題になることすら知りませんでした。(汗)
特に問題なのは、動的に iframe がページに追加される場合です。さらに、ライブラリとして dll で提供されているカスタムコントロールの場合は、いくらソースを眺めていても、iframe は出てこないのでわかりません。
カスタムコントロールの具体的な例としては、AJAX Control Toolkit の AsyncFileUpload コントロールに使用されている iframe があります。このコントロール場合、src 属性には about:blank が設定されるのですが、about:blank も非 https と見なされるらしく、やはり警告が出ます。
解決方法は、KB261188 にあるように、iframe の src 属性の初期値を設定しない場合は、ダミー html ページを設定しておくことだそうです。
ただし、ダミーといっても存在しないファイルを設定するとサーバー側でエラーログが残るという話があるので(確かめたわけではありません)、中身は空でも実存するファイルを指定するのがよいそうです。
AJAX Control Toolkit の場合はソースが入手できますので、ソースを修正して再コンパイルするなりして解決できますが、そうでない場合は手の打ちようがないですね。