我正在尝试将我的应用程序转换为MVVM体系结构,但是我遇到了项目源的问题。没有可用的命令属性。
如何将被点击的项目和Menuitem转换为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>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;
}
}发布于 2018-03-13 16:12:36
好的,那么首先,在您的项目中添加以下两个类--无耻地从我的注释中的示例中偷来的类:
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一起调用。在这种情况下,使用转换器从事件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将显式参数传递给要绑定到的命令。有关详细信息,请参阅EventToCommandBehavior的EventToCommandBehavior方法。
编辑:关于使用命令的解释。实现该命令的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;
}
}https://stackoverflow.com/questions/49236497
复制相似问题