将ContentPage的布局分离为多个ContentViews。

huangapple go评论78阅读模式
英文:

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:

&lt;ContentPage xmlns=&quot;http://schemas.microsoft.com/dotnet/2021/maui&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             x:Class=&quot;MyApp.Pages.HomePage&quot;
             xmlns:controls=&quot;clr-namespace:MyApp.Controls;assembly=MyApp&quot;
             x:DataType=&quot;viewModels:HomeViewModel&quot;
             Title=&quot;&quot;&gt;

  &lt;Grid&gt;
    &lt;Grid.RowDefinitions&gt;
      &lt;RowDefinition Height=&quot;*&quot; /&gt;
      &lt;RowDefinition Height=&quot;*&quot; /&gt;
    &lt;/Grid.RowDefinitions&gt;
    &lt;Grid.ColumnDefinitions&gt;
      &lt;ColumnDefinition Width=&quot;*&quot; /&gt;
      &lt;ColumnDefinition Width=&quot;*&quot; /&gt;
    &lt;/Grid.ColumnDefinitions&gt;

    &lt;controls:MyControl Grid.Row=&quot;0&quot; Grid.Column=&quot;0&quot; Grid.ColumnSpan=&quot;2&quot; BindingContext=&quot;{Binding}&quot; /&gt;

  &lt;/Grid&gt;


&lt;/ContentPage&gt;

MyControl.xaml:

&lt;ContentView xmlns=&quot;http://schemas.microsoft.com/dotnet/2021/maui&quot;
             xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
             xmlns:viewModels=&quot;clr-namespace:MyApp.ViewModels&quot;
             x:DataType=&quot;viewModels:HomeViewModel&quot;
             xmlns:models=&quot;clr-namespace:MyApp.Models&quot;
             x:Name=&quot;this&quot;
             x:Class=&quot;MyApp.Controls.MyControl&quot;&gt;

  &lt;ContentView.BindingContext&gt;
    &lt;x:Reference Name=&quot;this&quot; /&gt;
  &lt;/ContentView.BindingContext&gt;

  &lt;Grid&gt;
    &lt;Grid.ColumnDefinitions&gt;
      &lt;ColumnDefinition Width=&quot;*&quot;/&gt;
      &lt;ColumnDefinition Width=&quot;*&quot;/&gt;
    &lt;/Grid.ColumnDefinitions&gt;

      &lt;Frame Opacity=&quot;0&quot; BackgroundColor=&quot;{Binding ThemeColor}&quot;&gt;
        &lt;Grid&gt;
          &lt;Grid.RowDefinitions&gt;
            &lt;RowDefinition Height=&quot;30&quot;/&gt;
            &lt;RowDefinition Height=&quot;*&quot;/&gt;
          &lt;/Grid.RowDefinitions&gt;
          &lt;Label Text=&quot;Title&quot; FontSize=&quot;14&quot; Grid.Row=&quot;0&quot;&gt;&lt;/Label&gt;
          &lt;ListView ItemsSource=&quot;{Binding Data}&quot; Grid.Row=&quot;1&quot; SelectedItem=&quot;{Binding SelectedItem}&quot;&gt;
            &lt;ListView.ItemTemplate&gt;
              &lt;DataTemplate x:DataType=&quot;models:DataModel&quot;&gt;
                &lt;ViewCell&gt;
                  &lt;Grid Padding=&quot;5&quot;&gt;
                    &lt;Grid.ColumnDefinitions&gt;
                      &lt;ColumnDefinition Width=&quot;*&quot;/&gt;
                      &lt;ColumnDefinition Width=&quot;Auto&quot;/&gt;
                    &lt;/Grid.ColumnDefinitions&gt;
                    &lt;Label Grid.Column=&quot;0&quot; Text=&quot;{Binding Subject}&quot;/&gt;
                    &lt;Label Grid.Column=&quot;1&quot; Text=&quot;{Binding Number}&quot;/&gt;
                  &lt;/Grid&gt;
                &lt;/ViewCell&gt;
              &lt;/DataTemplate&gt;
            &lt;/ListView.ItemTemplate&gt;
          &lt;/ListView&gt;
        &lt;/Grid&gt;
      &lt;/Frame&gt;
  &lt;/Grid&gt;

&lt;/ContentView&gt;

HomeViewModel.cs:

private ObservableCollection&lt;DataModel&gt; data;
public ObservableCollection&lt;DataModel&gt; Data
{
  get =&gt; 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

移除:

&lt;ContentView.BindingContext&gt;
    &lt;x:Reference Name=&quot;this&quot; /&gt;
&lt;/ContentView.BindingContext&gt;

然后它将继承其父级(包含元素)的BindingContext,即页面。

英文:

Remove:

&lt;ContentView.BindingContext&gt;
    &lt;x:Reference Name=&quot;this&quot; /&gt;
&lt;/ContentView.BindingContext&gt;

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&lt;DataModel&gt;();
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&lt;DataModel&gt;();
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 &quot;Subject&quot;, with property change notification.
    private string subject;
    [ObservableProperty]   // Compiler generate &quot;Number&quot; ...
    private string number;
}

huangapple
  • 本文由 发表于 2023年6月15日 05:36:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76477711.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定