【问题标题】:ViewModel data is being shared for each Page while Reusing single ViewModel for different Views为每个页面共享 ViewModel 数据,同时为不同的视图重用单个 ViewModel
【发布时间】:2021-05-29 13:24:52
【问题描述】:

这在某种程度上类似于Bind Two Different Views for same ViewModel 这样的重复问题 但我在 AppShell 上显示页面并遇到与数据相关的问题:

<FlyoutItem Title="News &amp; Updates" Icon="icon_feed.png" >
    <Tab x:Name="UriUpdates" Title="News &amp; Updates" >
        <ShellContent Title="News" Route="NewsPage" ContentTemplate="{DataTemplate local:NewsPage}" />
        <ShellContent Title="Notifications" Route="NotificationsPage" ContentTemplate="{DataTemplate local:NotificationsPage}" />           
    </Tab>        
</FlyoutItem>

两个页面都使用 ContentView 类型的自定义控件。 在页面的代码隐藏中,我提供了 BindingContext

BindingContext = _viewModel = new UpdatesViewModel("news");
BindingContext = _viewModel = new UpdatesViewModel("notifications");

ViewModel 有一个属性 UpdateType,它在构造函数中更新,它的值在命令(ExecuteLoadItemsCommand)中用于填写列表(Items):

Items = new ObservableCollection<UpdatesModel>();
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());

ExecuteLoadItemsCommand() 事件导致 VM 从 DataStore 填充数据

public IDataStore<UpdatesModel> DataStore => DependencyService.Get<IDataStore<UpdatesModel>>();

ExecuteLoadItemsCommand 被按预期调用并每次都填充相应的项目。

async Task ExecuteLoadItemsCommand()
{
    IsBusy = true;
    try
    {
        Items.Clear();
        var items = await DataStore.GetItemsAsync(UpdateType);
        if(items!=null)
        foreach (var item in items)
        {
            Items.Add(item);
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
    }
    finally
    {
        IsBusy = false;
    }
}

GetItemsAsync 在返回所需项目列表的 DataStore 中:

public async Task<IEnumerable<UpdatesModel>> GetItemsAsync(string updateType, bool forceRefresh = false)
{
    if (forceRefresh || items == null || items.Count == 0)
    {
        Uri uri = new Uri(Constants.GetUpdatesUrl(updateType));
        try
        {
            HttpResponseMessage response = await client.GetAsync(uri);
            if (response.IsSuccessStatusCode)
            {
                string content = await response.Content.ReadAsStringAsync();
                items = (List<UpdatesModel>)JsonConvert.DeserializeObject<IEnumerable<UpdatesModel>>(content);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(@"\tERROR {0}", ex.Message);
        }
    }   
    return await Task.FromResult(items);
}

我认为,每当为页面 News/Notifications 填充项目时,它不能为每次选项卡更改单击一次又一次地调用 Rest-Service。它应该在需要时更新项目。 因此,为此我在 DataStore 的方法 GetItemsAsync 中应用了一个条件(它是 IDataStore 的实现)作为

if (forceRefresh || items == null || items.Count == 0)
{
    //Rest Service Call to fill items list
}

问题是项目(这是 VM 中的一个属性)总是非空的,并且填充了第一页数据(新闻项目)。 NewsPage 加载后,不知道为什么在加载NotificationsPage 时项目不为空,并且被NewsPage 数据填充。

请帮我看看我是否在为不同的视图共享 VM 方面存在不好的做法,还是存在某些问题导致页面具有由第一页填充的数据?

【问题讨论】:

  • 更新类型或更新模型?因为我在您的代码中没有看到 UpdateType。物品或物品?我认为您应该包含更多代码我认为我们不能仅从您共享的内容中看到整个画面。
  • 感谢您的回复。当 BindingContext = _viewModel = new UpdatesViewModel("news");正在被调用,viewModel 有一个属性 UpdateType,用于从休息服务获取相应的数据,正如我所提到的,这个 UpdateType(新闻/通知)正在 ExecuteLoadItemsCommand 中使用
  • 当我在每次调用 ExecuteLoadItemsCommand 时允许填充项目时一切正常,但是当我在项目为空的情况下强制填充项目时,问题就来了。对于第一页项目因为它是空的而被填充,对于下一个选项卡,当相同的 VM 用于第二页时,它显示项目不是空的,并且第二页也获得了与第一页填充的相同的项目列表
  • @Cfun 可以在必要的时候清除项目,但问题是为什么项目已经为具有相同 VM 的第二页填充?在加载第一页(NewsPage)时进行调试时,项目已填充,但在加载第二页时,项目不为空,并且具有先前填充项目的数据。
  • 项目在 ContentView 中显示为&lt;CollectionView x:Name="ItemsListView" ItemsSource="{Binding Items}" SelectionMode="None"&gt;

标签: c# xamarin.forms mvvm viewmodel


【解决方案1】:

错误是什么,我指责 ViewModel 导致数据在 VM 中定义的项目列表的每个页面上共享:

public ObservableCollection&lt;UpdatesModel&gt; Items { get; }

但是由于DependencyService :

,数据被共享

public IDataStore&lt;UpdatesModel&gt; DataStore =&gt; DependencyService.Get&lt;IDataStore&lt;UpdatesModel&gt;&gt;();

所以它有共享代码,这意味着它是一个单独的实例并被共享到 VM。

我通过在 DataStore 中引入 Dictionary 解决了这个问题(IDataStore for DependencyService 的实现)

private Dictionary&lt;string, List&lt;UpdatesModel&gt;&gt; itemsDict;

代替

private List&lt;UpdatesModel&gt; items;

并更改了 GetItemsAsync(从 Rest Service 返回数据):

public async Task<IEnumerable<UpdatesModel>> GetItemsAsync(string updateType, bool forceRefresh = false)
{
    if (itemsDict == null || itemsDict.Count == 0) itemsDict = new Dictionary<string, List<UpdatesModel>>();

    if (forceRefresh || itemsDict == null || itemsDict.Count == 0 || !itemsDict.ContainsKey(updateType))
    {
        Uri uri = new Uri(Constants.GetUpdatesUrl(updateType, startDate));
        try
        {
            HttpResponseMessage response = await client.GetAsync(uri);
            if (response.IsSuccessStatusCode)
            {
                string content = await response.Content.ReadAsStringAsync();
                itemsDict[updateType] = (List<UpdatesModel>)JsonConvert.DeserializeObject<IEnumerable<UpdatesModel>>(content);
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(@"\tERROR {0}", ex.Message);
        }
    }
    return await Task.FromResult(itemsDict[updateType]);
}

因此,对于从 ViewModel 传递给 GetItemsAsync 的所有不同 updateTypes,将返回一个字典值 (itemsDict[updateType]),我不必一次又一次地重新填充项目,因为只填充了字典一次或强制重新填充任何 updateType 作为 Key。

【讨论】:

    猜你喜欢
    • 2021-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    • 2019-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多