Avatar billede morten-hbm Nybegynder
08. marts 2011 - 17:10 Der er 15 kommentarer og
1 løsning

ObserverCollection<T> og LINQ

Hej

Jeg står med dette problem/udfordring, at jeg har en ObserverCollection<T>, hvor jeg gerne vil hente alle dem ud, som har et child-element med name "test" f.eks.

Jeg har 3 klasser:
Categori
Company
Jobs

Hvor jeg så har gjort følgende efter:
Categori cat = new Categori(data....);
Company comp = new Company(data....);
Job job = new Job(data....);
comp.Jobs.Add(job);
job = new Job(data....);
comp.Jobs.Add(job);
job = new Job(data....);
comp.Jobs.Add(job);
cat.Companys.Add(comp);

comp = new Company(data2....);
Job job = new Job(data....);
comp.Jobs.Add(job);
job = new Job(data....);
comp.Jobs.Add(job);
job = new Job(data....);
comp.Jobs.Add(job);
cat.Companys.Add(comp);

comp = new Company(data3....);
Job job = new Job(data....);
comp.Jobs.Add(job);
job = new Job(data....);
comp.Jobs.Add(job);
job = new Job(data....);
comp.Jobs.Add(job);
cat.Companys.Add(comp);

Men nu vil jeg gerne have alle de jobs ud, som har name = "test"

Men jeg er lidt i tvivl om hvordan jeg gør dette.

Jeg har kigget lidt på om man kunne gøre følgende:
var x = from ca in cat
        from co in ca.Companys
        from jo in co.Jobs
        Where(j=>j.name == "test")
        select ca;

Men dette giver mig ikke det rigtige resultat. Nogle ide om hvad jeg skal gøre anderledes eller hvad det er jeg gør forkert?

På forhånd tak for hjælpen :)
Avatar billede mrtn Nybegynder
08. marts 2011 - 19:56 #1
Avatar billede janus_007 Nybegynder
08. marts 2011 - 20:26 #2
Gad vide hvad mrtn mener?

Anyway...

Du selecter ca, den vil give dig Category, men den har du kun en af!


Hvad oplever du går galt?
Avatar billede morten-hbm Nybegynder
08. marts 2011 - 21:46 #3
@mrtn - Kender godt siden, og har også været inde og kigge på den, men fandt ikke rigtigt noget jeg kunne bruge, eller også kunne jeg bare ikke finde ud af det :)

@janus_007 - Det jeg oplever er, at hvis jeg bruger overstående kode, så får jeg kun den ene categori ud, da den indeholder det child element med name = "test", men den tager alle child elementerne med ud for den categori.
Avatar billede janus_007 Nybegynder
09. marts 2011 - 00:58 #4
Hej Morten.

Jep det er klart, du siger nemlig "Giv mig den category som indeholder Jobs med name lig med "test""

Spørgsmålet er hvad du vil? Det har du ikke rigtigt beskrevet :)

Jeg gætter på at du måske vil have de Jobs som indeholder name lig med "test"?

var x = from ca in cat
        from co in ca.Companys
        from jo in co.Jobs
        Where(j=>j.name == "test")
        select jo;
Avatar billede morten-hbm Nybegynder
09. marts 2011 - 06:20 #5
Hmm, har nok lige glemt at beskrive hvad det er jeg vil :)

Det er fordi jeg har denne liste med categorier og dens companys og jobs. Dette smider jeg i et treeview. Men jeg vil så lave en search boks, som går ind og søger på alle de jobs med name = "test", og så viser den kun den categori(s), company(s) og job(s) der passer med dette.

Categori 1
- Company 1
- - Job 1
- - Job 2
- Company 2
- - Job 1
- Company 3
- - Job 1
- - Job 2
- - Job 3
Categori 2
- Company 1
- - Job 1
- - Job 2
- Company 2
- - Job 1
- Company 3
- - Job 1
- - Job 2
- - Job 3
Categori 3
- Company 1
- - Job 1
- - Job 2
- Company 2
- - Job 1
- Company 3
- - Job 1
- - Job 2
- - Job 3

så hvis jeg søger på job med name = "Job 3", så skal den kun vise sådan ser:

Categori 1
- Company 3
- - Job 3
Categori 2
- Company 3
- - Job 3
Categori 3
- Company 3
- - Job 3

Håber dette gav mere mening :)
Avatar billede morten-hbm Nybegynder
09. marts 2011 - 06:37 #6
Problemet er bare, at jeg kun får jobbet ud, og ikke dens parents med, så kan ikke se, hvilket company jobbet hører til eller lign.

Derfor jeg ville prøve på den anden måde :) Men tror jeg må se om jeg kan lave et lille hacks i stedet for.
Avatar billede johny Nybegynder
09. marts 2011 - 18:35 #7
Tja, så vil du jo pludselig kun have en del af objektmodellen. Hvordan populerer du dit TreeView, og de objekter du har med at gøre, er det data eller view objekter? For hvis det er det sidste, kunne du jo f.eks. udvide dem med henholdsvis "FilteredCompanies" og "FilteredJobs" og bruge dem til at populere dit TreeView med i stedet? (Det ville jeg så f.eks. implementere som DependencyProperties, så du kan lave live opdatering af dit TreeView)

Men ja, der er et hav af løsningsmuligheder til denne problemstilling, dog ikke den du forsøger at bruge, i hvert fald ikke hvad jeg ved af, ikke uden at redigere i de originale objekter, og så vil du ikke kunne fjerne filteret igen.

Implementerer du derimod clone metoder, ja så kan det så igen lade sig gøre med alm. linq, eller alternativt kan du implementere din clone metode i linq udtrykket, men det vil være ret grimt og fylde en hel del. :)
Avatar billede morten-hbm Nybegynder
10. marts 2011 - 13:06 #8
@johny: Jeg bruger ViewModels til mit treeview. Men jeg er ikke helt med rent kodningsmæssigt hvordan dette ville se ud. Har du mulighed for at poste noget kode på det?
Avatar billede johny Nybegynder
10. marts 2011 - 13:17 #9
Hvordan populerer du træet så? Kunne du poste den kode først eventuelt? Men hvilken tilgang vil du helst? Clone, eller properties med filtrering?
Avatar billede morten-hbm Nybegynder
10. marts 2011 - 16:00 #10
private void LoadData() {
            cats = new ObservableCollection<Categori>();
            #region if resource is selected
            foreach (var cat in result.Where(r => r.ParentId == 0)) {
                Categori c = new Categori(cat.DescrText, cat.RootId, (int)cat.ParentId, (int)cat.ItemId, (int)cat.DataType);
                foreach (var company in result.Where(_c => _c.ParentId == cat.RootId && _c.DataType == 2)) {
                    Company comp = new Company(company.DescrText, (int)company.RootId, (int)company.ParentId, (int)company.ItemId, (int)company.DataType);
                    foreach (var job in result.Where(j => j.DataType == 3 && j.ParentId == company.RootId)) {
                        Job _job = new Job(job.DescrText, (int)job.RootId, (int)job.ParentId, (int)job.ItemId, (int)job.DataType);
                        comp.Jobs.Add(_job);
                    }
                    c.Companys.Add(comp);
                }
                cats.Add(c);
            }
            tvResult.ItemsSource = cats;
            #endregion
        }

Og så bruger jeg til TreeViewet
<HierarchicalDataTemplate DataType="{x:Type local:Categori}">
            <HierarchicalDataTemplate.ItemsSource>
                <MultiBinding Converter="{StaticResource folderConverter}">
                    <Binding Path="Companys" />
                </MultiBinding>
            </HierarchicalDataTemplate.ItemsSource>
            <StackPanel Orientation="Horizontal">
                <Image Source="/OutlookAddIn2007;component/Images/FolderOpen.png"></Image>
                <TextBlock Text="{Binding Path=Desc}" Tag="{Binding Path=rootId}" />
            </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:Company}">
            <HierarchicalDataTemplate.ItemsSource>
                <MultiBinding Converter="{StaticResource folderConverter}">
                    <Binding Path="Jobs" />
                </MultiBinding>
            </HierarchicalDataTemplate.ItemsSource>
            <StackPanel Orientation="Horizontal">
                <Image Source="/OutlookAddIn2007;component/Images/FolderOpen.png"></Image>
                <TextBlock Text="{Binding Path=Desc}" Tag="{Binding Path=rootId}" />
            </StackPanel>
        </HierarchicalDataTemplate>

        <HierarchicalDataTemplate DataType="{x:Type local:Job}">
            <StackPanel Orientation="Horizontal">
                <Image Source="/OutlookAddIn2007;component/Images/followup_16.png"></Image>
                <TextBlock Text="{Binding Path=Desc}" Tag="{Binding Path=rootId}"/>
            </StackPanel>
        </HierarchicalDataTemplate>

Jeg hælder nok mest til properties med filtrering, da det andet bliver noget rod :)

Skal lige siges at det er første gang jeg arbejder i WPF, og har derfor ikke den store erfaring med dette :)
Avatar billede Knobtemor Nybegynder
11. marts 2011 - 10:49 #11
Hej morten-hbm

Du skal anvende

usingSystem.Linq;

//snip

så tager du din liste af companies således:

List<companies> cList = ObserverCollection.where(tmp => tmp.company.job.name.equals("test"));

presto, listen af companies med test job.
Avatar billede johny Nybegynder
12. marts 2011 - 14:14 #12
Jeg har prøve at lave et lidt forsimplet eksempel på din kode, som har nogle af idéerne til hvordan det skal gøres, men der er stadig nogle ting der trænger til at blive tweaket, som f.eks. har jeg kun én kategori her, og jeg har ikke lavet nogen implementation for filtrering af disse, samt at når filteret skiftes, så tømmer jeg listerne og fylder dem igen, i stedet for blot at redigere forskellen, men her er "the basic idea", og så må du sige til hvis der skal mere til derfra. :)

DTO KLASSER:

public class Category
{
  private JobFilter _jobFilter;

  public string Name { get; set; }
  public System.Collections.ObjectModel.ObservableCollection<Company> Companies { get; set; }
  public System.Collections.ObjectModel.ObservableCollection<Company> FilteredCompanies { get; set; }

  public Category(JobFilter jobFilter)
  {
    _jobFilter = jobFilter;

    this.Companies = new System.Collections.ObjectModel.ObservableCollection<Company>();
    this.FilteredCompanies = new System.Collections.ObjectModel.ObservableCollection<Company>();

    this.Companies.CollectionChanged += Companies_CollectionChanged;
    _jobFilter.FilterChanged += jobFilter_FilterChanged;
  }

  void Companies_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
  {
    RefreshFiltered();
  }

  void jobFilter_FilterChanged(object sender, System.EventArgs e)
  {
    RefreshFiltered();
  }

  private void RefreshFiltered()
  {
    this.FilteredCompanies.Clear();

    foreach (Company company in this.Companies)
    {
      if (company.RefreshFilteredJobs(_jobFilter.Filter))
      {
        this.FilteredCompanies.Add(company);
      }
    }
  }
}

public class Company
{
  public string Name { get; set; }
  public System.Collections.ObjectModel.ObservableCollection<Job> Jobs { get; set; }
  public System.Collections.ObjectModel.ObservableCollection<Job> FilteredJobs { get; set; }

  public Company()
  {
    this.Jobs = new System.Collections.ObjectModel.ObservableCollection<Job>();
    this.FilteredJobs = new System.Collections.ObjectModel.ObservableCollection<Job>();
  }

  public bool RefreshFilteredJobs(string filter)
  {
    bool includeThis = false;

    this.FilteredJobs.Clear();

    foreach (Job job in this.Jobs.Where(e => e.Name.Contains(filter)))
    {
      this.FilteredJobs.Add(job);

      includeThis = true;
    }

    return includeThis;
  }
}

public class Job
{
  public string Name { get; set; }
}

FILTER KLASSE:

public class JobFilter
{
  public event System.EventHandler FilterChanged;

  private string _filter;
  public string Filter
  {
    get
    {
      return _filter;
    }
    set
    {
      SetFilter(value);
    }
  }
  public JobFilter(string initialFilter = "")
  {
    SetFilter(initialFilter, notify: false);
  }

  private void SetFilter(string filter, bool notify = true)
  {
    _filter = filter ?? "";

    if (notify && this.FilterChanged != null)
    {
      this.FilterChanged(this, System.EventArgs.Empty);
    }
  }
}

UI KODE:

public partial class MainWindow : Window
{
  private JobFilter _jobFilter = new JobFilter();
  public MainWindow()
  {
    InitializeComponent();

    var category = new Category(_jobFilter) { Name = "Category 1" };
    var comp1 = new Company { Name = "Company 1" };
    comp1.Jobs.Add(new Job { Name = "Job 1.1" });
    comp1.Jobs.Add(new Job { Name = "Job 1.2" });
    category.Companies.Add(comp1);
    var comp2 = new Company { Name = "Company 2" };
    comp2.Jobs.Add(new Job { Name = "Job 2.1" });
    comp2.Jobs.Add(new Job { Name = "Job 2.2" });
    comp2.Jobs.Add(new Job { Name = "Job 2.3" });
    category.Companies.Add(comp2);
    var comp3 = new Company { Name = "Company 3" };
    comp3.Jobs.Add(new Job { Name = "Job 3.1" });
    comp3.Jobs.Add(new Job { Name = "Job 3.2" });
    category.Companies.Add(comp3);

    var source = new System.Collections.ObjectModel.ObservableCollection<Category>();
    source.Add(category);

    this.TreeView.ItemsSource = source;
  }

  private void textBox1_TextChanged(object sender, TextChangedEventArgs e)
  {
    _jobFilter.Filter = this.textBox1.Text;
  }
}

<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Grid.Resources>
      <HierarchicalDataTemplate DataType="{x:Type local:Category}" ItemsSource="{Binding Path=FilteredCompanies}">
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Path=Name}" />
        </StackPanel>
      </HierarchicalDataTemplate>
      <HierarchicalDataTemplate DataType="{x:Type local:Company}" ItemsSource="{Binding Path=FilteredJobs}">
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Path=Name}" />
        </StackPanel>
      </HierarchicalDataTemplate>
      <HierarchicalDataTemplate DataType="{x:Type local:Job}">
        <StackPanel Orientation="Horizontal">
          <TextBlock Text="{Binding Path=Name}" />
        </StackPanel>
      </HierarchicalDataTemplate>
    </Grid.Resources>
    <TreeView Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" Name="TreeView" VerticalAlignment="Top" Width="247" />
    <TextBox Height="23" HorizontalAlignment="Left" Margin="293,37,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" TextChanged="textBox1_TextChanged" />
  </Grid>
</Window>
Avatar billede johny Nybegynder
13. marts 2011 - 23:17 #13
Any luck?
Avatar billede morten-hbm Nybegynder
15. marts 2011 - 06:29 #14
Tak for svaret, jeg prøver det lige af her senere i dag eller i morgen og tester om det virker :) Har lige haft lidt travlt her de seneste par dage, så har ikke haft tid.
Avatar billede morten-hbm Nybegynder
03. oktober 2011 - 11:58 #15
Beklager det meget meget meget sene svar, jeg har godt nok gjort på en helt 3. måde, som var mere tilegnet det nuværende kode - og har derfor ikke mulighed for at poste koden.

Dog har johny's kode bidraget til min løsning, så hvis du lige kommer med et svar
Avatar billede johny Nybegynder
03. oktober 2011 - 14:00 #16
Glad for at det kunne bruges til lidt. :)
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
IT-kurser om Microsoft 365, sikkerhed, personlig vækst, udvikling, digital markedsføring, grafisk design, SAP og forretningsanalyse.

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester