Search code examples
wpfmultibinding

WPF a Multibinding on ColumnDefinitions


I have a Grid that holds segments such as:

--
  ---
      -----------
                 ---
                    -------------------

Each segment's visual length is dependant upon the total length, and its position depends upon the end of the previous segment.

I have a MultiBinding to achieve this, that calculates Width of 3 ColumnDefinition.

I first thought I'd calculate the entire ColumnDefinitions so that each item is placed correctly, but as it happens, the compiler doesn't allow me to bind Grid.ColumnDefinitions, i.e you can't do this:

<Grid.ColumnDefinitions>
     <MultiBinding Converter="{StaticResource yourConverter}">
            <Binding Path=firstValueForCalculus />
            <Binding Path=secondValueForCalculus />
            ...   
     </MultiBinding>
</Grid.ColumnDefinitions>

So now, I'm thinking my only option is to make 3 IMultiValueConverters, bind each of the 3 ColumnDefinition.Width to their respective converter, and calculate everything... but that sounds very nasty and my "Oh-My-God-You're-Going-Down" alarm rang.

Any idea to do this elegantly?

Thanks!

EDIT: Yurek's code a bit modified : (Is that what you meant? or did I destroy it? :p)

public class GridHelper
    {
        public static readonly DependencyProperty ColumnDefinitionsProperty =
            DependencyProperty.RegisterAttached("ColumnDefinitions", typeof(ColumnDefinitionCollection), typeof(GridHelper),
            new FrameworkPropertyMetadata(null, new PropertyChangedCallback(
            (sender, e) => 
            {
                var element = sender as Grid;
                if (element != null)
                {
                    element.ColumnDefinitions.Clear();
                    foreach (ColumnDefinition col in (IEnumerable<ColumnDefinition>)e.NewValue)
                    {
                        element.ColumnDefinitions.Add(col);
                    }
                    //element.ColumnDefinitions.AddRange((IEnumerable<ColumnDefinition>)e.NewValue);
                }
            })));


        public static IEnumerable<ColumnDefinition> GetColumnDefinitions(Grid element)
        {
            return (IEnumerable<ColumnDefinition>)element.GetValue(ColumnDefinitionsProperty);
        }

        public static void SetColumnDefinitions(Grid element, IEnumerable<ColumnDefinition> columnDefinitions)
        {
            element.SetValue(ColumnDefinitionsProperty, columnDefinitions);
        }
    }

Solution

  • You can't set ColumnDefinitions using binding because it is read only. But there is an elegant solution - to use attched dependency property.

    public class GridHelper
        {
            public static readonly DependencyProperty ColumnDefinitionsProperty =
                DependencyProperty.RegisterAttached("ColumnDefinitions", typeof(ColumnDefinitionCollection), typeof(GridHelper),
                new FrameworkPropertyMetadata(null,
                (sender, e) =>
                {
                    var element = sender as Grid;
                    if(element != null)
                    {
                         element.ColumnDefinitions.Clear();
                         element.ColumnDefinitions.AddRange((IEnumerable<ColumnDefinition>)e.NewValue);
                    }
                }));
    
            public static IEnumerable<ColumnDefinition> GetColumnDefinitions(Grid element)
            {
                return (IEnumerable<ColumnDefinition>)element.GetValue(ColumnDefinitionsProperty);
            }
    
            public static void SetColumnDefinitions(Grid element, IEnumerable<ColumnDefinition> columnDefinitions)
            {
                element.SetValue(ColumnDefinitionsProperty, columnDefinitions);
            }
        }
    

    Now you can use binding for column definitions creation:

    <Grid>
         <local:GridHelper.ColumnDefinitions>
            <MultiBinding Converter="{StaticResource yourConverter}">
                <Binding Path=firstValueForCalculus />
                <Binding Path=secondValueForCalculus />
                ...   
            </MultiBinding>
         </local:GridHelper.ColumnDefinitions>
    </Grid>