c# - 如何创建 ItemSource Xamarin 命令

标签 c# xamarin mvvm xamarin.forms command

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

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

 <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>

页面模型方法

 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;

            }
        }

最佳答案

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

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>

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

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 独有的,它们可以与派生自的任何类一起使用查看。此外,如果您不能/不想使用事件参数,还有另一个属性 CommandParameter 可用于将显式参数传递给您要绑定(bind)的命令。有关详细信息,请参阅 EventToCommandBehaviorOnEvent 方法。

编辑: 使用命令的说明。执行命令的 ViewModel 应该看起来像这样(不使用转换器)

public class MainVM
{
    public ObservableCollection<Thingy> Thingies { get; set; }
    public ICommand FooCommand { get; set; }

    public MainVM()
    {
        Thingies = new ObservableCollection<Thingy>();
        FooCommand = new Command(Bar);
    }

    private void Bar(object eventArgs)
    {
        var args = eventArgs as SelectedItemChangedEventArgs;
        if (args == null)
            return;

        var selectedItem = args.SelectedItem;
    }
}

关于c# - 如何创建 ItemSource Xamarin 命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49236497/

相关文章:

c# - double.NaN 和 double.NegativeInfinity 的 CompareTo 行为

c# - 使用 Xamarin Forms 在 Visual Studio 中创建 Hello World 失败

android - Xamarin 白标应用

c# - 通过mvvmcross进行非线性导航

mvvm - zk 表单重置在 MVVM 中不起作用

c# - 如何找出局部变量声明使用了什么关键字?

c# - 将空值作为字符串传递

c# - 单个表达式中的多个正则表达式匹配

ios - Xamarin 无法识别已安装的字体

.net - 哪个 WPF 控制套件最适合 MVVM?