【问题标题】:Xamarin Forms - Multiple items in BindingContext in ViewCellXamarin Forms - ViewCell 中 BindingContext 中的多个项目
【发布时间】:2020-07-24 17:20:07
【问题描述】:

我在 xaml 中有一个“主”页面,其中包含我的 cellview。

<ContentPage.Content>
    <ListView 
        Margin="0,15,0,0"
        SelectionMode="None"
        RowHeight= "150"
        ItemsSource="{Binding ObjectItems}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <viewcells:ObjectItemViewCell/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage.Content>

在我的 CellView 中,我想添加 2 个转换器作为 BindableContext 并将它们集成到我的一些字段中:

<<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:local="clr-namespace:OperaMobile.Views.Postlogin" 
             xmlns:converterFontFamily="clr-namespace:OperaMobile.Converters"
             xmlns:converterColor="clr-namespace:OperaMobile.Converters"
             mc:Ignorable="d"
             x:Class="OperaMobile.ViewCells.ObjectItemViewCell">
    <ViewCell.BindingContext>
        <converterFontFamily:BoolToStringConverter x:Key="fontFamilyConverter"/>
        <!--<converterColor:BoolToStringConverter x:Key="fontFamilyConverter"/>-->
    </ViewCell.BindingContext>
    <ViewCell.View>
        <StackLayout>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="80*"/>
                    <ColumnDefinition Width="50*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>
                <StackLayout 
                    Padding="0,10" 
                    Grid.Column="0" 
                    Grid.Row="0" 
                    Margin="15,0" 
                    VerticalOptions="Center" 
                    Orientation="Horizontal">
                    <Image Source="pin"/>
                    <Label FontAttributes="Bold" Text="{Binding Label}" Grid.Column="0" Grid.Row="0"/>
                </StackLayout>
                <StackLayout 
                    Grid.Column="0" 
                    Grid.Row="1" 
                    BindableLayout.ItemsSource="{Binding InfoBox.CountDetailsItemsRows}"
                    Orientation="Horizontal"
                    Margin="13,10,0,0">
                    <BindableLayout.ItemTemplate>
                        <DataTemplate>
                            <StackLayout>
                                <Label FontAttributes="Bold" Text="{Binding BoldLabelTitle}"/>
                                <Label Text="{Binding LabelValue}"/>
                            </StackLayout>
                        </DataTemplate>
                    </BindableLayout.ItemTemplate>
                </StackLayout>
                <ListView 
                    Margin="15,0" 
                    Grid.Column="0" 
                    Grid.Row="1" 
                    SeparatorVisibility="None" 
                    HasUnevenRows="True" 
                    IsEnabled="False" 
                    VerticalScrollBarVisibility="Never"  
                    ItemsSource="{Binding InfoBox.DetailsObjectInfos}">
                    <ListView.ItemTemplate>
                        <DataTemplate>
                            <ViewCell>
                                <StackLayout Grid.Column="1" Orientation="Horizontal" Padding="0" Spacing="0">
                                    <Label FontAttributes="Bold" Text="{Binding BoldLabelTitle}" Padding="0"/>
                                    <Label Text="{Binding LabelValue}"/>
                                </StackLayout>
                            </ViewCell>
                        </DataTemplate>
                    </ListView.ItemTemplate>
                </ListView>
                <StackLayout VerticalOptions="Start" Grid.Column="1" Grid.RowSpan="2" Spacing="15" Padding="20,10" Orientation="Horizontal" HorizontalOptions="End">
                    <StackLayout.Resources>
                        <ResourceDictionary>
                            <converterColor:BoolToStringConverter 
                            x:Key="colorConverter"
                            TrueValue="#0275BA"
                            FalseValue="#949494"/>
                            <converterFontFamily:BoolToStringConverter 
                            x:Key="fontFamilyConverter"
                            TrueValue="FA-S"
                            FalseValue="FA-R"/>
                        </ResourceDictionary>
                    </StackLayout.Resources>
                    <!--"{Binding IsFavorite, Converter={StaticResource fontFamilyConverter}}-->
                    <Label FontSize="Medium" FontFamily="{Binding IsFavorite, Converter={StaticResource Key=fontFamilyConverter}}" TextColor="{Binding IsFavorite, Converter={StaticResource Key=fontFamilyConverter}}" Text="{StaticResource IconStar}">
                        <Label.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Path=BindingContext.ToggleFavoriteObjectCommand}" CommandParameter="{Binding Id}"/>
                        </Label.GestureRecognizers>
                    </Label>
                    <Label FontSize="Medium" Style="{DynamicResource BlueColorStyle}" Text="{StaticResource IconEye}">
                        <Label.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Path=BindingContext.ViewObjectDetailsCommand}" CommandParameter="{Binding Id}"/>
                        </Label.GestureRecognizers>
                    </Label>
                    <Label FontSize="Medium" Style="{DynamicResource BlueSolidColorStyle}" Text="{StaticResource IconPin}">
                        <Label.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding Path=BindingContext.ViewObjectOnMapCommand}" CommandParameter="{Binding Id}"/>
                        </Label.GestureRecognizers>
                    </Label>
                </StackLayout>
            </Grid>
        </StackLayout>
    </ViewCell.View>
</ViewCell>

这是我将 viewcell 添加为 DataTemplate 的主页的虚拟机,所以这里是:

 public class SearchObjectsViewModel : BaseViewModel, INotifyPropertyChanged
    {
        public SearchObjectsViewModel()
        {
            Task.Run(async () => { await GetObjectInstancesList(); });
            ToggleFavoriteObjectCommand = new Command(async(data) => await ToggleFavoriteObjects(data));
            ViewObjectDetailsCommand = new Command(async (data) => await GetObjectDetails(data));
            ViewObjectOnMapCommand = new Command(async (data) => await ViewObjectOnMap(data));
        }


        #region Properties
        private string searchedText;
        public string SearchedText
        {
            get { return searchedText; }
            set
            {
                SetProperty(ref searchedText, value);
                ObjectsSearch(searchedText);
            }
        }


        ObservableCollection<CustomPin> _objectItems { get; set; }
        public ObservableCollection<CustomPin> ObjectItems
        {
            get
            {
                return _objectItems;
            }
            set
            {
                if (_objectItems != value)
                {
                    _objectItems = value;
                    OnPropertyChanged(nameof(ObjectItems));
                }
            }
        }

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            var changed = PropertyChanged;
            if (changed == null)
                return;

            changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

        public ICommand ViewObjectOnMapCommand { get; set; }
        public ICommand ViewObjectDetailsCommand { get; set; }
        public ICommand ToggleFavoriteObjectCommand { get; set; }
        #endregion

        #region Methods

        private async Task GetObjectInstancesList()
        {
            ObjectItems = new ObservableCollection<CustomPin>();

            var objectsResponse = await ApiServiceProvider.GetObjectInstances();

            Device.BeginInvokeOnMainThread(() =>
            {
                if (objectsResponse.Succeeded)
                {
                    foreach (var item in objectsResponse.ObjectInstances)
                    {
                        CustomPin pinData = new CustomPin();

                        pinData.Id = item.IdObjectInstance;
                        pinData.Label = item.ObjectClassName;
                        pinData.IsFavorite = item.IsFavorite.HasValue ? item.IsFavorite.Value : false;

                        if (item.Points != null)
                        {
                            pinData.Position = new Position(item.Points.FirstOrDefault().Latitude, item.Points.FirstOrDefault().Longitude);
                        }
                        else
                        {
                            //add polygon
                        }

                        foreach (var s in item.Strings)
                        {
                            if (s.ShowInBallon)
                            {
                                pinData.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo
                                {
                                    BoldLabelTitle = s.ClassParameterName + ": ",
                                    LabelValue = s.StringValue
                                });
                            }
                        }
                        foreach (var i in item.Integers)
                        {
                            if (i.ShowInBallon)
                            {
                                pinData.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo
                                {
                                    BoldLabelTitle = i.ClassParameterName + ": ",
                                    LabelValue = i.IntValue.ToString()
                                });
                            }
                        }
                        foreach (var date in item.Dates)
                        {
                            if (date.ShowInBallon)
                            {
                                pinData.InfoBox.DetailsObjectInfos.Add(new Models.MapModels.DetailsObjectInfo
                                {
                                    BoldLabelTitle = date.ClassParameterName + ": ",
                                    LabelValue = date.DateValue.ToString()
                                });
                            }
                        }

                        ObjectItems.Add(pinData);
                        pinData.InfoBox.CountDetailsItemsRows = pinData.InfoBox.DetailsObjectInfos.Count * 85;
                    }
                    
                }

                TemporalData.ObjectsData = ObjectItems;
                OnPropertyChanged(nameof(ObjectItems));
                OnPropertyChanged(nameof(TemporalData.ObjectsData));
            });
        }
        private void ObjectsSearch(string searchedText)
        {
            if (!string.IsNullOrWhiteSpace(searchedText))
            {
                var result = TemporalData.ObjectsData.Where(x => x.Label.ToLowerInvariant().Contains(searchedText.ToLowerInvariant())).ToList();
                ObjectItems = new ObservableCollection<CustomPin>(result);
            }
            else
            {
                ObjectItems = new ObservableCollection<CustomPin>(TemporalData.ObjectsData);
            }

            OnPropertyChanged(nameof(ObjectItems));
        }
        private async Task ViewObjectOnMap(object objectId)
        {
            CustomPin selectedPin = App.SelectedPin = ObjectItems.Where(x => x.Id == Convert.ToInt32(objectId)).FirstOrDefault();

            App.GoToPinCommand = new Command(() => App.GoToPinCommand.Execute(selectedPin));
            await Shell.Current.GoToAsync(Routes.MapPage);
        }

        private async Task GetObjectDetails(object objectId)
        {
            App.SelectedPin = ObjectItems.Where(x => x.Id == Convert.ToInt32(objectId)).FirstOrDefault();
            await Shell.Current.GoToAsync(Routes.ItemDetailsPage);
        }

        private async Task ToggleFavoriteObjects(object objectId)
        {
            int id = Convert.ToInt32(objectId);
            var objectItem = ObjectItems.Where(x => x.Id == id).FirstOrDefault();
            var favoriteToggleResponse = await ApiServiceProvider.ToggleFavoriteObjectById(id, !objectItem.IsFavorite);
            if (!favoriteToggleResponse.Succeeded)
            {
                await Shell.Current.DisplayAlert("Error", "Lost communication with server. Try again.", "OK");
            }
            else
            {
                ObjectItems.Where(x => x.Id == id).Select(c => { c.IsFavorite = !c.IsFavorite; return c; }).ToList();
            }
        }

        #endregion
    }

我收到错误:BindingCOntext is set more than once.

我遇到的其他问题是模拟器无法识别 TapGestureRecognizer 例如这个标签,您可以参考代码:

<Label FontSize="Medium" FontFamily="{Binding IsFavorite, Converter={StaticResource Key=fontFamilyConverter}}" TextColor="{Binding IsFavorite, Converter={StaticResource Key=fontFamilyConverter}}" Text="{StaticResource IconStar}">
    <Label.GestureRecognizers>
        <TapGestureRecognizer Command="{Binding Path=BindingContext.ToggleFavoriteObjectCommand}" CommandParameter="{Binding Id}"/>
    </Label.GestureRecognizers>
</Label>

我需要输入Command="{Binding Path=BindingContext.ToggleFavoriteObjectCommand,Source={x:Reference Page}}" 之类的内容,但我不知道在我的情况下是否可以引用“父页面”,这将是我调用 cellView 的“主”页面。

【问题讨论】:

    标签: c# xaml xamarin xamarin.forms


    【解决方案1】:

    原因

    你设置了两次BindingContext

    第一个:实际上下面的代码确实在 Cell 上自动设置了 BindingContext ,内容是列表 ObjectItems 中的项目。

        <ListView.ItemTemplate>
            <DataTemplate>
                <viewcells:ObjectItemViewCell/>
            </DataTemplate>
        </ListView.ItemTemplate>
    

    第二个

    <ViewCell.BindingContext>
        <converterFontFamily:BoolToStringConverter x:Key="fontFamilyConverter"/>
        <!--<converterColor:BoolToStringConverter x:Key="fontFamilyConverter"/>-->
    </ViewCell.BindingContext>
    

    解决方案

    不要在ViewCell里面设置BindingContext,如果要使用转换器,可以直接在页面中添加。

    <ListView 
        Margin="0,15,0,0"
        SelectionMode="None"
        RowHeight= "150"
        ItemsSource="{Binding ObjectItems}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell BindingContext="{Binding Converter = {StaticResource fontFamilyConverter}}"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
    

    由于绑定路径不正确,该命令未触发,将这些命令放入模型CustomPin即可解决问题。

    【讨论】:

      【解决方案2】:

      问题是,您在 BindingContext 标记中设置转换器。它们需要这样设置:

      <ContentPage.BindingContext>
            YOUR BINDING CONTEXT
          </ContentPage.BindingContext>
           <ContentPage.Resources>
              <ResourceDictionary>
                 <converterFontFamily:BoolToStringConverter x:Key="fontFamilyConverter"/>
                <converterColor:BoolToStringConverter x:Key="fontFamilyConverter"/>
              </ResourceDictionary>
          </ContentPage.Resources>
      

      由于 BindingContext 错误,您的命令无法正常工作。

      【讨论】:

      • 但我没有 ContentPage,我有 CellVIew!请提供有关上述示例的示例。
      • 好的,能分享一下你的 ViewModel 和 ViewCell 后端代码吗?
      • 你可以从主页找到我更新的viewcellpage,以及我的viewModel
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-20
      • 1970-01-01
      相关资源
      最近更新 更多