Search code examples
c#windows-phoneapplication-bar

Cannot update info after clicking the appbar with Save function


I display a list of fullname of Member and when I select a member, it will go to the detail page. I edit info directly in the textbox and then click Save in application bar but the info is not changed as I edited...it still remains the old value

Plz help me!

I have the List name of member like this

<ListBox x:Name="Listmember" Height="500" SelectionChanged="Listmember_SelectionChanged" ItemsSource="{Binding Data}" >
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid Width="466" Margin="0, 0, 0, 12">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="30" />
                            <ColumnDefinition Width="*" />
                            <ColumnDefinition Width="Auto" />
                        </Grid.ColumnDefinitions>
                        <Grid Grid.Column="0"></Grid>
                        <StackPanel Grid.Column="1" >
                            <TextBlock FontSize="40"   Text="{Binding FullName}" Foreground="#FFEA0909" FontWeight="Normal" FontStyle="Normal" Style="{StaticResource PhoneTextTitle3Style}" TextWrapping="Wrap"/>


                        </StackPanel>
                        <Grid Grid.Column="2">
                            <Button x:Name="Deletebutton" Height="60" Width="60" Click="deleteButton_Click" BorderBrush="{StaticResource TransparentBrush}">

                                <Image Source="/Assets/delete.dark.png" Visibility="{StaticResource PhoneDarkThemeVisibility}" Margin="-24"/>
                            </Button>
                        </Grid>
                    </Grid>

                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

and the SelectionChanged is:

private void Listmember_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        // If no customer is selected, just return
        if (Listmember.SelectedItem == null) return;
        // Get the parent application that contains the customer being edited
        App thisApp = Application.Current as App;

        // Set this to the selected customer
        thisApp.SelectedMember = Listmember.SelectedItem as Member;

        // Navigate to the detail page
        NavigationService.Navigate(new Uri("/View/MemberDetail.xaml", UriKind.RelativeOrAbsolute));

    }

In the MemberDetails.xaml which is navigated to:

<Grid Name="ListDetails" Grid.Row="0">
            <TextBlock Grid.Row="0" Name="fn" Text="FullName" FontSize="30" VerticalAlignment="Bottom"/>
            <TextBox Grid.Row="1" Name="fullnameTextBox" Text="{Binding FullName, Mode=TwoWay}" TextWrapping="Wrap"/>
            <TextBlock Grid.Row="2" Name="ad" Text="Address" FontSize="30" VerticalAlignment="Bottom"/>
            <TextBox Grid.Row="3" Name="addressTextBox" Text="{Binding Address, Mode=TwoWay}" TextWrapping="Wrap" />
 </Grid>
 <phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
        <shell:ApplicationBarIconButton 
            IconUri="/Images/save.png" 
            Text="Save" 
            x:Name="SaveButton" 
            Click="Save_Click"/>
    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

and the code behind

 public MemberDetail()
    {
        InitializeComponent();
        this.DataContext = App.MainViewModel;
    }
    ViewDetails view = new ViewDetails();



    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        // Get the parent application that contains the active custom
        App thisApp = Application.Current as App;

        // Load the active customer into the viewmodel
        view.LoadDetails(thisApp.SelectedMember);

        // Set the data context to the viewmodel
        ListDetails.DataContext = view;

    }




 private void Save_Click(object sender, EventArgs e)
        {
            App thisApp = Application.Current as App;

           view.UpdateDetails(thisApp.SelectedMember);

            // Go back to the previous page
            NavigationService.GoBack();
        }

and class ViewDetails in ViewModel:

public class ViewDetails : INotifyPropertyChanged
{

    private string _fullname;

    public string FullName
    {
        get
        {
            return _fullname;
        }
        set
        {
            _fullname = value;

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("Fullname"));
            }
        }
    }

    private string _address;

    public string Address
    {
        get
        {
            return _address;
        }
        set
        {
            _address = value;

            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs("Address"));
            }
        }
    }

    public void UpdateDetails(Member abc)
    {
        abc.FullName = FullName;
        abc.Address = Address;


    }

    public void LoadDetails(Member abc)
    {
        FullName = abc.FullName;
        Address = abc.Address;
    }



     #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    // Used to notify the app that a property has changed.
    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

Solution

  • TextBox in Binding Mode Twoway will not update the source until lost focus happens.

    • If you try to press button, you will find that pressing a button will trigger LostFocus event of text box so the The data will be updated and will be in the correct state before you execute the code in your button.

    • And if you try to press ApplicationBarIconButton you will find that LostFocus didn't fire. So the data will not be updated and will be in old state.

    Now how can we overcome this?

    Simple you can do one of the following solutions which update source each time text changes

    1. Using Explicit Binding Combined with OnTextChanged from this question

      Explicit: Updates the binding source only when you call the UpdateSource method. It saves you one extra binding set when the user leaves the TextBox.

      in xaml

      <TextBox TextChanged="OnTextBoxTextChanged" Text="{Binding MyText, Mode=TwoWay, UpdateSourceTrigger=Explicit}" />
      

      in C#

      private void OnTextBoxTextChanged( object sender, TextChangedEventArgs e )
      {
        TextBox textBox = sender as TextBox;
        // Update the binding source
        BindingExpression bindingExpr = textBox.GetBindingExpression( TextBox.TextProperty );
        bindingExpr.UpdateSource();
      }
      
    2. You can use behavior like the one from this link which listens to TextChanged event and updates the source. I prefer this solution as it is easy to add to your textbox without adding more lines to code behind. All you need to do is to add this lines to your textbox.

      <TextBox x:Name="tbx" Text="{Binding Name, Mode=TwoWay}">
      <i:Interaction.Behaviors>
      <local:UpdateSourceOnTextChangedBehavior />
      </i:Interaction.Behaviors>
      </TextBox>
      

      Don't forget to reference System.Windows.Interactivity also add these namespaces to your page namespace definitions

      xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
       xmlns:local="name space of the behavior"