我在页面中填充一个ListBox,其中包含来自Web服务的一些数据,一个评论回复列表。在同一页面上有一个文本框和一个帖子按钮,允许用户在RepliesPage上发布对评论的回复。当我运行应用程序并发布回复时,列表不会立即更新,直到我离开页面并导航回页面,我将看到新回复添加到列表中。我该怎么解决这个问题? Page的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>
背后的代码
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;
}
这是ViewModel代码
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
}
}
这是在BaseViewModel中,RepliesViewModel继承自
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;
}
我已经尝试过几个调用ListBox.Items.Clear()的东西,这会返回一个调试器错误,我尝试了ListBox.ItemsSource = null并再次绑定它,仍然dint工作。我需要做什么来触发刷新并立即更新ListBox中显示的数据?
答案 0 :(得分:0)
要解决您的问题,请更改代码,以便在构造函数中设置数据上下文:
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;
}
并在ListBox
绑定ItemsSource
:
<ListBox x:Name="lbxReplies"
ItemsSource="{Binding ReplyList}"
Margin="0,0,-12,0"
>
编辑:我认为您的GetResponseUserSubmitReplyStreamCallback
方法中可能有错误:
RepliesViewModel repliesViewModel = RepliesViewModel.GetSingleInstance();
repliesViewModel.GetReplyList();
如果您的RepliesViewModel.GetSingleInstance()
方法创建了new
个实例,那么此代码将无效。
如果GetResponseUserSubmitReplyStreamCallback
方法在您的网页类中,那么您可能只想调用
repliesViewModel.GetReplyList();
答案 1 :(得分:0)
您需要创建一种新的继承自ObservableCollection
的列表(看起来您已经这样做了):
public class MyItems:ObservableCollection<Item>
{
}
此外,您需要MyItemsInstance以符合INotifyPropertyChanged
“模式”
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));
}
}
}
然后您可以填写调用该服务的列表实例。
如果您以正确的方式使用MVVM,则此方法有效。所以在你的XAML中你需要将列表源与列表实例绑定,我可以在你的代码中看到这一部分。
看看ItemsSource
属性
<ListBox x:Name="lbxReplies" ItemsSource="{Binding MyItemsInstanceInModel}"
Margin="0,0,-12,0"
>
<!--All other stuff-->
</ListBox>
如果绑定没问题,一旦修改了MyItemsInstanceInModel
内容,或者即使你分配了一个新实例,UI也应该自行刷新。
答案 2 :(得分:0)