BindableProperty的setter仅在xaml中使用时才被调用

rqenqsqc  于 2023-09-28  发布在  其他
关注(0)|答案(2)|浏览(101)

我正在尝试创建一个内容视图,我想在其中封装尽可能多的逻辑,以保持我的代码DRY,但我不确定我是否做错了什么,或者只是限制了可绑定属性及其访问器如何触发通知更改事件。不管怎样,这是我的例子
ContentView.xaml.cs

public static readonly BindableProperty TotalItemsProperty = BindableProperty.Create(nameof(TotalItems), typeof(int), typeof(SortingPageView),
    defaultBindingMode: BindingMode.TwoWay);

public int TotalItems
{
    get => (int)GetValue(TotalItemsProperty);
    set
    {
        SetValue(TotalItemsProperty, value);
        OnPropertyChanged(nameof(CurrentPageText));
    }
}

public string CurrentPageText { get => $" / {TotalPages}"; }

ContentView.xaml

<Grid ColumnDefinitions="50, auto, 50" Grid.Column="1" HorizontalOptions="End">
    <Image Grid.Column="0" IsVisible="{Binding CanGoBack}" Source="left_arrow.png" HeightRequest="30">
        <Image.GestureRecognizers>
            <TapGestureRecognizer Tapped="GoBack"/>
        </Image.GestureRecognizers>
    </Image>
    <HorizontalStackLayout Margin="10, 0" Grid.Column="1">
        <Entry FontSize="20" Text="{Binding CurrentPage}"/>
        <Label FontSize="20" Text="{Binding CurrentPageText}" VerticalOptions="Center"/>
    </HorizontalStackLayout>
    <Image Grid.Column="2" Source="right_arrow.png" HeightRequest="30">
        <Image.GestureRecognizers>
            <TapGestureRecognizer Tapped="GoNext"/>
        </Image.GestureRecognizers>
    </Image>
</Grid>

ViewModel.cs

public  async Task LoadJobs(bool resetCurrentPage = false)
{
    this.IsLoading = true;

    try
    {
        if(resetCurrentPage)
        {
            this.CurrentPage = 1;
        }

        var jobs = await this.jobsService.GetJobs(Filter, Pagination);

        this.InitialJobs = jobs.Items.Select(x => new JobDto(x)).ToList();
        this.TotalItems = jobs.TotalItems;   -- only this line is importent

        MainThread.BeginInvokeOnMainThread(() =>
        {
            this.Jobs.Clear();
            this.Jobs.AddRange(jobs.Items);
        });
    }
    catch(Exception ex)
    {

    }
    finally 
    { 
        this.IsLoading = false;
    }
}

请注意TotalItems属性并不显示在实际的ContentView中。xaml仅用于计算基于它的其他属性,但它的setter永远不会被调用(只有当TotalItems属性在xaml中时才有断点),所以我在这里做错了什么吗?
PS ContentView.xaml.cs构造函数

public SortingPageView()
    {
        InitializeComponent();
        Content.BindingContext = this;
    }

更新

视图模型并不是针对contentView的,而是针对我想要显示内容视图的视图。
据我所知,我需要在内容视图中定义一个BindableProperty,其中包含accessero属性,而在视图模型中,我需要传递totalItems属性。有点像vue,棱角分明

mwkjh3gx

mwkjh3gx1#

创建ContentViews时,请将其视为ContentPage中的内容。因此它应该依赖于ContentPage的ViewModel,而不是拥有自己的ViewModel或绑定到自身。我认为,如果你的ContentView需要自己的视图模型,你就在某个地方犯了一个设计错误。但这是个见仁见智的问题。
因此,请删除指向此的BindingContext,并将控件所需的所有逻辑从控件所属的View中移到ViewModel中的数据和属性上。你没有给我们一个完整的代码结构,所以我不能告诉你我认为它应该是什么。如果你想使用bindnig并指向你自己的代码,你可以使用引用,比如{Binding MessageText, Source={x:Reference ThisView}},其中ThisView是你拥有的Control的x:Name

bprjcwpo

bprjcwpo2#

好了,我认为问题要么出在视图模型中绑定的方式上,要么出在conentView.xaml中没有添加

BindingContext="{x:Reference this}"

在contentView in my case网格中的根元素上

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Name="this"
             x:Class="MedMark.Components.PagingFunctionality">

    <Grid BindingContext="{x:Reference this}" ColumnDefinitions="50, auto, 50" Grid.Column="1" HorizontalOptions="End">

您可以看到这是实际的内容视图,与我读到的应该绑定到内容视图的绑定上下文相反,我仍然这样做了,并且它按预期的ContentView.xaml.cs工作

public partial class PagingFunctionality : ContentView
{
    public PagingFunctionality()
    {
        InitializeComponent();
        Content.BindingContext = this;
    }
}

好了,现在我只是粘贴一些代码,作为其他人(或我未来的自己)的模板:D
ContentView.xaml.cs

public partial class PagingFunctionality : ContentView
{
    public PagingFunctionality()
    {
        InitializeComponent();
        Content.BindingContext = this;
    }

    public static readonly BindableProperty TotalPagesProperty = BindableProperty.Create(nameof(TotalPages), typeof(int), typeof(PagingFunctionality), 0,
        BindingMode.OneWay, propertyChanged: TotalPagesChanged);

    public static readonly BindableProperty CurrentPageProperty = BindableProperty.Create(nameof(CurrentPage), typeof(int), typeof(PagingFunctionality), 0,
        BindingMode.TwoWay, propertyChanged: CurrentPageChanged);

    private static void TotalPagesChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var view = bindable as PagingFunctionality;
        view.NotifyPagingDetails();
    }

    private static void CurrentPageChanged(BindableObject bindable, object oldValue, object newValue)
    {
        var view = bindable as PagingFunctionality;
        view.NotifyPagingDetails();
    }

    public int TotalPages 
    { 
        get => (int)GetValue(TotalPagesProperty);
        set => SetValue(TotalPagesProperty, value);
    }

    public int CurrentPage
    {
        get => (int)GetValue(CurrentPageProperty);
        set => SetValue(CurrentPageProperty, value);
    }

    public bool CanGoBack { get => this.CurrentPage > 1; }
    
    public bool CanGoNext { get => this.CurrentPage < this.TotalPages; }

    private void NotifyPagingDetails()
    {
        OnPropertyChanged(nameof(CanGoNext));
        OnPropertyChanged(nameof(CanGoBack));
    }

    private void GoBack(object sender, TappedEventArgs e)
    {
        if (CanGoBack)
        {
            this.CurrentPage--;
        }
    }

    private void GoNext(object sender, TappedEventArgs e)
    {
        if (CanGoNext)
        {
            this.CurrentPage++;
        }
    }
}

ContentView.xaml

<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Name="this"
             x:Class="MedMark.Components.PagingFunctionality">

    <Grid BindingContext="{x:Reference this}" ColumnDefinitions="50, auto, 50" Grid.Column="1" HorizontalOptions="End">
        <Image 
            Grid.Column="0"
            IsVisible="{Binding CanGoBack}" 
            Source="left_arrow.png" HeightRequest="30">
            <Image.GestureRecognizers>
                <TapGestureRecognizer Tapped="GoBack"/>
            </Image.GestureRecognizers>
        </Image>
        <HorizontalStackLayout Margin="10, 0" Grid.Column="1">
            <Entry x:Name="CurrentPageLabel" Text="{Binding CurrentPage}" FontSize="20"/>
            <Label x:Name="TotalPageLabel" Text="{Binding TotalPages, StringFormat=' / {0}'}" FontSize="20" VerticalOptions="Center"/>
        </HorizontalStackLayout>
        <Image 
            Grid.Column="2" 
            IsVisible="{Binding CanGoNext}"
            Source="right_arrow.png" HeightRequest="30">
            <Image.GestureRecognizers>
                <TapGestureRecognizer Tapped="GoNext"/>
            </Image.GestureRecognizers>
        </Image>
    </Grid>
</ContentView>

JobsView.xaml

<components:PagingFunctionality
                Grid.Column="1"
                TotalPages="{Binding TotalPages}"
                CurrentPage="{Binding CurrentPage}"/>

JobsViewModel.cs

[ObservableProperty]
    [NotifyPropertyChangedFor(nameof(TotalPages))]
    private int totalItems;

    [ObservableProperty]
    private int currentPage = 1;

    public int TotalPages { get => this.TotalItems % this.Pagination.PageSize == 0 ? this.TotalItems / this.Pagination.PageSize : this.TotalItems / this.Pagination.PageSize + 1; }

    partial void OnCurrentPageChanged(int oldValue, int newValue)
    {
        this.Pagination.CurrentPageNo = newValue;

        Task.Run(async () => await LoadJobs(false));
    }

由于ContentView.xaml.cs中的CurrentPageProperty具有BindingMode = TwoWay,因此在viewModel中,我侦听CurrentPage中的更改以加载相应的项。
请注意,转到下一页的逻辑在ContentView中通过属性CanGoNextCanGoBack以及方法(绑定到点击事件)GoNextGoBack进行验证和触发

相关问题