WPF: Binding List of List

WPF: Binding List of List
2015-06-19T13:43:21+02:00
2015-06-23T14:11:44+02:00
2022-12-02T15:25:38+01:00
BIG W
Üdv!

MVVM szerkezeti mintával készítettem egy olyan modelt, ami LINQ to XML-t hajt végre, és egy generikus listába tárolja azokat. A generikus lista egy osztály property-jeit tartalmazza. Az egyik property szintén egy generikus lista, másik osztállyal, ami eltárolja az xml root gyermekelemeit. Ez a része működik:

public class EdzestervModel { private List<Edzesterv> EdzestervList = new List<Edzesterv>(); private List<Feladat> Feladat = new List<Feladat>(); public Int32 EdzestervID; public List<Edzesterv> GetEdzesterv() { if (Directory.Exists(@"EOSZX")) { string[] eoszxFiles = Directory.GetFiles(@"EOSZX", "*.eoszx", SearchOption.AllDirectories); foreach (var eoszxFile in eoszxFiles) { var edzestervLINQ = (from edzestervObjektum in XDocument.Load(eoszxFile) .Element("edzestervek").Elements("edzesterv") where (int) edzestervObjektum.Attribute("id")==EdzestervID select new { ID=Convert.ToInt32(edzestervObjektum.Attribute("id").Value), Cim=Convert.ToString(edzestervObjektum.Element("cim").Value), Hanynaponta=Convert.ToInt32(edzestervObjektum.Element("hanynaponta").Value), TesttomegIndex=edzestervObjektum.Element("testtomegIndex").Value, Ajanlott=Convert.ToString(edzestervObjektum.Element("ajanlott").Value), Feladat = edzestervObjektum.Elements("feladat").Select(feladat => new Feladat { FeladatID = Convert.ToInt32(feladat.Attribute("sorszam").Value), Nap = Convert.ToInt32(feladat.Attribute("nap").Value), FeladatCim = (string)feladat.Element("cim").Value, Tartalom = (string)feladat.Element("tartalom").Value }).ToList(), }).SingleOrDefault(); Edzesterv edzesterv = new Edzesterv {EdzestervID=edzestervLINQ.ID, EdzestervCim=edzestervLINQ.Cim, Hanynaponta=edzestervLINQ.Hanynaponta, TesttomegIndex=edzestervLINQ.TesttomegIndex,Ajanlott=edzestervLINQ.Ajanlott}; EdzestervList.Add(edzesterv); } } return EdzestervList; } } public class Edzesterv { public int EdzestervID { get; set; } public string EdzestervCim { get; set; } public int Hanynaponta { get; set; } public string TesttomegIndex { get; set; } public string Ajanlott { get; set; } public List<Feladat> Feladat { get; set; } } public class Feladat { public Int32 FeladatID { get; set; } public Int32 Nap { get; set; } public String FeladatCim { get; set; } public String Tartalom { get; set; } }
Majd ugye ViewModel-ben kapcsolatot létesítek a View és a Model között:

class EdzestervViewModel : INotifyPropertyChanged { #region INotifyPropertyChanged implementációja public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion private Models.EdzestervModel MODEL; public EdzestervViewModel(int ID) { MODEL = new Models.EdzestervModel(); this.ID = ID; EdzestervList = MODEL.GetEdzesterv(); } public EdzestervViewModel() { MODEL = new Models.EdzestervModel(); } public int ID { get { return MODEL.EdzestervID; } set { MODEL.EdzestervID = value; } } private List<Models.Edzesterv> _edzestervList = new List<Models.Edzesterv>(); public List<Models.Edzesterv> EdzestervList { get { return _edzestervList; } set { _edzestervList = value; OnPropertyChanged("EdzestervList"); } } private ICommand _EdzestervMegjelenit; public ICommand EdzestervMegjelenit { get { return _EdzestervMegjelenit ?? (_EdzestervMegjelenit = new CommandHandler(() => Action(), _execute)); } } private void Action() { EdzestervList = null; EdzestervList = MODEL.GetEdzesterv(); } private bool _execute=true; }
Végül a View-ban DataContext-tel a ViewModel-t kötöm:

Hyperlink link = sender as Hyperlink; stckPanel.Children.Clear(); stckPanel.DataContext = new ViewModels.EdzestervViewModel(); EdzestervView edzestervView = new EdzestervView(Convert.ToInt32(link.Tag)); stckPanel.Children.Add(edzestervView);
És XAML-ben is:

<ItemsControl ItemsSource="{Binding EdzestervList,Mode=TwoWay}"> <ItemsControl.ItemTemplate> <DataTemplate> <Grid Height="300"> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Border Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="50" Background="#666" CornerRadius="5"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding EdzestervCim, Mode=TwoWay}" VerticalAlignment="Center" FontSize="24" Foreground="White"/> </StackPanel> </Border> <StackPanel Grid.Row="1" Orientation="Vertical"> <StackPanel> <Grid Height="100" Width="300" HorizontalAlignment="Left"> <Grid.RowDefinitions> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Grid.Row="0" Text="Hány naponta?" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Hanynaponta}" Foreground="White"/> <TextBlock Grid.Column="0" Grid.Row="1" Text="Testtömeg Index" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding TesttomegIndex}" Foreground="White"/> <TextBlock Grid.Column="0" Grid.Row="2" Text="Ajánlott" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Ajanlott}" Foreground="White"/> </Grid> </StackPanel> </StackPanel> </Grid> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> <ItemsControl ItemsSource="{Binding EdzestervList.Feladat, Mode=TwoWay}"> <ItemsControl.ItemTemplate> <DataTemplate> <DockPanel Background="Azure" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Border Width="150" Height="150" Background="#666"> <StackPanel> <TextBlock Text="{Binding Nap}" Foreground="White"/> <TextBlock Text="{Binding Cim}" Foreground="White"/> <TextBlock Text="{Binding Tartalom}" Foreground="White" TextWrapping="Wrap"/> </StackPanel> </Border> </DockPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
A problémám a XAML-ben van. Ki is emeltem félkövér betűtípussal, hogy melyik sorban. A gondom ott van, hogy ItemsSource-hoz hiába kötöm az EdzestervList.Feladatát,  nem jeleníti meg a tartalmat. Próbálkoztam ItemsSource={Binding Feladat, Mode=TwoWay} megoldással is, de ez sem sikerült. Hogyan tudom kötni a Feladat generikus listát?

W.
Mutasd a teljes hozzászólást!
Szia!

Az EdzestervList az egy lista, aminek nincs Feladat tulajdonsága, csak az elemeinek van Feladat tulajdonsága. Pontosan mit szeretnél megjeleníteni? Ha egy adott edzésterv feladatait, akkor kellene egy KivalasztottEdzesterv tulajdonság és azt kellene kötnöd.
Mutasd a teljes hozzászólást!

  • Szia!

    Most vettem észre, hogy Edzesterv osztály deklarálását, inicializálását nem bővítettem ki az értékek hozzáadásánál:

    Edzesterv edzesterv = new Edzesterv {EdzestervID=edzestervLINQ.ID, EdzestervCim=edzestervLINQ.Cim, Hanynaponta=edzestervLINQ.Hanynaponta, TesttomegIndex=edzestervLINQ.TesttomegIndex,Ajanlott=edzestervLINQ.Ajanlott, Feladat=edzestervLINQ.Feladat};
    Most kibővítettem, de még így se kapom meg a Feladat Lista eredményeit.

    Pontosan mit szeretnél megjeleníteni?

    Az Edzesterv generikus listájának a Feladat property generikus listának tagjainak eredményét.
    Mutasd a teljes hozzászólást!
  • Ha egy adott edzésterv feladatait, akkor kellene egy KivalasztottEdzesterv tulajdonság és azt kellene kötnöd.

    És azt hova, és mit tárol?
    Mutasd a teljes hozzászólást!
  • Az EdzestervViewModel-be tehetnéd és a kiválasztott edzéstervet kellene tárolnia.
    Mutasd a teljes hozzászólást!
  • Úgy látszik, rosszul fogalmaztam.

    Azt a LINQ elvégzi, egy ID-t kap paraméter értékként, és kiválasztja az edzéstervet, amikhez tartoznak feladatok. Egy edzésterv több feladatot is tartalmazhat, egy xml adatbank viszont több edzéstervet tartalmaz elv alapján működik, a kliensnek csak meg kell jeleníteni a kiválasztott edzésterv nevét, és annak feladatait a view-ban kapott ID alapján.
    Tehát az Edzésterv List<Feladat> Feladat listáját szeretném megjeleníteni kliens oldalon.
    Mutasd a teljes hozzászólást!
  • Akkor szerintem rossza ViewModel. Mert abban nem egy edzésterved van, hanem egy edzésterv listád és a listában lévő edzésterveknek vannak feladatai. És a View-d is egy edzésterv listát jelenít meg:

    <ItemsControl ItemsSource="{Binding EdzestervList,Mode=TwoWay}">
    Akkor ha jól értem amit írtál így kellen módosítanod a kódot:

    public List<Edzesterv> GetEdzesterv()
    helyett

    public Edzesterv GetEdzesterv()
    És az

    public List<Models.Edzesterv> EdzestervList
    helyett

    public Models.Edzesterv Edzesterv
    kellene.

    A View pedig:

    <Grid Height="300" DataContext="{Binding Edzesterv}"> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Border Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="50" Background="#666" CornerRadius="5"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding EdzestervCim, Mode=TwoWay}" VerticalAlignment="Center" FontSize="24" Foreground="White"/> </StackPanel> </Border> <StackPanel Grid.Row="1" Orientation="Vertical"> <StackPanel> <Grid Height="100" Width="300" HorizontalAlignment="Left"> <Grid.RowDefinitions> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Grid.Row="0" Text="Hány naponta?" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Hanynaponta}" Foreground="White"/> <TextBlock Grid.Column="0" Grid.Row="1" Text="Testtömeg Index" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding TesttomegIndex}" Foreground="White"/> <TextBlock Grid.Column="0" Grid.Row="2" Text="Ajánlott" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Ajanlott}" Foreground="White"/> </Grid> </StackPanel> </StackPanel> </Grid> <ItemsControl ItemsSource="{Binding Edzesterv.Feladat}"> <ItemsControl.ItemTemplate> <DataTemplate> <DockPanel Background="Azure" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Border Width="150" Height="150" Background="#666"> <StackPanel> <TextBlock Text="{Binding Nap}" Foreground="White"/> <TextBlock Text="{Binding Cim}" Foreground="White"/> <TextBlock Text="{Binding Tartalom}" Foreground="White" TextWrapping="Wrap"/> </StackPanel> </Border> </DockPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
    Mutasd a teljes hozzászólást!
  • public List<Models.Edzesterv> EdzestervList { get { return _edzestervList; } set { _edzestervList = value; OnPropertyChanged("EdzestervList"); } }
    Ez így biztosan nem lesz jó. Ismerkedj meg az ObservableCollection-nel! Az pont arra van kitalálva, hogy amennyiben az elemeid változnak, frissüljön az UI-d.

    De a problémád valószínűleg nem ez okozza, mivel List-et is simán lehet kötni az UI-hoz, csak nem fog frissülni. Tehát nem emiatt nem jelenik meg.

    Kicsit káosz van a kódodban, és nem nagyon van időm most projectet csinálni belőle, hogy jobban átlássam, viszont indulásnak nézd meg az Output ablakod, amikor megjeleníted a listát. Ha nem sikerül egy property-hez a kötés, ilyen jellegű hiba jelenik meg:

    System.Windows.Data Error: 40 : BindingExpression path error: 'Feladat' property not found on 'object'...
    Ez megjelenik?
    Mutasd a teljes hozzászólást!
  • Kihagyva az adatbetöltést a kódból nagyjából így néz ki amire gondoltam:

    public class EdzestervModel { public Int32 EdzestervID; public Edzesterv GetEdzesterv() { var edzest = new Edzesterv { EdzestervID = 1, EdzestervCim = "Cím 1", Hanynaponta = 2, TesttomegIndex = "?", Ajanlott = "?" }; edzest.Feladat = new List<Feladat>(3); edzest.Feladat.Add(new Feladat { FeladatID = 1, Nap = 1, FeladatCim = "Első", Tartalom = "?" }); edzest.Feladat.Add(new Feladat { FeladatID = 2, Nap = 3, FeladatCim = "Második", Tartalom = "?" }); edzest.Feladat.Add(new Feladat { FeladatID = 3, Nap = 5, FeladatCim = "Harmadik", Tartalom = "?" }); return edzest; } } public class Edzesterv { public int EdzestervID { get; set; } public string EdzestervCim { get; set; } public int Hanynaponta { get; set; } public string TesttomegIndex { get; set; } public string Ajanlott { get; set; } public List<Feladat> Feladat { get; set; } } public class Feladat { public Int32 FeladatID { get; set; } public Int32 Nap { get; set; } public String FeladatCim { get; set; } public String Tartalom { get; set; } } class EdzestervViewModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } private EdzestervModel MODEL; public EdzestervViewModel(int ID) { MODEL = new EdzestervModel(); this.ID = ID; Edzesterv = MODEL.GetEdzesterv(); } public EdzestervViewModel() { MODEL = new EdzestervModel(); } public int ID { get { return MODEL.EdzestervID; } set { MODEL.EdzestervID = value; } } private Edzesterv _edzesterv; public Edzesterv Edzesterv { get { return _edzesterv; } set { _edzesterv = value; OnPropertyChanged("Edzesterv"); } } }

    Hozzá a View:

    <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid Grid.Row="0" Height="300" DataContext="{Binding Edzesterv}"> <Grid.RowDefinitions> <RowDefinition Height="50"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Border Grid.Row="0" VerticalAlignment="Top" HorizontalAlignment="Stretch" Height="50" Background="#666" CornerRadius="5"> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding EdzestervCim, Mode=TwoWay}" VerticalAlignment="Center" FontSize="24" Foreground="White"/> </StackPanel> </Border> <StackPanel Grid.Row="1" Orientation="Vertical"> <StackPanel> <Grid Height="100" Width="300" HorizontalAlignment="Left"> <Grid.RowDefinitions> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Grid.Row="0" Text="Hány naponta?" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="0" Text="{Binding Hanynaponta}" Foreground="White"/> <TextBlock Grid.Column="0" Grid.Row="1" Text="Testtömeg Index" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="1" Text="{Binding TesttomegIndex}" Foreground="White"/> <TextBlock Grid.Column="0" Grid.Row="2" Text="Ajánlott" Foreground="White"/> <TextBlock Grid.Column="1" Grid.Row="2" Text="{Binding Ajanlott}" Foreground="White"/> </Grid> </StackPanel> </StackPanel> </Grid> <ItemsControl Grid.Row="1" ItemsSource="{Binding Edzesterv.Feladat}"> <ItemsControl.ItemTemplate> <DataTemplate> <DockPanel Background="Azure" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <Border Width="150" Height="150" Background="#666"> <StackPanel> <TextBlock Text="{Binding Nap}" Foreground="White"/> <TextBlock Text="{Binding FeladatCim}" Foreground="White"/> <TextBlock Text="{Binding Tartalom}" Foreground="White" TextWrapping="Wrap"/> </StackPanel> </Border> </DockPanel> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Grid>
    Mutasd a teljes hozzászólást!
  • Szia!

    Köszi a tippet, megváltoztattam, és igen, ez a hiba jelenik meg az output ablakon.
    Mutasd a teljes hozzászólást!
  • Bocsi, de szerintem nem értjük egymást, és ez valószínűleg az én hibám.:S
    Mutasd a teljes hozzászólást!
  • Az a hiba pontosan azért jelenik meg az Output ablakba, mint amit fentebb írtam:

    Az EdzestervList az egy lista, aminek nincs Feladat tulajdonsága, csak az elemeinek van Feladat tulajdonsága.

    Szerintem azért nézd meg amit küldtem. Másold be egy üres projektbe, próbáld ki. Hátha mégis értjük egymást. Ha nem, akkor sajons nem tudtam segíteni. Szép hétvégét!
    Mutasd a teljes hozzászólást!
  • Sikerült megoldani. Kicsit átalakítottam a kódot:

    Model:

    public class EdzestervModel { public Int32 edzestervID; public Edzesterv GetEdzesterv() { Edzesterv edzesterv=null; string[] eoszxFileArray = Directory.GetFiles(@"EOSZX"); foreach (var eoszxFile in eoszxFileArray) { XDocument document = XDocument.Load(eoszxFile); var edzestervekLINQ = from doc in document.Descendants("edzestervek").Elements("edzesterv") where Convert.ToInt32(doc.Attribute("id").Value)==edzestervID select new { ID = doc.Attribute("id").Value, Cim = doc.Element("cim").Value, Hanynaponta = doc.Element("hanynaponta").Value, Allapot = doc.Element("allapot").Value, Ajanlott = doc.Element("ajanlott").Value }; edzesterv = new Edzesterv { EdzestervID=(from selectedEdzesterv in edzestervekLINQ select Convert.ToInt32(selectedEdzesterv.ID)).First(), EdzestervCim=(from selectedEdzesterv in edzestervekLINQ select Convert.ToString(selectedEdzesterv.Cim)).First(), Hanynaponta=(from selectedEdzesterv in edzestervekLINQ select Convert.ToInt32(selectedEdzesterv.Hanynaponta)).First(), Allapot=(from selectedEdzesterv in edzestervekLINQ select Convert.ToString(selectedEdzesterv.Allapot)).First(), Ajanlott=(from selectedEdzesterv in edzestervekLINQ select Convert.ToString(selectedEdzesterv.Ajanlott)).First(), Feladat=GetFeladatCollection() }; } return edzesterv; } public ObservableCollection<feladat> GetFeladatCollection() { ObservableCollection<feladat> feladatCollection = new ObservableCollection<feladat>(); string[] eoszxFileArray = Directory.GetFiles(@"EOSZX"); foreach (var eoszxFile in eoszxFileArray) { XDocument document = XDocument.Load(eoszxFile); foreach(var doc in document.Elements("edzestervek").Elements("edzesterv").Where(i=&gt;Convert.ToInt32(i.Attribute("id").Value)==edzestervID).Descendants("feladat")){ Feladat feladat = new Feladat { FeladatID = Convert.ToInt32(doc.Attribute("sorszam").Value), Nap = Convert.ToInt32(doc.Attribute("nap").Value), FeladatCim = Convert.ToString(doc.Element("cim").Value), Tartalom = Convert.ToString(doc.Element("tartalom").Value) }; feladatCollection.Add(feladat); } } return feladatCollection; } } public class Edzesterv { public int EdzestervID { get; set; } public string EdzestervCim { get; set; } public int Hanynaponta { get; set; } public string Allapot { get; set; } public string Ajanlott { get; set; } public ObservableCollection<feladat> Feladat { get; set; } } public class Feladat { public int FeladatID { get; set; } public int Nap { get; set; } public string FeladatCim { get; set; } public string Tartalom { get; set; } }
    ViewModel-ben csak az ObservableCollection<Edzesterv>-et implementáltam:

    public class EdzestervViewModel : INotifyPropertyChanged { #region INotifyPropertyChanged implementációja public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } #endregion public Models.EdzestervModel MODEL; public EdzestervViewModel(int ID) { MODEL = new EdzestervModel(); this.ID = ID; MODEL.edzestervID = this.ID; EdzestervObj = MODEL.GetEdzesterv(); } public int ID { get { return MODEL.edzestervID; } set { MODEL.edzestervID = value; } } private Edzesterv _edzestervObj = new Edzesterv(); public Edzesterv EdzestervObj { get { return _edzestervObj; } set { _edzestervObj = value; OnPropertyChanged("EdzestervObj"); } } }
    Végül a view-ban a xaml:

    <Grid DataContext="{Binding EdzestervObj}"> <Grid.RowDefinitions> <RowDefinition Height="1*"/> <RowDefinition Height="4*"/> </Grid.RowDefinitions> <Border Background="#666" CornerRadius="5" Grid.Row="0"> <StackPanel Orientation="Vertical"> <TextBlock Foreground="White" Margin="5" FontSize="24" Text="{Binding EdzestervCim, Mode=TwoWay}"/> </StackPanel> </Border> <WrapPanel Grid.Row="1"> <ItemsControl ItemsSource="{Binding Feladat}"> <ItemsControl.ItemTemplate> <DataTemplate> <Border Width="200" Height="200" Margin="5" Background="#666"> <TextBlock Text="{Binding FeladatCim}"/> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </WrapPanel> </Grid>
    A XAML-ben annyit módosítottam, hogy az ItemsControl ItemsSource-ába A Binding-ban nem kötöttem még egyszer az EdzestervObj ObservableCollection-t, Így is az Output ablakban hasonló hibaüzenet jelent meg, mint amit Kukipapa írt. Köszönöm a segítségeteket mindkettőtöknek!

    W.
    Mutasd a teljes hozzászólást!
Tetszett amit olvastál? Szeretnél a jövőben is értesülni a hasonló érdekességekről?
abcd