简体   繁体   中英

How can I refresh a ListBox to display latest data in C# and XAML

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.

  1. Add lbxReplies.Items.Refresh() after emptying the listbox(lbxReplies.Items.Clear() or lbxReplies.ItemsSource = null).
    Or
  2. Since, lisReplyList is binded to lbxReplies. So, Call ReplyList.Clear() instead of lbxReplies.Items.Clear() or lbxReplies.ItemsSource = null.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM