QA@IT

ユーザーコントロール内のイベントで親webフォーム内の要素のパラメータを変更したい

6790 PV

ASP.NET(VB)で開発を行っています。
プログラミングは未経験のため拙い文書、コードではありますがご教授頂きたく思います。

実現したい挙動が以下のように、ユーザーコントロールAの押下イベントで、親のwebフォームに配置した別のユーザーコントロールBのvisibleのプロパティ値を変更する処理です。

A:Visible=true
B:Visible=false
↓ ユーザーコントロールA内のbutton押下
A:Visible=false
B:Visible=true

uc1のbuttonをクリックしたタイミングでuc1とuc2のvisible切替を行いたいのですが、uc1のButton1_Clickイベントが走る前にtopのPage_Loadイベントが走るため、 Select Case処理内でのvisivle切り替えがうまく行きません。
uc1のButton1_Clickイベント時のSession("prmID")の値は残っているので、再度Button1_Clickイベントを起こす or topのButton1_Clickイベントを発生させてポストバックさせるとvisibleは切り替わります。

何か処理が足りないのか、そもそもパラメータの切り替えをする正しい方法があるのか、ご教授お願い致します。

<top.aspx>

(head略)
<body>
    <form id="form1" runat="server">
        <asp:Button ID="Button1" runat="server" Text="Button" />
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        <contents:uc1 ID="uc1" runat="server" Visible="true" />
        <contents:uc2 ID="uc2" runat="server" Visible="false" />
    </form>
</body>

<top.aspx.vb>

Public Class top
    Inherits System.Web.UI.Page
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        If Not IsPostBack Then
        Else
            Select Case Session("prmID")
                Case "Check"
                    uc1.Visible = False
                    uc2.Visible = True
            End Select
        End If
    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Label1.Text = uc1.visiblePrmID()
    End Sub
End Class

<uc1.ascx>

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="uc1.ascx.vb" Inherits="Page_uc1" %>
<h2>最初の画面</h2>
    <asp:Button ID="Button1" runat="server" Text="確認画面" />

<uc1.ascx.vb>

Public Class Page_uc1
    Inherits System.Web.UI.UserControl
    Public prmID As String
    Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
        If Not IsPostBack Then
        Else
            prmID = Session("prmID")
        End If
    End Sub

    Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        prmID = "Check"
        Session("prmID") = prmID
    End Sub

    Public Property visiblePrmID() As String
        Get
            Return prmID
        End Get
        Set(ByVal value As String)
            If value Is Nothing Then
                prmID = ""
            Else
                prmID = value
            End If
        End Set
    End Property
End Class

<uc2.ascx>

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="uc2.ascx.vb" Inherits="Page_uc2" %>
<h2>2つ目の画面</h2>
  • そもそもユーザーコントロールが状態を保持する必要がないのでは?
    単にそれぞれのユーザーコントロールが、ボタン押下時に何らかのイベントを発行するようにし、ページでそのイベントをハンドリングしてそこで表示を切り替えれば良いだけでは
    -
  • 単に「ユーザーコントロールAの押下イベントで、親のwebフォームに配置した別のユーザーコントロールBのvisibleのプロパティ値を変更する」を実現するだけなら、A の Button1_Click メソッドで、自分自身の Visible プロパティを false に設定し、B の Visible プロパティを true にすればよいはずです。回答欄に例を追記しておきます。 -
  • Takac様、コメントありがとうございます。
    uc1のButton1_Clickイベント内に何か記述を追加して、親ページがそれを取得するようにする。ということでしょうか?それともButton1_Clickイベントそのものを親ページが取得する方法があるということでしょうか?
    知識不足で申し訳ございません。ご教授いただけると助かります。
    -
  • ユーザーコントロールに独自のイベントを定義し、それをページでハンドリングします
    回答に簡単なコードを書いときます
    -
  • Takac様、確認が遅くなり大変申し訳ございません。
    イベントを独自で定義するのとかできるのですね。知識不足でお恥ずかしい限りです・・・。
    ご回答頂いていたコードで望んだ挙動を実現できました。ありがとうございました。
    -

回答

ユーザーコントロールにイベントを定義するやり方です
イベントの名前や引数はなんでもよいのですが
ここではClickというイベントを定義して、ボタンクリック時にそのイベントを直接発生させます
引数は標準的なイベント引数に合わせてあります

uc1.ascx

<%@ Control Language="VB" AutoEventWireup="false" CodeFile="uc1.ascx.vb" Inherits="uc1" %>
<h2>最初の画面</h2>
    <asp:Button ID="Button1" runat="server" Text="確認画面" />

uc1.ascx.vb

Public Class uc1
    Inherits System.Web.UI.UserControl

    Public Event Click(ByVal sender As Object, ByVal e As EventArgs)

    Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        RaiseEvent Click(Me, EventArgs.Empty)
    End Sub

End Class

ページ側でイベント処理する方法は、ほかのコントロールと同じようにHandlesでイベント指定します
VisualStudioなら、エディタのドロップダウンで選択すれば自動的に作成されます
(先にユーザーコントロールをビルドしないとだめかもしれません)
あとはそこでVisibleを切り替えるだけです

top.aspx

<%@ Page Language="VB" AutoEventWireup="false" CodeFile='top.aspx.vb' Inherits="top"%>
<%@ Register Src="~/uc1.ascx" TagPrefix="contents" TagName="uc1" %>
<%@ Register Src="~/uc2.ascx" TagPrefix="contents" TagName="uc2" %>
<head>
    <title>top</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:Button ID="Button1" runat="server" Text="Button" />
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        <contents:uc1 ID="uc1" runat="server" Visible="true" />
        <contents:uc2 ID="uc2" runat="server" Visible="false" />
    </form>
</body>

top.aspx.vb

Public Class top
    Inherits System.Web.UI.Page

    Private Sub uc1_Click(sender As Object, e As EventArgs) Handles uc1.Click
        uc1.Visible = False
        uc2.Visible = True
    End Sub

End Class

uc2は省略します
最低限これだけで、表示の切り替えは実現できます

編集 履歴 (1)

以下のようになっているので期待したようにならないと思うのですが。

(1) 初期画面では A が表示され B は非表示。

(2) A の[確認画面]ボタンをクリックするとポストバックがかかる。

(3) top.aspx.vb の Page_Load メソッドに制御が飛ぶが Session("prmID") は Nothing なので何も起こらない。

(4) uc1.ascx.vb の Button1_Click メソッドに制御が飛んで Session("prmID") に文字列 "Check" が設定される。

(5) top.aspx の A, B の Visible プロパティの設定は変わらない。それゆえ、top.aspx がブラウザに再描画された時は A が表示 B は非表示のままになる。

(6) A の[確認画面]ボタンまたは top.aspx の[Button1]ボタンをクリックするとポストバックがかかる。

(7) 今度は Session("prmID") は Nothing ではなくて文字列 "Check" が設定されているので top.aspx.vb の Page_Load メソッドで A, B の Visible プロパティが切り替わる。

期待した動きをさせるには (3) で A, B の Visible プロパティを切り替えることを考えてみてください。

#そもそもユーザーコントロールを使うのが間違っている様な気がします。「確認画面」という名前からすると、A でユーザーが情報を入力し、B で入力した情報を確認するという感じですが、そうであれば Panel とか Wizard とか MultiView を使うことをお勧めします。

---------------- 2016/1/23 追記 ----------------

コメント欄に、

単に「ユーザーコントロールAの押下イベントで、親のwebフォームに配置した別のユーザーコントロールBのvisibleのプロパティ値を変更する」を実現するだけなら、A の Button1_Click メソッドで、自分自身の Visible プロパティを false に設定し、B の Visible プロパティを true にすればよいはずです。回答欄に例を追記しておきます。

と書きましたが、その例を書きます。A には B の参照を設定 / 取得するための UserControlB という名前のプロパティを配置します。Session や visiblePrmID プロパティは使いません。以下のような動きになります。

(1) 初期画面では A が表示され B は非表示。

(2) A の[確認画面]ボタンをクリックするとポストバックがかかる。

(3) 親ページの Page_Load メソッドで UserControlB に B オブジェクトへの参照を設定。

(4) A の Button1_Click メソッドで自分自身の Visible を false に、B の Visible を true に設定。

(5) 親ページがブラウザに再描画された時 A は非表示に B は表示に切り替わる。

コードは C# ですが以下の通りです。Web サイトプロジェクトでコードビハインド形式ではないので注意してください。Web アプリケーションプロジェクトでも .aspx と .aspx.cs に分ければ同じ動作をするはずです。

なお、先にも書きましたが、ユーザーコントロールの代わりに Panel とか Wizard とか MultiView を使うことも検討してみてください。そちらの方が簡単かつスマートなはずです。

<親ページ>

<%@ Page Language="C#" %>
<%@ Register src="0147-UserControl-A.ascx" tagname="uca" tagprefix="uc" %>
<%@ Register src="0147-UserControl-B.ascx" tagname="ucb" tagprefix="uc" %>

<!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)
    {
        ucA.UserControlB = ucB; 
    }
</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <uc:uca ID="ucA" runat="server" />
        <uc:ucb ID="ucB" runat="server" Visible="False" />
    </form>
</body>
</html>

<User Control A (0147-UserControl-A.ascx)>

<%@ Control Language="C#" ClassName="_0147_UserControl_A" %>

<script runat="server">

    protected void Button1_Click(object sender, EventArgs e)
    {
        this.Visible = false;
        this.UserControlB.Visible = true;
    }

    public Control UserControlB { get; set; }

</script>
<h1>User Control A</h1>
<asp:Button ID="Button1" runat="server" Text="確認画面" OnClick="Button1_Click" />

<User Control B (0147-UserControl-B.ascx)>

<%@ Control Language="C#" ClassName="_0147_UserControl_B" %>

<script runat="server">

</script>
<h1>User Control B</h1>
編集 履歴 (3)
  • SurferOnWww様、回答ありがとうございます。最初のコードでは実現出来ないロジックだった事は納得致しました。
    まずPanel,Wizard,MultiView の検討ですが、実はAに該当するページが複数あり、A1→B、A2→B、・・・A10→Bのように、Bは複数画面がら遷移されてくるものためBを共通化したいのです。(Aもまた、他の複数ページから遷移されてくる画面です)
    -
  • 先に、C#およびVB.netに関してあまりにも無知なことをお詫び申し上げます。
    親ページのucA.UserControlB = ucB;
     →「(3)B オブジェクトへの参照を設定」にあたる処理かと思うのですが、VBだとどういった記述になるのでしょうか。
    -
  • User Control Aのpublic Control UserControlB { get; set; }
    →VBだとget内に返す値、set内に値の代入先?が必要、だと思うのですが、記載が無くても機能するものなのでしょうか?
    -
  • 「実はAに該当するページが複数あり、A1→B、A2→B、・・・A10→Bのように、Bは複数画面がら遷移されてくるものためBを共通化したいのです」というのが意味不明です。私に分かるように書いていただけませんか? -
  • 私の回答が必要なら上の私の質問に答えてください。興味本位とかで聞いているのではなく、回答が的外れにならないように聞いているわけですから、きちんと答えて下さい。「Bは複数画面がら遷移」とか言われると既に私の回答は的外れになっているような気がしますし。 -
  • それから、できれば、局所的な質問だけでなく、全体のシナリオを含めて何がしたいのかも書いていただくようお願いします。そうすれば、質問の局所的な部分は全体の目的に不適切でも、「全体的なやりたいことはこうすれば実現できる」というような代案も出てくるかもしれません。 -
  • SurferOnWww様、確認が遅くなり大変申し訳ございません。
    レイアウトや挙動が異なる入力ページAが複数あるのに対し、出力ページBがひとつのためBを共通化したく、 ユーザーコントロールを用いました。説明が足りず申し訳ございません。今後は全体も併せて質問させて頂きます。また、Wizard,MultiViewはお恥ずかしながら初めて知りました。改めて勉強させていただきます。ありがとうございました
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。