This project is read-only.

How to use this toolkit to make DataGridTextColumn bindable?

Dec 15, 2009 at 5:26 AM

Hi all,

I am new to silverlight and a bit struggling in understanding this framework... :(

Can someone please tell me how to use this framework to make the DataGridTextColumn bindable in a viewmodel way?

 

Many thanks!

Xin

Dec 18, 2009 at 12:45 AM

Hi Xin,

Below is a sample code that shows how to make a Bindable DataGridTextColumn class,  in this case we have just made the Header property bindable but you could change other stuff too.

    public class BindableDataGridTextColumn : DataGridTextColumn

    {

        public static readonly DependencyProperty HeaderProperty =

            DependencyProperty.Register("Header", typeof(object), typeof(BindableDataGridTextColumn),

            new PropertyMetadata(OnHeaderChanged));

        private Dictionary<DependencyProperty, BindingInfo> _pendingBindings;

        private FrameworkElement _associatedObject;

        public FrameworkElement AssociatedObject

        {

            get { return _associatedObject; }

            set

            {

                _associatedObject = value;

                if (value != null) ApplyAllPendingBindings();

            }

        }

        public Binding HeaderBinding

        {

            get { return GetBinding(HeaderProperty); }

            set { SetBinding<object>(HeaderProperty, value); }

        }

#region Binding Helpers

        protected void SetBinding

(DependencyProperty bindingProperty, Binding value)

        {

            SetBinding(bindingProperty, typeof(P), value);

        }

        protected void SetBinding(DependencyProperty bindingProperty, Type bindingType, Binding value)

        {

            if (this.AssociatedObject != null)

                this.SetAttachedBinding(AssociatedObject, bindingProperty, bindingType, value);

            else

            {

                if (_pendingBindings == null)

                    _pendingBindings = new Dictionary<DependencyProperty, BindingInfo>();

                _pendingBindings.Add(bindingProperty,

                    new BindingInfo() { BindingProperty = bindingProperty, BindingType = bindingType, Binding = value });

            }

        }

        protected Binding GetBinding(DependencyProperty bindingProperty)

        {

            if (this.AssociatedObject != null)

                return this.GetAttachedBinding(bindingProperty);

            else if (_pendingBindings != null && _pendingBindings.ContainsKey(bindingProperty))

                return _pendingBindings[bindingProperty].Binding;

            else

                return null;

        }

        protected void ApplyAllPendingBindings()

        {

            if (_pendingBindings == null || _pendingBindings.Count == 0) return;

            // set all bindings

            foreach (var _bindingInfo in _pendingBindings.Values)

                this.SetAttachedBinding(AssociatedObject, _bindingInfo);

            // clear the pending

            _pendingBindings.Clear();

            _pendingBindings = null;

        }

        protected void ClearAllBindings()

        {

            if (_pendingBindings != null) _pendingBindings = null;

            this.ClearAttachedBindings();

        }

#endregion

#region Handlers

        static void OnHeaderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)

        {

            ((BindableDataGridTextColumn)d).UpdateHeader(e.NewValue);

        }

        void UpdateHeader(Object header)

        {

            this.Header = header;

        }

#endregion

    }

Now, there is one thing you need to supply before it will work –the AssociatedObject (to which the binding “attaches”) of type FrameworkElement. For we could just loop n’ set the DataGrid itself as the associated object:

foreach (var _col in this.dataGrid.Columns)

{

    if (typeof(BindableDataGridTextColumn).IsAssignableFrom(_col.GetType()))

    {

        ((BindableDataGridTextColumn)_col).AssociatedObject = this.dataGrid;

    }

}

A better way would be to make the setting of the AssociatedObject a behavior, and perhaps introduce an Interface.

Hope that helps,

Rishi

Dec 31, 2009 at 12:43 AM

Could you please help me with the binding sintax that I think is going to be something like  Header = { Binding HeaderBinding } but how can I read the current value that will be displayed in the column header? I'm trying to i18n the headers of my SL application.

Thanks!

 Paul

Dec 31, 2009 at 12:48 AM

Thanks Orktane and sorry for the late reply.

I think I will wait until Silverlight 4 comes out because with that version we will be able to bind any Dependency Objects so the datagrid column header will be bindable... :)

Dec 31, 2009 at 12:44 PM

@pauljs, With the BindableDataGridTextColumn you will use the HeaderBinding property, so something like will work:

<BindableDataGridTextColumn HeaderBinding={Binding SalesTitle} ... />

where SalesTitle is like a property in your ViewModel. Now for more realistic solutions rather than having per column header property in your ViewModel, you will use some kind of TypeDescriptor or Reflected meta-data from your Model.

Rishi

Dec 31, 2009 at 12:56 PM

@Xin, absolutely SL4 will make binding life so much easier.. also, if you find the BindableDataGridTextColumn thing a bit overwhelming, there is another solution for SL3 - you can make use of ValueRelays. Basically, you declare ValueRelays as a resource, and then using BridgeValueBehavior you can source the value from your ViewModel. Once you've bridged to your ViewModel, you can bind the ValueRelay to your header with StaticResource binding (eg. Header = {StaticResource SalesTitleRelay}). The problem with this that is you'll have to do this per column - so that's the trade-off. 

Value Relays are described here http://www.orktane.com/Blog/post/2009/10/10/Introducing-nRouteToolkit-for-Silverlight-(Part-II).aspx and it works just like Command Relays sample described in that post. 

Rishi

Dec 31, 2009 at 6:25 PM
Edited Dec 31, 2009 at 6:31 PM

Thanx! It worked! Now, regarding to having a per column header property in the ViewModel, my approach is to have a property in the ViewModel that returns the generated resource strongly typed class, so I can access the values from the xaml.

Thanks again!

Paul.