c# - Xamarin.Forms SwipeView CommandParameter 为 null,除非您重新加载 UI HotReload
问题描述
好的,所以我有一个 CollectionView 绑定到“显示”对象的集合。这个 CollectionView 有很多事情要做。当用户选择一个节目(单击重定向到详细信息页面)时,我有一个命令,选择多个项目以执行多个项目删除,以及滑动以对每个单独的节目执行基本的发布功能。一切正常,除了刷卡。出于某种原因,当触发其中一个滑动命令时,传递给该命令的“显示”对象为空。但是,我为此启用了 HotReload,如果我所做的只是保存 xaml 文件,它工作正常。通过的节目不为空,它按我的预期工作。当我保存 xaml 文件时,我什至不需要更改任何内容,我实际上只是保存而没有新的更改(ctrl + s),并且代码开始工作并且我的滑动项命令很好。
xml:
<ContentPage.ToolbarItems>
<ToolbarItem IconImageSource="icon_plus.png" Command="{Binding AddShowCommand}" />
<ToolbarItem Text="Delete" IconImageSource="icon_trash.png" Command="{Binding DeleteMultipleShowsCommand}" CommandParameter="{Binding SelectedShows}" />
</ContentPage.ToolbarItems>
<!--
x:DataType enables compiled bindings for better performance and compile time validation of binding expressions.
https://docs.microsoft.com/xamarin/xamarin-forms/app-fundamentals/data-binding/compiled-bindings
-->
<RefreshView x:DataType="local:ShowsViewModel" Command="{Binding LoadShowsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<CollectionView x:Name="ItemsListView"
ItemsSource="{Binding Shows}"
SelectionMode="Multiple"
SelectedItems="{Binding SelectedShows}"
SelectionChangedCommand="{Binding SelectionChangedCommand}">
<CollectionView.ItemTemplate>
<DataTemplate>
<SwipeView>
<SwipeView.RightItems>
<SwipeItems>
<SwipeItem Text="Activate"
IconImageSource="favorite.png"
BackgroundColor="LightGreen"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ShowsViewModel}}, Path=SwipeIsActiveCommand}"
CommandParameter="{Binding}" />
<SwipeItem Text="Published"
IconImageSource="delete.png"
BackgroundColor="LightPink"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ShowsViewModel}}, Path=SwipeIsPublishCommand}"
CommandParameter="{Binding}" />
<SwipeItem Text="Sold Out"
IconImageSource="delete.png"
BackgroundColor="LightPink"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ShowsViewModel}}, Path=SwipeIsSoldOutCommand}"
CommandParameter="{Binding}" />
</SwipeItems>
</SwipeView.RightItems>
<!-- Content -->
<Grid BackgroundColor="White">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="1"/>
</Grid.RowDefinitions>
<StackLayout Grid.Column="0" Grid.Row="0" Padding="15" x:DataType="model:Show">
<Label Text="{Binding ShowTitle}"
Grid.Row="0"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<Label Text="{Binding ShowDate}"
Grid.Row="1"
LineBreakMode="NoWrap"
Style="{DynamicResource ListItemTextStyle}"
FontSize="16" />
<StackLayout.GestureRecognizers>
<TapGestureRecognizer
NumberOfTapsRequired="1"
Command="{Binding Source={RelativeSource AncestorType={x:Type local:ShowsViewModel}}, Path=ShowSelectedCommand}"
CommandParameter="{Binding}">
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
</StackLayout>
<BoxView HeightRequest="1"
BackgroundColor="Black"
Grid.ColumnSpan="2"
Grid.Row="1"
VerticalOptions="End"/>
</Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup Name="CommonStates">
<VisualState Name="Normal" />
<VisualState Name="Selected">
<VisualState.Setters>
<Setter Property="BackgroundColor" Value="Yellow" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</SwipeView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
xml.cs
public partial class ShowsPage : ContentPage
{
private ShowsViewModel _viewModel;
public ShowsPage()
{
InitializeComponent();
BindingContext = _viewModel = new ShowsViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
视图模型:
public class ShowsViewModel : BaseViewModel
{
public ObservableCollection<Show> Shows { get; }
public ObservableCollection<object> SelectedShows { get; }
public Command SelectionChangedCommand { get; }
public Command ShowSelectedCommand { get; }
public Command DeleteMultipleShowsCommand { get; }
public Command AddShowCommand { get; }
public Command LoadShowsCommand { get; }
public Command SwipeIsPublishCommand { get; }
public Command SwipeIsActiveCommand { get; }
public Command SwipeIsSoldOutCommand { get; }
public ShowsViewModel()
{
Title = "Shows";
Shows = new ObservableCollection<Show>();
SelectedShows = new ObservableCollection<object>();
LoadShowsCommand = new Command(async () => await LoadItems());
AddShowCommand = new Command(OnAddItem);
ShowSelectedCommand = new Command<Show>(OnItemSelected);
DeleteMultipleShowsCommand = new Command(async () => await DeleteShows(), () => CanDeleteShows());
SelectionChangedCommand = new Command(DeleteMultipleShowsCommand.ChangeCanExecute);
SwipeIsPublishCommand = new Command<Show>(async (show) => await PublishShow(show));
SwipeIsActiveCommand = new Command<Show>(async (show) => await ActivateShow(show));
SwipeIsSoldOutCommand = new Command<Show>(async (show) => await SoldOutShow(show));
}
private async Task LoadItems()
{
IsBusy = true;
try
{
Shows.Clear();
var shows = await DataService.Shows.GetItemsAsync();
foreach (var show in shows)
{
this.Shows.Add(show);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
public void OnAppearing()
{
IsBusy = true;
}
private async void OnAddItem(object obj)
{
await Shell.Current.GoToAsync(nameof(CreateShowPage));
}
private async void OnItemSelected(Show show)
{
if (show == null)
return;
// This will push the ItemDetailPage onto the navigation stack
await Shell.Current.GoToAsync($"{nameof(ShowDetailPage)}?{nameof(ShowDetailViewModel.ShowId)}={show.ShowId}");
}
private bool CanDeleteShows()
{
return SelectedShows.Count() > 0;
}
private async Task DeleteShows()
{
if (SelectedShows != null && SelectedShows.Count() > 0)
{
bool confirm = await App.Current.MainPage.DisplayAlert("Delete?", $"Are you sure you want to delete the selected show(s)?", "Yes", "No");
if (confirm)
{
IsBusy = true;
var totalDeleted = 0;
foreach (var showobj in SelectedShows)
{
var show = (Show)showobj;
var response = await DataService.Shows.DeleteItemAsync(show.ShowId.ToString());
if (response)
{
totalDeleted++;
}
}
await App.Current.MainPage.DisplayAlert("Delete Results", $"Deleted {totalDeleted} show(s).", "Ok");
// This will reload the page
IsBusy = true;
}
}
}
private async Task PublishShow(Show show)
{
if (show != null)
{
show.IsPublished = !show.IsPublished;
await DataService.Shows.UpdateItemAsync(show);
var message = show.IsPublished ? "published" : "unpublished";
await App.Current.MainPage.DisplayAlert($"Show Updated!", $"Show: {show.ShowTitle} has been {message}", "Ok");
}
}
private async Task ActivateShow(Show show)
{
if (show != null)
{
show.IsActive = !show.IsActive;
await DataService.Shows.UpdateItemAsync(show);
var message = show.IsActive ? "activated" : "archived";
await App.Current.MainPage.DisplayAlert($"Show Updated!", $"Show: {show.ShowTitle} has been {message}", "Ok");
}
}
private async Task SoldOutShow(Show show)
{
if (show != null)
{
show.IsSoldOut = !show.IsSoldOut;
await DataService.Shows.UpdateItemAsync(show);
var message = show.IsSoldOut ? "sold out" : "not sold out";
await App.Current.MainPage.DisplayAlert($"Show Updated!", $"Show: {show.ShowTitle} has been marked as {message}", "Ok");
}
}
}
我正在使用带有 USB 调试的 android 物理设备进行测试。我不确定为什么会这样,而且仅仅用 HotReload 重新加载 UI 会改变任何事情似乎很奇怪。
解决方案
尝试:
CommandParameter="{Binding Source={RelativeSource Self}, Path=BindingContext}"
以前我使用 SwipeItem Invoked 事件作为解决方法:
private async void PublishedSwipeItem_Invoked(object sender, System.EventArgs e)
{
var show = (sender as SwipeItem)?.BindingContext as Show;
if (show!= null)
await viewModel.PublishShow(show);
}
推荐阅读
- django - Django ModelForms - 显示与将要保存的不同的选择
- javascript - 检查数组包含另一个数组的每个元素
- android - 将一行从一个列表视图发送到另一个列表视图
- wso2 - 使用 Property Mediator 时如何获取 json 属性?
- javascript - BottomTabNavigator 隐藏项目可见性
- artifactory - Jenkins Artifactory Plugin - 使用 Jenkins 管道进行发布管理
- python - 使用树莓派上的 python SPI 接口从 ADXL355 读取数据
- wordpress - Wordpress Post Grid Shortcode 不显示类别模板中的最新帖子,但显示所有其他帖子
- c# - SubscriptionClient.RegisterMessageHandler() 在 Azure 服务总线中使用“代理连接”吗?
- android - 将函数作为参数传递时出现 IllegalStateException