gpt4 book ai didi

c# - 自定义 View 中的 BindableProperty 不会取消订阅 PropertyChanged

转载 作者:太空狗 更新时间:2023-10-30 01:32:10 26 4
gpt4 key购买 nike

背景信息

我正在 XAML 中开发 Xamarin Forms(v4.1.1.3,在 iOS 上测试)应用程序,使用 MVVM 和先查看方法;我正在使用 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);

问题

我已经实现了一个自定义控件( View ?),它应该以类似图 block 的布局显示搜索结果。当从搜索 NavigationPage 导航到搜索结果 ContentPage 时创建此控件。

每次我返回搜索页面并导航回搜索结果时,都会重建 View 并订阅 BindablePropertiesPropertyChanged。这些 PropertyChanged 事件永远不会取消订阅,因此每次我导航到搜索结果 View 并更改绑定(bind)的 ViewModel 属性时,该事件都会被触发多次。

在下面的代码中,根据我从搜索 View 导航到搜索结果 View 的次数,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 吗?
  • 出现这种情况是否是因为我将 View 与 View 模型相关联和/或浏览页面的方式?
  • 我应该自己处理取消订阅这些事件吗?如何处理?

编辑;额外的导航信息

我有一个 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);
}
}
}

最佳答案

  • BindableProperty 不应该自行取消订阅 PropertyChanged 和 -Changing 吗?
    • 是的 - 它应该。如果不是,那肯定是一个错误
  • 发生这种情况是否是因为我将 View 与 View 模型相关联和/或浏览页面的方式?
    • 这很可能也是一种选择,因为我还没有经历过您描述的行为。您将需要共享更多周边设置代码。
  • 我应该自己处理取消订阅这些事件吗?如何处理?
    • 您很难始终控制取消订阅,因为大多数时候它将控制订阅事件(除非您自己动手,在这种情况下,您始终有责任再次取消订阅)

虽然它很丑陋,但有时需要快速解决方法,在您的情况下,这将是浏览 xamarin 如何保存更改委托(delegate)列表并在出现的页面上手动取消订阅它们。

我希望这能回答您的问题。如果没有,请随时发表评论。

更新

在你的情况下,我会调试你的页面基础,并验证是否

  • OnDisappearing 被正确调用
  • 您的处理程序在取消订阅后消失了
  • (这很懒惰,但我通常在取消订阅之前取消订阅事件,只是为了确保不会发生此类错误,因为如果您尝试取消订阅未注册的处理程序,大多数 EventManagement 服务都不会抛出。 )

至少这是您遇到问题的最可能原因。

关于c# - 自定义 View 中的 BindableProperty 不会取消订阅 PropertyChanged,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38070967/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com