Examples

Coordinator
Apr 17, 2008 at 10:20 PM
Well I didn't think Program.cs in the Test project was a very good introduction. I improved it (Added some comments, made it a bit less abstract/obscure). And it can be downloaded from 'Source Code'. I hope this is better? What would be a good example? EXAML?
Mar 18, 2009 at 11:09 AM
Hi all.

I'm not sure I can understand the whole code in Program.cs :-( to be able to apply it to my code. Consider next situation - we have 2 classes - Book & Shelf. Each Book has an Id and is placed on 1 Shelf. Shelves have their own Id's. All Books & Shelves are stored in static collections. Using relation Book.ShelfId = Shelf.Id we can define 2 properties: Book.Shelf and Shelf.Books. Can you advise me what classes from Obtics to use to implement these properties that will be updated each time both collections and Id's of Books & Shelves are changed?
Thanx in advance to any reply.

Regards, 
allexey

Coordinator
Mar 26, 2009 at 5:02 PM
Hi Allexey,

I'm not completely sure what you mean; but the ObservableEnumerable.ToLookup() or ObservableEnumerable.ToDictionary() methods come to mind.
Say in pseudo code:

using Obtics.Collections;

class Root
{
    internal ObservableShelfCollection _Shelves; //observable collection with shelves

    internal IObservableDictionary<TShelfId,Shelf> _ShelveDict = _Shelves.ToDictionary( shelf => shelf.Id ); //auto updating map from shelf ids to shelves.
}

class Shelf
{
    public TShelfId Id { get; }
}

class Book
{
    IValueProvider<TShelfId> _ShelfId = ValueProvider.Dynamic<TShelfId>() ; //just a place to store the shelf id

    public IValueProvider<Shelf> Shelf //Shelf.Value property will return the shelf
    {
        get
        {
            return Root._ShelveDict[_ShelfId]; 
        }        
    }

    public Shelf CurrentShelf { get{ return Shelf.Value; } }
}

Does this help?

Program.cs is not a very good example. It is more an OLD piece of testing code. If you get the latest sources there are 3 better ones; ObticsExaml, GroupingTest and RegexTool. 

Regs,

Thomas.
Coordinator
Mar 26, 2009 at 5:26 PM
Also; including the other way (still pseudo):

using Obtics.Collections;

class Root
{
    static internal ObservableShelfCollection _Shelves; //observable collection with shelves
    static internal ObservableBookCollection _Books; //observable collection with books

    //auto updating map from shelf ids to shelves.
    static internal IObservableDictionary<TShelfId,Shelf> _ShelveDict = _Shelves.ToDictionary( shelf => shelf.Id ); 

    //auto updating map from shelf ids to books.
    //note that here ShelfId is of type IValueProvider<TShelfId>

    static internal IObservableLookup<TShelfId,Book> _BookLookup = _Books.ToLookup( book => book.ShelfId );
}

class Shelf
{
    public TShelfId Id { get; }

    //books on the shelf
    IEnumerable<Book> Books
    {
        get
        {
            return Root._BookLookup[Id];
        }
    }
}

class Book
{
    //just a place to store the shelf id
    IValueProvider<TShelfId> _ShelfId = ValueProvider.Dynamic<TShelfId>() ; 

    IValueProvider<TShelfId> ShelfId { get{ return _ShelfId; } }

    //Shelf.Value property will return the shelf
    public IValueProvider<Shelf> Shelf 
    {
        get
        {
            return Root._ShelveDict[ShelfId]; 
        }        
    }

    public Shelf CurrentShelf { get{ return Shelf.Value; } }
}

Mar 27, 2009 at 5:20 PM
Hi Throb,

thank you for provided examples. I hope I got the idea how to implement those relations. But I've got another question regarding ability to implement joins using Obtics. 
Suggest that we have a library with several rooms in it, each having shelves with books. Is it possible to implement such properties like Book.Room and Room.Books?
I guess that these relations between books and Rooms will require use of Join 2 collections ('Books join Shelves' in case of Room & 'Rooms join Shelves' in case of Book).

Thanx in advance

Coordinator
Mar 27, 2009 at 6:23 PM
Hi Allexey,

Well yes; that would be easy: Assuming you have Room, Shelf and Book classes implemented similarly as described in the previous examples with shelves and books. That means you will have:

class Room
{
    IEnumerable<Shelf> Shelves { get; }
}

class Shelf
{
    IValueProvider<Room> Room { get; }
    IEnumerable<Books> Books { get; }
}

class Book
{
    IValueProvider<Shelf> Shelf { get; }
}

Then it is easy to add a property to Room and Bool classes:

using Obtics.Collections;

class Room
{
    IEnumerable<Book> Books
    {
        get
        {
            return Shelves.SelectMany{ shelf => shelf.Books };
        }
    } 
}

class Book
{
    IValueProvider<Room> Room
    {
        get
        {
            return Shelf.Select( shelf => shelf.Room );
        }
    }
}

Just make sure you use the right version of the extension methods (the Obtics.Collections.ObservableEnumerable ones and not the System.Linq.Enumerable ones)

Regs,

Thomas.
Mar 30, 2009 at 11:40 AM
Hi Thomas,

I'm quite ashamed that I only now tried to really implement the code you provided before, because I've got a question that i should have asked right after the first answer of yours - if the collection of Books in Shelves is declared as IEnumerable how does it raise an event that it was updated? Meaning - if a Book that belongs to one Shelf changes its ShelfId to another - when the change of Books collection of that another is raised? On next access to Shelf.Books property?

Regards,
Allexey
Coordinator
Mar 30, 2009 at 3:26 PM
Hi Allexey,

Though the result of Shelf.Books is declared just as IEnumerable<Book> the returned instance will also implement INotifyCollectionChanged and INotifyPropertyChanged. Just cast the result to either of these interfaces to register for change notifications. Using Obtics though I rarely find need to handle change notifications manually. 

Important:
When manually handling change events, don't forget to unregister when you are done listening. Register for changes on the same instance as where you get your elements from and unregister from that very same instance.
(you are not quaranteed to receive the same object instance during multiple calls to Shelf.Books)

So don't do this:

//register
((INotifyCollectionChanged)Shelf.Books).CollectionChanged += new NotifyCollectionChangedEventArgs( yourHandler );

//done listening
((INotifyCollectionChanged)Shelf.Books).CollectionChanged -= new NotifyCollectionChangedEventArgs( yourHandler );

void yourHandler( object sender, NotifyCollectionChangedEventArgs args)
{
    foreach(Book book in Shelf.Books)
    {
        //your thing
    }
}

But this:

//keep a reference to the IEnumerable instance
IEnumerable<Book> books = Shelf.Books;

//register
((INotifyCollectionChanged)books).CollectionChanged += new NotifyCollectionChangedEventArgs( yourHandler );

//done listening
((INotifyCollectionChanged)books).CollectionChanged -= new NotifyCollectionChangedEventArgs( yourHandler );

void yourHandler( object sender, NotifyCollectionChangedEventArgs args)
{
    foreach(Book book in books)
    {
        //your thing
    }
}

When binding directly to Shelf.Books from XAML you don't need to worry about anything.

Hope this helps?

Regs,

Thomas.
Coordinator
Mar 30, 2009 at 3:34 PM
Just a small correction; Only the result of ObservableEnumerable.ToList and Cap methods can be cast to INotifyPropertyChanged since they return IList objects.

IValueProvider objects usually can be cast to INotifyPropertyChanged and not to INotifyCollectionChanged.

Regs,

Thomas.