Xamarin.Forms:是否可以在没有ViewModel调用VIew的情况下动态地向行添加行?

时间:2017-12-08 00:39:08

标签: mvvm xamarin.forms

在我向用户显示收据的页面上,我有一个列出小计,任何税金和总额的部分。由于适用的税费因地区而异,并且根据所售产品而定,因此本节中的行数会有所不同。例如,这里有3个单独的订单,一个包含GST和PST,一个只有GST,另一个没有:

GST+PST

GST Only

No Tax

我通过在XAML中将SubTotal放在网格中,并在我从ViewModel调用的方法中将其余部分添加到代码隐藏中来完成此操作。但是,我真的很想避免这样做,所以我想知道是否有一种方法可以实现这一点,并不需要让ViewModel知道View。

由于多种原因,ListView不适用于此:

  • 这些控件位于ScrollView内,ListView内有ScrollView会导致各种奇怪的问题。
  • 我想让列尽可能地缩小它们最宽的元素。这可以使用Grid,但ListView会占用其父级的整个宽度,无论如何。
  • 我既不需要也不希望我的行可以选择

因此,如果没有ViewModel知道View 而不诉诸ListView,我可以做到这一点吗?

2 个答案:

答案 0 :(得分:1)

封装所需功能以便视图和视图模型不耦合的一种方法是创建用户控件。

我创建了一个名为TotalsGridControl的新用户控件。这是XAML

<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="ScratchPad.UserControls.TotalsGridControl"
             x:Name="TotalsGrid">

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

</Grid>

这是背后的代码。

public partial class TotalsGridControl : Grid
{
    public TotalsGridControl()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty TotalsProperty =
        BindableProperty.Create(nameof(Totals), typeof(List<TotalItem>), typeof(TotalsGridControl), null,
            BindingMode.OneWay, null, OnTotalsChanged);

    private static void OnTotalsChanged(BindableObject bindable, object oldvalue, object newvalue)
    {
        var control = (TotalsGridControl)bindable;
        if (control != null)
        {
            if (newvalue is List<TotalItem> totals)
            {
                var rowNumber = -1;
                double grandTotal = 0;

                foreach (var totalItem in totals)
                {
                    grandTotal += totalItem.Value;

                    var descLabel = new Label {Text = totalItem.Description};
                    var valueLabel = new Label { Text = totalItem.Value.ToString("c") };

                    rowNumber++;
                    control.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto});
                    control.Children.Add(descLabel, 0, rowNumber);
                    control.Children.Add(valueLabel, 1, rowNumber);
                }

                var grandTotalDescLabel = new Label { Text = "Total" };
                var grandTotalValueLabel = new Label { Text = grandTotal.ToString("c") };

                rowNumber++;
                control.RowDefinitions.Add(new RowDefinition { Height = GridLength.Auto });
                control.Children.Add(grandTotalDescLabel, 0, rowNumber);
                control.Children.Add(grandTotalValueLabel, 1, rowNumber);
            }
        }
    }

    public List<TotalItem> Totals
    {
        get => (List<TotalItem>)GetValue(TotalsProperty);
        set => SetValue(TotalsProperty, value);
    }
}

我使用了一个可绑定属性来允许将TotalItem列表绑定到用户控件。

以下是视图模型中的数据

public List<TotalItem> Totals { get; set; }

Totals = new List<TotalItem>
{
    new TotalItem {Description = "SubTotal", Value = 99.91},
    new TotalItem {Description = "GST", Value = 5.0},
    new TotalItem {Description = "PST", Value = 4.9}
};

,这是页面中的XAML

<userControls:TotalsGridControl Totals="{Binding Totals}"/>

输出

enter image description here

答案 1 :(得分:0)

在:

public class ReceiptPageModel : PageModelBase
{
    private Receipt _receipt;
    public Receipt Receipt
    {
        get => _receipt;
        private set => Set(ref _receipt, value);
    }

    public override void Init(object initData)
    {
        Receipt = (Receipt) initData;
        ((ReceiptPage) CurrentPage).AddTaxes(Receipt);
    }
}

后:

public class ReceiptPageModel : PageModelBase
{
    private Receipt _receipt;
    public Receipt Receipt
    {
        get => _receipt;
        private set => Set(ref _receipt, value);
    }

    public override void Init(object initData)
    {
        Receipt = (Receipt) initData;
    }
}

public partial class ReceiptPage : FreshBaseContentPage
{
    public ReceiptPage()
    {
        InitializeComponent();
        BindingContextChanged += HandlePageModelAdded;
    }

    private void HandlePageModelAdded(object sender, EventArgs e)
    {
        var pageModel = (ReceiptPageModel)BindingContext;
        if (pageModel.Receipt != null)
        {
            AddTaxes(pageModel.Receipt);
        }
        else
        {
            pageModel.PropertyChanged += (s, args) =>
            {
                if (args.PropertyName == nameof(pageModel.Receipt))
                    AddTaxes(pageModel.Receipt);
            };
        }
    }

    private void AddTaxes(Receipt receipt)
    {
        ...
    }
}