【发布时间】:2016-06-28 08:29:55
【问题描述】:
背景信息
我正在使用带有 View first 方法的 MVVM 在 XAML 中开发 Xamarin Forms(v4.1.1.3,在 iOS 上测试)应用程序;我正在使用 MVVMLight 的 ViewModelLocator 服务将单实例 ViewModel 分配给 View:
BindingContext="{Binding [SearchViewModel], Source={StaticResource ViewModelLocator}}"
当导航到另一个页面时,我正在构建一个新的页面实例,每次都会收到相同的 ViewModel 实例。
var page = new SearchView();
var tabbedPage = Application.Current.MainPage as TabbedPage;
if (tabbedPage != null)
await tabbedPage.CurrentPage.Navigation.PushAsync(page);
问题
我已经实现了一个自定义控件(视图?),它应该以类似平铺的布局显示搜索结果。此控件是在从搜索 NavigationPage 导航到搜索结果 ContentPage 时创建的。
每次我返回搜索页面并导航回搜索结果时,都会重建视图并订阅BindableProperties 的PropertyChanged。这些PropertyChanged 事件永远不会被取消订阅,所以每次我导航到搜索结果视图并更改绑定的 ViewModel 属性时,都会多次触发该事件。
在以下代码中,OnItemsPropertyChanged 被多次触发,具体取决于我从搜索视图导航到搜索结果视图的次数:
public class WrapLayout : Grid
{
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create("Items", typeof(IEnumerable), typeof(WrapLayout), null, propertyChanged: OnItemsPropertyChanged);
public IEnumerable Items
{
get { return (IEnumerable)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public WrapLayout()
{
...
}
private static void OnItemsPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
...
}
}
我的问题:
-
BindableProperty不应该自己取消订阅PropertyChanged和-Changing吗? - 这是因为我将视图与 ViewModel 关联和/或浏览页面的方式造成的吗?
- 我应该自己处理退订这些事件吗?如何处理?
编辑;其他导航信息
我有一个MainView TabbedPage,它将SearchView 创建为NavigationPage:
public MainView()
{
InitializeComponent();
Children.Add(new NavigationPage(new SearchView())
{
Title = AppResources.Tab_Search,
Icon = "tab_search"
});
}
SearchView 在创建时具有由本主题开头提到的 ViewModelLocator 分配的单实例 ViewModel,使用 MVVMLight 的 SimpleIoc 容器。
当SearchView 中的搜索命令被触发时,我向返回搜索结果的 API 发送请求。这些结果显示在另一个页面上,我从 SearchView 的 ViewModel 导航到该页面:
await _navigationService.NavigateTo(ViewModelLocator.PageKeyFileResults, searchResult);
哪个功能看起来有点像这样:
public async Task NavigateTo(string pagekey, object viewModelParameter)
{
var constructor = _pagesByKey[pagekey].Constructor; //Gets the Func<Page> that simple creates the requested page, without using reflection.
var page = constructor() as Page;
var viewModel = page.BindingContext as BaseViewModel;
if (viewModel != null)
viewModel.Initialize(viewModelParameter);
var tabbedPage = Application.Current.MainPage as TabbedPage;
if (tabbedPage != null)
await tabbedPage.CurrentPage.Navigation.PushAsync(page);
else
await Application.Current.MainPage.Navigation.PushAsync(page);
}
构建的页面看起来有点像:
<pages:BaseContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Views.FileResultsView"
xmlns:pages="clr-namespace:Views.Pages;assembly=Views"
xmlns:controls="clr-namespace:Views.Controls;assembly=Views"
BindingContext="{Binding [FileResultsViewModel], Source={StaticResource ViewModelLocator}}">
<ScrollView>
<controls:WrapLayout
Items="{Binding SearchResults}" />
</ScrollView>
</pages:BaseContentPage>
BaseContentPage 在哪里:
public class BaseContentPage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
MessagingCenter.Subscribe<DialogMessage>(this, "ShowDialog", (dialogMessage) =>
{
if (string.IsNullOrWhiteSpace(dialogMessage.AcceptButton))
DisplayAlert(dialogMessage.Title, dialogMessage.Content, dialogMessage.CancelButton);
else
DisplayAlert(dialogMessage.Title, dialogMessage.Content, dialogMessage.AcceptButton, dialogMessage.CancelButton);
});
}
protected override void OnDisappearing()
{
base.OnDisappearing();
MessagingCenter.Unsubscribe<DialogMessage>(this, "ShowDialog");
}
}
而这里的 ViewModel 基本上是这样的:
public class FileResultsViewModel : BaseViewModel
{
private IEnumerable<ASRow> _searchResults;
public IEnumerable<ASRow> SearchResults
{
get { return _searchResults; }
set { Set(ref _searchResults, value); }
}
internal override void Initialize(object parameter)
{
base.Initialize(parameter);
if (parameter is AdvancedSearchResponse)
{
var searchResults = parameter as AdvancedSearchResponse;
SearchResults = new List<ASRow>(searchResults.Rows);
}
}
}
【问题讨论】:
标签: c# mvvm xamarin xamarin.forms