Xamarin中MVVM命令执行后页面标签未刷新问题
电商APP购物车删除商品后总价与积分标签不更新问题解决
我开发了一款电商APP的购物车页面,在XAML的数据模板外设置了显示订单总价和累计积分的Label控件。ViewModel中实现了购物车商品删除命令,但删除商品后,订单总价与积分标签无法自动更新,相关代码如下:
XAML代码
<Button ImageSource="bin.png" x:Name="btnDelete" Command="{Binding Source={x:Reference cartCollectionView}, Path=BindingContext.CartsCommand}" CommandParameter="{Binding .}" Clicked="btnDelete_Clicked" BackgroundColor="Transparent" HorizontalOptions="EndAndExpand"/> </StackLayout> </StackLayout> </Frame> </StackLayout> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> <StackLayout Orientation="Horizontal"> <Label Text="Order #:" FontSize="20" TextColor="Black"/> <Label x:Name="txtOrderNumber" FontSize="20" TextColor="Black"/> </StackLayout> <StackLayout Orientation="Horizontal"> <Label Text="Total Woof Bits earned:" FontSize="Title" Margin="0,0,0,0" TextColor="Black"/> <Label x:Name="txtPoints" Text="{Binding strPoints}" FontSize="Title" Margin="0,0,0,0" TextColor="Black"/> </StackLayout> <StackLayout Orientation="Horizontal"> <Label Text="Total Order:" FontSize="Title" TextColor="Black"/> <Label x:Name="txtTotalOrder" Text="{Binding strTotalOrder}" FontSize="Title" TextColor="Black"/> </StackLayout>
View代码
public CartPage(CartViewModel viewModels) { InitializeComponent(); BindingContext = viewModels; BindingContext = new CartViewModel(); ((CartViewModel)BindingContext).LoadCartsCommand.Execute(null); ((CartViewModel)BindingContext).LoadTotalsCommand.Execute(null); txtOrderNumber.Text = SigninPage.strOrderNumber; txtPoints.Text = CartViewModel.strPoints; txtTotalOrder.Text = CartViewModel.strTotalOrder; } protected override void OnAppearing() { base.OnAppearing(); ((CartViewModel)BindingContext).LoadCartsCommand.Execute(null); ((CartViewModel)BindingContext).LoadTotalsCommand.Execute(null); txtPoints.Text = CartViewModel.strPoints; txtTotalOrder.Text = CartViewModel.strTotalOrder; } private void Button_Clicked(object sender, EventArgs e) { ((CartViewModel)BindingContext).PayCommand.Execute(null); } private void btnDelete_Clicked(object sender, EventArgs e) { ((CartViewModel)BindingContext).LoadTotalsCommand.Execute(null); txtPoints.Text = CartViewModel.strPoints; txtTotalOrder.Text = CartViewModel.strTotalOrder; DisplayAlert("Successful", "Item removed from Cart (▼´•̥ᴥ•̥`) ", "OK"); }
ViewModel代码
private ICommand _loadTotalsCommand; public ICommand LoadTotalsCommand { get { if (_loadTotalsCommand == null) { _loadTotalsCommand = new Command(async () => await LoadTotals()); OnPropertyChanged(); } return _loadTotalsCommand; } } public async Task LoadTotals() { string sqlstring = "MySQL string"; MySqlConnection mysqlcon = new MySqlConnection(sqlstring); MySqlCommand mysqlcom; MySqlDataReader mdr; mysqlcon.Open(); string selectquery = "SELECT ordernumber, quantity, price, sum(price * quantity) AS Total, sum(pointvalue) AS Total1 FROM cart WHERE ordernumber = '" + SigninPage.strOrderNumber + "' GROUP BY ordernumber;"; mysqlcom = new MySqlCommand(selectquery, mysqlcon); mdr = mysqlcom.ExecuteReader(); while (mdr.Read()) if (mdr.FieldCount >= 1) { TotalOrder = mdr.GetDecimal(3); Points = mdr.GetDecimal(4); } mysqlcon.Close(); strTotalOrder = "R" + Convert.ToString(TotalOrder); strPoints = "🐾" + Convert.ToString(Points); } private ICommand _cartCommand; public ICommand CartsCommand { get { if (_cartCommand == null) { _cartCommand = new Command<Cart>(async (cart) => { await OnCartSelected(cart); }); } return _cartCommand; } } public async Task OnCartSelected(Cart cart) { string constring5 = "MySQL string"; MySqlConnection con5 = new MySqlConnection(constring5); con5.Open(); MySqlTransaction transaction5 = con5.BeginTransaction(); using (MySqlCommand cmd5 = new MySqlCommand("DELETE FROM cart WHERE ordernumber = '" + strOrderNumber4 + "'", con5)) { cmd5.Transaction = transaction5; cmd5.ExecuteNonQuery(); transaction5.Commit(); con5.Close(); } LoadTotalsCommand.Execute(null); LoadCartsCommand.Execute(null); }
问题根源与解决方案
核心问题
- ViewModel未实现
INotifyPropertyChanged接口:strTotalOrder和strPoints属性更新时,没有通知UI刷新,这是数据绑定不生效的关键原因。 - View手动赋值破坏绑定:在构造函数和
OnAppearing中直接给Label的Text赋值,覆盖了XAML中的数据绑定逻辑。 - 删除SQL逻辑错误:
OnCartSelected中使用未定义的strOrderNumber4变量,且未使用参数化查询,存在SQL注入风险。 - 异步操作不规范:数据库操作未使用异步方法,可能导致UI阻塞。
修正步骤
1. 让ViewModel实现INotifyPropertyChanged
public class CartViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // 原有成员变量和方法... }
2. 将strTotalOrder和strPoints改为可通知属性
private string _strTotalOrder; public string strTotalOrder { get => _strTotalOrder; set { _strTotalOrder = value; OnPropertyChanged(); } } private string _strPoints; public string strPoints { get => _strPoints; set { _strPoints = value; OnPropertyChanged(); } }
3. 移除View中的手动赋值代码
修改CartPage构造函数和OnAppearing方法:
public CartPage(CartViewModel viewModels) { InitializeComponent(); // 复用注入的ViewModel实例,避免重复创建 BindingContext = viewModels ?? new CartViewModel(); ((CartViewModel)BindingContext).LoadCartsCommand.Execute(null); ((CartViewModel)BindingContext).LoadTotalsCommand.Execute(null); txtOrderNumber.Text = SigninPage.strOrderNumber; // 移除手动赋值,保留数据绑定 // txtPoints.Text = CartViewModel.strPoints; // txtTotalOrder.Text = CartViewModel.strTotalOrder; } protected override void OnAppearing() { base.OnAppearing(); ((CartViewModel)BindingContext).LoadCartsCommand.Execute(null); ((CartViewModel)BindingContext).LoadTotalsCommand.Execute(null); // 移除手动赋值 // txtPoints.Text = CartViewModel.strPoints; // txtTotalOrder.Text = CartViewModel.strTotalOrder; } private void btnDelete_Clicked(object sender, EventArgs e) { // 删除后命令已自动刷新数据,无需手动调用 // ((CartViewModel)BindingContext).LoadTotalsCommand.Execute(null); // txtPoints.Text = CartViewModel.strPoints; // txtTotalOrder.Text = CartViewModel.strTotalOrder; DisplayAlert("Successful", "Item removed from Cart (▼´•̥ᴥ•̥`) ", "OK"); }
4. 修正删除逻辑并使用参数化查询
public async Task OnCartSelected(Cart cart) { string constring5 = "MySQL string"; using (MySqlConnection con5 = new MySqlConnection(constring5)) { await con5.OpenAsync(); using (MySqlTransaction transaction5 = con5.BeginTransaction()) { // 使用参数化查询,删除指定订单下的指定商品 using (MySqlCommand cmd5 = new MySqlCommand("DELETE FROM cart WHERE ordernumber = @orderNumber AND id = @cartId", con5)) { cmd5.Transaction = transaction5; cmd5.Parameters.AddWithValue("@orderNumber", SigninPage.strOrderNumber); cmd5.Parameters.AddWithValue("@cartId", cart.Id); // 假设Cart类包含Id属性 await cmd5.ExecuteNonQueryAsync(); await transaction5.CommitAsync(); } } } // 异步执行刷新命令 await ((Command)LoadTotalsCommand).ExecuteAsync(null); await ((Command)LoadCartsCommand).ExecuteAsync(null); }
5. 优化LoadTotals方法的异步逻辑
public async Task LoadTotals() { string sqlstring = "MySQL string"; using (MySqlConnection mysqlcon = new MySqlConnection(sqlstring)) { await mysqlcon.OpenAsync(); // 简化查询,只获取需要的总价和积分 string selectquery = "SELECT sum(price * quantity) AS Total, sum(pointvalue) AS Total1 FROM cart WHERE ordernumber = @orderNumber GROUP BY ordernumber;"; using (MySqlCommand mysqlcom = new MySqlCommand(selectquery, mysqlcon)) { mysqlcom.Parameters.AddWithValue("@orderNumber", SigninPage.strOrderNumber); using (MySqlDataReader mdr = await mysqlcom.ExecuteReaderAsync()) { if (await mdr.ReadAsync()) { TotalOrder = mdr.GetDecimal(0); Points = mdr.GetDecimal(1); } else { // 购物车为空时重置数值 TotalOrder = 0; Points = 0; } } } } strTotalOrder = "R" + TotalOrder.ToString(); strPoints = "🐾" + Points.ToString(); }
内容的提问来源于stack exchange,提问作者SUI
相关产品推荐
相关产品推荐

