I populate a ListBox in a page with some data from a webservice, a list of comment replies. On that same page is a textbox and a post button that allows users to post a reply to a comment while on the RepliesPage. When I run the app and post a reply, the List isn't updated immediately, until I navigate away from the page and navigate back to the page will I see the new reply added to the List. How do I get around this?. Page's XAML
<phone:PhoneApplicationPage
x:Class="CeFlix.Views.RepliesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
xmlns:cimbalinoBehaviors="clr-namespace:Cimbalino.Phone.Toolkit.Behaviors;assembly=Cimbalino.Phone.Toolkit"
xmlns:cimbalinoHelpers="clr-namespace:Cimbalino.Phone.Toolkit.Helpers;assembly=Cimbalino.Phone.Toolkit.Background"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
shell:SystemTray.IsVisible="True"
xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
xmlns:UserControl="clr-namespace:CeFlix.UserControls">
<i:Interaction.Behaviors>
<cimbalinoBehaviors:ApplicationBarBehavior>
<cimbalinoBehaviors:ApplicationBarIconButton x:Name="postReply"
Text="post"
IconUri="/Assets1/AppBar/send.text.png"
IsEnabled="True"
Command="{Binding UserSubmitReplyCommand,Mode=TwoWay}"/>
<!--<cimbalinoBehaviors:ApplicationBarIconButton x:Name="home"
Text="home"
IconUri="/Assets1/AppBar/basecircle.png"
IsEnabled="True"
Click="home_Click"/>-->
</cimbalinoBehaviors:ApplicationBarBehavior>
</i:Interaction.Behaviors>
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar>
<shell:ApplicationBarIconButton x:Name="postReply"
IconUri="/Assets1/AppBar/send.text.png"
IsEnabled="True" Text="Post"
Click="UserSubmitReplyCommand"/>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="#0C426B">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Orientation="Horizontal" Grid.Row="0" Style="{StaticResource ContentHeaderStyle}" Height="72">
<Button Command="{Binding Path=ClickShowPopUpCommand,Mode=TwoWay}" Style="{StaticResource ButtonStyle}" Height="65">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/logo.png" Height="45" Margin="20,0,0,0"/>
<TextBlock Text="REPLIES" Margin="20,0,0,0" Style="{StaticResource PhoneTextTitle2Style}" Foreground="#0C426B" FontWeight="Bold" FontFamily="Segoe WP Black"/>
</StackPanel>
</Button>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" >
<ListBox x:Name="lbxReplies"
ItemsSource="{Binding ReplyList,Mode=TwoWay}"
Margin="0,0,-12,0"
>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Ellipse Width="50" Height="50" Margin="8" VerticalAlignment="Top">
<Ellipse.Fill>
<ImageBrush ImageSource="{Binding profile_pic}"/>
</Ellipse.Fill>
</Ellipse>
<Border Grid.ColumnSpan="2" Grid.Row="0" HorizontalAlignment="Stretch" BorderBrush="Black" BorderThickness="0,0,0,0.5"/>
<StackPanel Grid.Column="1">
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" FontSize="16" Text="{Binding username}" Foreground="#FFF8DE7E" FontFamily="Consolas" />
<TextBlock VerticalAlignment="Center"
HorizontalAlignment="Left"
FontSize="16" Text="{Binding comment}"
TextWrapping="Wrap"
TextOptions.DisplayColorEmoji="True"
TextOptions.TextHintingMode="Animated"
TextTrimming="WordEllipsis" Foreground="White" FontFamily="Consolas" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
<StackPanel Orientation="Horizontal" Grid.Row="2">
<TextBox x:Name="replyTextBox"
Width="475"
Text="{Binding Message,Mode=TwoWay}"
TextChanged="replyTextBox_TextChanged"
BorderThickness="0"
GotFocus="replyTextBox_GotFocus">
<TextBox.Background>
<ImageBrush ImageSource="/Assets/watermark2.png"/>
</TextBox.Background>
<TextBox.InputScope>
<InputScope>
<InputScopeName NameValue="Text" />
</InputScope>
</TextBox.InputScope>
</TextBox>
<
</StackPanel>
<UserControl:DataLoading x:Name="loaderForReplies" Grid.RowSpan="2" Visibility="{Binding LoaderForReplyVisibility}" />
</Grid>
Code behind
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string parameter = this.NavigationContext.QueryString["parameter"];
BaseViewModel.SelectedCommentID = parameter;
}
private async void ContentPanel_Loaded(object sender, RoutedEventArgs e)
{
await repliesViewModel.GetReplyList();
this.DataContext = this.repliesViewModel;
lbxReplies.ItemsSource = null;
lbxReplies.ItemsSource = repliesViewModel.ReplyList;
}
And this is the ViewModel code
using CeFlix.Common;
using CeFlix.Entities;
using CeFlix.OtherEntities;
using Microsoft.Phone.Tasks;
using Microsoft.Xna.Framework.Media;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Xml.Linq;
using Windows.Storage;
namespace CeFlix.ViewModels
{
public class RepliesViewModel : BaseViewModel
{
#region Properties
private UserStatusData userStatusData;
public UserStatusData UserStatusData
{
get
{
return this.userStatusData;
}
set
{
SetProperty(ref userStatusData, value);
}
}
private string message;
public string Message
{
get
{
return message;
}
set
{
SetProperty(ref message, value);
}
}
private Visibility replyListControlVisibility = Visibility.Visible;
public Visibility ReplyListControlVisibility
{
get
{
return this.replyListControlVisibility;
}
set
{
SetProperty(ref replyListControlVisibility, value);
}
}
private Visibility userReplyPopUpOpen;
public Visibility UserReplyPopupOpen
{
get
{
return this.userReplyPopUpOpen;
}
set
{
SetProperty(ref this.userReplyPopUpOpen, value);
}
}
private Visibility loaderForReplyVisibility = Visibility.Collapsed;
public Visibility LoaderForReplyVisibility
{
get
{
return this.loaderForReplyVisibility;
}
set
{
SetProperty(ref this.loaderForReplyVisibility, value);
}
}
private Visibility signInPopUpVisibility = Visibility.Collapsed;
public Visibility SignInPopUpVisibility
{
get
{
return this.signInPopUpVisibility;
}
set
{
SetProperty(ref this.signInPopUpVisibility, value);
}
}
private ObservableCollection<Reply> replyList;
public ObservableCollection<Reply> ReplyList
{
get
{
return replyList;
}
set
{
SetProperty(ref this.replyList, value);
}
}
private ReplyDetails replyDetailsData;
public ReplyDetails ReplyDetailsData
{
get
{
return this.replyDetailsData;
}
set
{
SetProperty(ref this.replyDetailsData, value);
}
}
// Comment
private CommentDetails comment;
public CommentDetails Comment
{
get
{
return this.comment;
}
set
{
SetProperty(ref this.comment, value);
}
}
private Comment selectedComment;
public Comment SelectedComment
{
get
{
return this.selectedComment;
}
set
{
SetProperty(ref this.selectedComment, value);
if (value != null)
{
BaseViewModel.SelectedCommentID = this.selectedComment.id;
this.GetCommentDetail();
this.GetReplyList();
}
}
}
private CommentDetails commentsDetailsData;
public CommentDetails CommentsDetailsData
{
get
{
return this.commentsDetailsData;
}
set
{
SetProperty(ref this.commentsDetailsData, value);
}
}
private CeFlix.Entities.Detail videoDetail;
public CeFlix.Entities.Detail VideoDetail
{
get
{
return this.videoDetail;
}
set
{
SetProperty(ref this.videoDetail, value);
}
}
private CeFlix.Entities.VideoDetails videoDetailData;
public CeFlix.Entities.VideoDetails VideoDetailData
{
get
{
return this.videoDetailData;
}
set
{
SetProperty(ref this.videoDetailData, value);
}
}
#endregion
#region Constructor
public RepliesViewModel()
{
this.ReplyList = new ObservableCollection<Reply>();
}
#endregion
#region Single Instance could be replacement for Singleton that I know of
private static RepliesViewModel currentInstance = null;
public static RepliesViewModel GetSingleInstance()
{
if (currentInstance == null)
{
currentInstance = new RepliesViewModel();
}
return currentInstance;
}
#endregion
#region Delegate Commands
private DelegateCommand goHome;
public DelegateCommand GoHome
{
get
{
if (goHome == null)
goHome = new DelegateCommand(GoHomeCommandClick);
return goHome;
}
}
private DelegateCommand userSubmitReplyCommand;
public DelegateCommand UserSubmitReplyCommand
{
get
{
if (userSubmitReplyCommand == null)
userSubmitReplyCommand = new DelegateCommand(UserSubmitReplyCommandClick);
return userSubmitReplyCommand;
}
}
#endregion
#region Methods
private void GoHomeCommandClick()
{
BaseViewModel.NavigationService.NavigateToPage(Entities.Enums.Views.DashboardPage);
}
public async Task GetCommentDetail()
{
try
{
string Api_Url = string.Format(Constants.LoadComment_API, BaseViewModel.SelectedCommentID) + "?timestamp" + DateTime.Now.ToString();
if (BaseViewModel.HelperClass.IsInternet())
{
var response = await BaseViewModel.HelperClass.Get(Api_Url);
var selectedCommentDetails = JSONHelper.DeserializeFromJson<CeFlix.Entities.CommentDetails>(response);
// this.SelectedComment = selectedCommentDetails.Comments;
foreach (Comment item in selectedCommentDetails.Comments)
{
this.SelectedComment = item;
}
}
else
{
await System.Threading.Tasks.Task.Delay(1000);
MessageBoxResult msgResult = MessageBox.Show(Constants.NETWORK_ERROR, Constants.NETWORK_ERROR_TITLE, MessageBoxButton.OKCancel);
if (msgResult == MessageBoxResult.OK)
{
await GetCommentDetail();
}
else
{
Application.Current.Terminate();
}
}
}
catch (Exception)
{
BaseViewModel.ShowErrorMessage();
}
}
public async Task GetReplyList()
{
try
{
// LoaderForReplyVisibility = Visibility.Visible; // add this later, it think it should control display of usercontrol for replying
string Api_Url = string.Format(Constants.LoadCommentReplies_API, BaseViewModel.SelectedCommentID) + "?timestamp" + DateTime.Now.ToString();
if (BaseViewModel.HelperClass.IsInternet())
{
var response = await BaseViewModel.HelperClass.Get(Api_Url);
this.ReplyDetailsData = JSONHelper.DeserializeFromJson<ReplyDetails>(response);
if (ReplyDetailsData.Error.ToLower() == "none")
{
this.ReplyList = this.ReplyDetailsData.details.replies.ToObservableCollection();
LoaderForReplyVisibility = Visibility.Collapsed;
}
}
else
{
await System.Threading.Tasks.Task.Delay(1000);
MessageBoxResult msgResult = MessageBox.Show(Constants.NETWORK_ERROR, Constants.NETWORK_ERROR_TITLE, MessageBoxButton.OKCancel);
if (msgResult == MessageBoxResult.OK)
{
await GetReplyList();
}
else
{
Application.Current.Terminate();
}
}
}
catch (Exception)
{
BaseViewModel.ShowErrorMessage();
}
}
public async Task GetVideoDetail()
{
try
{
string Api_Url = string.Format(Constants.VideoDetail_API, BaseViewModel.SelectedVideoID) + "?timestamp" + DateTime.Now.ToString();
if (BaseViewModel.HelperClass.IsInternet())
{
var response = await BaseViewModel.HelperClass.Get(Api_Url);
this.VideoDetailData = JSONHelper.DeserializeFromJson<CeFlix.Entities.VideoDetails>(response);
if (VideoDetailData.Error.ToLower() == "none")
{
this.VideoDetail = this.VideoDetailData.Details.FirstOrDefault();
// this.GetVideoViews();
}
}
else
{
await System.Threading.Tasks.Task.Delay(1000);
MessageBoxResult msgResult = MessageBox.Show(Constants.NETWORK_ERROR, Constants.NETWORK_ERROR_TITLE, MessageBoxButton.OKCancel);
if (msgResult == MessageBoxResult.OK)
{
await GetVideoDetail();
}
else
{
Application.Current.Terminate();
}
}
}
catch (Exception)
{
BaseViewModel.ShowErrorMessage();
}
}
private async void UserSubmitReplyCommandClick()
{
try
{
if (BaseViewModel.HelperClass.IsInternet())
{
if (!string.IsNullOrEmpty(this.Message))
{
HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(Constants.Reply_API);
webRequest.Method = "POST";
webRequest.UserAgent = "immtv.loveworldapis.com";
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.BeginGetRequestStream(new AsyncCallback(GetRequestSubmitReplyStreamCallback), webRequest);
}
else
{
MessageBox.Show("First Enter Reply");
}
}
else
{
await System.Threading.Tasks.Task.Delay(1000);
MessageBoxResult msgResult = MessageBox.Show(Constants.NETWORK_ERROR, Constants.NETWORK_ERROR_TITLE, MessageBoxButton.OKCancel);
if (msgResult == MessageBoxResult.OK)
{
UserSubmitReplyCommandClick();
}
else
{
Application.Current.Terminate();
}
}
}
catch (Exception)
{
BaseViewModel.ShowErrorMessage();
}
}
// settle down to analyze this method because it's the body of the POST for posting a reply
void GetRequestSubmitReplyStreamCallback(IAsyncResult callbackResult)
{
try
{
string requestBody = "";
HttpWebRequest webRequest = (HttpWebRequest)callbackResult.AsyncState;
Stream postStream = webRequest.EndGetRequestStream(callbackResult);
// requestBody = "email=" + BaseViewModel.UserEmailId + "&reply=" + Message + "&video_id=" + BaseViewModel.SelectedVideoID;
requestBody = "commentID=" + BaseViewModel.SelectedCommentID + "&comment=" + Message + "&email=" + BaseViewModel.UserEmailId;
byte[] byteArray = Encoding.UTF8.GetBytes(requestBody);
postStream.Write(byteArray, 0, byteArray.Length);
postStream.Close();
webRequest.BeginGetResponse(new AsyncCallback(GetResponseUserSubmitReplyStreamCallback), webRequest);
}
catch (Exception)
{
BaseViewModel.ShowErrorMessage();
}
}
// and this too
void GetResponseUserSubmitReplyStreamCallback(IAsyncResult calldatabackResult)
{
try
{
HttpWebRequest request = (HttpWebRequest)calldatabackResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(calldatabackResult);
using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
{
string result = httpWebStreamReader.ReadToEnd();
UserStatusData = JSONHelper.DeserializeFromJson<CeFlix.Entities.UserStatusData>(result);
}
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
if (UserStatusData.status == "OK")
{
// this.GetReplyList();
this.Message = string.Empty;
MessageBox.Show("Reply Added Successfully");
RepliesViewModel repliesViewModel = RepliesViewModel.GetSingleInstance();
ReplyListControlVisibility = Visibility.Collapsed;
repliesViewModel.GetReplyList();
ReplyListControlVisibility = Visibility.Visible;
}
});
}
catch (Exception)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
BaseViewModel.ShowErrorMessage();
});
}
}
#endregion
}
}
and this inside the BaseViewModel, which the RepliesViewModel inherits from
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
I have tried several things like calling ListBox.Items.Clear(), this returned a debugger error, I tried ListBox.ItemsSource = null and bind it again, still dint work. What do i need to do to trigger a refresh and update the data displayed in the ListBox immediately?
To fix your problem change your code behind just to set the data context in your constructor:
public MyPageConstructor() // Your page constructor
{
this.DataContext = new RepliesViewModel(); // Or any other way how you create your view model
}
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
string parameter = this.NavigationContext.QueryString["parameter"];
BaseViewModel.SelectedCommentID = parameter;
}
private async void ContentPanel_Loaded(object sender, RoutedEventArgs e)
{
await repliesViewModel.GetReplyList();
//this.DataContext = this.repliesViewModel;
//lbxReplies.ItemsSource = null; *removed*
//lbxReplies.ItemsSource = repliesViewModel.ReplyList;
}
and on your ListBox
bind the ItemsSource
:
<ListBox x:Name="lbxReplies"
ItemsSource="{Binding ReplyList}"
Margin="0,0,-12,0"
>
EDIT: I think you may have a bug in your GetResponseUserSubmitReplyStreamCallback
method:
RepliesViewModel repliesViewModel = RepliesViewModel.GetSingleInstance();
repliesViewModel.GetReplyList();
If your RepliesViewModel.GetSingleInstance()
method creates a new
instance then this code will do nothing useful.
If the GetResponseUserSubmitReplyStreamCallback
method is in your page class then you probably want to only call
repliesViewModel.GetReplyList();
You need to create a new kind of list inheriting from ObservableCollection
(looks like you already have done that) :
public class MyItems:ObservableCollection<Item>
{
}
Also you need MyItemsInstance to comply with INotifyPropertyChanged
"pattern"
public class YourViewModelClass:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MyItems MyItemsInstance
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can fill up your list instance calling the service.
This works if your are using MVVM in the right way. so in your XAML you need to bind the list source with your list instance , I can see this part in your code. Take a look to the ItemsSource
property
<ListBox x:Name="lbxReplies" ItemsSource="{Binding MyItemsInstanceInModel}"
Margin="0,0,-12,0"
>
<!--All other stuff-->
</ListBox>
If the binding is OK, once you modify MyItemsInstanceInModel
contents or even if you assign a new instance the UI should be refreshed by itself.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.