Wednesday, October 19, 2011

Refactoring to expose an ObservableCollection as a ReadOnlyObservableCollection property

I was working with ObservableCollection<> objects and as the code/design/architecture matured, I wanted to expose them as true read-only properties.
This is what my initial code looked like:

//This is what I had
private ObservableCollection<int> _myIntCollection;
publ ObservableCollection<int> myIntCollection
{
//This does not give me much other than not being able to create a new myIntCollection. I can add/delete items from the collection
    get
    {
        return _myIntCollection;
    }
}
//...Code that creates a new _myIntCollection who knows where and how many times

Intellisense showed me ReadOnlyObservableCollection<> along with ObservableCollection<> and that looked like just what I wanted!

//First change
private ObservableCollection<int> _myIntCollection;
public ReadOnlyObservableCollection<int> myIntCollection
{
    get
    {
//Cool: now my presenters/clients cannot modify the collection also
//But: I create a new collection on every read,
//and that can affect the performance as well as cause other unexpected issues
        return new ReadOnlyObservableCollection<int>(_myIntCollection);
    }
}
//...Code that creates a new _myIntCollection who knows where and how many times

I figured that it is not neat to create a new instance of a readonly collection every time I want to access the property, and found this post:
ReadOnlyObservableCollection anti-pattern
I liked the idea of creating the new readonly collection when I create my base observable collection, but I did not want find all references and add code to every "new" statement.
A more manageable way of refactoring to expose an ObservableCollection as a ReadOnlyObservableCollection is to convert the base ObservableCollection object also into a property that initializes the ReadOnlyObservableCollection object whenever the base object is set:

//Much better, and I don't have to worry about
//"who knows where and how many times" _myIntCollection is created in my code

//The real writable collection
private ObservableCollection<int> _w_myIntCollection;

//My existing code uses _myIntCollection to create the collection and I don't want to change that
private ObservableCollection<int> _myIntCollection
{
    get
    {
        return _w_myIntCollection;
    }
    set
    {
        _w_myIntCollection = value;
//with this, whereever there is _myIntCollection = new ObservableCollection in the existing code,
//it will create my readonly collection also now
        _r_myIntCollection = new ReadOnlyObservableCollection<int>(_w_myIntCollection);
    }
}
//my read-only collection used within the class
private ReadOnlyObservableCollection<int> _r_myIntCollection;

//read-only property so that presenters/clients cannot create a new read-only collection
public ReadOnlyObservableCollection<int> myIntCollection
{
    get
    {
        return _r_myIntCollection;
    }
}
//...Code that creates a new _myIntCollection who knows where and how many times

Friday, October 14, 2011

Multidimensional Indexed property binding in WPF

When I searched for indexed property binding in WPF, I did find some useful information like this one:
If I have a binding like so in XAML:
Text="{Binding [5], Mode=OneWay}"

Then, with this, if my datacontext has an integer indexer this[int indx] then I would get the element at index 5.
And if my datacontext has an indexer this[string sindx] (and no integer indexer), then I will get the element indexed by "5".
So far so good.

But, if I want to bind to a multidimensional indexed property, then it took a bit of "research" to figure out that I can do it by:
Text="{Binding [5\,1], Mode=OneWay}"

Good to know... {Binding [2\,hello], Mode=OneWay} in the example below translates to:


            Binding b = new Binding("[2,hello]");
            b.Mode = BindingMode.OneWay;
            textBlock5.SetBinding(TextBox.TextProperty, b);

XAML to test indexer binding:



<TextBox Name="textBlock1" Text="{Binding [5], Mode=OneWay}" />
<TextBox Name="textBlock2" Text="{Binding [some\,string],Mode=OneWay}" />
<TextBox Name="textBlock3" Text="{Binding (Name), Mode=OneWay,RelativeSource={x:Static RelativeSource.Self }}" />
<TextBox Name="textBlock4" Text="{Binding SomeProp, Mode=OneWay}" />
<TextBox Name="textBlock5" Text="{Binding [2\,hello], Mode=OneWay}" />
<TextBox Name="textBlock6" Text="{Binding [Jagged][Jagged indexer property], Mode=OneWay}"/>


ViewModel:

    public class TestVM : INotifyPropertyChanged
    {
        public string this[int i]
        { get { return "Int single dimensional indexer: " + i.ToString(); } }
        public string this[string s1, string s2]
        { get{ return "String Multi Dimensional Indexer Called: " + s1 + " , " + s2; } }
        public string this[int i1, string s1]
        { get{ return "Integer String Multi Dimensional Indexer Called: " + i1.ToString() + " , " + s1; } }
        public string SomeProp
        { get{ return "SomeProp: Hello";} }
        public object this[string s]
        {
            get
            {
                if (s == "Jagged")
                    return this;//Just being lazy: test accessing something as [][] when the initial indexer is "Jagged"
                else
                    return "String single dimensional indexer: " + s;
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;
        public void Notify(string propertyname)
        {if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyname)); }
    }