正如标题所说的那样,如果我试图刷新绑定到RestroomDetailPage.xaml
中的ObservableRangeCollection
评论的列表视图,它只会卡在活动指示器上,没有任何更新。
我已经检查了ObservableRangeCollection
,它工作得很好。在尝试刷新列表视图时没有引发任何错误。
以下是repo:https://github.com/K-Ketchup/Dook
Review.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SQLite;
namespace Dook.Shared.Models
{
public class Review : INotifyPropertyChanged
{
private int id;
private string username;
private double stars;
private string text;
private int restroomid;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
[SQLite.PrimaryKey, SQLite.AutoIncrement]
[Column("Id")]
public int Id
{
get { return id; }
set
{
if (value != id)
{
id = value;
OnPropertyChanged("Id");
}
}
}
[Column("Username")]
public string Username
{
get { return username; }
set
{
if (value != username)
{
username = value;
OnPropertyChanged("Username");
}
}
}
[Column("Stars")]
public double Stars
{
get { return stars; }
set
{
if (value != stars)
{
stars = value;
OnPropertyChanged("Stars");
}
}
}
[Column("Text")]
public string Text
{
get { return text; }
set
{
if (value != text)
{
text = value;
OnPropertyChanged("Text");
}
}
}
[Column("RestroomId")]
public int RestroomId
{
get { return restroomid; }
set
{
if (value != restroomid)
{
restroomid = value;
OnPropertyChanged("RestroomId");
}
}
}
//[Column("Tags")]
//public string Tags { get; set; }
}
}
字符串
InternetReviewService.cs
using Dook.Shared.Models;
using Newtonsoft.Json;
using SQLite;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dook.Services
{
public static class InternetReviewService
{
//static string Baseurl = DeviceInfo.Platform == DevicePlatform.Android ?
// "http://10.0.2.2:5000" : "http://localhost:5000";
static string BaseUrl = "https://dookwebapp.azurewebsites.net/";
static HttpClient client;
static InternetReviewService()
{
client = new HttpClient()
{
BaseAddress = new Uri(BaseUrl)
};
}
static Random random = new Random();
public static async Task AddReviewAsync(string username, double stars, string text, int restroomid)
{
int idNum = random.Next(0, 100000);
try
{
//Check to see if ID is a duplicate
var randomReview = await client.GetStringAsync($"api/Review/Individual/{idNum}");
await AddReviewAsync(username, stars, text, restroomid);
}
catch(HttpRequestException ex)
{
var review = new Review()
{
Username = username,
Stars = stars,
Text = text,
Id = idNum,
RestroomId = restroomid
};
var json = JsonConvert.SerializeObject(review);
var content =
new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync("api/Review", content);
if (!response.IsSuccessStatusCode)
Debug.WriteLine("Response Success!");
}
}
public static async Task RemoveReviewAsync(int id)
{
var response = await client.DeleteAsync($"api/Review/{id}");
if (!response.IsSuccessStatusCode)
Debug.WriteLine("Response Success!");
}
public static async Task<IEnumerable<Review>> GetReviewAsync(int restId)
{
try
{
var json = await client.GetStringAsync($"api/Review/List/{restId}");
Console.WriteLine(json);
var reviews = JsonConvert.DeserializeObject<IEnumerable<Review>>(json);
return reviews;
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP request failed: {ex.Message}");
return Enumerable.Empty<Review>();
}
}
}
}
型
RestroomDetailPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:Dook.Shared.Models;assembly=Dook.Shared"
xmlns:viewmodel="clr-namespace:Dook.ViewModel"
xmlns:controls="clr-namespace:Dook.Controls"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:Class="Dook.View.RestroomDetailPage"
x:Name="RestroomPage"
Title="Pin">
<ContentPage.BindingContext>
<viewmodel:InternetRestroomDetailViewModel/>
</ContentPage.BindingContext>
<ContentPage.Resources>
<ResourceDictionary>
<toolkit:SelectedItemEventArgsConverter x:Key="SelectedItemEventArgsConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
<VerticalStackLayout>
<Frame
Margin="10"
BorderColor="Transparent"
CornerRadius="50"
HeightRequest="250"
WidthRequest="250"
IsClippedToBounds="True"
HorizontalOptions="Center"
VerticalOptions="Center">
<Image
Source="dook_toilet.png"
Aspect="AspectFill"
Margin="0"
HeightRequest="300"
WidthRequest="300" />
</Frame>
<Label
x:DataType="model:Restroom"
Text="{Binding Name}"
FontSize="23"
HorizontalOptions="Center"/>
<Label
x:DataType="model:Restroom"
Text="{Binding Address}"
FontSize="18"
HorizontalOptions="Center"/>
<Label
x:DataType="model:Restroom"
Text="{Binding Username, StringFormat='Registered by: {0}'}"
HorizontalOptions="Center"
FontSize="18"/>
<ListView
x:DataType="viewmodel:InternetRestroomDetailViewModel"
BindingContext="{Binding Source={viewmodel:InternetRestroomDetailViewModel}}"
BackgroundColor="LightGray"
CachingStrategy="RecycleElement"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
IsRefreshing="{Binding IsBusy, Mode=OneWay}"
ItemsSource="{Binding Review}"
RefreshControlColor="#37A7FC"
SelectionMode="None"
SeparatorVisibility="None"
WidthRequest="340"
HeightRequest="350"
x:Name="ReviewList"
Margin="10">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Review">
<ViewCell>
<ViewCell.ContextActions>
<ToolbarItem
x:DataType="viewmodel:InternetRestroomDetailViewModel"
Command="{Binding RemoveCommand}"
CommandParameter="{Binding .}"
IsDestructive="True"
Text="Delete" />
</ViewCell.ContextActions>
<Grid Padding="10">
<Frame CornerRadius="20" HasShadow="True">
<StackLayout Orientation="Horizontal">
<StackLayout VerticalOptions="Center">
<Label
FontSize="Large"
Text="{Binding Username}"
VerticalOptions="Center"/>
<controls:RatingControl
Amount="5"
CurrentValue="{Binding Stars}"
AccentColor="Yellow"
StarSize="36"
VerticalOptions="End" />
<Label
FontSize="Small"
Text="{Binding Text}"
VerticalOptions="Center"/>
</StackLayout>
</StackLayout>
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</VerticalStackLayout>
</ContentPage>
型
RestroomDetailPage.xaml.cs
using Dook.Services;
using Dook.Shared.Models;
using Dook.ViewModel;
using Microsoft.Maui.Controls.Maps;
using System.Windows.Input;
namespace Dook.View;
[QueryProperty(nameof(RestroomId), nameof(RestroomId))]
public partial class RestroomDetailPage : ContentPage
{
public string RestroomId { get; set; }
public Restroom restroom { get; set; }
public RestroomDetailPage()
{
InitializeComponent();
var vm = (InternetRestroomDetailViewModel)this.BindingContext;
ToolbarItem item = new ToolbarItem
{
Text = "Add"
};
item.Clicked += async (s, args) =>
{
if (vm.AddCommand.CanExecute(restroom.Id.ToString()))
await vm.AddCommand.ExecuteAsync(restroom.Id.ToString());
};
this.ToolbarItems.Add(item);
ICommand refreshCommand = new Command(async () =>
{
if (vm.RefreshCommand.CanExecute(restroom.Id.ToString()))
vm.RefreshCommand.ExecuteAsync(restroom.Id.ToString());
});
ReviewList.RefreshCommand = refreshCommand;
}
protected override async void OnAppearing()
{
base.OnAppearing();
int.TryParse(RestroomId, out var result);
restroom = await InternetRestroomService.GetSingularPinAsync(result);
BindingContext = restroom;
}
}
型
InternetRestroomDetailViewModel.vs
using Dook.Shared.Models;
using MvvmHelpers.Commands;
using MvvmHelpers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dook.Services;
namespace Dook.ViewModel
{
public class InternetRestroomDetailViewModel : BaseViewModel
{
public ObservableRangeCollection<Review> Review { get; set; }
public String rID { get; set; }
public AsyncCommand<String> RefreshCommand { get; }
public AsyncCommand<String> AddCommand { get; }
public AsyncCommand<Review> RemoveCommand { get; }
public InternetRestroomDetailViewModel()
{
Title = "My Review";
Review = new ObservableRangeCollection<Review>();
RefreshCommand = new AsyncCommand<String>(RefreshAsync);
AddCommand = new AsyncCommand<String>(AddAsync);
RemoveCommand = new AsyncCommand<Review>(RemoveAsync);
}
async Task AddAsync(String restId)
{
var username = await App.Current.MainPage.DisplayPromptAsync("Username", "Username for review");
var stars = await App.Current.MainPage.DisplayPromptAsync("Stars", "Stars for review", maxLength: 1, keyboard: Keyboard.Numeric);
var text = await App.Current.MainPage.DisplayPromptAsync("Text", "Add text", maxLength: 50);
rID = restId;
if (username == null || stars == null || text == null) { return; }
await InternetReviewService.AddReviewAsync(username, Double.Parse(stars), text, Int32.Parse(restId));
await RefreshAsync(restId);
}
async Task RemoveAsync(Review review)
{
await InternetReviewService.RemoveReviewAsync(review.Id);
await RefreshAsync(rID);
}
async Task RefreshAsync(String restId)
{
IsBusy = true;
await Task.Delay(2000);
Review.Clear();
var reviews = await InternetReviewService.GetReviewAsync(Int32.Parse(restId));
try
{
Review.AddRange(reviews);
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
}
IsBusy = false;
}
}
}
型
BaseViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dook.ViewModel
{
public partial class BaseViewModel : ObservableObject
{
public BaseViewModel()
{
}
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(IsNotBusy))]
bool isBusy;
[ObservableProperty]
string title;
public bool IsNotBusy => !IsBusy;
}
}
型
1条答案
按热度按时间fzsnzjdm1#
我通过删除-
RestroomDetailPage.xaml.cs
后面的代码中的第一个绑定上下文解决了这个问题。看起来这个绑定上下文干扰了列表视图的绑定,因为列表视图使用了不同的绑定上下文。