Page view counter

Custom Databound Control Losing Data on Postback

Last post 08-21-2008 10:32 AM by pawicks. 4 replies.

Sort Posts:

  • Custom Databound Control Losing Data on Postback

    08-18-2008, 7:10 PM
    • Loading...
    • pawicks
    • Joined on 06-26-2008, 11:46 AM
    • Posts 29
    • Points 38

    I have a custom Data Bound control that I am building. But on postback I lose the all state and I can't figure out why state isn't coming back.

    This control presents a list of tasks in the left pane, this is my "frozen" pane. On the right is a panel with a scrollbar.  Across the top is a header with a column for each day in the selected date range.  Then for each Task per day there is another control, derived from the textbox, where the user can enter in data.  Currently no saving of this data is being attempted, one step at a time.

     I am re-generating the controls that are supposed to be there, as you will see in the code, but they always just come out empty.

     Any help would be greatly appreciated.

    --Peter

    Code (better to much then not enough):

      

    Public Class EstimatedDoseDisplayer
    Inherits System.Web.UI.WebControls.DataBoundControl

    Public Property StartDate() As DateTime
    Get
    Dim
    o As Object = ViewState("StartDate")
    If o Is Nothing Then
    Return
    DateTime.MinValue
    Else
    Return
    Convert.ToDateTime(o)
    End If
    End Get
    Set
    (ByVal value As DateTime)
    ViewState("StartDate") = value
    If (Initialized) Then
    OnDataPropertyChanged()
    End If
    End Set
    End Property

    Public Property
    EndDate() As DateTime
    Get
    Dim
    o As Object = ViewState("EndDate")
    If o Is Nothing Then
    Return
    DateTime.MinValue
    Else
    Return
    Convert.ToDateTime(o)
    End If
    End Get
    Set
    (ByVal value As DateTime)
    ViewState("EndDate") = value
    If (Initialized) Then
    OnDataPropertyChanged()
    End If
    End Set
    End Property


    'Master Panel that will surround the two table
    Protected ParentPanel As Panel
    'Main Table used for layout
    Protected LayoutTable As Table

    'Row and Cells for layout table
    Protected LayoutRow As TableRow
    Protected TasksCell As TableCell
    Protected DatesCell As TableCell

    'Main Tasks table
    Protected TasksTable As Table

    'Scrolling Panel that DatesTable goes in
    Protected DatesPanel As Panel

    'Main Dates Table
    Protected DatesTable As Table


    Public Sub New()
    End Sub

    Protected Overrides Sub
    PerformSelect()
    ' Call OnDataBinding here if bound to a data source using the
    ' DataSource property (instead of a DataSourceID), because the
    ' databinding statement is evaluated before the call to GetData.

    If Not IsBoundUsingDataSourceID Then
    OnDataBinding(EventArgs.Empty)
    End If

    ' The GetData method retrieves the DataSourceView object from
    ' the IDataSource associated with the data-bound control.

    GetData().Select(CreateDataSourceSelectArguments(), _
    AddressOf OnDataSourceViewSelectCallback)

    ' The PerformDataBinding method has completed.
    RequiresDataBinding = False
    MarkAsDataBound()

    ' Raise the DataBound event.
    OnDataBound(EventArgs.Empty)

    End Sub

    Private Sub
    OnDataSourceViewSelectCallback(ByVal retrievedData As IEnumerable)
    ' Call OnDataBinding only if it has not already been
    ' called in the PerformSelect method.

    If IsBoundUsingDataSourceID Then
    OnDataBinding(EventArgs.Empty)
    End If
    ' The PerformDataBinding method binds the data in the
    ' retrievedData collection to elements of the data-bound control.

    PerformDataBinding(retrievedData)

    End Sub

    Protected Overrides Sub
    PerformDataBinding(ByVal retrievedData As IEnumerable)
    'Ensure child controls have been created
    Me.EnsureChildControls()

    MyBase.PerformDataBinding(retrievedData)

    ' Verify data exists.
    If Not retrievedData Is Nothing And Me.Visible Then
    CreateControlHierarchy(True, retrievedData)
    End If
    End Sub

    Protected Sub
    CreateHeaders(ByVal StartDate As DateTime, ByVal EndDate As DateTime, ByRef TasksTable As Table, ByRef DatesTable As Table)
    'Basic objects to use for table creation
    Dim tr As TableRow = Nothing
    Dim
    td As TableCell = Nothing

    '--Add in Work Orders Header--
    tr = New TableRow()
    td = New TableCell()
    td.Text = "Tasks"
    'make "Work Orders" bold
    td.Style.Add("font-weight", "bold")
    'Add the cell to the row
    tr.Cells.Add(td)
    'add the "header" row to the work orders table
    TasksTable.Rows.Add(tr)

    '--Add in Dates Header for this month--
    'New table row to hold date cells

    tr = New TableRow()
    'Used to hold current date
    Dim CurrentDate As DateTime = StartDate
    'While we have not passed the end date
    While CurrentDate <= EndDate
    td = New TableCell()
    td.Text = CurrentDate.ToShortDateString()
    'Handle Holiday/Weekend coloring/marking
    HandleSpecialDays(td, CurrentDate)
    'Make Date bold
    td.Style.Add("font-weight", "bold")
    'Add date to header row
    tr.Cells.Add(td)
    'Increment the date by 1
    CurrentDate = CurrentDate.AddDays(1)
    End While
    'Add in the Dates header
    DatesTable.Rows.Add(tr)
    End Sub

    Protected Sub
    HandleSpecialDays(ByRef TD As TableCell, ByVal SelectedDate As DateTime)
    'Later on we will add a Special Days table to the database so we can highlight special days for reporting/etc
    If SelectedDate.DayOfWeek = DayOfWeek.Saturday Or SelectedDate.DayOfWeek = DayOfWeek.Sunday Then
    TD.BackColor = Drawing.Color.LightSkyBlue
    TD.ToolTip = "Weekend"
    End If
    End Sub

    Protected Sub
    AutoGenerateDates(ByVal Seed As DateTime)
    StartDate = New DateTime(Seed.Year, Seed.Month, 1)
    'We also need to generate an End Date if this is the case, start with the 28th of the month, then move forward
    EndDate = New DateTime(StartDate.Year, StartDate.Month, 28)
    'move end date forward until the last day of the month
    While True
    'If adding a day will not take us outside of this month then add a day,
    ' else exit the loop and stick with the date we've got.

    If EndDate.AddDays(1).Month = StartDate.Month Then
    EndDate = EndDate.AddDays(1)
    Else
    Exit While
    End If
    End While
    End Sub

    Protected Overrides Sub
    CreateChildControls()
    'Clear out old controls
    Controls.Clear()
    'Make all the new controls
    MyBase.CreateChildControls()

    'Create the Control Hierarchy for the data
    'If the viewstate exists we'll use that

    If Not ViewState("ItemCount") Is Nothing Then
    CreateControlHierarchy(False, Nothing)
    End If
    End Sub

    Protected Sub
    CreateControlHierarchy(ByVal UseDataSource As Boolean, ByRef RawData As IEnumerable(Of Labor))
    'Create the Parent Panel
    ParentPanel = New Panel()

    'Create Layout Table
    LayoutTable = New Table()
    LayoutRow = New TableRow()
    TasksCell = New TableCell()
    DatesCell = New TableCell()

    'Main Tasks table
    TasksTable = New Table()

    'Scrolling Panel that DatesTable goes in
    DatesPanel = New Panel()

    'Main Dates Table
    DatesTable = New Table()

    '--Put Controls Together--'
    'Add parent Panel to parent control

    Controls.Add(ParentPanel)

    'Add layout table to ParentPanel
    ParentPanel.Controls.Add(LayoutTable)

    'Add layout row
    LayoutTable.Rows.Add(LayoutRow)

    'Add layout cells
    LayoutRow.Cells.Add(TasksCell)
    LayoutRow.Cells.Add(DatesCell)

    'Load Tasks Table into Parent Panel
    TasksCell.Controls.Add(TasksTable)

    'Load Dates Panel into Parent Panel
    DatesCell.Controls.Add(DatesPanel)

    'Load Dates Table into DatesPanel
    DatesPanel.Controls.Add(DatesTable)

    Dim DataContext As New DoseReportingDataContext()
    'Convert our datasource into the appropriate object type
    Dim Data As IEnumerable(Of Labor) = Nothing
    Dim
    RecordCount As Integer = 0
    If UseDataSource Then
    Data = CType(RawData, IEnumerable(Of Labor))
    RecordCount = Data.Count()
    ViewState("ItemCount") = RecordCount
    Else
    RecordCount = ViewState("ItemCount")
    Data = New List(Of Labor)(RecordCount)
    End If
    'if no data then return
    If RecordCount < 1 Then
    'We should have a No Data Template sort of thing that gets displayed here
    Return
    End If



    'Auto generate dates if needed
    'Check if we have a default date

    If StartDate = DateTime.MinValue Or EndDate = DateTime.MinValue Then
    'if we don't have a start date or we don't have an end date
    ' then get the first entry's start date and use it as a seed

    Dim l As Labor = Data.First()
    AutoGenerateDates(l.StartDate)
    End If

    '--Create and add together all of the major controls--'
    'Set Tasks Table cell padding/spacing

    TasksTable.CellPadding = 0
    TasksTable.CellSpacing = 0
    'Set scrollbars and width of dates panel
    DatesPanel.ScrollBars = ScrollBars.Horizontal
    DatesPanel.Style.Add("width", "600px")
    'Set cell padding/spacing of Dates Table
    DatesTable.CellPadding = 0
    DatesTable.CellSpacing = 0

    'Create Headers
    CreateHeaders(StartDate, EndDate, TasksTable, DatesTable)
    'Get count of distinct Labor entries
    Dim count As Integer = RecordCount

    '--Create Rows and Cells--'
    'Basic objects to use for table creation

    Dim tr As TableRow = Nothing
    Dim
    td As TableCell = Nothing

    'Create Table stuff for this work order
    Dim LaborCurrentCount As Integer = 0 'used to track our progress through the list
    While LaborCurrentCount < count
    '--Create the entry in the Tasks table--
    'Create new row for work orders table

    tr = New TableRow()
    'Add new cell with Work Order name/number
    td = New TableCell()
    'Set standard height on the cell
    td.Height = New WebControls.Unit(30, UnitType.Pixel)

    'Get current workorder
    Dim CurrentLabor As Labor = Nothing

    If
    UseDataSource Then
    CurrentLabor = Data(LaborCurrentCount)
    'set cell text
    td.Text = CurrentLabor.AlaraTask.WorkOrderTask.WorkOrder + ":" + CurrentLabor.AlaraTask.TaskNumber.ToString("00") + ":" + CurrentLabor.AlaraTask.TaskNumber.ToString("00")
    td.ToolTip = CurrentLabor.AlaraTask.AlaraDescription + Environment.NewLine + CurrentLabor.AlaraTask.AlaraSubDescription
    End If
    'Add cell to row
    tr.Cells.Add(td)
    'Add row to work orders table
    TasksTable.Rows.Add(tr)
    '--Load per date info if present--
    'Copy the Start Date

    Dim TaskDate As DateTime = StartDate
    'Create table row for this work orders date info
    tr = New TableRow()
    'while we have not passed the end date
    While TaskDate <= EndDate
    'new table cell for this day
    td = New TableCell()
    'set to uniform height
    td.Height = New WebControls.Unit(30, UnitType.Pixel)
    'Check for holidays/weekend
    HandleSpecialDays(td, TaskDate)
    'Will instantiate this once we know how we are going to do it
    Dim dData As EstimatedDoseItem = Nothing
    'if we are using the datasource
    If UseDataSource Then
    'Query if there is any data for this day/Labor Entry
    ' for this query we ONLY have to look for the day and work order number
    ' in our previous query we already slimmed it down to department and month/year

    Dim wodData = From c In CurrentLabor.DoseEstimates _
    Where c.DateForEstimate.Date = TaskDate.Date
    'if we have data then load it into the EstimatedDoseItem, if no data then input placeholders
    If wodData.Count > 0 Then
    dData = New EstimatedDoseItem(wodData.First(), Page)
    Else
    dData = New EstimatedDoseItem(TaskDate, CurrentLabor.LaborID, CurrentLabor.DepartmentID, Page)
    End If
    Else

    dData = New EstimatedDoseItem()
    End If
    'Add event handler for when the text is changed
    'AddHandler dData.DoseChanged, AddressOf DataUpdated
    'Add the Estimated Dose Item to the TD

    td.Controls.Add(dData)
    'Add cell to the row
    tr.Cells.Add(td)
    'Increment the date by 1
    TaskDate = TaskDate.AddDays(1)
    End While

    'Add in the Row
    DatesTable.Rows.Add(tr)
    'Move onto the next work order
    LaborCurrentCount += 1
    End While
    End Sub
    End Class

    Public Class
    EstimatedDoseItem
    Inherits TextBox

    Public Event DoseChanged As EventHandler

    Public ReadOnly Property DoseEstimate() As DoseEstimate
    Get
    Return
    _DoseEstimateStore.GetDoseEstimate()
    End Get
    End Property

    Protected
    _DoseEstimateStore As DoseEstimateStore

    Public Sub New(ByVal EstimateDate As DateTime, ByVal LaborID As Integer, ByVal Department As Integer, ByVal ParentPage As Page)
    'Load all values into a DoseEstimate object
    Dim DoseEstimate As New DoseEstimate()
    DoseEstimate.DateForEstimate = EstimateDate
    DoseEstimate.LaborID = LaborID
    DoseEstimate.EstimatedDose = 0D

    _DoseEstimateStore = New DoseEstimateStore(DoseEstimate)

    'ParentPage.RegisterRequiresControlState(Me) 'Let the page know that this control needs it's state saved
    End Sub

    Public Sub New
    (ByVal DoseEstimate As DoseEstimate, ByVal ParentPage As Page)
    'Load in provided Dose Estimate object
    _DoseEstimateStore = New DoseEstimateStore(DoseEstimate)
    'ParentPage.RegisterRequiresControlState(Me) 'Let the page know that this control needs it's state saved
    End Sub

    Public Sub New
    ()
    _DoseEstimateStore = New DoseEstimateStore()
    End Sub

    Protected Overrides Sub
    OnLoad(ByVal e As System.EventArgs)
    'On load change the width of the txt box
    Me.Columns = 5
    'Center the text
    Me.Style.Add("text-align", "center")
    MyBase.OnLoad(e)
    End Sub

    Protected Overrides Sub
    OnTextChanged(ByVal e As System.EventArgs)
    RaiseEvent DoseChanged(Me, e)
    MyBase.OnTextChanged(e)
    End Sub

    'Override Text property of textbox so that it references the Estimated Dose
    Public Overrides Property Text() As String
    Get
    Return
    _DoseEstimateStore.EstimatedDose.ToString()
    End Get
    Set
    (ByVal value As String)
    _DoseEstimateStore.EstimatedDose = Convert.ToDecimal(value)
    End Set
    End Property

    Protected Overrides Function
    SaveControlState() As Object
    Dim
    state(2) As Object 'object array to hold data

    state(0) = MyBase.SaveControlState() 'Save the base controls state

    state(1) = _DoseEstimateStore 'Save dose estimate object

    Return state 'Return the object array for saving
    End Function

    Protected Overrides Sub
    LoadControlState(ByVal savedState As Object)
    MyBase.LoadControlState(savedState(0)) 'First load the base controls state

    _DoseEstimateStore = savedState(1) 'Load date for estimate
    End Sub

    'This function saves the data in the cell back to the database
    Public Sub Save(ByRef DataContext As DoseReportingDataContext)
    'if this is an existing entry then update it
    If _DoseEstimateStore.EstimateID <> 0 Then
    'Get Dose Estimate object
    Dim DoseEstimate As DoseEstimate = _DoseEstimateStore.GetDoseEstimate()
    'Attach it, with modified set to true so it will get updated
    DataContext.DoseEstimates.Attach(DoseEstimate, True)
    Else
    'if this is a new entry, and it has data
    If _DoseEstimateStore.EstimatedDose <> 0D Then
    DataContext.DoseEstimates.InsertOnSubmit(_DoseEstimateStore.GetDoseEstimate())
    End If
    End If
    End Sub
    End Class


    <Serializable()> _
    Public Class DoseEstimateStore
    Public EstimateID As Integer = 0
    Public DateForEstimate As DateTime = DateTime.MinValue
    Public LaborID As Integer = 0
    Public EstimatedDose As Decimal = 0D

    Public Sub New()
    End Sub

    Public Sub New
    (ByRef DoseEstimate As DoseEstimate)
    With DoseEstimate
    EstimateID = .EstimateID
    DateForEstimate = .DateForEstimate
    LaborID = .LaborID
    EstimatedDose = .EstimatedDose
    End With
    End Sub

    Public Function
    GetDoseEstimate() As DoseEstimate
    Dim DoseEstimate As New DoseEstimate()

    With DoseEstimate
    .EstimateID = EstimateID
    .DateForEstimate = DateForEstimate
    .LaborID = LaborID
    .EstimatedDose = EstimatedDose
    End With

    Return
    DoseEstimate
    End Function
    End Class
     
  • Re: Custom Databound Control Losing Data on Postback

    08-19-2008, 9:48 AM
    Answer
    • Loading...
    • deblendewim
    • Joined on 12-20-2006, 4:32 PM
    • Antwerp, Belgium
    • Posts 951
    • Points 5,590

    Hi

    Sorry I don't have time to look into your code.

    I had a problem once with losing state. It all had to do with saving the state in the wrong time in the life cycle of the page. Maybe this is happening to you too!?

    Here I found some info about ViewState which helped me to understand it better. http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx

     

    If the problem still occurs, you should try to make a small demo website, and in that demo you build your custom control from scratch up.
    This way it is easier to track down the possible error.

     

    Kind regards,
    Wim

    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
  • Re: Custom Databound Control Losing Data on Postback

    08-19-2008, 1:56 PM
    • Loading...
    • pawicks
    • Joined on 06-26-2008, 11:46 AM
    • Posts 29
    • Points 38

     Thanks for the link, that helped allot. Learned lots of new stuff too.  Problem now is the Control WON'T Lose It's State when I try to make it.  I made a seperate post however since the issue is different.

     What I did to fix my issue:

    I made it so that the control is dynamically added on every page load during the PageLoad event.  During the controls OnInit event it builds itself from the data the page passes in.  After this the State is automatically loaded and works perfectly.  I had tried rebuilding the Control Tree before but had not been able to duplicate it properly enough for it to work.

    Thanks,

       --Peter

  • Re: Custom Databound Control Losing Data on Postback

    08-20-2008, 7:27 AM
    • Loading...
    • deblendewim
    • Joined on 12-20-2006, 4:32 PM
    • Antwerp, Belgium
    • Posts 951
    • Points 5,590

    pawicks:
    I made a seperate post however since the issue is different.

    Great Peter!

    That's the way to get the most out of this forum.

    You could however always post the link of the new thread in this thread.

     

    Good luck!
    Wim

    Please remember to click “Mark as Answer” on the post that helps you, and to click “Unmark as Answer” if a marked post does not actually answer your question. This can be beneficial to other community members reading the thread.
  • Re: Custom Databound Control Losing Data on Postback

    08-21-2008, 10:32 AM
    • Loading...
    • pawicks
    • Joined on 06-26-2008, 11:46 AM
    • Posts 29
    • Points 38

     And here it is: http://forums.asp.net/t/1307741.aspx

    I was able to work with another forum member to get this issue resolved.  It's now a Composite Control but works wonderfully.

Page 1 of 1 (5 items)