ViewModel 是否应该继承 Xamarin.Forms 中的其他 ViewModel?
Should ViewModels inherit other ViewModels in Xamarin.Forms?
是否应该ViewModels
继承其他ViewModels
?
我有一个 MerchandiserViewModel
,其中包含 Merchandiser
模型的基本属性和数据库功能。
MerchandiserViewModel
有一个 SelectedMerchandiser
属性 保存从 ItemSelected
中选择的 Merchandiser
在 ListView
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...
}
MerchandiserViewModel
在 App.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
与跟单员相关,例如MerchandiserProfile
、EditProfile
等。我创建了一个新的 ViewModel
并继承了 MerchandiserViewModel
MerchandiserProfileViewModel.cs继承了MerchandiserViewModel
public class MerchandiserProfileViewModel : MerchandiserViewModel
{
// Logic Specific to the Merchandiser Profile View
}
问题是...当我创建一个新的 [Page]ViewModel
并继承“MerchandiserViewModel”时,我收到以下错误消息。
我认为这可能是因为创建了 MerchandiserViewModel
的新实例,所以我没有引用初始 SelectedMerchandiser
属性.
这让我觉得继承 ViewModel 不是一个好主意?
这种情况一般是怎么处理的?我是否应该将每个 page/view 的所有逻辑都塞进 MerchandiserViewModel
中?我希望我的代码尽可能干净和分离,因此希望尽可能避免这种情况。
事后思考
我可以在 C# 的静态资源中访问 MerchandiserViewModel
的属性吗?这样我就可以在不继承 MerchandiserViewModel
的情况下将所需的属性传递给新的 ViewModel ... 想听听对此的想法吗?
The MerchandiserViewModel has a SelectedMerchandiser property that holds the selected Merchandiser from the ItemSelected in a ListView
根据你的描述,你要绑定的是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>
模型 class,包含一些绑定到 UI 的属性。
public class Merchandiser
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
MerchandiserViewmodel class,视图模型实现视图可以数据绑定到的属性和命令,并通过更改通知事件通知视图任何状态更改。视图模型提供的属性和命令定义了 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是class实现INotifyPropertyChanged接口,通知数据改变。对于 MerchandiserView 模型,Selected Merchandiser(selecteditem) 必须实现 INotifyPropertyChanged 以在您每次从 ListView select编辑项目时通知数据更改。
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();
}
}
更新:
如果你想在selectListView item时导航到详细页面,你可以在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;
}
}
是否应该ViewModels
继承其他ViewModels
?
我有一个 MerchandiserViewModel
,其中包含 Merchandiser
模型的基本属性和数据库功能。
MerchandiserViewModel
有一个 SelectedMerchandiser
属性 保存从 ItemSelected
中选择的 Merchandiser
在 ListView
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...
}
MerchandiserViewModel
在 App.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
与跟单员相关,例如MerchandiserProfile
、EditProfile
等。我创建了一个新的 ViewModel
并继承了 MerchandiserViewModel
MerchandiserProfileViewModel.cs继承了MerchandiserViewModel
public class MerchandiserProfileViewModel : MerchandiserViewModel
{
// Logic Specific to the Merchandiser Profile View
}
问题是...当我创建一个新的 [Page]ViewModel
并继承“MerchandiserViewModel”时,我收到以下错误消息。
我认为这可能是因为创建了 MerchandiserViewModel
的新实例,所以我没有引用初始 SelectedMerchandiser
属性.
这让我觉得继承 ViewModel 不是一个好主意?
这种情况一般是怎么处理的?我是否应该将每个 page/view 的所有逻辑都塞进 MerchandiserViewModel
中?我希望我的代码尽可能干净和分离,因此希望尽可能避免这种情况。
事后思考
我可以在 C# 的静态资源中访问 MerchandiserViewModel
的属性吗?这样我就可以在不继承 MerchandiserViewModel
的情况下将所需的属性传递给新的 ViewModel ... 想听听对此的想法吗?
The MerchandiserViewModel has a SelectedMerchandiser property that holds the selected Merchandiser from the ItemSelected in a ListView
根据你的描述,你要绑定的是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>
模型 class,包含一些绑定到 UI 的属性。
public class Merchandiser
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
MerchandiserViewmodel class,视图模型实现视图可以数据绑定到的属性和命令,并通过更改通知事件通知视图任何状态更改。视图模型提供的属性和命令定义了 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是class实现INotifyPropertyChanged接口,通知数据改变。对于 MerchandiserView 模型,Selected Merchandiser(selecteditem) 必须实现 INotifyPropertyChanged 以在您每次从 ListView select编辑项目时通知数据更改。
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();
}
}
更新:
如果你想在selectListView item时导航到详细页面,你可以在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;
}
}