☑️

C# Web FormsでのViewStateとDisabled属性の罠

に公開

概要

C# Web Formsをベースにした社内システムを改修する中で、
サーバーコントロールの状態(ViewState)とinputの関係について、
ちょっと面白い挙動を見つけました。

特に、日付を和暦(例:令和)で表示する必要がある画面において、
ViewStateとJavaScriptのデータ連携にいくつか注意が必要なポイントがいくつかありました。

  1. 和暦変換とViewStateの関係

Web Formsのサーバーコントロールは、基本的に西暦で値を保持します。
そのため、和暦を画面に表示する場合は、以下のような構成を取りました。

  • サーバー側では西暦(YYYY)データを送信
  • クライアント側(JavaScript)で和暦に変換し、画面へ反映

しかし、ViewStateには和暦の情報を直接保持できないため、
Postback時にはHiddenFieldを使って一時的に値を格納し、JSで再描画するという実装にしました。

  1. 発生した問題

特定の入力項目において、次の条件を同時に満たす場合に問題が発生しました。

  • ReadOnly = true
  • Disabled = true
  • HiddenFieldには値が存在し、JSでinputにも値をセットしている

この場合、Postback後に画面上の値が消えてしまうという現象が起こりました。
HiddenFieldにも値があり、JSで再設定する処理もあるのに、なぜか空白になってしまうのです。

  1. 原因の分析 調査の結果、原因は以下の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が優先される」 という構成が、今回の問題の本質でした。
  1. 解決方法

最終的に、以下の方法で安定して動作するようになりました。

4-1. Postback前にReadOnly=falseまたはDisabled=falseに変更

  • サーバーが正しく値を受け取れるようにする
  • ViewStateが最新の値に更新される

4-2. 値を格納した後、再度ReadOnly/Disabledを設定

  • 画面では編集不可を維持
  • Postback後も値が消えず、安定して表示される

この方法によって、Postbackの有無にかかわらず入力値が正しく保持されるようになりました。

  1. 補足(Tips)
  • Disabled属性のinputはサーバーに値を送信しないため、 重要なデータには使用しない方が安全です。

おわりに

ViewStateやPostbackといった仕組みは理解していたつもりでしたが、
「ViewStateに保存されない値をReadOnlyやDisabledで扱うと値が消える」 という挙動には正直驚きました。

結果的に、ViewStateやPostbackといった仕組みを一つずつ調査する良い学びの機会になりました。

Discussion