英文:
Separate layout of ContentPage to multiple ContentViews
问题
HomePage.xaml.cs:
public HomePage(IHomeViewModel homeViewModel)
{
this.InitializeComponent();
this.BindingContext = homeViewModel;
this.HomeViewModel = homeViewModel;
}
HomePage.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Pages.HomePage"
xmlns:controls="clr-namespace:MyApp.Controls;assembly=MyApp"
x:DataType="viewModels:HomeViewModel"
Title="">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:MyControl Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" BindingContext="{Binding}" />
</Grid>
</ContentPage>
MyControl.xaml:
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:MyApp.ViewModels"
x:DataType="viewModels:HomeViewModel"
xmlns:models="clr-namespace:MyApp.Models"
x:Name="this"
x:Class="MyApp.Controls.MyControl">
<ContentView.BindingContext>
<x:Reference Name="this" />
</ContentView.BindingContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Frame Opacity="0" BackgroundColor="{Binding ThemeColor}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Text="Title" FontSize="14" Grid.Row="0"></Label>
<ListView ItemsSource="{Binding Data}" Grid.Row="1" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:DataModel">
<ViewCell>
<Grid Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding Subject}"/>
<Label Grid.Column="1" Text="{Binding Number}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Frame>
</Grid>
</ContentView>
HomeViewModel.cs:
private ObservableCollection<DataModel> data;
public ObservableCollection<DataModel> Data
{
get => this.data;
set
{
this.data = value;
this.OnPropertyChanged();
}
}
DataModel.cs:
public class DataModel
{
public string Subject { get; set; }
public string Number { get; set; }
}
英文:
I was experimenting with NET MAUI and got to the point where my layout of HomePage became too long, so I would like to split layout to several controls. I have registered ViewModel as Interface using Dependency Injection.
The problem is that currently it looks like my HomeViewModel is not passed to MyControl as I am seeing any content specified in MyControl in my HomePage. I would like to pass HomeViewModel from HomePage to MyControl. How it should be done correctly?
HomePage.xaml.cs:
public HomePage(IHomeViewModel homeViewModel)
{
this.InitializeComponent();
this.BindingContext = homeViewModel;
this.HomeViewModel = homeViewModel;
}
HomePage.xaml:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Pages.HomePage"
xmlns:controls="clr-namespace:MyApp.Controls;assembly=MyApp"
x:DataType="viewModels:HomeViewModel"
Title="">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<controls:MyControl Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" BindingContext="{Binding}" />
</Grid>
</ContentPage>
MyControl.xaml:
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModels="clr-namespace:MyApp.ViewModels"
x:DataType="viewModels:HomeViewModel"
xmlns:models="clr-namespace:MyApp.Models"
x:Name="this"
x:Class="MyApp.Controls.MyControl">
<ContentView.BindingContext>
<x:Reference Name="this" />
</ContentView.BindingContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Frame Opacity="0" BackgroundColor="{Binding ThemeColor}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Text="Title" FontSize="14" Grid.Row="0"></Label>
<ListView ItemsSource="{Binding Data}" Grid.Row="1" SelectedItem="{Binding SelectedItem}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:DataModel">
<ViewCell>
<Grid Padding="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Text="{Binding Subject}"/>
<Label Grid.Column="1" Text="{Binding Number}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Frame>
</Grid>
</ContentView>
HomeViewModel.cs:
private ObservableCollection<DataModel> data;
public ObservableCollection<DataModel> Data
{
get => this.data;
set
{
this.data = value;
this.OnPropertyChanged();
}
}
DataModel.cs:
public class DataModel
{
public string Subject { get; set; }
public string Number { get; set; }
}
答案1
得分: 1
移除:
<ContentView.BindingContext>
<x:Reference Name="this" />
</ContentView.BindingContext>
然后它将继承其父级(包含元素)的BindingContext,即页面。
英文:
Remove:
<ContentView.BindingContext>
<x:Reference Name="this" />
</ContentView.BindingContext>
Then it will inherit BindingContext from its parent (containing element), which is the page.
Correct Initialization of Data
All DataModel
instances must set their Subject
and Number
BEFORE they are made visible to XAML.
REASON: DataModel
does not implement INotifyPropertyChanged
on its properties; property changes are NOT automatically "recursive".
3 possible ways to fix:
FIX #1:
// Prepare all data BEFORE setting `Data`:
var data = new ObservableCollection<DataModel>();
foreach (...)
{
var model = new DataModel();
// Can set Subject and Number here.
model.Subject = ...;
model.Number = ...;
// Because model is not yet visible in `Data`, we can do `data.Add`
// EITHER before or after setting `Subject` and/or `Number`.
data.Add(model);
// Or set them here.
model.Subject = ...;
model.Number = ...;
}
// When all data is fully prepared:
Data = data; // Now XAML sees the prepared data.
OR FIX #2:
// Prepare each model BEFORE adding it to `Data`:
Data = new ObservableCollection<DataModel>();
foreach (...)
{
var model = new DataModel(){ Subject = ..., Number = ... };
// IMPORTANT: Subject and Number MUST be set BEFORE `Data.Add(model)`!
Data.Add(model);
}
// When all data is fully prepared:
Data = data; // Now XAML sees the prepared data.
OR FIX #3:
class DataModel : ObservableObject
{
[ObservableProperty] // Compiler generates public property "Subject", with property change notification.
private string subject;
[ObservableProperty] // Compiler generate "Number" ...
private string number;
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论