【问题标题】:Should ViewModels inherit other ViewModels in Xamarin.Forms?ViewModel 是否应该继承 Xamarin.Forms 中的其他 ViewModel?
【发布时间】:2021-04-19 04:07:02
【问题描述】:

ViewModels应该继承其他ViewModels吗?

我有一个MerchandiserViewModel,其中包含Merchandiser 模型的基本属性和数据库函数。

MerchandiserViewModel 有一个 SelectedMerchandiser 属性,该属性在 ListView 中保存从 ItemSelected 中选择的 Merchandiser

MerchandiserViewModel.cs

public MerchandiserViewModel : INotifyPropertyChanged
{
    // Property to hold the selected Merchandiser
    // Generally I would make this static but then I can't bind the property
    public Merchandiser SelectedMerchandiser {get; set;}

    // Other logic...

}

MerchandiserViewModelApp.xaml 中被实例化为Static Resource,因此我只有一个视图模型实例。

App.xaml

<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MobileApp.App"
             xmlns:ViewModels="clr-namespace:MobileApp.ViewModels">
    <Application.Resources>
        <ViewModels:MerchandiserViewModel x:Key="MerchandiserViewModel" />
        <ViewModels:MerchandiserProfileViewModel x:Key="MerchandiserProfileViewModel" />
    </Application.Resources>
</Application>

对于与跟单员相关的每个View,例如MerchandiserProfileEditProfile 等。我创建了一个新的ViewModel 并继承了MerchandiserViewModel

MerchandiserProfileViewModel.cs 继承 MerchandiserViewModel

public class MerchandiserProfileViewModel : MerchandiserViewModel
{
    // Logic Specific to the Merchandiser Profile View
}

问题是...当我创建一个新的[Page]ViewModel 并继承“MerchandiserViewModel”时,我收到以下错误消息。

我认为这可能是因为创建了 MerchandiserViewModel 的新实例,所以我没有引用初始的 SelectedMerchandiser 属性。

这让我觉得继承 ViewModel 不是个好主意?

这种情况通常如何处理?我是否应该将每个页面/视图的所有逻辑都塞到MerchandiserViewModel 中?我希望我的代码尽可能干净且独立,因此尽可能避免这种情况。

三思而后行 我可以在 C# 的静态资源中访问MerchandiserViewModel 的属性吗?这样我就可以将所需的属性传递给新的 ViewModel 而无需继承 MerchandiserViewModel ... 想听听对此的想法吗?

【问题讨论】:

  • 我试过MerchandiserViewModel merchandiserVM = (MerchandiserViewModel)Application.Current.Resources["MerchandiserViewModel"];,但我似乎无法以这种方式访问​​属性。
  • stackoverflow.com/questions/67156588/… 上发布的有关访问静态资源属性的问题的回复。你没有,至少在 MVVM 中没有。 MVVM 将视图及其数据(视图模型)解耦。您要实现的目标违反了 MVVM 原则。这似乎是一个 XY 问题——MindSwipe
  • XY问题链接xyproblem.info

标签: c# xamarin xamarin.forms mvvm viewmodel


【解决方案1】:

MerchandiserViewModel 有一个 SelectedMerchandiser 属性,该属性保存从 ListView 中的 ItemSelected 中选择的 Merchandiser

根据你的描述,你要绑定ListView,对于MerchandiserViewModel,不需要继承其他ViewModel,建议你看看the Model-View-ViewModel Pattern

我做了一个使用MVVM绑定ListView的示例,请看一下。

<StackLayout>
        <ListView
            x:Name="listview1"
            HasUnevenRows="True"
            ItemsSource="{Binding mers}"
            SelectedItem="{Binding selecteditem}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label
                                FontSize="Large"
                                Text="{Binding Name}"
                                VerticalOptions="Center" />

                            <Label
                                FontSize="Small"
                                Text="{Binding PhoneNumber}"
                                VerticalOptions="Center" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>

模型类,包含一些绑定到 UI 的属性。

 public class Merchandiser
{
    public string Name { get; set; }    
    public string PhoneNumber { get; set; }
    
}

MerchandiserViewmodel 类,视图模型实现视图可以数据绑定到的属性和命令,并通过更改通知事件通知视图任何状态更改。视图模型提供的属性和命令定义了 UI 提供的功能,但视图决定了该功能的显示方式。

 public class MerchandiserViewmodel:ViewModelBase
{
    public ObservableCollection<Merchandiser> mers { get; set; }
    private Merchandiser _selecteditem;
    public Merchandiser selecteditem
    {
        get { return _selecteditem; }
        set
        {
            _selecteditem = value;
            RaisePropertyChanged("selecteditem");
        }
    }
    public MerchandiserViewmodel()
    {
        mers = new ObservableCollection<Merchandiser>();
        getdata();
    }
    private void getdata()
    {
        for(int i=0;i<20;i++)
        {
            Merchandiser mer = new Merchandiser();
            mer.Name = "merchandiser "+i;
            mer.PhoneNumber = "123";
            mers.Add(mer);

        }
    }
}

ViewModelBase 是实现inf INotifyPropertyChanged 接口的类,通知数据发生变化。对于 MerchandiserViewmodel,Selected Merchandiser(selecteditem) 必须实现 INotifyPropertyChanged 以在您每次从 ListView 中选择商品时通知数据更改。

public class ViewModelBase : INotifyPropertyChanged
{
   
    public event PropertyChangedEventHandler PropertyChanged;

   
    public void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

将 ViewModel 绑定到 ContentPage

 public partial class Page10 : ContentPage
{
    public Page10()
    {
        InitializeComponent();
        this.BindingContext = new MerchandiserViewmodel();
    }
}

更新:

如果您想在选择ListView项目时导航到详细页面,您可以在ListView_ItemSelected事件中使用构造函数传值。

<StackLayout>
        <ListView
            x:Name="listview1" ItemSelected="listview1_ItemSelected" SelectionMode="Single"
            HasUnevenRows="True"
            ItemsSource="{Binding mers}"
            SelectedItem="{Binding selecteditem}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout>
                            <Label
                                FontSize="Large"
                                Text="{Binding Name}"
                                VerticalOptions="Center" />

                            <Label
                                FontSize="Small"
                                Text="{Binding PhoneNumber}"
                                VerticalOptions="Center" />
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>

  private async void listview1_ItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        Merchandiser item = (Merchandiser)e.SelectedItem;
       await Navigation.PushAsync(new simplecontrol.Page29(item));
    }

详细页面:

<ContentPage.Content>
    <StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="Name: " />
            <Label
                FontSize="Large"
                Text="{Binding Name}"
                VerticalOptions="Center" />
        </StackLayout>
        <StackLayout Orientation="Horizontal">
            <Label Text="PhoneNumber: " />
            <Label
                FontSize="Small"
                Text="{Binding PhoneNumber}"
                VerticalOptions="Center" />
        </StackLayout>


    </StackLayout>
</ContentPage.Content>

public partial class Page29 : ContentPage
{
    public Page29(Merchandiser mer)
    {
        InitializeComponent();
        this.BindingContext = mer;
    }
}

【讨论】:

  • 感谢 Cherry,当我点击 ListView SelectedItem 时,我想导航到新视图 (ContentPage) 并加载选定的跟单员详细信息。如何从新的ContentPage 访问ListView SelectedItem?如果我绑定MerchandiserViewModel 的新实例,selecteditem 属性将重置为null
  • 感谢 Cherry,这太棒了。我遇到了另一个问题。我正在尝试添加一个新的MerchandiserEditPage 来编辑SelectedItem。本质上,它将具有与DetailedPage 相同的结构,但用Entry 字段代替Labels 并包含Save ButtonMerchandiserEditPage 应该可以从“DetailedPage”访问。理想情况下,Save Button 应该在 MerchandiserViewModel 中调用 Save Command,因为它将访问数据库。我怎么能做到这一点?
  • Cherry,因为我上面的评论引入了一个新问题,我创建了一个新帖子,可以在这里查看stackoverflow.com/questions/67169884/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-24
  • 2011-04-09
  • 2011-09-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-10
相关资源
最近更新 更多