views:

740

answers:

8

When I click my "Query" button, my TestID textbox TestIDTxt retained its value, but both myDatePicker's are blank without any value.

Question: How do I preserve StartDate.SelectedDate and EndDate.SelectedDate between postbacks?

Solution: As suggested multiple times, removing ReadOnly="true" on the DateTxt kept the text value across postbacks. (Thanks Raj Kaimal!)

I then set the SelectedDate (Date?) property by Date.Parse'ing the text value automatically on Page_Load() for that user control see code below.

I never did use the ViewState object directly.

myDatePicker.ascx.vb (FINAL)

Imports System.Globalization
Partial Public Class myDate
    Inherits System.Web.UI.UserControl

    Public Property SelectedDate() As Date?
        Get 'Simply return the date'
            Return Me.DateTxt_CalendarExtender.SelectedDate
        End Get
        Set(ByVal value As Date?)
            If value = Nothing Then Exit Property
            Me.DateTxt_CalendarExtender.SelectedDate = value
            Me.DateTxt.Text = Format(value, Me.DateTxt_CalendarExtender.Format)
        End Set
    End Property

    Public Sub Refresh()
        Dim tempDate As Date 'SelectedDate is nullable Date (Date?)'
        Dim parseState As Boolean = False

        parseState = Date.TryParseExact(DateTxt.Text, _
                                        DateTxt_CalendarExtender.Format, _
                                        DateTimeFormatInfo.CurrentInfo, _
                                        DateTimeStyles.None, tempDate)
        If parseState Then 'if successful, set the date'
            Me.DateTxt_CalendarExtender.SelectedDate = tempDate
        End If
    End Sub

    Private Sub Page_Load(ByVal sender As Object, _
                          ByVal e As System.EventArgs) Handles Me.Load
        Me.Refresh()
    End Sub
End Class

myDatePicker.ascx.vb (ORIGINAL)

Partial Public Class myDate
    Inherits System.Web.UI.UserControl

    Public Property SelectedDate() As Date?
        Get
            Dim o As Object = ViewState("SelectedDate")
            If o = Nothing Then
                Return Nothing
            End If
            Return Date.Parse(o)
        End Get
        Set(ByVal value As Date?)
            ViewState("SelectedDate") = value
        End Set
    End Property
End Class

myDatePicker.ascx

<%@ Control Language="vb" CodeBehind="myDatePicker.ascx.vb"
    Inherits="Website.myDate" AutoEventWireup="false" %>

<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" 
             tagprefix="asp" %>

<asp:TextBox ID="DateTxt" runat="server" ReadOnly="True" />
<asp:Image ID="DateImg" runat="server" ImageUrl="~/Calendar_scheduleHS.png"
              EnableViewState="True" EnableTheming="True" />

<asp:CalendarExtender ID="DateTxt_CalendarExtender" runat="server" Enabled="True"
                      TargetControlID="DateTxt" PopupButtonID="DateImg"
                      DefaultView="Days" Format="ddd MMM dd, yyyy"
                      EnableViewState="True"/>

Default.aspx

<%@ Page Language="vb" AutoEventWireup="false" CodeBehind="Default.aspx.vb"
         Inherits="Website._Default" EnableEventValidation="false"
         EnableViewState="true" %>

<%@ Register TagPrefix="my" TagName="DatePicker" Src="~/myDatePicker.ascx" %>
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit"
             TagPrefix="ajax" %>
<%@ Register Assembly="..." Namespace="System.Web.UI.WebControls"
             TagPrefix="asp" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
...
<body>
<form id="form1" runat="server">
<div class="mainbox">
      <div class="query">
          Start Date<br />
          <my:DatePicker ID="StartDate" runat="server" EnableViewState="True" />
          End Date
          <br />
          <my:DatePicker ID="EndDate" runat="server" EnableViewState="True" />
        <br/>
        <asp:TextBox ID="TestIdTxt" runat="server" /><br /> 
        <div class="query_buttons">
            <asp:Button ID="Button1" runat="server" Text="Query" />
        </div>
 </div>
 <asp:GridView ID="GridView1" ... >
 </form>
 </body>
 </html>

Default.aspx.vb

Imports System.Web.Services
Imports System.Web.Script.Services
Imports AjaxControlToolkit

Protected Sub Page_Load(ByVal sender As Object, _
                        ByVal e As EventArgs) Handles Me.Load

End Sub

Partial Public Class _Default
    Inherits System.Web.UI.Page
    Protected Sub Button1_Click(ByVal sender As Object, _
                                ByVal e As EventArgs) Handles Button1.Click
        GridView1.DataBind()
    End Sub
End Class
A: 

Set the button's onclick event to an event handler in your code:

<asp:Button ID="Button1" runat="server" Text="Query" OnClick="MyButtonClickEventHandler" />
derek
I added my codebehind showing the function that button1 calls.
Steven
@Steven but where do you tell the button that this is it's event?
Earlz
`Protected Sub Button1_Click(...) Handles Button1.Click`
Steven
A: 

here is an article that should help

http://msdn.microsoft.com/en-us/library/ms972975.aspx

Basically you need to create an event that causes a postback

KenL
Please elaborate. The button causes a postback.
Steven
A: 

You should be able to add something like this to your calendar to automatically submit your page when a date is selected:

OnClientDateSelectionChanged="document.getElementById('form1').submit();"

You could replace the JavaScript submit with a specific buttonclick if needed, or call an asp.net postback event.

AaronS
+2  A: 

I want the user-control to be exactly what it was before the postback (in both appearance and value).

You might try using ControlState for this.

In your init handler:

protected override void OnInit(EventArgs e) 
{
    Page.RegisterRequiresControlState(this);
}

Elsewhere in your control:

    protected override object SaveControlState()
    {
        NameValueCollection state = new NameValueCollection();
        state.Add("value", this.DateTxt.Text);
        return state;
    }

    protected override void LoadControlState(object savedState)
    {
        NameValueCollection state = (NameValueCollection)savedState;
        this.DateTxt.Text = state["value"].ToString();
    }

I adapted this from some code I had laying around, so you may need to tweak a bit for your purposes. The ASP.NET runtime will call those two methods during the page lifecycle to save and restore the value of that textbox for you in a ViewState independent way.

mwilson
Didn't work. When I submit the page, the user controls do not retain their values and show up blank.
Steven
A: 

If I understund your question, You have just to overload UserControl.DataBind in the myDate class.

Look here for better documentation.

If your component is created using inner component like textfield you can do like the following example. So, bind the data entered or modified to the inner component and make sure to track the state, and save/load it every postback.

If you draw your own component make sure to override the DataBinding, cause you have to record what user imput in the viewstate, but is much more complicated.

Example of simple UserControl: WebUserControl1.ascx.vb:

    Public Class WebUserControl1
    Inherits System.Web.UI.UserControl

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    End Sub


    Public Property SelectedText() As String
        Get
            Return TextBox1.Text
        End Get
        Set(ByVal value As String)
            TextBox1.Text = value
        End Set
    End Property

    Public Overrides Sub DataBind()
        MyBase.OnDataBinding(EventArgs.Empty)
        ' Reset the control's state.
        ' Controls.Clear()
        ' Check for HasChildViewState to avoid unnecessary calls to ClearChildViewState.
        If HasChildViewState Then
            ClearChildViewState()
        End If
        ChildControlsCreated = True
        If Not IsTrackingViewState Then
            TrackViewState()
        End If
    End Sub


    Protected Overrides Sub LoadControlState(ByVal savedState As Object)
        SelectedText = savedState


    End Sub

    Protected Overrides Function SaveControlState() As Object
        Return SelectedText

    End Function
End Class

WebUserControl1.ascx:

<%@ Control Language="vb" AutoEventWireup="true" CodeBehind="WebUserControl1.ascx.vb" Inherits="WebApplication1.WebUserControl1" %>
PIPPO:
<asp:TextBox ID="TextBox1" runat="server" Width="384px"></asp:TextBox>

Default.aspx:

<%@ Register src="WebUserControl1.ascx" tagname="WebUserControl1" tagprefix="uc2" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">

    <br />
    -----------------------------------<br />

    <uc2:WebUserControl1 ID="WebUserControl11" runat="server" SelectedText="aaaa" />
    <br />
    -----------------------------------<br />
    <asp:Button ID="Button1" runat="server" Text="Button" />
    <br />
    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
    <br />

</asp:Content>

Default.aspx.vb

Public Class _Default
    Inherits System.Web.UI.Page

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        DataBindChildren()
    End Sub

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
        Label1.Text = WebUserControl11.SelectedText


    End Sub
End Class
Guaido79
DataBind? What is the DataSource?
Steven
I hope to understund your problems, and I've edit the post with a simple example.
Guaido79
A: 

In the OnLoad event of your user control you can use the values you saved in your viewstate to populate the control. (not 100% if this VB is correct)

Protected Sub Page_Load(ByVal sender As Object, _
                        ByVal e As EventArgs) Handles Me.Load
    Me.DateTxt_CalendarExtender.(property that sets the date) = Me.SelectedDate
    //or Me.DateTxt_CalendarExtender.(property that sets the date picked) = ViewState["SelectedDate"] As Date
End Sub
s_hewitt
Do the same for the textbox and img.
s_hewitt
It didn't work either. Also, in `Page_Load()` the value `Me.ViewState.Count` is zero.
Steven
You'll have to put some data in SelectedDate at some point.
s_hewitt
In my web browser, I pick a start and end date, type a testid, and click "Query". The page comes back. Start date is blank. End date is blank. TestID retained its value. The results in the grid view were ONLY filtered by testid.
Steven
In `Page_Load()`, `Me.ViewState.Count` is zero. Also, I noticed in the in the browser that `TestIDTxt` received its value in the `value=` attribute of `<input name="TestIdTxt" type="text" ...>`
Steven
When do you attempt to set the SelectedDate? I think what you're looking to do is like @mwilson or you'll need to inherit CalendarExtender and use it to put a value in SelectedDate during one of its events.
s_hewitt
+3  A: 

Your textbox is readonly therefore it will not store in viewstate any changes to the text value. This is a security feature to prevent tampering of readonly controls.

First of all, does everything work normally if the readonly attribute is removed?

I want the user-control to be exactly what it was before the postback (in both appearance and value).

if so, can you use css/jquery to make the textbox field appear disabled?

Raj Kaimal
+1  A: 

The CalendarExtender doesn't work properly with with readonly textboxes. There is a relatively simple workaround, though it looks a bit dirty and isn't really the webforms way. Try putting this in Page_Load:

DateTxt.Text = Request[DateTxt.UniqueID]

Obviously if you're also populating the textbox from ViewState in some cases you'll need to be mindful of that too.

richeym