我正在努力使用.Net Maui和一个独立于UI的MVVM模式将一个对象从一个视图传递到另一个视图。
到目前为止,我发现的大多数或所有MAUI示例都使用并推广了MVVM。他们还倾向于在UI项目中存储视图模型,并直接利用MAUI导航。在我看来和理解中,这可能忽略了MVVM的一些好处,因为View模型不会与其他UI项目一起工作。
我尝试创建一个工作示例*,视图模型、模型和服务位于一个独立的UI独立项目中,并由包含视图的UI项目引用。示例项目发布到GitHub我想用一个解决方案来更新这个项目,这样它就可以成为一个有用的例子。
当从对象列表中单击单个对象时,它会打开一个详细视图,但该对象没有被成功传递。在本例中,我的示例使用了客户订单列表,当单击订单列表中的订单时,我尝试在详细视图中打开订单。
我的问题在XAML绑定到GoToOrdersCommand、命令如何实现和订单对象传递给Order之间。
为什么我同时有一个DelegateCommand和RelayCommand,我不确定。我看过的不同的例子使用了这些名字,我不确定一个名字是否正确。或者它们是否应该合并成一个类。我认为这个例子唯一的显著区别是RelayCommand接受一个对象作为参数。
订单视图-模型
namespace Orders.Common.ViewModel
{
public class OrdersViewModel : ViewModelBase
{
public ObservableCollection<Order> Orders { get; } = new();
private readonly OrderDataProvider _orderDataProvider;
private IOrderNavigation _navigationService;
public RelayCommand<Order> GoToOrdersCommand { get; }
private Order _order;
public OrdersViewModel(IOrderNavigation navigationService)
{
Orders = new ObservableCollection<Order>();
_orderDataProvider = new OrderDataProvider();
_navigationService = navigationService;
_order = new Order();
GoToOrdersCommand = new RelayCommand<Order>((order) => OrderDetails(_order));
Load();
}
public void Load()
{
var orders = _orderDataProvider.GetAllOrders();
Orders.Clear();
foreach (var order in orders)
Orders.Add(order);
}
public void OrderDetails(Order order)
{
if (order == null)
return;
_navigationService.NavigateToOrderAsync(order);
}
}
}
订单视图-模型
public class OrderViewModel : ViewModelBase
{
private Order _order;
public Order Order
{
get => _order;
set
{
if (_order != value)
{
_order = value;
RaisePropertyChanged();
}
}
}
private OrderDataProvider _orderDataProvider = new OrderDataProvider();
public DelegateCommand SaveCommand { get; }
public ICommand SaveOrder { get; set; }
public OrderViewModel()
{
_order = new Order() { OrderID = -1, Customer = "", OrderDate = DateTime.Now };
SaveCommand = new DelegateCommand(Save, () => CanSave);
}
public OrderViewModel(Order order)
{
_order = order;
SaveCommand = new DelegateCommand(Save, () => CanSave);
}
public bool CanSave => !string.IsNullOrEmpty(CustomerName) && CustomerName.Length >= 3;
public void Save()
{
throw new NotImplementedException();
}
public int OrderID
{
get => _order.OrderID;
set
{
if (_order.OrderID != value)
{
_order.OrderID = value;
RaisePropertyChanged();
}
}
}
public string CustomerName
{
get => _order.Customer;
set
{
if (_order.Customer != value)
{
_order.Customer = value;
RaisePropertyChanged();
RaisePropertyChanged(nameof(CanSave));
SaveCommand.RaiseCanExecuteChanged();
}
}
}
public DateTime OrderDate
{
get => _order.OrderDate;
set
{
if (_order.OrderDate != value)
{
_order.OrderDate = value;
RaisePropertyChanged();
}
}
}
}
继承的UI特定导航
namespace TestOrders.Navigate
{
public class OrderNavigationService : IOrderNavigation
{
public void NavigateToOrdersAsync()
{
Shell.Current.GoToAsync(nameof(OrdersPage));
}
public void NavigateToOrderAsync(Order order)
{
Shell.Current.GoToAsync(nameof(OrderPage), true, new Dictionary<string, object> { { "Order", order } });
}
}
}
RelayCommand
namespace Orders.Common.ViewModel.Command
{
public class RelayCommand<T> : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
订单视图
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="TestOrders.Pages.OrdersPage"
Title="OrdersPage"
xmlns:model="clr-namespace:Orders.Common.Model;assembly=Orders.Common"
xmlns:viewmodel="clr-namespace:Orders.Common.ViewModel;assembly=Orders.Common"
x:DataType="viewmodel:OrdersViewModel">
<VerticalStackLayout>
<Label Text="Orders" HorizontalOptions="Center"/>
<CollectionView
ItemsSource="{Binding Orders}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="model:Order">
<Grid Padding="10">
<Frame HeightRequest="70">
<Frame.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:OrdersViewModel}}, Path=GoToOrdersCommand}"
CommandParameter="{Binding .}"/>
</Frame.GestureRecognizers>
<Grid Padding="0" ColumnDefinitions="20,*">
<Label Text="{Binding OrderID}" Grid.Column="0"/>
<Label Text="{Binding Customer}" Grid.Column="1"/>
</Grid>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
*对于.Net Maui、基于XAML的UI、MVVM模式以及对接口和委托等其他主题缺乏经验,我可能错过了一些最佳实践或编码约定。我还试图避免使用MVVM社区工具包或其他工具包,直到我更好地了解MVVM以及接口和命令是如何工作的。
发布于 2022-09-14 02:42:52
我相信这个
GoToOrdersCommand = new RelayCommand<Order>((order) => OrderDetails(_order));
应该是
GoToOrdersCommand = new RelayCommand<Order>((order) => OrderDetails(order));
_order
是在前面的行上创建的一个新实例,而不是从视图的绑定传入的实例
发布于 2022-09-14 22:44:18
更改为OrdersViewModel构造器
public OrdersViewModel(IOrderNavigation navigationService)
{
Orders = new ObservableCollection<Order>();
_orderDataProvider = new OrderDataProvider();
_navigationService = navigationService;
GoToOrdersCommand = new RelayCommand<Order>((order) => OrderDetails((Order)order));
GoToNewOrderCommand = new DelegateCommand(NewOrder); // Additional navigate without object
Load();
}
用于导航到添加到OrdersViewModel的新/空订单详细信息以供参考的附加命令方法
private void NewOrder()
{
_navigationService.NavigateToOrderAsync();
}
更新的顺序导航
public class OrderNavigationService : IOrderNavigation
{
public void NavigateToOrdersAsync()
{
Shell.Current.GoToAsync(nameof(OrdersPage));
}
public void NavigateToOrderAsync(Order order)
{
Shell.Current.GoToAsync(nameof(OrderPage), true, new Dictionary<string, object> { { "Order", order } });
}
public void NavigateToOrderAsync()
{
Shell.Current.GoToAsync(nameof(OrderPage));
}
}
OrderPage查看文件背后的代码
添加了QueryProperty,如果提供了Order对象,则创建OrderViewModel的字段和方法。
[QueryProperty(nameof(order), "Order")]
public partial class OrderPage : ContentPage
{
public Order order
{
set
{
Load(value);
}
}
public OrderPage()
{
InitializeComponent();
BindingContext = new OrderViewModel();
}
private void Load(Order order)
{
if (order != null)
BindingContext = new OrderViewModel(order);
}
}
https://stackoverflow.com/questions/73710578
复制相似问题