C# Web FormsでのViewStateとDisabled属性の罠
概要
C# Web Formsをベースにした社内システムを改修する中で、
サーバーコントロールの状態(ViewState)とinputの関係について、
ちょっと面白い挙動を見つけました。
特に、日付を和暦(例:令和)で表示する必要がある画面において、
ViewStateとJavaScriptのデータ連携にいくつか注意が必要なポイントがいくつかありました。
- 和暦変換とViewStateの関係
Web Formsのサーバーコントロールは、基本的に西暦で値を保持します。
そのため、和暦を画面に表示する場合は、以下のような構成を取りました。
- サーバー側では西暦(YYYY)データを送信
- クライアント側(JavaScript)で和暦に変換し、画面へ反映
しかし、ViewStateには和暦の情報を直接保持できないため、
Postback時にはHiddenFieldを使って一時的に値を格納し、JSで再描画するという実装にしました。
- 発生した問題
特定の入力項目において、次の条件を同時に満たす場合に問題が発生しました。
- ReadOnly = true
- Disabled = true
- HiddenFieldには値が存在し、JSでinputにも値をセットしている
この場合、Postback後に画面上の値が消えてしまうという現象が起こりました。
HiddenFieldにも値があり、JSで再設定する処理もあるのに、なぜか空白になってしまうのです。
- 原因の分析 調査の結果、原因は以下の3点の組み合わせにありました。
3-1. ReadOnly = true
- サーバーは、ユーザーが値を編集できないフィールドとして認識します。
- そのためPostback時には、フォームデータよりもViewStateの値を優先して適用します。
- つまり、JSで画面に表示していても、ViewState側の空の値で上書きされることがあります。
3-2. Disabled = true
- ブラウザはDisabled属性のinputをフォームデータに含めず送信しません。
- そのためサーバー側は値を受け取れず、結果的に空の状態として扱われます。
3-3. HiddenFieldとの組み合わせ
- HiddenFieldの値自体はPOSTされますが、
ViewStateの復元処理が優先されるため、空のViewStateで上書きされることがあります。 - 結果として「Disabledでサーバーに値が送られない+ReadOnlyで
ViewStateが優先される」 という構成が、今回の問題の本質でした。
- 解決方法
最終的に、以下の方法で安定して動作するようになりました。
4-1. Postback前にReadOnly=falseまたはDisabled=falseに変更
- サーバーが正しく値を受け取れるようにする
- ViewStateが最新の値に更新される
4-2. 値を格納した後、再度ReadOnly/Disabledを設定
- 画面では編集不可を維持
- Postback後も値が消えず、安定して表示される
この方法によって、Postbackの有無にかかわらず入力値が正しく保持されるようになりました。
- 補足(Tips)
- Disabled属性のinputはサーバーに値を送信しないため、 重要なデータには使用しない方が安全です。
おわりに
ViewStateやPostbackといった仕組みは理解していたつもりでしたが、
「ViewStateに保存されない値をReadOnlyやDisabledで扱うと値が消える」 という挙動には正直驚きました。
結果的に、ViewStateやPostbackといった仕組みを一つずつ調査する良い学びの機会になりました。
Discussion