tags:

views:

45

answers:

3

In my code I can reference the Column using it's index. But I'd much rather use it's name. Is this possible? I have no idea. :D

<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="800">   

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition x:Name="LeftColumn" Width="200"></ColumnDefinition>
        <ColumnDefinition x:Name="MiddleColumn" Width="200"></ColumnDefinition>
        <ColumnDefinition x:Name="RightColumn" Width="*"></ColumnDefinition>
    </Grid.ColumnDefinitions>

    <Button x:Name="WelcomeButton" Grid.Column="1">Click me.</Button> //Here!
</Grid>

Edit:

If it isn't possible to reference it by name, what use is there to set a name for a column? (I'm new to WPF :P)

A: 

No, it isn't.

If you really wanted to, you could write a custom ValueConverter or attached property to fake it, but I wouldn't recommend it.

SLaks
A: 

No, it's not possible. Name is here to access it in code behind so that you can resize it, or bind it with other elements xaml to resize it at runtime.

viky
+3  A: 

Yes! The beauty of WPF is how trivial it is to add the features you want. You can easily add named grid rows and columns using attached properties. For example, the code below will allow you to do this:

<Grid ShowGridLines="True">
  <Grid.ColumnDefinitions>
    <ColumnDefinition x:Name="LeftColumn" Width="200" />
    <ColumnDefinition x:Name="MiddleColumn" Width="200" />
    <ColumnDefinition x:Name="RightColumn" Width="*" />
  </Grid.ColumnDefinitions>
  <Button my:Grid_Named.Column="RightColumn">Click me.</Button>
</Grid>

Here's the code itself. It's quite straightforward and took only a few minutes to write:

public static class Grid_Named
{
  // Define Grid_Name.Column property
  public static string GetColumn(DependencyObject obj) { return (string)obj.GetValue(ColumnProperty); }
  public static void SetColumn(DependencyObject obj, string value) { obj.SetValue(ColumnProperty, value); }
  public static readonly DependencyProperty ColumnProperty = DependencyProperty.RegisterAttached("Column", typeof(string), typeof(Grid_Named), new UIPropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
      {
        if(e.OldValue!=null) BindingOperations.ClearBinding(obj, Grid.ColumnProperty);
        if(e.NewValue!=null) BindingOperations.SetBinding(obj, Grid.ColumnProperty, _columnBinding);
      },
  });

  // Define Grid_Named.Row property
  public static string GetRow(DependencyObject obj) { return (string)obj.GetValue(RowProperty); }
  public static void SetRow(DependencyObject obj, string value) { obj.SetValue(RowProperty, value); }
  public static readonly DependencyProperty RowProperty = DependencyProperty.RegisterAttached("Row", typeof(string), typeof(Grid_Named), new UIPropertyMetadata
  {
    PropertyChangedCallback = (obj, e) =>
    {
      if(e.OldValue!=null) BindingOperations.ClearBinding(obj, Grid.RowProperty);
      if(e.NewValue!=null) BindingOperations.SetBinding(obj, Grid.RowProperty, _rowBinding);
    },
  });

  // Construct bindings for Grid.Column and Grid.Row
  private static BindingBase _columnBinding = BuildBinding(grid => grid.ColumnDefinitions, ColumnProperty);
  private static BindingBase _rowBinding = BuildBinding(grid => grid.RowDefinitions, RowProperty);
  private static BindingBase BuildBinding(Func<Grid, IList> getDefinitions, DependencyProperty nameProperty)
  {
    var binding = new MultiBinding
    {
      Converter = new NameLookupConverter { GetDefinitions = getDefinitions }
    };
    binding.Bindings.Add(new Binding { Path = new PropertyPath(nameProperty), RelativeSource = RelativeSource.Self });
    binding.Bindings.Add(new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(Grid), 1) });
    return binding;
  }

  // Converter to take a row/column name and a Grid, and find the index of that row/column in the grid
  private class NameLookupConverter : IMultiValueConverter
  {
    public Func<Grid, IList> GetDefinitions;

    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
      var name = values[0] as string;
      var grid = values[1] as Grid;

      if(grid==null || name==null) return 0;

      object namedObject = grid.FindName(name);
      if(namedObject==null) return 0;

      int index = GetDefinitions(grid).IndexOf(namedObject);
      return index<0 ? 0 : index;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
      throw new NotImplementedException();
    }
  }
}

As you can see, any time you add an attached property Grid_Named.Column or Grid_Named.Row, it simply creates a binding for Grid.Column or Grid.Row that uses a IMultiValueConveter to select the appropriate column or row number.

Simple, no?

Ray Burns