Pokazywanie postów oznaczonych etykietą kolekcje. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą kolekcje. Pokaż wszystkie posty

niedziela, 27 stycznia 2013

[WPF] Caliburn.Micro: Kolekcje

Podczas bindowania kolekcji najpierw należy zdefiniować model danych. Przykładowa klasa reprezentująca dane osoby.

public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
    public bool IsMale { get; set; }
}

Następnie w ViewModelu definiujemy obserwowalną kolekcję, która zapewni nam synchronizację widoku z modelem danych.

public class PersonListViewModel : Screen
{
    public ObservableCollection<Person> Items { get; set; }

    private Person _selectedItem;
    public Person SelectedItem
    {
        get { return _selectedItem; }
        set { _selectedItem = value; }
    }

    public PersonListViewModel()
    {
        Items = new ObservableCollection<Person>
        {
            new Person{ Age = 15, IsMale = true, Name= "Michael"},
            new Person{ Age = 80, IsMale = true, Name= "Steven"},
            new Person{ Age = 25, IsMale = false, Name= "Kate"},
            new Person{ Age = 64, IsMale = true, Name= "Matthew"}

        };
    }
}

Dodatkowo, dzięki konwencjom, property SelectedItem będzie synchronizowane z wybranym elementem z kolekcji Items.
Ostatni krok to binding w ViewModelu. Kolekcje można wyświetlać w kilku elementach WPFa. Poniżej pokazano, jak to zrobić w ListBoxie.

<ListBox x:Name="Items" 
         cal:Message.Attach="[Event SelectionChanged] = [Action NewSelectedItem($eventargs)]"
         SelectionMode="Single">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Width="200" Height="100" cal:Bind.Model="{Binding}" >
                <TextBlock x:Name="Name" VerticalAlignment="Top" />
                <Label x:Name="Age" VerticalAlignment="Top" />
                <CheckBox x:Name="IsMale" VerticalAlignment="Top" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Podobnie jak w przypadku skalarnych propercji, zaczynamy od ustawienia atrybutu x:Name. Dzięki cal:Message.Attach możemy przypisywać do zdarzeń widoku funkcje z ViewModelu. Następnie dla całego ListBoxa definiuje się ItemTemplate, w którym na kontenerze zaznaczamy binding do propercji poprzez atrybut cal:Bind.Model. Poziom niżej w drzewie wizualnym możemy już bindować się do pojedynczych propercji poprzez standardowe x:Name.

sobota, 22 września 2012

[HTML|JS|CSS] KnockoutJS : Kolekcje

Data binding do kolekcji elementów jest czymś powszechnie stosowanym. Potrzeba wyświetlania zestawów danych na własne potrzeba pojawia się przy każdym większym projekcie. KnockoutJS daje możliwość prostego bindingu przy użyciu atrybutów oraz odpowiednich typów w ViewModelu.

Wyświetlanie kolekcji

Binding do kolekcji tworzy się w widoku za pomocą dyrektywy foreach, a po dwukropku podajemy nazwę kolekcji z ViewModelu. Jeżeli w przykładzie poniżej people jest kolekcją obiektów, to name, surname oraz added są kolejnymi properties takich obiektów.

<div data-bind="foreach: people">
 <div>
      <strong data-bind="text: name"></strong>
      <strong data-bind="text : surname"></strong>
      <strong data-bind="text : added"></strong> 
 </div>
</div>

W ViewModelu kolekcja musi być typu ko.observableArray, co zapewni śledzenie zmian. Warto przygotować sobie również funkcję - konstruktor do szybkiego tworzenia obiektów.

//funkcja konstruktora dla osoby

function Person(name, surname){
 var self = this;
 self.name = name; 
 self.surname = surname;
 self.added = new Date();
}

function AppViewModel() {

    // pozostala czesc inicjalizacji
    
    this.people = ko.observableArray([
     new Person("John","Doe"),
     new Person("Jack","The Ripper")
     ]);   
}

ko.applyBindings(new AppViewModel());

Powyższy kod wyświetli dane sformatowane do naszych potrzeb.

Dodawanie elementów

Jeżeli zajdzie potrzeba dynamicznego dodawania do kolekcji elementów, można stworzyć odpowiednią funkcję wewnątrz ViewModelu.
Od strony widoku można dodać przycisk z bindingiem do zdarzenia click.

<button data-bind="click: addPerson">Add</button>

W ViewModelu należy z kolei dodać funkcję, w której zwiększamy kolekcję używając funkcji push. Wszystkie zmiany w kolekcji spowodują automatyczne zmiany w widoku.

function AppViewModel() {
    //pozostala czesc inicjalizacji
    var that = this;
    this.addPerson = function(){
     that.people.push(new Person(that.firstName, that.lastName));      
    };
}

Edytowanie elementów

Dzięki śledzeniu zależności, każda zmiana wartości automatycznie odświeży kolekcję. Aby edytować elementy za pomocą elementu select, należy nieco zmodyfikować widok:

<div data-bind="foreach: people">
   <div>
 <!--pozostale elementy-->
 <select data-bind="options: $root.jobs, value: job, optionsText: 'name'"></select>
 <strong data-bind="text : job().salary"></strong>
   </div>
</div>

Obiekt select dostaje informacje o tym, z której kolekcji ma pobierać listę wartości (tablica jobs z ViewModelu), do którego property elementu z kolekcji people przypisać wartość (obserwowalny element job) oraz które property z elementów jobs wyświetlać jako opcję do wyboru.

Tymczasem po stronie ViewModelu:

function Person(name, surname,job){
 var self = this;
 //pozostale properties
 self.job = ko.observable(job);
}

function AppViewModel() {    

    this.jobs = [
    {name: "manger", salary : 6000},
    {name: "tester", salary : 2000},
    {name: "developer", salary : 4000}
    ];

    var that = this;
}

Jak widzimy jobs to kolekcja obiektów. Wyświetlamy tylko property name, ale po wybraniu obiektu mamy też dostęp do property salary. Możemy je wyświetlać w widoku za pomocą instrukcji data-bind="text : job().salary", jako odwołanie do property salary z obiektu job, który jest property elementu kolekcji people.

Usuwanie elementów

Po stronie widoku należy dodać element powodujący usunięcie elementu np. poprzez click.

<div data-bind="foreach: people">
 <div>
 <!--pozostale elementy -->
 <a data-bind="click: $root.removePerson">Remove item</a>
 </div>
</div>

Jako lokalizację funkcji podaje się $root, czyli najbardziej zewnętrzny ViewModel.


function AppViewModel() {
    var that = this;

    this.removePerson = function(person){
     that.people.remove(person);
    }
}


Agregacja wartości:

Pisanie swoich funkcji wyliczających pewne wartości na całej kolekcji staje się proste dzięki mechanizmowi śledzenie zależności. Gdy chcemy posumować wszystkie wartości i zbindować tę sumę do osobnego elementu, wartość zostanie odświeżona automatycznie przy każdej zmianie na kolekcji. Możemy także zbindować się do właściwości visible i wyświetlać dany element w zależności od spełnienia pewnego warunku logicznego. Na przykład:

<div data-bind="visible: totalSalary()>0">
 <p>Total salary:</p>
 <strong data-bind="text: totalSalary"></strong>
</div>

gdzie total salary jest typem ko.computed w ViewModelu.


that.totalSalary = ko.computed(function() {
   var total = 0;
   for (var i = 0; i < that.people().length; i++)
       total += that.people()[i].job().salary;
   return total;
});