Fields and collections using generics

Thanks to code contributions from David Piepgrass and Timothy Pratley, there is a new way to declare fields and collections in Update Controls. No longer do you have to declare sentries. No longer do you have to use the Ctrl+D, G code generator. Now you can use generics.

UpdateControls.Fields

  • Independent<T>
  • Dependent<T>

UpdateControls.Collections

  • IndependentList<T>
  • IndependentDictionary<Key, Value>
  • DependentList<T>

Independent fields

Models contain independent fields. User actions change these fields. Declare an independent field using Independent<T>.

public class Agent
{
    private Independent<string> _name = new Independent<string>();

    public string Name
    {
        get { return _name; }
        set { _name.Value = value; }
    }
}

The property exposes the field as the native type. Independent<T> is implicitly convertable to T, so the getter returns it directly. The opposite is not true, however, so the setter needs to assign to the Value property.

Independent collections

The Independent<T> class is useful only for single-valued fields. Do not try to combine it with a collection class. Instead, use IndependentList<T> or IndependentDictionary<Key, Value>.

public class Agency
{
    private IndependentList<Agent> _agents = new IndependentList<Agent>();
    private IndependentDictionary<Case, Agent> _leadByCase = new IndependentDictionary<Case,Agent>();

    public IEnumerable<Agent> Agents
    {
        get { return _agents; }
    }

    public void AddAgent(Agent agent)
    {
        _agents.Add(agent);
    }

    public void RemoveAgent(Agent agent)
    {
        _agents.Remove(agent);
    }

    public Agent GetLeadForCase(Case c)
    {
        return _leadByCase[c];
    }

    public void SetLeadForCase(Case c, Agent lead)
    {
        _leadByCase[c] = lead;
    }
}

These two classes are used the same as regular List<T> and Dictionary<Key, Value>. The only difference is that they participate in dependency tracking.

Dependent fields

Whereas a model contains independents, a view model contains dependents. User input does not directly change dependents. Instead, dependents are calculated. Update Controls automatically turns all view model properties into dependents, but you still might want to use Dependent<T> to cache expensive calculations.

public class AgentViewModel
{
    private Agent _agent;

    private Dependent<int> _totalCasesSolved;

    public AgentViewModel(Agent agent)
    {
        _agent = agent;

        _totalCasesSolved = new Dependent<int>(() => _agent.Cases.Count(c => c.IsSolved));
    }

    public int TotalCasesSolved
    {
        get { return _totalCasesSolved; }
    }
}

The constructor takes a lambda expression that calculates the dependent value. It is cached in the field. Dependent<T> is implicitly convertable to T, so the property returns it directly. When the independent is changed, the dependent is recalculated. You cannot set the Value of a dependent.

Dependent collections

Rather than caching one single value, a view model can also cache a collection of objects. When doing so, use DependentList<T>.

class AgencyViewModel
{
    private Agency _agency;

    private DependentList<AgentViewModel> _agentViewModels;

    public AgencyViewModel(Agency agency)
    {
        _agency = agency;

        _agentViewModels = new DependentList<AgentViewModel>(() =>
            from a in _agency.Agents
            select new AgentViewModel(a)
        );
    }

    public IEnumerable<AgentViewModel> AgentViewModels
    {
        get { return _agentViewModels; }
    }
}

The constructor takes a lambda which produces a collection. Typically, it will do so with a linq expression. The field caches the generated collection, and the property returns it as IEnumerable<T>.

DependentList<T> uses object recycling to reuse elements of the collection. If an object was already in the collection and it is returned from the linq query, then the existing object is reused. So if it in turn has dependent fields or collections, those dependents are not recalculated. To support object recycling, the elements of the collection must implement Equals, GetHashCode, and Dispose.

public class AgentViewModel : IDisposable
{
    ...
    public override bool Equals(object obj)
    {
        if (obj == this)
            return true;
        AgentViewModel that = obj as AgentViewModel;
        if (that == null)
            return false;
        return this._agent == that._agent;
    }

    public override int GetHashCode()
    {
        return _agent.GetHashCode();
    }

    public void Dispose()
    {
    }
}

The Dispose method will be called for objects removed from the dependent collection, so you can use it to manage any extra resources required to represent the collection in an external system. If you have no such resources, leave the body of Dispose blank.

These new generics should make it easier to declare independent fields and collections in the model, and dependent fields and collections in the view model. With them, no plug in or generated code is required.

Comments

Data Virtualization with IndependentList<T>

Hi, Im trying UpdateControls and it was going very good so far, but now Im facing problems using IndependentList with a lot of data and I want to virtualize it into a datagrid.
VirtualizingStackPanel.IsVirtualizing property seens to be not working in this model.
Could you please give me some tips about the better way to do that?

Great work with UpdateControls.

Regards.
Murilo

Data virtualization not implemented

Data virtualization is a great technique for displaying large collections of data from a database, web service, or some other time-consuming source. Unfortunately, it is not implemented in Update Controls.

Update Controls does all of its dependency tracking in memory. When you bind the view model to the view, it needs to already have loaded all of the backing data. Update Controls does not hook into your data access layer or service agent, so it can't go back and load more data as needed.

My advice would be to use traditional data binding for large collections, especially those backed by a database query or web service call. Use Update Controls once the user has narrowed their focus down to a set of objects that fit comfortably in memory.

Alternatively, you could change your user interface paradigm so that you don't present scrolling lists of data to the user. Use search or browse patterns instead of lists. That way your code can decide when to pull a logical set of data from the database, and the results will always fit in memory. Don't take this alternative just to work around the memory-bound limitations of Update Controls, however. Do it if it makes more sense for your users.

I did it, but now Im having another problem.

Hi, Michael.

Thanks for your reply, It clarifies a lot what I was doing wrong.

Now I have an IndependentList that is been constantly updated by another thread and in my presentation class I have a DependentList that is calculated by this IndependentList.

Ok, that was a good approach, but all is going fine and the list is been updated on screen until something happens and it stops to be refreshed.

I'll post my example here. Could you please tell me what I'm doing wrong again?

public class PresentationClass
{

private DependentList m_depListEntity;
private Dependent m_depItemsToBeDisplayed;

private NavigationClass m_objNavigation;
private WindowModel m_objWindowModel;

public PresentationClass(WindowModel p_objWindowModel, NavigationClass p_objNavigation) {

m_objWindowModel = p_objWindowModel;
m_objNavigation = p_objNavigation;

m_depListEntity = new DependentList(
() => UpdateListEntity()
);
m_depItemsToBeDisplayed = new Dependent(() => CalculateItemsToBeDisplayed());
}

public NavigationClass Navigation
{
get { return m_objNavigation; }
}

private IEnumerable UpdateListEntity()
{
return Navigation.SelectedSource != null ? Navigation.SelectedSource.MyEntityList.Take(m_depItemsToBeDisplayed) : new List();
}

private int CalculateItemsToBeDisplayed() {
//I'll show only the itens to fill the window heigth. My datagrid is not scrolling.
//m_objWindowModel.Height is an Independent property of m_objWindowModel.
int result = Math.Max(3,Convert.ToInt32(m_objWindowModel.Height / 16) + 3);
return result;
}

public IEnumerable ListEntity
{
get { return m_depListEntity; }
}

}

public class NavigationClass
{
public NavigationClass()
{
}
private Independent m_objSelectedSource = new Independent();

public Source SelectedSource
{
get { return m_objSelectedSource; }
set { m_objSelectedSource.Value = value; }
}
}

public class Source
{

private IndependentList m_objMyEntityList = new IndependentList();

public IEnumerable MyEntityList
{
get { return m_objMyEntityList; }
}

//This method is used to provide access to another thread to modify this list;
public IList MyEntityList
{
get { return m_objMyEntityList; }
}

}

In my XAML the DataContext is a UpdateControls Wrapper of my PresendationClass instance.

I don't know why it stops to be refreshed and there are no exceptions been throwing.

Please, help me.

Best Regards.
Thanks in advance.

ps.: In the IndependentList and DependentList source code I changed the following methods to prevent "Collection was modified exception":

public IEnumerator GetEnumerator()
{
_indList.OnGet();
for (int i = 0; i < _list.Count; i++)
yield return _list[i];
//return _list.GetEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
_indList.OnGet();
for (int i = 0; i < _list.Count; i++)
yield return _list[i];
//return ((System.Collections.IEnumerable)_list).GetEnumerator();
}

Murilo

Finally

Hi, Michael.

Finally I did it.
I just removed the DependetList and put the calculate content on the property binded to my datagrid.

public IEnumerable MyEntityList
{
get { return Navigation.SelectedSource != null ? Navigation.SelectedSource.MyEntityList.Take(m_depItemsToBeDisplayed) : new List(); }
}

It solved my problem, but Im confused about when I should use a DEPENDENT object and why it stoped to refresh like it was losing its precedents to be refreshed.

Regards.

Murilo

Multithreaded Independents

Sorry for the delay in getting back with you. I was out at DevLink in Chatanooga. Great conference.

I think the problem is that you don't have a lock(this) around the IndependentList. Whenever you are updating an Independent from a background thread, you need to lock around every read and write.

I think you were just getting lucky that the only problem was that it stopped updating. At worst, not locking around a shared resource can corrupt the list or throw an exception.

Give this a try and let me know if it works.

DependentList requiring IDisposable

I was surprised to learn that DependentList<T> requires T to implement IDisposable. I looked at DependentList's source code, and I couldn't find a call to Dispose()... then I realized that it's actually a constraint of RecycleBin. I actually wanted to use DependentList for filtering--not for transforming the Model to a ViewModel, but just filtering a list without changing its type. I'm guess I should use Dependent<List<T>> for this purpose instead. Hmm...

Non-IDisposable version checked in

Dependent<List<T>> would work fine for filtering, since it contains objects that were already created. I wouldn't recommend it for collections that you fill with new objects, since it circumvents object recycling, which could be a noticeable performance hit.

I checked in a change that removes the IDisposable constraint from DependentList and RecycleBin. I just haven't released this one, yet. If you'd like to test it, please get the latest.

Performance hit?

Assuming a list of "trivial" ViewModels (stateless wrappers around model objects), what would cause the performance hit over and above what's already there? After all, even with a RecycleBin, the overhead still exists of creating a new series of ViewModels, and a new series of ListBoxItem/DependentListViewItem. In the case of WinForms at least (as that's what I'm using), the list control (ListBox or ListView) is cleared and refilled "from scratch" for every change.

RecycleBin performance

The performance hit that I refer to when circumventing RecycleBin is related to updating its dependents. If you use a RecycleBin to manage a collection of objects, and those objects use Dependent<T> or Dependent to manage dependent properties, then those dependent properties are not recalculated. If you don't use RecycleBin, then they are.

In you example, you have a trivial view model, so it doesn't manage its own dependent properties. Furthermore, you aren't creating the objects while updating the collection: they are provided by another owner. So you would not see any performance gain by using RecycleBin (or any hit by circumventing it).

In the case of ListBox and ListView, I could have used RecycleBin to manage the contents of the list more efficiently. Instead of clearing and rebuilding the Items collection, I could have determined the fewest number of Inserts and Removes required to get the desired results. This is a patterns I've started using in WPF to manage ObservableCollections, but I never applied it to WinForms. Please let me know if you find any performance issues here, and we can work on improving this code.