locked
How to persist data binding during pagefunction navigation? RRS feed

  • Question

  • In my example below I have a "MainWindow" with 2 text boxes showing data from a Class1 object.

    On loading the "MainWindow" I create a new Class1 object, "myObj", and assign this object to the DataContext of the "MainWindow" grid.

    The TextBox Text properties were bound in the XAML.

    The new data is displayed correctly on the page.

    I click the "Next" button to navigate to a PageFunction which displays a message and a "Back" button.

    On clicking the "Back" button the click event executes an OnReturn.

    Back in the "MainWindow" the OnReturn event handler displays a message indicating that the event was executed.

    Question:

    When the "MainWindow" is displayed the fields are blank - what is the recommended method for persisting the data binding definitions?

    A solution that I tried (reflected in my code below under the heading "Page function with persisted data"):

    1) Add a Public Property in the PageFunction to receive the Class1 object ("myObj" to be persisted)
    2) In the PageFunction "Back" button click event, return the object in the OnReturn statement.
    3) In the "MainWindow"'s return event handler, assign the returned object to the "myObj".

    This alone does not seem to be sufficient as the TextBoxes are still blank - it seems as though the data binding also needs to be re-established!

    4) Re-define the databinding in the OnReturn event handler (so the binding is now defined in the XAML and in code! - NOT GOOD !!)

    A major problem with this approach is that it only works when I navigate back using the "Back" button on the page.

    If I navigate back using the Navigation GUI button then the OnReturn event is NOT EXECUTED and hence my form is blank.

    I can of course hide the Navigation GUI interface, but that seems to then limit my functionality and also treat the symptom not the cause.

    So, I believe I am barking up the wrong tree!  How should I be persisting this data?

     

     

    Sample Code:

    ------------------------------------

    <NavigationWindow x:Class="NavigationWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="NavigationWindow" Height="300" Width="300" >

        <NavigationWindow.Content>
                <Border BorderThickness="2" Margin="0,22,0,0"  BorderBrush="Black">
                <Frame Name="NavigationFrame" Source="/MainWindow.xaml" />
        </Border>
    </NavigationWindow.Content>
    </NavigationWindow>

    ------------------------------------

    <Page x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Grid Name="Grid1">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
                <RowDefinition Height="56*" />
                <RowDefinition Height="250*" />
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal">
                <TextBlock  Text="Name"  />
                <TextBox Name="txtName" Text="{Binding Path=Name}" />
            </StackPanel>
            <StackPanel Grid.Row="1" Orientation="Horizontal">
                <TextBlock  Text="Description:"  />
                <TextBox Name="txtDescription" Text="{Binding Path=Description}" />
            </StackPanel>
            <Button Content="Next" Grid.Row="2" Height="23" HorizontalAlignment="Left" Name="Button2" />
        </Grid>
    </Page>


    ------------------------------------

    Class MainWindow
        Dim myObj As New Class1("Default", "Object created on first load")

        Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
            Grid1.DataContext = myObj
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
            Dim pf As New PageFunction1
            AddHandler pf.Return, New ReturnEventHandler(Of String)(AddressOf pf_Return)
            MyBase.NavigationService.Navigate(pf)
        End Sub

        Private Sub pf_Return(ByVal sender As Object, ByVal e As ReturnEventArgs(Of String))
            MsgBox("you are back")
        End Sub

    End Class

    ------------------------------------

    Public Class Class1
        Public Property Name As String
        Public Property Description As String

        Public Sub New(ByVal bvName As String, ByVal bvDescription As String)
            Name = bvName
            Description = bvDescription
        End Sub
    End Class

    ------------------------------------

    <PageFunction
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        x:Class="PageFunction1"
        x:TypeArguments="sys:String"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="300"
        Title="PageFunction1">
        <Grid>
            <TextBlock Text="This is the page function"/>
            <Button Content="Back" Height="23" HorizontalAlignment="Left" Margin="116,142,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />
        </Grid>
    </PageFunction>


    ------------------------------------

    Class PageFunction1

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click

            OnReturn(New ReturnEventArgs(Of String)(""))
        End Sub
    End Class

    --------------------------------------
    --------------------------------------
    Modified code with attempted data persistence

    Class MainWindow
        Dim myObj As New Class1("Default", "Object created on first load")

        Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
            Grid1.DataContext = myObj
        End Sub

        Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
            Dim pf As New PageFunction1
            AddHandler pf.Return, New ReturnEventHandler(Of String)(AddressOf pf_Return)
            MyBase.NavigationService.Navigate(pf)

        End Sub

        Private Sub pf_Return(ByVal sender As Object, ByVal e As ReturnEventArgs(Of String))
            MsgBox("you are back")
        End Sub

        Private Sub Button_pfObjectProperty_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button_pfObjectProperty.Click
            Dim pfObjProperty As New pfObjectProperty
            pfObjProperty.PersistedObject = myObj
            AddHandler pfObjProperty.Return, New ReturnEventHandler(Of Object)(AddressOf pfObjProperty_Return)
            MyBase.NavigationService.Navigate(pfObjProperty)

        End Sub
        Private Sub pfObjProperty_Return(ByVal sender As Object, ByVal e As ReturnEventArgs(Of Object))
            MsgBox("you are back with persisted data")
            myObj = CType(e.Result, Class1)
            txtName.SetBinding(TextBox.TextProperty, "Name")
            txtDescription.SetBinding(TextBox.TextProperty, "Description")
        End Sub

    End Class

    -------------------------------------
    Page function with persisted data
    -------------------------------------

    <PageFunction
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:sys="clr-namespace:System;assembly=mscorlib"
        x:Class="pfObjectProperty"
        x:TypeArguments="sys:Object"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        mc:Ignorable="d"
        d:DesignHeight="300" d:DesignWidth="300"
        Title="pfObjectProperty">
        <Grid>
            <TextBlock Text="Page Function persisting data via a local Property"/>
            <Button Content="Back" Height="23" HorizontalAlignment="Left" Margin="116,142,0,0" Name="Button1" VerticalAlignment="Top" Width="75" />

        </Grid>
    </PageFunction>

    -------------------------------------

    Class pfObjectProperty

        Public Property PersistedObject As Class1

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
            OnReturn(New ReturnEventArgs(Of Object)(PersistedObject))
        End Sub
    End Class

     

    Wednesday, September 29, 2010 2:08 AM

Answers

  • Hi Siggy01,

    REgarding to your scenario, first of all, we need to understand the lifetime of the Page. Please refer to the related sections on Navigation Overview: http://msdn.microsoft.com/en-us/library/ms750478.aspx#XAMLBrowserApplications

    According to the document, the document said that TextBox status can be remembered in the journal with the KeepLive property is true (default is false), and the data is stored across Page navigations.

    However, from your code, the Page did not use te KeepLive to store the data in default, so the controls status can not be restored (Grid1.DataContext is Nothing). Even if the instance of the Class1 can be created agian, but the Page does not fire the Loaded event.

    Solutions:

    1. The solution to change the PageFunction to store the instance of Class1 as parameter (same with your "persisted data" solution, we need to change the constructor of the PageFunction):

    PageFunction code:

    Partial Public Class PageFunction1
     Inherits PageFunction(Of Class1)
    
     Dim returnValue As Class1
    
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
      OnReturn(New ReturnEventArgs(Of Class1)(returnValue))
     End Sub
    
     Public Sub New(ByVal p As Class1)
    
      ' This call is required by the designer.
      InitializeComponent()
    
      ' Add any initialization after the InitializeComponent() call.
      returnValue = p
     End Sub
    End Class
    
    XAML:
    <PageFunction
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:sys="clr-namespace:System;assembly=mscorlib" 
      x:Class="PageFunction1"
      x:TypeArguments="local:Class1"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      mc:Ignorable="d"
      xmlns:local="clr-namespace:WpfApplication2"
      d:DesignHeight="300" d:DesignWidth="300"
      Title="PageFunction1">
    ......
    

    And the MainWindow should be changed as your code:

     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
      Dim pf As New PageFunction1(myObj)
      AddHandler pf.Return, New ReturnEventHandler(Of Class1)(AddressOf pf_Return)
      MyBase.NavigationService.Navigate(pf)
     End Sub
    
     Private Sub pf_Return(ByVal sender As Object, ByVal e As ReturnEventArgs(Of Class1))
      Grid1.DataContext = e.Result
      txtName.SetBinding(TextBox.TextProperty, "Name")
      txtDescription.SetBinding(TextBox.TextProperty, "Description")
      MsgBox("you are back")
     End Sub
    

    In this solution, we have to reset the TextBoxes property and reset the DataContext value of the Grid in code. I think you do not like to code the bindings twice in XAML and code both.

    So the next solution is very simple.

    2. Using KeepAlive property of the Page to maintain the status of the controls, using the journal to store the status of the controls:

    <Page x:Class="MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="MainWindow"
      KeepAlive="True">
    ...
    

    Hope this helps.

    Sincerely,
    Bob Bao

    MSDN Subscriber Support in Forum 

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    • Marked as answer by Siggy01 Friday, October 1, 2010 12:37 AM
    Thursday, September 30, 2010 2:27 AM

All replies

  • Hi Siggy01,

    REgarding to your scenario, first of all, we need to understand the lifetime of the Page. Please refer to the related sections on Navigation Overview: http://msdn.microsoft.com/en-us/library/ms750478.aspx#XAMLBrowserApplications

    According to the document, the document said that TextBox status can be remembered in the journal with the KeepLive property is true (default is false), and the data is stored across Page navigations.

    However, from your code, the Page did not use te KeepLive to store the data in default, so the controls status can not be restored (Grid1.DataContext is Nothing). Even if the instance of the Class1 can be created agian, but the Page does not fire the Loaded event.

    Solutions:

    1. The solution to change the PageFunction to store the instance of Class1 as parameter (same with your "persisted data" solution, we need to change the constructor of the PageFunction):

    PageFunction code:

    Partial Public Class PageFunction1
     Inherits PageFunction(Of Class1)
    
     Dim returnValue As Class1
    
     Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button1.Click
      OnReturn(New ReturnEventArgs(Of Class1)(returnValue))
     End Sub
    
     Public Sub New(ByVal p As Class1)
    
      ' This call is required by the designer.
      InitializeComponent()
    
      ' Add any initialization after the InitializeComponent() call.
      returnValue = p
     End Sub
    End Class
    
    XAML:
    <PageFunction
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:sys="clr-namespace:System;assembly=mscorlib" 
      x:Class="PageFunction1"
      x:TypeArguments="local:Class1"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      mc:Ignorable="d"
      xmlns:local="clr-namespace:WpfApplication2"
      d:DesignHeight="300" d:DesignWidth="300"
      Title="PageFunction1">
    ......
    

    And the MainWindow should be changed as your code:

     Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles Button2.Click
      Dim pf As New PageFunction1(myObj)
      AddHandler pf.Return, New ReturnEventHandler(Of Class1)(AddressOf pf_Return)
      MyBase.NavigationService.Navigate(pf)
     End Sub
    
     Private Sub pf_Return(ByVal sender As Object, ByVal e As ReturnEventArgs(Of Class1))
      Grid1.DataContext = e.Result
      txtName.SetBinding(TextBox.TextProperty, "Name")
      txtDescription.SetBinding(TextBox.TextProperty, "Description")
      MsgBox("you are back")
     End Sub
    

    In this solution, we have to reset the TextBoxes property and reset the DataContext value of the Grid in code. I think you do not like to code the bindings twice in XAML and code both.

    So the next solution is very simple.

    2. Using KeepAlive property of the Page to maintain the status of the controls, using the journal to store the status of the controls:

    <Page x:Class="MainWindow"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="MainWindow"
      KeepAlive="True">
    ...
    

    Hope this helps.

    Sincerely,
    Bob Bao

    MSDN Subscriber Support in Forum 

    If you have any feedback on our support, please contact msdnmg@microsoft.com


    Please remember to mark the replies as answers if they help and unmark them if they provide no help.
    Are you looking for a typical code sample? Please download all in one code framework !
    • Marked as answer by Siggy01 Friday, October 1, 2010 12:37 AM
    Thursday, September 30, 2010 2:27 AM
  • Thanks Bob for your detailed response - the KeepAlive on the Page was my missing piece in the puzzle.

    Friday, October 1, 2010 12:37 AM
  • While I don't disagree with your solution, because it works; there is inconsistent behaviors I'm noticing. Whether or not KeepAlive is true, DatePicker DOES remember its bindings on back navigation. Strange!
    Tuesday, March 25, 2014 4:01 PM