背景信息
我正在使用MVVM和 View first 方法在XAML中开发Xamarin Forms(v4.1.1.3,在iOS上测试)应用程序;我正在使用MVVMLight的ViewModelLocator服务将单实例ViewModel分配给Views:
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);
问题
我实现了一个自定义控件(视图?),它应该以类似于tile的布局显示搜索结果。从搜索NavigationPage
导航到搜索结果ContentPage
时,会创建此控件。
每次我返回搜索页面并导航回搜索结果时,都会重建视图并订阅PropertyChanged
的{{1}}。这些BindableProperties
事件永远不会取消订阅,因此每次导航到搜索结果视图并更改绑定的ViewModel属性时,事件都会多次触发。
在以下代码中,PropertyChanged
会根据我从搜索视图导航到搜索结果视图的次数多次触发:
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
MainView
,其TabbedPage
为SearchView
:
NavigationPage
public MainView()
{
InitializeComponent();
Children.Add(new NavigationPage(new SearchView())
{
Title = AppResources.Tab_Search,
Icon = "tab_search"
});
}
在创建时,使用MVVMLight的SearchView
容器,在本主题开头提到的ViewModelLocator
分配的单实例ViewModel。
当SimpleIoc
中的搜索命令被触发时,我向API返回搜索结果的请求。这些结果显示在另一个页面上,我从SearchView
的ViewModel导航到该页面:
SearchView
哪种功能看起来像这样:
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);
}
BaseContentPage是:
<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>
ViewModel的基本情况如下:
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");
}
}
答案 0 :(得分:2)
虽然它很丑陋,但有时需要快速解决方法,在您的情况下,它将浏览xamarin如何保存更改代表的列表,并在页面上手动取消订阅,例如。
我希望能回答你的问题。如果没有,请随意发表评论。
在你的情况下,我会调试你的页面基础,并验证是否
至少是您问题的最可能原因。
答案 1 :(得分:1)
没有。 BindableProperty
班负责处理这个问题。不是BindingContext
。
您正在看到这一点,因为您忘记了导航堆栈在内存中保留了一个页面列表。由于多个页面指向相同的BindingContext,因此有多个观察者可以进行更改。如果您没有重复使用View Models,则不会遇到此特定问题。
没有。如果确实存在问题,请在页面消失时将null
设置为<group name="mygroup">
<groupFooter>
<band height="123">
<rectangle>
<reportElement mode="Opaque" x="1" y="1" width="558" height="23" backcolor="#00CC99" />
</rectangle>
<textField isStretchWithOverflow="true" isBlankWhenNull="true">
<reportElement stretchType="RelativeToBandHeight" x="0" y="28" width="553" height="86" />
<textElement>
<font fontName="Times New Roman" size="7" isBold="false"/>
</textElement>
<textFieldExpression><![CDATA[$P{Disclosure}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" pattern="$ #,##0" isBlankWhenNull="true">
<reportElement mode="Transparent" x="49" y="2" width="80" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{sumFaceAmount}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" pattern="" isBlankWhenNull="true">
<reportElement mode="Transparent" x="0" y="2" width="48" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{sumCounts}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" pattern="$ #,##0.00" isBlankWhenNull="true">
<reportElement mode="Transparent" x="385" y="2" width="80" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{avgPremium}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" pattern="$ #,##0.00" isBlankWhenNull="true">
<reportElement mode="Transparent" x="130" y="2" width="85" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{sumPremium}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" pattern="$ #,##0" isBlankWhenNull="true">
<reportElement mode="Transparent" x="304" y="2" width="80" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{avgFaceAmount}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" isBlankWhenNull="true">
<reportElement mode="Transparent" x="533" y="2" width="25" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{Avg_Tage}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" isBlankWhenNull="true">
<reportElement mode="Transparent" x="507" y="2" width="26" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{Avg_Fage}]]></textFieldExpression>
</textField>
<textField evaluationTime="Report" isBlankWhenNull="true">
<reportElement mode="Transparent" x="478" y="2" width="28" height="14" forecolor="#000000" />
<textElement textAlignment="Center" verticalAlignment="Middle">
<font fontName="Times New Roman" size="10" isBold="true"/>
</textElement>
<textFieldExpression><![CDATA[$V{Avg_Mage}]]></textFieldExpression>
</textField>
</band>
</groupFooter>
</group>
,然后在重新显示时将其恢复。请记住,虽然这仍然有成本,特别是如果你的用户界面真的很忙并且有很多由数据绑定控制的动态内容。