我试图将我的应用程序转换为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;
}
}
答案 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();
}
}
}
请注意,前两个类 - BehaviorBase
和EventToCommandBehavior
并非ListView
所独有,它们可用于从View
派生的任何类。此外,如果您不想/不想使用该事ARGS。有关详细信息,请参阅CommandParameter
OnEvent
方法。
修改强> 使用命令的说明。实现命令的ViewModel应该看起来像这样(不使用转换器)
EventToCommandBehavior