如何创建ItemSource Xamarin的命令

时间:2018-03-12 13:30:57

标签: c# xamarin mvvm xamarin.forms command

我试图将我的应用程序转换为MVVM架构,但我遇到了itemsource的问题。没有可用的命令属性。

如何将itemtapped和Menuitem转换为MVVM命令? 目前使用FRESHMVVM。我在google搜索时找到了一些解决方案,但它们似乎非常复杂。

 <ContentPage.Resources>
            <ResourceDictionary>
                <converters:StatusTextConverter x:Key="IntStatusToTextConverter" />
                <converters:DateTextConverter x:Key="DateToTextConverter" />
            </ResourceDictionary>
        </ContentPage.Resources>

        <ListView ItemsSource="{Binding Issues}" ItemTapped="OnEventSelected" SeparatorColor="#444444" RowHeight="90" IsPullToRefreshEnabled="True" IsRefreshing="{Binding IsBusy}" RefreshCommand="{Binding PullRefreshCommand}" >
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell >
                        <ViewCell.ContextActions>
                            <MenuItem Clicked="OnDelete" Text="Delete" IsDestructive="True" />
                        </ViewCell.ContextActions>

                        <ViewCell.View>
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="Auto"></RowDefinition>
                                    <RowDefinition Height="Auto"></RowDefinition>
                                    <RowDefinition Height="*"></RowDefinition>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="70"></ColumnDefinition>
                                    <ColumnDefinition Width="*"></ColumnDefinition>
                                    <ColumnDefinition Width="50"></ColumnDefinition>
                                </Grid.ColumnDefinitions>

                                <Image Grid.Row="0" Grid.RowSpan="3" Grid.Column="0" Source="{Binding SeverityImagePath}" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="70"/>
                                <Image Grid.Row="0" Grid.RowSpan="3" Grid.Column="2" Source="{Binding StatusImagePath}" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="60"/>

                                <Label Grid.Row="0" Grid.Column="1" Text="{Binding Title}" LineBreakMode="TailTruncation" YAlign="Center" VerticalOptions="Start" Font="Bold, Medium"/>
                                <Label Grid.Row="1" Grid.Column="1" Text="{Binding Created, Converter={StaticResource DateToTextConverter}}" YAlign="Center" VerticalOptions="Start" Font="Medium"/>
                                <Label Grid.Row="2" Grid.Column="1" Text="{Binding Description}" LineBreakMode="WordWrap" YAlign="Start" VerticalOptions="Start" Font="Small"/>
                            </Grid>

                        </ViewCell.View>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>

PageModel方法

 public async void OnEventSelected(object o, ItemTappedEventArgs e)
        {
            if (IsBusy)
                return;


            var item = e.Item as IssueModel;
            if (item != null)
            {
                MessagingCenter.Subscribe<IssuePageModel>(this, "refresh", async (sender) =>
                {
                    MessagingCenter.Unsubscribe<IssueListPageModel>(this, "refresh");
                    await OnRefreshContent();
                });

                IssuePageModel page = null;
                await Task.Run(() =>
                {
                    UserDialogs.Instance.ShowLoading("Loading event...", maskType: MaskType.Clear);

                    // Does lost of stuff, So show loading message

                    UserDialogs.Instance.HideLoading();
                });

                if (item != null)
                    await CoreMethods.PushPageModel<IssuePageModel>(item);
            }

        }


 public async void OnDelete(object sender, EventArgs e)
        {
            var mi = ((MenuItem)sender);
            IssueModel issue = mi.BindingContext as IssueModel;
            if (issue != null)
            {
                IsBusy = true;

                bool bDeleted = await Task.Run(() =>
                {
                    try
                    {
                        App.Client.DeleteIssue(issue.Id);
                        return Issues.Remove(issue);
                    }
                    catch (Exception /*ex*/)
                    {
                        return false;
                    }
                });

                if (bDeleted == false)
                    await CoreMethods.DisplayAlert("Failed", "Failed to remove item", "Continue");

                IsBusy = false;

            }
        }

1 个答案:

答案 0 :(得分:2)

好的,首先,将以下两个类 - 从我的评论中的示例中无耻地窃取 - 添加到您的项目中:

using System;
using Xamarin.Forms;

namespace EventToCommandBehavior
{
    public class BehaviorBase<T> : Behavior<T> where T : BindableObject
    {
        public T AssociatedObject { get; private set; }

        protected override void OnAttachedTo(T bindable)
        {
            base.OnAttachedTo(bindable);
            AssociatedObject = bindable;

            if (bindable.BindingContext != null)
            {
                BindingContext = bindable.BindingContext;
            }

            bindable.BindingContextChanged += OnBindingContextChanged;
        }

        protected override void OnDetachingFrom(T bindable)
        {
            base.OnDetachingFrom(bindable);
            bindable.BindingContextChanged -= OnBindingContextChanged;
            AssociatedObject = null;
        }

        void OnBindingContextChanged(object sender, EventArgs e)
        {
            OnBindingContextChanged();
        }

        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();
            BindingContext = AssociatedObject.BindingContext;
        }
    }
}

using System;
using System.Reflection;
using System.Windows.Input;
using Xamarin.Forms;

namespace EventToCommandBehavior
{
    public class EventToCommandBehavior : BehaviorBase<View>
    {
        Delegate eventHandler;

        public static readonly BindableProperty EventNameProperty =
            BindableProperty.Create("EventName",
                typeof(string),
                typeof(EventToCommandBehavior),
                null,
                propertyChanged: OnEventNameChanged);

        public static readonly BindableProperty CommandProperty =
            BindableProperty.Create("Command",
                typeof(ICommand),
                typeof(EventToCommandBehavior),
                null);

        public static readonly BindableProperty CommandParameterProperty =
            BindableProperty.Create("CommandParameter",
                typeof(object),
                typeof(EventToCommandBehavior),
                null);

        public static readonly BindableProperty InputConverterProperty =
            BindableProperty.Create("Converter",
                typeof(IValueConverter),
                typeof(EventToCommandBehavior),
                null);

        public string EventName
        {
            get { return (string)GetValue(EventNameProperty); }
            set { SetValue(EventNameProperty, value); }
        }

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        public IValueConverter Converter
        {
            get { return (IValueConverter)GetValue(InputConverterProperty); }
            set { SetValue(InputConverterProperty, value); }
        }

        protected override void OnAttachedTo(View bindable)
        {
            base.OnAttachedTo(bindable);
            RegisterEvent(EventName);
        }

        protected override void OnDetachingFrom(View bindable)
        {
            DeregisterEvent(EventName);
            base.OnDetachingFrom(bindable);
        }

        void RegisterEvent(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                return;
            }

            EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
            if (eventInfo == null)
            {
                throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
            }
            MethodInfo methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
            eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
            eventInfo.AddEventHandler(AssociatedObject, eventHandler);
        }

        void DeregisterEvent(string name)
        {
            if (string.IsNullOrWhiteSpace(name))
            {
                return;
            }

            if (eventHandler == null)
            {
                return;
            }
            EventInfo eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
            if (eventInfo == null)
            {
                throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
            }
            eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
            eventHandler = null;
        }

        void OnEvent(object sender, object eventArgs)
        {
            if (Command == null)
            {
                return;
            }

            object resolvedParameter;
            if (CommandParameter != null)
            {
                resolvedParameter = CommandParameter;
            }
            else if (Converter != null)
            {
                resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
            }
            else
            {
                resolvedParameter = eventArgs;
            }

            if (Command.CanExecute(resolvedParameter))
            {
                Command.Execute(resolvedParameter);
            }
        }

        static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var behavior = (EventToCommandBehavior)bindable;
            if (behavior.AssociatedObject == null)
            {
                return;
            }

            string oldEventName = (string)oldValue;
            string newEventName = (string)newValue;

            behavior.DeregisterEvent(oldEventName);
            behavior.RegisterEvent(newEventName);
        }
    }
}

EventToCommandBehavior类可用于从具有派生自View的任何类的事件调用命令。您可以将其附加到ListView,如下所示:

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:behaviors="clr-namespace:EventToCommandBehavior"
             x:Class="FormsBehaviors.Views.MainPage">
    <ContentPage.Resources>
        <ResourceDictionary>
            <behaviors:SelectedItemEventArgsToSelectedItemConverter x:Key="SelectedItemConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.Content>
        <ListView ItemsSource="{Binding Thingies}">
            <ListView.Behaviors>
                <behaviors:EventToCommandBehavior EventName="ItemSelected" 
                                                  Command="{Binding FooCommand}" 
                                                  Converter="{StaticResource SelectedItemConverter}" />
            </ListView.Behaviors>
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Name}"/>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </ContentPage.Content>
</ContentPage>

转换器是可选的。如果省略它,将使用相关的事件参数调用您的命令。在这种情况下,使用转换器从事件args中提取所选项并将其作为命令参数传递是有意义的。这是转换器,为了完整性:

using System;
using System.Globalization;
using Xamarin.Forms;

namespace EventToCommandBehavior
{
    public class SelectedItemEventArgsToSelectedItemConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var eventArgs = value as SelectedItemChangedEventArgs;
            return eventArgs.SelectedItem;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

请注意,前两个类 - BehaviorBaseEventToCommandBehavior并非ListView所独有,它们可用于从View派生的任何类。此外,如果您不想/不想使用该事ARGS。有关详细信息,请参阅CommandParameter OnEvent方法。

修改 使用命令的说明。实现命令的ViewModel应该看起来像这样(不使用转换器)

EventToCommandBehavior