如何将 MVVMCross 与 C# 的标记绑定

Posted

技术标签:

【中文标题】如何将 MVVMCross 与 C# 的标记绑定【英文标题】:How to bind MVVMCross with Markup for C# 【发布时间】:2021-07-06 05:52:21 【问题描述】:

我正在尝试使用 Xamarin 社区工具包中的 C# markup extensions 创建 Xamarin.Forms 视图,但想使用 MvvmCross 绑定视图。

我有一个 MvxContentPage 的工作解决方案,但我不确定每次设置 ViewModel 时调用 Bind 是正确的方法。还有更好的地方吗?

这是我的“工作”内容页面

[MvxTabbedPagePresentation(WrapInNavigationPage = true, Title = "Equipment")]
    public sealed class EquipmentPage : MvxContentPage<EquipmentViewModel>
    
        private ListView _listView;

        public EquipmentPage()
        
            Build();
        
        
        protected override void OnViewModelSet()
        
            base.OnViewModelSet();
            Bind();
        

        private void Build()
        
            //On<ios>().SetLargeTitleDisplay(LargeTitleDisplayMode.Always);  
            // Xamarin.Forms.DebugRainbows.DebugRainbow.SetShowColors(this, true);
            Visual = VisualMarker.Material;
            Content = EquipmentList.Assign(out _listView);
        

        private void Bind()
        
            var set = CreateBindingSet();
            set.Bind(_listView).For(v => v.IsRefreshing)
                .To(vm => vm.IsRefreshing);
            set.Bind(_listView).For(v => v.RefreshCommand)
                .To(nameof(ViewModel.LoadData));
            set.Bind(_listView).For(v => v.ItemsSource)
                .To(vm => vm.Equipment);
            set.Apply();
        
        
        private ListView EquipmentList => new ListView(ListViewCachingStrategy.RecycleElement)
        
            // RowHeight = 100,
            HasUnevenRows = true,
            IsPullToRefreshEnabled = true,
            ItemTemplate = new DataTemplate(() => new EquipmentViewCell())
        ;
    

下一步是为 ListView 绑定一个 ViewCell。我还没有弄清楚如何使这个工作。 bindingContext 必须是 IMvxViewModel 吗?

这是我当前的 ViewCell

public class EquipmentViewCell : MvxViewCell
    
        private Label _nameLabel, _modelLabel, _typeLabel;
        
        public EquipmentViewCell()
        
            Build();
        
        
        enum EquipmentRow
        
            Top,
            Bottom
        

        enum EquipmentColumn
        
            Left,
            Right
        
        
        private void Build()
        
            View = new Grid
            
                RowDefinitions = GridRowsColumns.Rows.Define(
                    (EquipmentRow.Top, GridLength.Auto),
                    (EquipmentRow.Bottom, GridLength.Auto)),

                ColumnDefinitions = GridRowsColumns.Columns.Define(
                    (EquipmentColumn.Left, GridLength.Star),
                    (EquipmentColumn.Right, GridLength.Auto)),

                Children =
                
                    new StackLayout
                    
                        Orientation = StackOrientation.Horizontal,
                        Children =
                        
                            new Label
                                
                                    LineBreakMode = LineBreakMode.TailTruncation
                                
                                .FontSize(20).Bold()
                                .StartExpand()
                                .Bind(nameof(EquipmentDto.Name))
                                .Assign(out _nameLabel),

                            new PancakeView
                                
                                    CornerRadius = new CornerRadius(8),
                                    Content = new Label
                                        
                                            TextColor = Color.White,
                                            LineBreakMode = LineBreakMode.TailTruncation
                                        .FontSize(16)
                                        .Bind(convert: (EquipmentDto x) =>
                                        
                                            var converter = new EquipmentStatusValueConverter();
                                            return converter.Convert(x, typeof(string), null, null);
                                        )
                                
                                .Padding(new Thickness(8, 2, 8, 2))
                                .End()
                                .Bind(VisualElement.BackgroundColorProperty, convert: (EquipmentDto x) =>
                                
                                    var converter = new EquipmentStatusColorValueConverter();
                                    return converter.Convert(x, typeof(Color), null, null);
                                ),
                        
                    .Row(EquipmentRow.Top).ColumnSpan(GridRowsColumns.All<EquipmentColumn>()),

                    new Label
                        
                            LineBreakMode = LineBreakMode.TailTruncation,
                            TextColor = ColorPalette.SecondaryText
                        .FontSize(16)
                        .Row(EquipmentRow.Bottom).Column(EquipmentColumn.Left)
                        .Bind(nameof(EquipmentDto.Model))
                        .Assign(out _modelLabel),

                    new Label
                        
                            LineBreakMode = LineBreakMode.TailTruncation,
                            HorizontalTextAlignment = TextAlignment.End,
                            TextColor = ColorPalette.SecondaryText
                        .FontSize(16)
                        .Row(EquipmentRow.Bottom).Column(EquipmentColumn.Right)
                        .Bind(nameof(EquipmentDto.Type.Name))
                        .Assign(out _typeLabel),
                
            .Padding(new Thickness(16, 8, 16, 8));
        

        private void Bind()
        
            var set = this.CreateBindingSet<EquipmentViewCell, EquipmentDto>();
            set.Bind(_nameLabel).To(vm => vm.Name);
            set.Bind(_typeLabel).To(vm => vm.Type.Name);
            set.Bind(_modelLabel).To(vm => vm.Type);
            set.Apply();
        
    

我是否在正确的时间绑定?如何绑定 ViewCell?

【问题讨论】:

【参考方案1】:

目前行之有效的方法。

MvxContentPage :使用OnViewModelChanged 触发Bind() 函数。

MvxViewCell :使用OnBindingContextChanged 触发Bind() 函数。

如果绑定已经完成,我还会添加一些逻辑来防止绑定。

private void Bind()

    if (_didBind) return;
    _didBind = true;

    var set = CreateBindingSet();
    ...
    set.Apply();

如果这不正确,请告诉我。

更新:这不适用于 ViewCell。项目现在出现在错误的单元格中。 :(

【讨论】:

以上是关于如何将 MVVMCross 与 C# 的标记绑定的主要内容,如果未能解决你的问题,请参考以下文章

如何使用MvvmCross将Android TextView绑定到Click事件

MvvmCross Visibility 插件不会隐藏我的 TextView

UWP MVVMCross将属性绑定到方法

02 vue 数据绑定与指令

C#中datagridview如何绑定ArrayList集合?

在 Xamarin.Android 中通过 MVVMCross 绑定 OxyPlot