2015-01-08 6 views
1

Я пишу приложение на основе MVVM и столкнулся с небольшой проблемой.Использование LINQ в XAML, с формами Xamarin

У меня есть два вида списка для создания друг в друге. Классы A и B должны быть показаны в них. Список, содержащий представления A (назовем их AView), построен из ObservableCollection в пределах viewmodel.

Каждый AView содержит список B (BView), технически весь экран представляет собой список списков.

Проблема заключается в том, что базовые данные не являются A-> B, но B-> A (A не содержит список B, но B имеет ссылку на его родительский A), и у меня есть глобальный список от B.

В коде это не сложно, так как я могу легко запустить ListB.Where (x => x.A == A), но я еще не нашел способ сделать это в XAML.

Возможно ли, чтобы это сделать, и если да, то каким будет метод? Я не хочу создавать отдельные Страницы с отдельными BindingContexts, чтобы сделать такой логически простой дисплей.

ответ

3

Внесение списков списков не поддерживается в Xamarin.Forms. Вы можете попробовать, но внутренний не будет прокручиваться, а потому бесполезен.

Если ListA имеет список ListB, будет ли он соответствовать вашим потребностям для группировки? Here is a quick image showing how grouping would look.

По существу, вы группируете свои списки B внутри элементов A (где на этом изображении я показал ваш список A - список мобильных устройств, а ваши списки B - это конкретные типы в общей ОС).

Для этого при создании списка и задании шаблона/источника данных вы также должны установить IsGroupingEnabled в true и GroupHeaderTemplate на созданный шаблон данных, чтобы показать, как он будет отображаться визуально (точно так же, как вы обычно сделало бы datatemplate с пользовательскими viewcells).

Единственное различие теперь вместо источника, являющегося список A или список B, это список А (который проходит ObservableCollection), который содержит список Б.

Вы в основном нужно сделать Модель просмотра расширяет ObserableCollection, поэтому A является A, но A также является списком/может использоваться как список.

Теперь вы просто кормите свой список A (где каждый A имеет список B) в качестве источника вашего ListView.

1

Вложение двух списков в форматах Xamarin не рекомендуется (или даже поддерживается, я думаю).

Лучшим способом, вероятно, будет использование сгруппированного ListView, связанного с вложенным ObservableCollection <T>. Здесь внешней коллекцией будет ObservableCollection <ViewModelA>. Каждый ViewModelA затем происходит от ObservableCollection <ViewModelB>. Так что это коллекция коллекций.

Кроме того, из-за вашего ViewModelB.Parent = некоторого требования к ViewModelA внутренние коллекции должны быть обновлены до нужного подмножества элементов ViewModelB (т. Е. С родителем == some ViewModelA), всякий раз, когда ваша плоская глобальная коллекция всех элементов ViewModelB изменения. Это потребует некоторой дополнительной логики в реализации ViewModelA.

Конкретно решение может быть следующим:

Сначала определяют три ViewModel классов (ViewModelMain, ViewModelA и ViewModelB) следующим образом:

ViewModelMain:

using System; 
using System.Collections.ObjectModel; 
using System.Windows.Input; 
using Xamarin.Forms; 

namespace XamLab1 
{ 
    public class ViewModelMain 
    { 
     public ObservableCollection<ViewModelA> AItems { get; set; } 
     public ObservableCollection<ViewModelB> AllBItems { get; set; } 

     public ViewModelMain() 
     { 
      AItems = new ObservableCollection<ViewModelA>(); 
      AllBItems = new ObservableCollection<ViewModelB>(); 

      this.UpdateCommand = new Command<string>((key) => 
      { 
       Update(); 
      }); 
     } 

     public ICommand UpdateCommand { protected set; get; } 

     public void Update() 
     { 
      // filling collections with dummy test data... 

      AItems.Clear(); 
      AllBItems.Clear(); 

      for (int i = 0; i < 10; i++) 
      { 
       long timestamp = DateTime.Now.Ticks; 
       var a = new ViewModelA("A-" + i + "-" + timestamp, AllBItems); 
       AItems.Add(a); 

       for (int j = 0; j < 3; j++) 
       { 
        AllBItems.Add(new ViewModelB { NameB = "B-" + i + "-" + j + "-" + timestamp, Parent = a }); 
       } 
      }    
     } 
    } 
} 

ViewModelA:

using System.Collections.ObjectModel; 
using System.Linq; 

namespace XamLab1 
{ 
    public class ViewModelA : ObservableCollection<ViewModelB> 
    { 
     public string NameA { get; private set; } 

     public ViewModelA(string name, ObservableCollection<ViewModelB> allBItems) 
     { 
      NameA = name; 
      _allBItems = allBItems; 
      _allBItems.CollectionChanged += _allBItems_CollectionChanged; 
     } 

     private ObservableCollection<ViewModelB> _allBItems; 

     private void _allBItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
     { 
      ClearItems(); 
      foreach (var item in _allBItems.Where(x => x.Parent == this)) 
      { 
       Add(item); 
      } 
     } 

    } 

} 

и ViewModelB:

namespace XamLab1 
{ 
    public class ViewModelB 
    { 
     public string NameB { get; set; } 
     public ViewModelA Parent { get; set; } 

    } 
} 

Наконец, связать модели вида на XAML, как это:

<?xml version="1.0" encoding="utf-8" ?> 
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
      xmlns:local="clr-namespace:XamLab1;assembly=XamLab1" 
      x:Class="XamLab1.MainPage"> 

    <ContentPage.BindingContext> 
    <local:ViewModelMain /> 
    </ContentPage.BindingContext> 

    <StackLayout> 
     <Button Text="Update" Command="{Binding UpdateCommand}"></Button> 
     <ListView ItemsSource="{Binding AItems}" IsGroupingEnabled="True" GroupDisplayBinding="{Binding NameA}" GroupShortNameBinding="{Binding NameA}" HasUnevenRows="True"> 
     <ListView.GroupHeaderTemplate> 
      <DataTemplate> 
      <ViewCell> 
       <StackLayout VerticalOptions="FillAndExpand" Padding="5" BackgroundColor="#707070"> 
       <Label Text="{Binding NameA}"/> 
       </StackLayout> 
      </ViewCell> 
      </DataTemplate> 
     </ListView.GroupHeaderTemplate> 
     <ListView.ItemTemplate> 
      <DataTemplate> 
      <ViewCell> 
       <StackLayout VerticalOptions="FillAndExpand" Padding="20, 5, 5, 5"> 
       <Label Text="{Binding NameB}" /> 
       </StackLayout> 
      </ViewCell> 
      </DataTemplate> 
     </ListView.ItemTemplate> 
     </ListView> 
    </StackLayout> 

</ContentPage> 

Теперь запустите полученное приложение и нажмите кнопку Update: вы должны увидеть ListView обновляются новыми данные ViewModel.

Обратите внимание: это решение может по-прежнему использовать некоторую оптимизацию. Например, вероятно, не очень эффективно обновлять все внутренние коллекции всякий раз, когда меняется коллекция глобальных BItems. Возможно, что-то вроде более ленивого обновления. Кроме того, если ваша коллекция AllBItems велика, вы можете рассмотреть некоторый механизм индексирования свойства Parent, чтобы ускорить фильтрацию.

Смежные вопросы