Xamarin Forms MvvM框架之FreshMvvM翻译一

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Xamarin Forms MvvM框架之FreshMvvM翻译一相关的知识,希望对你有一定的参考价值。

参考技术A FreshMvvm是专门为Xamarin.Forms设计的轻量Mvvm框架。 它是简单和灵活的。

当Xamarin.Forms发布时,我(Michael Ridland)是Xamarin传统应用程序的一部分。 我想将项目移到Xamarin.Forms上,但是在该项目中我使用的是MvvMCross。 当时MvvmCross不支持Xamarin.Forms,所以我有几个选择

关于MvvmCross的最好的部分是它是双向数据绑定到原生的ios / android控件,但由于Xamarin.Forms已经拥有Databinding内置,这是没有用的,MvvMCross太大了,我不需要这么大。
我也无法找到一个可以轻松移动的替代方案。所以我做了属于我自己简单并且灵活MvvM框架。

它是从这个帖子开始的 —— 翻译在这 ,为Xamarin.Forms实现自己的Mvvm。 我尽量为自己的MvvM框架做得简单。

从来没有想过来写一个框架,但在几次发布Mvvm解决方案之后,我发现很多人都想要它,并且似乎对此感兴趣。 另外考虑到我从Xamarin.Forms开始就在我所有的项目中使用了这个框架,我知道它的工作原理,所以我创建了FreshMvvm,于是它诞生了。

FreshMvvm中的主要导航形式是PageModel到PageModel,这实际上意味着我们的观点不了解导航。

可以通过实现IFreshNavigationService.来设置任何类型的导航。在示例应用程序中有一个示例,名为CustomImplementedNav.cs。

所以你不需要使用你自己的IOC容器,FreshMvvm自带了一个内置的IOC容器,它使用的是TinyIOC,但使用不同的命名来避免冲突。要在容器中注册服务注册:

注入时使用:

这也是驱动构建器注入的方式。

我们现在流畅支持API来设置对象在IOC容器内的生命周期。

如下所示,IFreshIOC接口方法返回IRegisterOptions接口。

从register方法返回的接口是IRegisterOptions。

当PageModels被推送到IOC容器中的services可以被推入构造函数。

每个PageModel都有一个名为“CoreMethods”的属性,当一个PageModel被推送时,它被自动填充,它是大多数应用程序需要的基本功能,如弹消息框,推送,弹出等。

PageModel Init PropertyChanged

在FreshMvvm中可以进行任何类型的导航,通过实现自定义导航服务来完成自定义或高级场景。即使有这种能力,发现在FreshMvvm中做高级导航方案有点困难。在我回顾了FreshMvvm的所有支持问题之后,我发现人们的基本问题是他们希望能够多次使用我们内置的导航容器,其中两个主要例子是

为了支持这两种情况,我得出结论,FreshMvvm需要具有命名NavigationServices的能力,以便我们可以支持多个NavigationService。

在下面我们运行一个单一的主细节的两个导航堆栈。

Xamarin.Forms中有些情况可能需要运行多个导航堆栈。 一个很好的例子是当你有一个用于认证的导航堆栈和一个应用程序主区域的堆栈。

首先我们可以为导航容器设置一些名称。

然后我们可以创建我们的两个导航容器并分配到主页面。

一旦我们设置好了,我们现在可以切换我们的导航容器。

FreshMvvm 1.0的第二个主要要求是允许自定义IOC容器。 在您的应用程序已经具有要使用的容器的情况下。

使用自定义IOC容器非常简单,因为您只需要实现单个接口。

然后在系统中设置IOC容器。

FreshIOC.OverrideContainer(myContainer);

Xamarin.forms MVVM。列表视图仍然为空

【中文标题】Xamarin.forms MVVM。列表视图仍然为空【英文标题】:Xamarin.forms MVVM. Listview remains empty 【发布时间】:2018-10-17 02:35:36 【问题描述】:

我对 Xamarin.Forms 和 MVVM 非常陌生,并且在 *** 上发布了问题,所以请多多包涵。我正在尝试在 Xamarin.Forms 中填充列表视图。我第一次在没有 MVVM 的情况下对其进行了编程,一切都像我想要的那样工作,但现在我想进入 MVVM,这就是它出错的地方,现在我的列表不会再填满了。 我做了一个viewmodel,只把所有的绑定都放在了viewmodel中,还没有实现eventhandlers。

这是背后的代码的一部分(我还有几个事件处理程序,但现在不相关):

    namespace CCXamarinApp
    
        [XamlCompilation(XamlCompilationOptions.Compile)]
        public partial class PatientsWithTagPage : ContentPage
        
            public PatientsWithTagPage()
            
                BindingContext = new PatientsWithTagViewModel();

                InitializeComponent();
                (BindingContext as PatientsWithTagViewModel).GetAllPatients();

                if((BindingContext as PatientsWithTagViewModel).IsEmptyPatientList)
                    HandleEmptyList();
                else
                    (BindingContext as PatientsWithTagViewModel).SortAndShowPatients();
            

            private void SearchBar_OnTextChanged(object sender, TextChangedEventArgs e)
            
            (BindingContext as PatientsWithTagViewModel).Searching(e.NewTextValue);
            
           ...

这是我的 XAML 页面

        <SearchBar x:Name="SearchBar" Placeholder="Zoek op naam of plaats..." HeightRequest="25" Margin="10"
                TextChanged="SearchBar_OnTextChanged"/>

        <Label Text="Binding LastRefreshed" FontAttributes="Italic" FontSize="15" />

        <Label x:Name="LabelEmptyList" FontSize="17" Text="Geen gegevens gevonden" FontAttributes="Bold"
                IsVisible="False" />

        <ListView x:Name="PatientListView" HasUnevenRows="True" SeparatorColor="Accent"
                VerticalOptions="FillAndExpand" IsPullToRefreshEnabled="True" IsRefreshing="Binding IsFetchingData, Mode=TwoWay"
                Refreshing="PatientListView_OnRefreshing" SelectedItem="Binding SelectedPatient, Mode=TwoWay"
                ItemSelected="PatientListView_OnItemSelected" ItemsSource="Binding Patients"
                  IsVisible="True" BackgroundColor="Aqua">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout Spacing="4">
                            <StackLayout Orientation="Horizontal" Margin="10,7,10,1">
                                <Label Text="Binding FullName" FontAttributes="Bold" FontSize="16" />
                                <Label Text="Binding DisplayTimeOfLastScan, StringFormat='0'"
                                        HorizontalOptions="EndAndExpand" />
                            </StackLayout>
                            <StackLayout Orientation="Horizontal" Margin="10,0,10,7">
                                <Label Text="Binding LastLocation" HorizontalOptions="Start" />
                                <Label Text="Binding DisplayDurationSinceLastScan, StringFormat='al 0'"
                                        HorizontalOptions="EndAndExpand" />
                            </StackLayout>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage.Content>

这是我的 viewmodel(不是所有代码,而是最相关的代码)。它派生的 BaseViewModel 来自 nuget 包“Refractored.Mvvmhelpers”:

class PatientsWithTagViewModel : BaseViewModel
    
    public ObservableCollection<PatientViewModel> Patients  get; private set;  = new ObservableCollection<PatientViewModel>();

    private PatientViewModel selectedPatient;
    public PatientViewModel SelectedPatient
    
        get => selectedPatient;
        set => SetProperty(ref selectedPatient, value);
    

    private readonly JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings
    
        DateFormatString = "dd-MM-yyyTH:mm",
        DateTimeZoneHandling = DateTimeZoneHandling.Utc,
    ;

    public bool IsEmptyPatientList => Patients.Count == 0;

    private string testJson = "[\"firstName\":\"P.\",\"lastName\":\"Selie\",\"tag\":\"tagId\":\"124\",\"tagSerialNumber\":\"ABC135\",\"scans\":[\"location\":\"Tuin\",\"dateTime\":\"May01,2018,10:10\",\"location\":\"Eetzaal\",\"dateTime\":\"May02,2018,10:15\",\"location\":\"Gang\",\"dateTime\":\"May02,2018,11:10\",\"location\":\"Kamer23\",\"dateTime\":\"May02,2018,12:09\"],\"id\":\"dcc4fe9929b3681f\"," +
                              "\"firstName\":\"W.\",\"lastName\":\"Janssens\",\"tag\":\"tagId\":\"132\",\"tagSerialNumber\":\"ABC167\",\"scans\":[\"location\":\"Kamer23\",\"dateTime\":\"May01,2018,23:39\",\"location\":\"Gang\",\"dateTime\":\"May02,2018,04:10\",\"location\":\"Eetzaal\",\"dateTime\":\"May02,2018,04:11\",\"location\":\"Gang\",\"dateTime\":\"May02,2018,04:20\",\"location\":\"Kamer23\",\"dateTime\":\"May02,2018,04:22\"],\"id\":\"a6dac28475327922\"]";

    public void GetAllPatients()
    
        IsFetchingData = true;
        try
        
            Patients = new ObservableCollection<PatientViewModel>(
                JsonConvert.DeserializeObject<ObservableCollection<PatientViewModel>>(testJson,
                    jsonSerializerSettings));
        
        catch(Exception e)
        
            Console.WriteLine("*****ERROR kon API niet ophalen");
            Console.WriteLine(e.Message);
        
        finally
        
            IsFetchingData = false;
        
    

这是我使用的模型

public class Patient : ObservableObject
    
        public string FirstName  get; set; 
        public string LastName  get; set; 
        public Tag Tag  get; set; 
        public List<Scan> Scans  get; set; 
        public string Id  get; set; 

        public override string ToString()
        
            return LastName + ", " + FirstName;
        

    

它也有自己的自己的视图模型,具有DisplayDurationSinceLastScan 之类的属性,但我认为这里不相关,如果您认为相关,请告诉我。

因此,使用此代码,我得到了我的页面,但列表中似乎没有项目,如果我调试,患者充满了项目,所以它根本不是空的,但我猜绑定出了点问题,但没有报错。

Here is a picture of what I get: the listview is shown (I added a blue background so I would know if the listview was visible or not), but there are no items in there. Still Patients is filled when I debug the app.

有人看到我犯的错误吗?

【问题讨论】:

【参考方案1】:

从可维护性的角度来看,我发现您的代码存在一些问题。尝试将所有代码保留在视图模型中,而不是同时保留视图模型和代码。理想情况下,您的代码不包含任何内容,如果有的话,它与视觉事物完全相关。

无论如何,关于你的问题:你每次都在创建一个新的ObservableCollection。这打破了绑定。只需将新的ObservableCollection 保留在顶部,然后当新数据进入时,清除它并重新填充它。像这样:

public void GetAllPatients()

    IsFetchingData = true;
    try
    
        var resultPatients =         JsonConvert.DeserializeObject<ObservableCollection<PatientViewModel>>(testJson, jsonSerializerSettings);

        Patients.Clear(); 

        foreach (var patient in resultPatients)
            Patients.Add(patient);
    
    catch(Exception e)
    
        Console.WriteLine("*****ERROR kon API niet ophalen");
        Console.WriteLine(e.Message);
    
    finally
    
        IsFetchingData = false;
    

您的数据现在应该会显示出来。

【讨论】:

谢谢,这成功了!你对我背后的代码有一个很好的观点,但我试图指出我还没有完成 MVVM 的实现,但显然我不够清楚:) 所以我很清楚我的代码背后需要一些重构,无论如何谢谢指出来! 现在我知道为什么我做了新的 ObservableCollection:如果我使用 linq 然后填充列表视图并将其转换为 observablecollection 我得到“System.InvalidExcpetion:指定的转换无效”。这就是我放置 new ObservableCollection 的原因,因为它没有给出任何错误。知道如何解决这个问题吗? 我并不是想变得聪明,实际上只是指出可以改进一些事情以使您走上正轨,但很高兴听到您在这方面!让我们将您的下一个问题离线。您可以直接通过 gerald@verslu.is 与我联系吗?我们也可以说荷兰语;-)

以上是关于Xamarin Forms MvvM框架之FreshMvvM翻译一的主要内容,如果未能解决你的问题,请参考以下文章

Xamarin.forms MVVM。列表视图仍然为空

在 Xamarin.Forms 项目中实现 MVVM

Xamarin.Forms:如何避免在 MVVM 绑定中硬编码字符串

从按钮命令 xamarin.forms MVVM 获取 ListView

关于xamarin.forms在MVVM情况下如何DisplayActionSheet

在 Xamarin.Forms 中使用 MVVM 进行页面导航