【问题标题】:iOS ListView Context Action does not swipe back automatically in Xamarin FormsiOS ListView 上下文操作不会在 Xamarin Forms 中自动向后滑动
【发布时间】:2017-06-24 21:41:53
【问题描述】:

Xamarin Forms 中(使用Prism,虽然不一定需要),我想从ListView 中的项目动态更改MenuItem ContextActions

MainPage.xaml

<ContentPage
x:Class="XamarinProject.Views.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
Title="MainPage"
prism:ViewModelLocator.AutowireViewModel="True">
<StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <ListView
        x:Name="mList"
        ItemsSource="{Binding Items}"
        SelectedItem="{Binding SelectedItem}">

        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ViewCell.ContextActions>
                        <MenuItem Command="{Binding CurrentCommand}" Text="{Binding Cycle}" />
                    </ViewCell.ContextActions>
                    <Label Text="{Binding State}" TextColor="Navy" />
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>
</ContentPage>

MainPageViewModel.cs

public class MainPageViewModel : BindableBase, INavigationAware
{
    public ObservableCollection<MyItemViewModel> Items { get; set; } = new ObservableCollection<MyItemViewModel>();

    public void OnNavigatedTo(NavigationParameters parameters)
    {
        Items.Add(new MyItemViewModel(_pageDialogService));
    }
}

MyItemViewModel

public class MyItemViewModel : BindableBase
{
    private string _cycle;

    public string Cycle
    {
        get { return _cycle; }
        set { SetProperty(ref _cycle, value); }
    }

    private DelegateCommand _currentCommand;
    private readonly DelegateCommand[] _commands;
    public DelegateCommand CurrentCommand
    {
        get { return _currentCommand; }
        set { SetProperty(ref _currentCommand, value); }
    }

    public MyItemViewModel(IPageDialogService dialogService)
    {
        _commands = new DelegateCommand[]
        {
            new DelegateCommand(show1),
            new DelegateCommand(show2),
            new DelegateCommand(show3)
        };
        show1();
    }

    private void show1()
    {
        show("1", 1, "I show 1");
    }

    private void show2()
    {
        show("2", 2, "I show 2");
    }

    private void show3()
    {
        show("3", 0, "I show 3");
    }

    private void show(string cycle, int newIndex, string message)
    {
        Cycle = cycle;
        CurrentCommand = _commands[newIndex];
        // dialogService available through Unity, it's available
        _dialogService.DisplayAlertAsync("Alert", message, "Ok");
    }
}

但是,在 iOS 上,当我滑动行时,我看到 ContextAction(s),然后勾选它,它会执行 Command,但 ContextActionMenu/row 不会自动向后滑动,因为它应该是.如果我将它绑定到单个DelegateCommand,而不动态地将其引用更改为另一个DelegateCommand,它就可以正常工作(就像在XAML 中这样:Command={Binding MyNonChangingCommand})。如何解决这个问题,以便该行自动“向后滑动”(就像它应该的那样)?或者它只是 Prism 或 Xamarin Forms 中的一个错误?

【问题讨论】:

    标签: ios xaml xamarin xamarin.forms prism


    【解决方案1】:

    Prism 是一个 MVVM 框架,不负责 UI 演示问题,但有时您可能需要对 MasterDetailPages 和 NavigationPages 进行一些技巧,以使它们以您希望使用 Prism 的方式显示。

    关于您的代码,您可能需要进行一些更改。

    • ViewModel 和 Model 之间存在差异。例如,您可能有一个 Monkey 模型、一个 MonkeyListViewModel 和一个 MonkeyDetailViewModel。 MonkeyListViewModel 将包含一个 ObservableCollection&lt;Monkey&gt; 和一个或多个要为特定 Monkey 执行的命令
    • 您需要创建一个DataTemplateSelector,其中包含模型的业务逻辑,并且可以选择合适的选择器(见下文)

    型号

    public class SomeItem
    {
        public string Name { get; set; }
    
        public bool SomeProperty { get; set; }
    }
    

    视图模型

    public class SomeItemListPageViewModel : BindableBase
    {
        public ObservableCollection<SomeItem> SomeItems { get; set; }
    
        public DelegateCommand<SomeItem> MyCommand { get; }
    
        private void OnMyCommandExecuted( SomeItem item )
        {
            // Do Foo
        }
    }
    

    数据模板选择器

    public class MyItemCellSelector : DataTemplateSelector
    {
        protected override DataTemplate OnSelectTemplate( object item, BindableObject container )
        {
            var myItem = item as MyItem;
    
            if( myItem.SomeProperty == true )
                return Foo;
    
            return Bar;
        }
    
        public DataTemplate Foo { get; set; }
    
        public DataTemplate Bar { get; set; }
    }
    

    查看

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:selector="clr-namespace:MyApp.Selectors;assembly=MyApp"
                 xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
                 prism:ViewModelLocator.AutowireViewModel="True"
                 x:Name="page"
                 x:Class="MyApp.Views.SomeItemListPage">
      <ContentPage.Resources>
        <ResourceDictionary>
          <!-- Sets up the Foo Template that has no Context Actions -->
          <DataTemplate x:Key="fooTemplate">
            <TextCell Text="{Binding Name}" />
          </DataTemplate>
    
          <!-- Sets up the Bar Template that has the Do Foo Context Action which executes
                MyCommand from the ViewModel -->
          <DataTemplate x:Key="barTemplate">
            <TextCell Text="{Binding Name}">
              <TextCell.ContextActions>
                <MenuItem Text="Do Foo" 
                  Command="{Binding BindingContext.MyCommand,Source={x:Reference page}}" 
                  CommandParameter="{Binding .}" />
              </TextCell.ContextActions>
            </TextCell>  
          </DataTemplate>
    
          <!-- Sets up the Selector to us the defined Templates for my Foo and Bar conditions -->
          <selector:MyItemCellSelector x:Key="myItemSelector"
                                       Default="{StaticResource fooTemplate}"
                                       PendingUpload="{StaticResource barTemplate}" />
        </ResourceDictionary>
      </ContentPage.Resources>
    
        <ListView ItemsSource="{Binding SomeItems}"
                  ItemTemplate="{StaticResource localInspectionSelector}">
        </ListView>
    
    </ContentPage>
    

    【讨论】:

    • 虽然这在 ListView 的创建时可以正常工作,但当您在显示项目后尝试修改上下文操作(基于更改的数据)时就不行了。如果您绑定 ContextAction 的 MenuItem 的 Text 属性,如果此属性发生更改,则 ContextAction MenuItem 确实反映了更改,但不再关闭,这对我来说更像是一个错误。无论如何,这并不能满足简单用例的需求,该用例具有“选择”或“取消选择”上下文操作,该操作会根据项目选择而改变。我还没有找到合适的解决方案。
    • @Fred,那你就错过了正确的通知。您应该使用 ObservableCollection 来通知数据更改的内容/时间。我可能会建议尝试 James Montemagno 的 MvvmHelpers
    • 感谢丹的回答。但是不,我确实有一个适当的 ObservableCollection。单元格的每一位都会在 UI 上立即更新。这不是这里真正的问题。我将在接下来的几天尝试发布一个完整的 SO 问题,以更详细地解释我的问题。
    • 我知道这是一个老问题,但我遇到了同样的问题。有没有人能够重新创建或解决这个问题? stackoverflow.com/questions/59491528/…
    猜你喜欢
    • 2018-02-22
    • 2021-12-16
    • 1970-01-01
    • 2015-07-10
    • 1970-01-01
    • 2010-12-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多