Mvvm Forms:是否可以在不调用视图模型的情况下,将行动态添加到网格中?

Mvvm Forms:是否可以在不调用视图模型的情况下,将行动态添加到网格中?,mvvm,xamarin.forms,Mvvm,Xamarin.forms,在向用户显示收据的页面上,我有一个部分列出了小计、所有税费和总额。由于适用的税收因地区和销售的产品而异,因此本节中的行数将有所不同。例如,这里有3个单独的订单,一个包含GST和PST,一个仅包含GST,另一个不包含GST: 我通过将小计放在XAML中的网格中,并将其余的代码添加到从ViewModel调用的方法中来实现这一点。但是,我确实希望避免这样做,因此我想知道是否有一种方法可以实现这一点,而不需要让ViewModel了解视图 列表视图不适用于此,原因如下: 这些控件位于滚动视图中,在

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

我通过将小计放在XAML中的网格中,并将其余的代码添加到从ViewModel调用的方法中来实现这一点。但是,我确实希望避免这样做,因此我想知道是否有一种方法可以实现这一点,而不需要让ViewModel了解视图

列表视图
不适用于此,原因如下:

  • 这些控件位于
    滚动视图
    中,在
    滚动视图
    中具有
    列表视图
    会导致各种奇怪的问题
  • 我想让柱子尽可能的窄,尽可能的宽。这在
    网格
    中是可能的,但是
    列表视图
    将占据其父视图的整个宽度
  • 我既不需要也不希望我的行是可选的
那么,有没有一种方法可以在ViewModel不知道视图的情况下执行此操作,而不必使用
ListView

之前:

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)
    {
        ...
    }
}
之前:

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)
    {
        ...
    }
}

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

我创建了一个名为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);
    }
}
公共部分类TotalsGridControl:网格
{
公共TotalsGridControl()
{
初始化组件();
}
公共静态只读BindableProperty TotalsProperty=
创建(nameof(Totals)、typeof(List)、typeof(TotalsGridControl)、null、,
BindingMode.OneWay,null,OnTotalsChanged);
私有静态void OnTotalsChanged(BindableObject bindable、object oldvalue、object newvalue)
{
var-control=(TotalsGridControl)可绑定;
if(控件!=null)
{
如果(新值为列表总计)
{
var rowNumber=-1;
总双倍=0;
foreach(总计中的var totalItem)
{
grandTotal+=totalItem.Value;
var descLabel=新标签{Text=totalItem.Description};
var valueLabel=new Label{Text=totalItem.Value.ToString(“c”)};
行数++;
添加(新的行定义{Height=GridLength.Auto});
控件.Children.Add(descLabel,0,rowNumber);
控件.Children.Add(valueLabel,1,rowNumber);
}
var grandTotalDescLabel=新标签{Text=“Total”};
var grandTotalValueLabel=新标签{Text=grandTotal.ToString(“c”)};
行数++;
添加(新的行定义{Height=GridLength.Auto});
控件.Children.Add(grandTotalDescLabel,0,rowNumber);
control.Children.Add(grandTotalValueLabel,1,行编号);
}
}
}
公开名单总数
{
get=>(列表)GetValue(TotalsProperty);
set=>SetValue(TotalsProperty,value);
}
}
我使用了一个bindable属性来允许将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}
};
公共列表总计{get;set;}
总计=新列表
{
新的TotalItem{Description=“SubTotal”,Value=99.91},
新TotalItem{Description=“GST”,Value=5.0},
新的TotalItem{Description=“PST”,Value=4.9}
};
这是页面中的XAML

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

以及输出


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

我创建了一个名为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);
    }
}
公共部分类TotalsGridControl:网格
{
公共TotalsGridControl()
{
初始化组件();
}
公共静态只读BindableProperty TotalsProperty=
创建(nameof(Totals)、typeof(List)、typeof(TotalsGridControl)、null、,
BindingMode.OneWay,null,OnTotalsChanged);
私有静态void OnTotalsChanged(BindableObject bindable、object oldvalue、object newvalue)
{
var-control=(TotalsGridControl)可绑定;
if(控件!=null)
{
如果(新值为列表总计)
{
var rowNumber=-1;
总双倍=0;
foreach(总计中的var totalItem)
{
grandTotal+=totalItem.Value;
var descLabel=新标签{Text=totalItem.Description};
var valueLabel=new Label{Text=totalItem.Value.ToString(“c”)};
行数++;
添加(新的行定义{Height=GridLength.Auto});
控件.Children.Add(descLabel,0,rowNumber);
控件.Children.Add(valueLabel,1,rowNumber);
}
var grandTotalDescLabel=新标签{Text=“Total”};
var grandTotalValueLabel=新标签{Text=grandTotal.ToString(“c”)};
行数++;
添加(新的行定义{Height=GridLength.Auto});
控制中心,Childre