簡體   English   中英

WP7 - ListBox綁定到嵌套的ObservableCollection

[英]WP7 - ListBox Binding to nested ObservableCollection

我有一個ObservableCollection對象如下:

public class UserDataViewModel
{
  private ObservableCollection<CategoryItem> _data = 
                                       new ObservableCollection<CategoryItem>();

  public ObservableCollection<CategoryItem> Data
  {
    get { return _data; }
    private set { }
  }
  // Other methods to set Data
}

CategoryItem類定義為:

public class CategoryItem : INotifyPropertyChanged
{
  private string _name = null;
  private ObservableCollection<EntryItem> _entries = 
                                 new ObservableCollection<EntryItem>();

  public string Name
  {
    get { return _name; }
    set {
      if( value != _name ) {
        _name = value;
        NotifyPropertyChanged( "Name" );
      }
    }
  }

  public ObservableCollection<EntryItem> Entries
  {
    get { return _entries; }
    set {
      if( value != _entries ) {
        _entries = value;
        NotifyPropertyChanged( "Entries" );
      }
    }
  }
  // INotifyPropertyChanged code follows
}

EntryItem類定義為:

public class EntryItem : INotifyPropertyChanged
{
  private string _name = null;

  public string Name
  {
    get { return _name; }
    set {
      if( value != _name ) {
        _name = value;
        NotifyPropertyChanged( "Name" );
      }
    }
  }
  // INotifyPropertyChanged code follows
}

我正在嘗試將其綁定到ListBox 每個ListBoxItem由2個TextBlock組成。 我希望第一個TextBlock顯示EntryItem.Name屬性,第二個顯示CategoryItem.Name屬性。 這是我在XAML中嘗試過的(沒有成功):

<ListBox x:Name="MyListBox"
         Margin="0,0,-12,0"
         ItemsSource="{Binding Data}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <StackPanel Margin="0,0,0,17">
        <!--This should display EntryItem.Name-->
        <TextBlock Text="{Binding Entries.Name}"
                   TextWrapping="Wrap"
                   Margin="12,0,0,0"
                   Style="{StaticResource PhoneTextExtraLargeStyle}" />

        <!--This should display CategoryItem.Name-->
        <TextBlock Text="{Binding Name}"
                   TextWrapping="Wrap"
                   Margin="12,-6,0,0"
                   Style="{StaticResource PhoneTextSubtleStyle}" />
      </StackPanel>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

在這個頁面的代碼隱藏我設置:

DataContext = App.ViewModel; // ViewModel is of type UserDataViewModel

我一直收到綁定錯誤:

System.Windows.Data Error: BindingExpression path error: 'Name' property not found on 'System.Collections.ObjectModel.ObservableCollection`1[NestedCollection.ViewModels.EntryItem]' 'System.Collections.ObjectModel.ObservableCollection`1[NestedCollection.ViewModels.EntryItem]' (HashCode=123081170). BindingExpression: Path='Entries.Name' DataItem='NestedCollection.ViewModels.CategoryItem' (HashCode=121425257); target element is 'System.Windows.Controls.TextBlock' (Name=''); target property is 'Text' (type 'System.String')..

NestedCollection是此項目的名稱,所有上述類都在NestedCollection.ViewModels命名空間中。

僅顯示第二個TextBlock的內容。 我該如何解決?

謝謝你的幫助,這讓我瘋了幾個小時吧!

編輯:

假設Data集合有2個條目,“信用卡”和“電子郵件帳戶”(這些是集合中每個CategoryItem對象的Name屬性。假設第一個CategoryItem具有EntryItem對象“Visa”,“Mastercard”和“American Express” ,第二個CategoryItem對象具有EntryItem對象“GMail”和“Hotmail”,然后我希望ListBox顯示:

Visa
Credit Cards

Mastercard
Credit Cards

American Express
Credit Cards

GMail
Email Accounts

Hotmail
Email Accounts

我意識到DataEntries屬性沒有Name屬性,其中的每個條目都有。 是否有索引到XAML綁定中的Entries

您正在嘗試將ObservableCollection<T>綁定到TextBox 想一想。

ObservableCollection<EntryItem>沒有名為Name的屬性。 EntryItem類可以。

我建議您使用ItemsControl或使用ConverterEntryItem名稱轉換為逗號分隔的字符串。

看完你的編輯后:

請嘗試以下代碼:

<ListBox x:Name="MyListBox"
            Margin="0,0,-12,0"
            ItemsSource="{Binding Data}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Name="RootGrid">
                <ItemsControl ItemsSource="{Binding Entries}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Margin="0,0,0,17">
                                <!--This should display EntryItem.Name-->
                                <TextBlock Text="{Binding Name}"
                                            TextWrapping="Wrap"
                                            Margin="12,0,0,0"
                                            Style="{StaticResource PhoneTextExtraLargeStyle}" />
                                <!--This should display CategoryItem.Name-->
                                <TextBlock Text="{Binding ElementName=RootGrid, Path=DataContext.Name}"
                                            TextWrapping="Wrap"
                                            Margin="12,-6,0,0"
                                            Style="{StaticResource PhoneTextSubtleStyle}" />
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

- 編輯 -

看起來這是Silverlight 3的一個已知問題。

http://forums.silverlight.net/forums/p/108804/280986.aspx

要解決這個問題,要么將CategoryItem的引用EntryItem名為Parent EntryItem中,要么類似於訪問它。

public class EntryItem : INotifyPropertyChanged
{
    public CategoryItem Parent { get; set; }
    ....
}

或者如上面的鏈接中所討論的,將DataTemplate放入UserControl以使其工作。

在我看來,您試圖從單個(嵌套)數據源在單個控件中顯示分組數據,在這種情況下,您應該考慮使用Silverlight Toolkit for WP7中的LongListSelector控件。 WindowsPhoneGeek有一篇關於如何在類似情況下使用它的博客文章

或者,您需要使用嵌套項控件。 如果您不需要選擇的概念,那么只需將ListBox的項模板設置為ItemsControl,其ItemsSource =“{Binding Entries}”。 對於ItemsControl,DataContext將是一個單獨的CategoryItem,因此您可以根據需要添加綁定到Name屬性的TextBlock標頭。 這基本上就是LongListSelector正在做的事情,但提供了更大的靈活性。

如果您需要選擇條目的概念,那么我懷疑您在CategoryItem級別不需要它,因此將root和ItemsControl以及ItemTemplate設為ListBox。 這種方式你需要小心滾動,ListBox為自己提供,所以你最終可能會有一個令人困惑的用戶體驗,因此我最初的建議是嘗試LongListSelector。

假設1:UserDataViewModel確實是一個ViewModel

類名末尾的術語“ViewModel”暗示該類的目的是支持特定的視圖。 您不會期望這樣的視圖模型使其附加的視圖難以完成其工作。

因此,我建議你的“ViewModel”搞砸了,需要重新開發。 從...開始:-

public class EntryItem
{
    public string Name {get; set;}
    public CategoryItem Category {get; set;}
}

您的CategoryItem不需要entires集合屬性,您的UserDataView返回所有EntryItem對象的扁平集合。 綁定很容易。

   <TextBlock Text="{Binding Name}"
                   TextWrapping="Wrap"
                   Margin="12,0,0,0"
                   Style="{StaticResource PhoneTextExtraLargeStyle}" />
   <TextBlock Text="{Binding Category.Name}"
                   TextWrapping="Wrap"
                   Margin="12,-6,0,0"
                   Style="{StaticResource PhoneTextSubtleStyle}" />   

假設2:UserDataViewModel實際上不是ViewModel

您所謂的視圖模型實際上可能只是一種與其存儲或一般用法相匹配的數據模型。 這將解釋為什么它與實際視圖的要求不匹配。

我將介紹另一個假設,在WP7上可能是真的(可能在其他地方)。 在顯示視圖期間,不修改集合的內容,也不修改項目的名稱。 因此,這些對象的Observable性質(盡管可能在其他地方有用)對於視圖的工作不是必需的。

如果這些假設為真,則可以使用值轉換器和附加類以更可接受的方式呈現項目: -

public class EntryHolder
{
    public EntryItem Entry {get; set;}
    public CategoryItem Category {get; set; }
}

public class CategoryToEntryItemExConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
         UserDataViewModel model = value as UserDataViewModel;
         if (model != null)
         {
              return model.Data.SelectMany(c => c.Entries
                 .Select(e => new EntryHolder() { Category = c, Entry = e})
              );
         }
         else
         {
             return null;
         }
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

現在你要調整你的Xaml: -

<Grid x:Name="LayoutRoot">
    <Grid.Resources>
   <local:CategoryToEntryItemExConverter x:Key="ItemsConv" />
</Grid.Resources>
</Grid>

...

<ListBox x:Name="MyListBox"
         Margin="0,0,-12,0"
         ItemsSource="{Binding Converter={StaticResource ItemsConv}}">
  <ListBox.ItemTemplate>
    <DataTemplate>
      <StackPanel Margin="0,0,0,17">
        <!--This should display EntryItem.Name-->
        <TextBlock Text="{Binding Entry.Name}"
                   TextWrapping="Wrap"
                   Margin="12,0,0,0"
                   Style="{StaticResource PhoneTextExtraLargeStyle}" />

        <!--This should display CategoryItem.Name-->
        <TextBlock Text="{Binding Category.Name}"
                   TextWrapping="Wrap"
                   Margin="12,-6,0,0"
                   Style="{StaticResource PhoneTextSubtleStyle}" />
      </StackPanel>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>  

您的問題沒有意義 - 條目列表中的哪個項目您想要名稱? 第一項? 整個集合沒有名稱,僅在該集合中的每個元素上。

如果你想要第一個項目你可以讓你的TextBox綁定到Entries [0] .Name - 我認為這適用於Windows Phone上的Silverlight(我不記得是否支持索引器)。

如果不支持索引器,那么您需要編寫一個可以從ObservableCollection<EntityItem>轉換為字符串的IValueConverter,並將其用作Binding中的Converter。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM