从 Web 请求返回值后,WP7 中未填充列表框

发布于 2024-10-10 01:51:40 字数 6156 浏览 0 评论 0原文

我正在尝试从头开始为 WP7 构建一个简单的应用程序。 MainPage.xaml 中的列表框如下所示:

<Grid x:Name="ContentPanel" Grid.Row="1" >
    <ListBox x:Name="MainListBox" ItemsSource="{Binding result}" SelectionChanged="MainListBox_SelectionChanged">
        <ListBox.ItemTemplate>
           <DataTemplate>
               <StackPanel>
                   <TextBlock Text="{Binding name}" TextWrapping="Wrap" />
                   <TextBlock Text="{Binding description}" TextWrapping="Wrap" />
               </StackPanel>
           </DataTemplate>
        </ListBox.ItemTemplate>
   </ListBox>
</Grid>

为了填充结果及其元素、名称和描述,我需要从 Web 服务器获取值,该服务器使用 json 响应我发送的 get 请求。由于我需要对使用不同 json 对象进行响应的服务器进行不同的调用,因此我为每个对象维护不同的 .cs 文件,独立于 mainpage.cs (其中我只初始化、定义 MainPage_Loaded 和 MainListBox_SelectionChanged)。所有数据均在各个 .cs 文件中获取和处理。 现在的问题是,当我发出 httpwebrequest 并检索响应时,UI 会在此之前加载。据我从其他帖子中了解到,BeginGetResponse 成为后台进程,并且加载 UI 以保持其响应能力。因此,总而言之,在 Web 请求与来自服务器的数据一起返回之前,UI 将加载为空白,因为数据未填充。现在我无法使用调度程序进行填充,因为我不在 MainPage.cs 中,因此无法直接访问列表框。我也尝试过回调,但没有成功。请帮助

MainPage.cs:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        this.Loaded +=new RoutedEventHandler(MainPage_Loaded);
    }

    private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (MainListBox.SelectedIndex == -1)
            return;

        NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainListBox.SelectedIndex, UriKind.Relative));

        MainListBox.SelectedIndex = -1;
    }

    // Load data for the ViewModel Items
    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (!App.ViewModel.IsDataLoaded)            
            App.ViewModel.LoadData();            
    }
}

LoadData() 本质上初始化请求参数,准备 URI 并将 HTTP 请求发送到另一个类(executeRequest.cs 文件)中的另一个函数,该函数获取响应并将请求处理为我可以映射到的对象:

public void decodeJson<E>(HttpWebRequest request)
    {
        request.Method = "GET";
        var reqresult = (IAsyncResult)request.BeginGetResponse(ResponseCallback<E>, request);
    }

    public void ResponseCallback<E>(IAsyncResult reqresult)
    {
        var request = (HttpWebRequest)reqresult.AsyncState;
        var response = (HttpWebResponse)request.EndGetResponse(reqresult);

        if (response.StatusCode == HttpStatusCode.OK)
        {
            var stream = response.GetResponseStream();
            var reader = new StreamReader(stream);
            var contents = reader.ReadToEnd();
            if (contents.ToString().StartsWith("{\"jsonrpc"))
            {
                using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(contents)))
                {
                    Type typeofe = typeof(E);
                    var deserializer = new DataContractJsonSerializer(typeof(E));
                    E element = (E)deserializer.ReadObject(ms);
                    populateResult<E>(element);
                }
            }
        }
    }

在 populateResult 中,我尝试填充 UI。但是,当控件进入 BeginGetResponse 时,我的 UI 已填充并处于 populateResult 中,因为列表框、MainListBox 不可访问,我无法使用调度程序使用新数据刷新 UI

要提供更多信息,结果是包含来自 json 的不同属性的类的 ObservableCollection

populateResult 非常简单:

private void processResult<E>(E element)
    {
        //Checking for the type of result so that I can map
        string typeis = typeof(E).ToString();
        if (typeis.EndsWith("MyViewModel")
            {
              App.ViewModel.result = (element as MyViewModel).result;
              ???
            }
    }

也许我应该承认这(???)是我陷入困境的地方。结果集合已更新,但 UI 中未更新。 UI 仍然是空白,我无法从 populateRsult 访问 MainListBox 进行更新。希望它是清楚的。否则请告诉我

出于礼貌,让我也提供 MyViewModel 和 SubViewModel

MyViewModel:

public class MyViewModel : INotifyPropertyChanged
{
    public MyViewModel()
    {            
        this.result = new ObservableCollection<SubViewModel>();
    }        
    public ObservableCollection<SubViewModel> result { get; set; }
    private string _sampleProperty = "Sample Runtime Property Value";
    public string SampleProperty
    {
        get
        {
            return _sampleProperty;
        }
        set
        {
            _sampleProperty = value;
            NotifyPropertyChanged("SampleProperty");
        }
    }

    public bool IsDataLoaded
    {
        get;
        private set;
    }
    public void LoadData()
    {
        //Initialize all the parameters
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(string.Format
            (uri with parameters);            
        request.Method = "GET";
        //call the decide json to get the reponse and parse it
        executeRequest execReq = new executeRequest();
        execReq.decodeJson<MyViewModel>(request);

        this.IsDataLoaded = true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        if (null != PropertyChanged)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

SubviewModel:

public class SubViewModel : INotifyPropertyChanged 
{
    private string _name;
    public string name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            NotifyPropertyChanged("name");
        }
    }

    private string _description;
    public string description
    {
        get
        {
            return _description;
        }
        set
        {
            _description = value;
            NotifyPropertyChanged("description");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName) 
    {
        if (null != PropertyChanged) 
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

I am trying to build a simple application for WP7 from scratch. The List Box in MainPage.xaml looks like this:

<Grid x:Name="ContentPanel" Grid.Row="1" >
    <ListBox x:Name="MainListBox" ItemsSource="{Binding result}" SelectionChanged="MainListBox_SelectionChanged">
        <ListBox.ItemTemplate>
           <DataTemplate>
               <StackPanel>
                   <TextBlock Text="{Binding name}" TextWrapping="Wrap" />
                   <TextBlock Text="{Binding description}" TextWrapping="Wrap" />
               </StackPanel>
           </DataTemplate>
        </ListBox.ItemTemplate>
   </ListBox>
</Grid>

In order to populate result and its elements, name and description, I need to fetch the values from a web server that responds with json for the get request I send. Since I need to make different calls to the server that respond with different json objects, I am maintaining different .cs files for each, independant of mainpage.cs (where i only initialize, define MainPage_Loaded and MainListBox_SelectionChanged). All the data is fetched and processed in the individual .cs files.
Now the problem is that when I make the httpwebrequest and the reponse is retrieved, the UI is loaded well before that. As I understand from other posts the BeginGetResponse becomes a background process and the UI is loaded to keep it reponsive. So to summarize before the web request is returned with the data from the server, the UI is loaded as blank since the data is not populated. Now I cannot use the Dispatcher to populate since I am not in MainPage.cs and so can't access the List Box directly. I tried callback also but with no success. Please help

MainPage.cs:

public partial class MainPage : PhoneApplicationPage
{
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        this.Loaded +=new RoutedEventHandler(MainPage_Loaded);
    }

    private void MainListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if (MainListBox.SelectedIndex == -1)
            return;

        NavigationService.Navigate(new Uri("/DetailsPage.xaml?selectedItem=" + MainListBox.SelectedIndex, UriKind.Relative));

        MainListBox.SelectedIndex = -1;
    }

    // Load data for the ViewModel Items
    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        if (!App.ViewModel.IsDataLoaded)            
            App.ViewModel.LoadData();            
    }
}

LoadData() essentially initializes the request parameters, prepares URI and send the HTTP request to another function in another class (executeRequest.cs file) that gets the reponse and processes the request into objects that I could map to:

public void decodeJson<E>(HttpWebRequest request)
    {
        request.Method = "GET";
        var reqresult = (IAsyncResult)request.BeginGetResponse(ResponseCallback<E>, request);
    }

    public void ResponseCallback<E>(IAsyncResult reqresult)
    {
        var request = (HttpWebRequest)reqresult.AsyncState;
        var response = (HttpWebResponse)request.EndGetResponse(reqresult);

        if (response.StatusCode == HttpStatusCode.OK)
        {
            var stream = response.GetResponseStream();
            var reader = new StreamReader(stream);
            var contents = reader.ReadToEnd();
            if (contents.ToString().StartsWith("{\"jsonrpc"))
            {
                using (var ms = new MemoryStream(Encoding.Unicode.GetBytes(contents)))
                {
                    Type typeofe = typeof(E);
                    var deserializer = new DataContractJsonSerializer(typeof(E));
                    E element = (E)deserializer.ReadObject(ms);
                    populateResult<E>(element);
                }
            }
        }
    }

In populateResult I am trying to populate the UI. But by the time control enters BeginGetResponse my UI is populated and in populateResult since the List Box, MainListBox is not accessible I cannot use the dispatcher to refresh the UI with the new data

To give more info, result is an ObservableCollection of a class that contains different properties that come in from the json

The populateResult is very simple:

private void processResult<E>(E element)
    {
        //Checking for the type of result so that I can map
        string typeis = typeof(E).ToString();
        if (typeis.EndsWith("MyViewModel")
            {
              App.ViewModel.result = (element as MyViewModel).result;
              ???
            }
    }

Probably I should admit this (???) is where I am stuck. The result collection is updated but not in the UI. The UI is still blank and I cannot access the MainListBox from populateRsult to update. Hope it is clear. Else please tell me

With due courtesy let me also provide with MyViewModel and the SubViewModel

MyViewModel:

public class MyViewModel : INotifyPropertyChanged
{
    public MyViewModel()
    {            
        this.result = new ObservableCollection<SubViewModel>();
    }        
    public ObservableCollection<SubViewModel> result { get; set; }
    private string _sampleProperty = "Sample Runtime Property Value";
    public string SampleProperty
    {
        get
        {
            return _sampleProperty;
        }
        set
        {
            _sampleProperty = value;
            NotifyPropertyChanged("SampleProperty");
        }
    }

    public bool IsDataLoaded
    {
        get;
        private set;
    }
    public void LoadData()
    {
        //Initialize all the parameters
        HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(string.Format
            (uri with parameters);            
        request.Method = "GET";
        //call the decide json to get the reponse and parse it
        executeRequest execReq = new executeRequest();
        execReq.decodeJson<MyViewModel>(request);

        this.IsDataLoaded = true;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName)
    {
        if (null != PropertyChanged)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

SubviewModel:

public class SubViewModel : INotifyPropertyChanged 
{
    private string _name;
    public string name
    {
        get
        {
            return _name;
        }
        set
        {
            _name = value;
            NotifyPropertyChanged("name");
        }
    }

    private string _description;
    public string description
    {
        get
        {
            return _description;
        }
        set
        {
            _description = value;
            NotifyPropertyChanged("description");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged(String propertyName) 
    {
        if (null != PropertyChanged) 
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

感受沵的脚步 2024-10-17 01:51:40

我没有看到任何关于如何将内容绑定到列表框的信息。您在 xaml 中绑定了 result,但没有显示其实际内容的声明。如果它是一个可观察集合,您的 populateResult 方法应该非常简单,只需用新数据更新可观察集合的内容即可。

如果您在填充结果中重新创建可观察集合,而不是仅仅更改其内容,那么这可能是您的问题。

更新:

最大的问题(从我从更新的代码中可以看出)是,当您设置结果时,您的视图模型不会触发属性更改。因此您需要执行以下两件事之一:

a) 在 result 上添加属性更改支持,就像视图模型类对 SampleProperty 的支持一样,这样当它被修改时,绑定就会看到更改:

private ObservableCollection<SubViewModel> _result = new ObservableCollection<SubViewModel>();
public ObservableCollection Result    
{
    get
    {
        return _result;
    }        
    set
    {
        _result = value;
        NotifyPropertyChanged("Result");
    }
}

// -- elsewhere --
private void processResult<E>(E element)    
{        
    // (why were you using the type **name** to check the 
    // type of the result instead of just using `as`?)
    var model = element as MyViewModel;
    if (model != null)
    {              
        App.ViewModel.Result = model.result;              
    }    
}

b) 或者您不需要设置 结果,而是修改其内容,因为它已经是一个可观察的集合:

private void processResult<E>(E element)    
{        
    var model = element as MyViewModel;
    if (model != null)
    {              
        // result is an ObservableCollection, so just modify its contents.  
        // anything bound to it will see the changes via collection change notifications
        App.ViewModel.result.ClearItems();
        foreach (var x in model.result)
            App.ViewModel.result.Add(x);
    }    
}

I don't see anything about how you're binding what to your listbox. you have a binding to result in your xaml, but aren't showing the declaration of what it actually is. If it is an observable collection, your populateResult method should be pretty trivial, just updating the observable collection's contents with new data.

If you are re-creating the observable collection in populate result, instead of just changing its content, that could be your problem.

update:

The biggest issue (from what i can tell from your updated code) is that your view model isn't firing a property change when you set result. so you need to do one of two things:

a) add property change support on result like the view model class has for SampleProperty, so that when it gets modified, bindings see the change:

private ObservableCollection<SubViewModel> _result = new ObservableCollection<SubViewModel>();
public ObservableCollection Result    
{
    get
    {
        return _result;
    }        
    set
    {
        _result = value;
        NotifyPropertyChanged("Result");
    }
}

// -- elsewhere --
private void processResult<E>(E element)    
{        
    // (why were you using the type **name** to check the 
    // type of the result instead of just using `as`?)
    var model = element as MyViewModel;
    if (model != null)
    {              
        App.ViewModel.Result = model.result;              
    }    
}

b) or you need to not set result, but modify its contents since it is an observable collection already:

private void processResult<E>(E element)    
{        
    var model = element as MyViewModel;
    if (model != null)
    {              
        // result is an ObservableCollection, so just modify its contents.  
        // anything bound to it will see the changes via collection change notifications
        App.ViewModel.result.ClearItems();
        foreach (var x in model.result)
            App.ViewModel.result.Add(x);
    }    
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文