【问题标题】:How to do databinding with two ObservableCollection?如何使用两个 ObservableCollection 进行数据绑定?
【发布时间】:2019-11-26 00:58:40
【问题描述】:

我有 2 个模型、2 个视图和 1 个视图模型。 基本上,我想将 GTSLocation 对象中的数据加载到 ListItem 中,但还要从具有与 GTSLocation 对象相同的工作站点 Guid 的 GTSWorkingSite 对象添加另外两个值。

来自 ObservableCollection 的项目 fooA:

WorkingSiteId : "A-A-A-A"
Longitude: 60
Latitude: 120

来自 ObservableCollection 的项目 fooB:

WorkingSiteId : null
Longitude: 70
Latitude: 130

来自 ObservableCollection 的项目 barA:

Id: "A-A-A-A"
Name: "WacDonald's"
Description: "A Fastfood resturaunt."

fooA 的列表项:

WorkingSiteName: "WacDonald's"
WorkingSiteDescription: "A Fastfood resturaunt."
Longitude: 60
Latitude: 120

fooB 的列表项:

WorkingSiteName: "Unknown Place"
WorkingSiteDescription: "No info to provide"
Longitude: 70
Latitude: 130

我只能想出一个解决方案,即创建另一个包含我想要的所有属性的模型。事实上,这是唯一的出路吗?我猜一定有更优雅的方式?

加起来,我有两个 ObservableCollection 的原因是因为 GTSWorkingSite 也单独用于另一个视图(WorkingSiteView.xaml)。

◎型号

    public class GTSLocation 
    {
        public string WorkingSiteId {get;set;} //GUID
        public double Longitude {get;set;}
        public double Latitude {get;set;}
        public DateTime Timestamp {get;set;}
    }
    public class GTSWorkingSite
    {
        public string Id {get;set;} //GUID
        public string Name {get;set;}
        public string Description {get;set;}
    }

◎视图模型

    public class GeneralViewModel : INotifyPropertyChanged 
    {
        //Assume these two collection filled with data
        public ObservableCollection<GTSLocation> UserLocations { get; set; }
        public ObservableCollection<GTSWorkingSite> WorkingSites { get; set; }
        //Ignored the rest of the code...
    }

◎查看:UserItemsPage.xaml

   <ListView ItemsSource="{Binding UserLocations}">
   <ListView.ItemTemplate>
       <DataTemplate>
           <ViewCell>
               <Grid>
                   <Grid.RowDefinitions>
                       <RowDefinition Height="1*" />
                       <RowDefinition Height="1*" />
                       <RowDefinition Height="1*" />
                   </Grid.RowDefinitions>
                   <Grid.ColumnDefinitions>
                       <ColumnDefinition Width="100" />
                       <ColumnDefinition Width="1*" />
                       <ColumnDefinition Width="2*" />
                   </Grid.ColumnDefinitions>
                   <Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" />
                   <Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding WorkingSiteName}" />
                   <Label Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding WorkingSiteDescription}" />
                   <Label Grid.Row="2" Grid.Column="1" Text="{Binding Longitude}" />
                   <Label Grid.Row="2" Grid.Column="2" Text="{Timestamp}" HorizontalTextAlignment="End" />
               </Grid>
           </ViewCell>
       </DataTemplate>
   </ListView.ItemTemplate>
</ListView>

如果这从一开始就是一个糟糕的设计,我愿意重构我的代码,如果有人可以给我建议。

【问题讨论】:

  • 请原谅我没有立即选择最佳答案。每个答案对我都有帮助。请给我两天时间,因为我想看到更多的答案和建议。

标签: c# xaml xamarin mvvm data-binding


【解决方案1】:

我认为在 ListView 上拥有两个 BindingContext 然后实际创建第三个类并将这两个列表合并为一个会更加复杂。

var newList = from locations in UserLocations
              join workingSite in WorkingSites on locations.WorkingSiteId equals workingSite.Id
              select new WhatEverClass(workingSite, locations);

WhatEverClass 中,您可以拥有GTSWorkingSiteGTSLocation 的支持值以及您想要的任何字段/属性。

更重要的是,当谈到重构你时,如果WorkingSiteId 真的是GUID,你为什么不使用GUID 而不是string
+您可以将经度和纬度包装到您的班级中-例如。 GeoCoordinate 或者您可以使用其他逻辑创建自己的类(如果需要)。 C# 是强类型的 ?

【讨论】:

  • 感谢 LINQ sn-p 和重构建议!我不知道 GUID 类型类。实际上,坐标一个已经嵌套的属性,我只是将它们展平以使问题更简单(无需附加坐标类),但无论如何感谢您提供的信息。 :)
【解决方案2】:

这很糟糕,因为您依赖于两个类之间的字符串(或任何 GUID)映射。如果您不想创建第三个模型但可以重构当前模型,并且如果 GTSLocation 与 GTSWorkingSite 有 1-1 映射,那么我认为您可以这样做:

1) 重构模型,让 GTSLocation 知道它有一个 GTSWotkingSite:

public class GTSLocation 
{
    public string WorkingSiteId {get;set;} //GUID
    public double Longitude {get;set;}
    public double Latitude {get;set;}
    public DateTime Timestamp {get;set;}
    //New property -> We will use it in the binding!
    public GTSWorkingSite WorkingSite {get;set;}
}
public class GTSWorkingSite
{
    public string Id {get;set;} //GUID
    public string Name {get;set;}
    public string Description {get;set;}
}

2) 然后在您的 XAML 中,当正确填充时,检索信息变得很容易(注意绑定 ["."] 使用新添加的 WorkingSite 属性略有变化):

<ListView ItemsSource="{Binding UserLocations}">
   <ListView.ItemTemplate>
       <DataTemplate>
           <ViewCell>
               <Grid>
                   <Grid.RowDefinitions>
                       <RowDefinition Height="1*" />
                       <RowDefinition Height="1*" />
                       <RowDefinition Height="1*" />
                   </Grid.RowDefinitions>
                   <Grid.ColumnDefinitions>
                       <ColumnDefinition Width="100" />
                       <ColumnDefinition Width="1*" />
                       <ColumnDefinition Width="2*" />
                   </Grid.ColumnDefinitions>
                   <Image Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" />
                   <Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding WorkingSite.Name}" />
                   <Label Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="2" Text="{Binding WorkingSite.Description}" />
                   <Label Grid.Row="2" Grid.Column="1" Text="{Binding Longitude}" />
                   <Label Grid.Row="2" Grid.Column="2" Text="{Timestamp}" HorizontalTextAlignment="End" />
               </Grid>
           </ViewCell>
       </DataTemplate>
   </ListView.ItemTemplate>
</ListView>

希望对您有所帮助,祝您编码愉快!

【讨论】:

  • 我明白了。 :) 困扰我的是 cpu/内存问题和重复执行。也许前者在现代设备上可以忽略不计,而后者有点……丑陋?但是我没有意识到字符串/GUID 映射策略是最大的问题。感谢重构技巧和完整的代码 sn-p!
猜你喜欢
  • 2011-06-16
  • 2011-06-13
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 2016-04-14
  • 1970-01-01
  • 2010-11-03
相关资源
最近更新 更多