2012-04-05 4 views
6

Сетка в WPF в настоящее время имеет подвесную систему, как это:Изменение сетки системы координат

Cols 
    + + + + + 
    | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|--- 
0 | | | | | 
+--+---|---|---|---|--- Rows 
1 | | | | | 
+--+---|---|---|---|--- 
2 | | | | | 
+--+---|---|---|---|--- 

Есть ли способ, чтобы заставить его вести себя так:

Cols 
    + + + + + 
    | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|--- 
2 | | | | | 
+--+---|---|---|---|--- Rows 
1 | | | | | 
+--+---|---|---|---|--- 
0 | | | | | 
+--+---|---|---|---|--- 

В идеале я хотел бы, RowSpan для увеличения предмета вверх, а не вниз.

Пример:

Мои DataSource хранит куб на карте как 0,0 с целью его отображается в левом нижнем углу. Однако сетка в WPF поместит этот куб в верхнем левом углу. Другая проблема заключается в том, что источник данных дает мне 2x2 с положением нижнего левого «якорного» положения с шириной и высотой. Ширина и высота связаны с ColSpan и RowSpan. RowSpan - проблема, потому что она будет расширяться по сетке, а не вверх.

+1

Не могли бы вы привести пример, который иллюстрирует, зачем вам это нужно? – sll

+0

Я добавил пример – Robert

+1

классная диаграмма :) – Aftnix

ответ

1

Вы должны быть в состоянии сделать это, не создавая пользовательский или пользовательский контроль, используя прикрепленные свойства.

Вот класс, который, как я думаю, должен делать то, что вы хотите. Вместо привязки значений Grid.Row и Grid.RowSpan к вашим строкам и высоте свяжите GridEx.RowFromBottom и GridEx.RowSpanFromBottom им. Обработчики изменения свойств для этих свойств будут вычислять новое значение Grid.Row на основе значений этих свойств и количества строк в сетке.

Одна из потенциальных проблем заключается в том, что это может неправильно обновляться, если вы добавляете или вычитаете строки из сетки во время выполнения.

public static class GridEx 
{ 
    public static readonly DependencyProperty RowFromBottomProperty = DependencyProperty.RegisterAttached("RowFromBottom", typeof(int?), typeof(GridEx), new FrameworkPropertyMetadata(default(int?), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, OnRowFromBottomChanged)); 
    public static readonly DependencyProperty RowSpanFromBottomProperty = DependencyProperty.RegisterAttached("RowSpanFromBottom", typeof(int?), typeof(GridEx), new FrameworkPropertyMetadata(default(int?), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, OnRowSpanFromBottomChanged)); 

    private static void OnRowFromBottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var grid = GetContainingGrid(d); 
     int? rowFromBottom = (int?) e.NewValue; 
     int? rowSpanFromBottom = GetRowSpanFromBottom(d); 
     if (rowFromBottom == null || rowSpanFromBottom == null) return; 
     int rows = grid.RowDefinitions.Count; 
     int row = Math.Max(0, Math.Min(rows, rows - rowFromBottom.Value - rowSpanFromBottom.Value)); 
     Grid.SetRow((UIElement) d, row); 
    } 

    private static void OnRowSpanFromBottomChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    { 
     var grid = GetContainingGrid(d); 
     int? rowFromBottom = GetRowFromBottom(d); 
     int? rowSpanFromBottom = (int?)e.NewValue; 
     if (rowFromBottom == null || rowSpanFromBottom == null) return; 
     int rows = grid.RowDefinitions.Count; 
     int row = Math.Max(0, Math.Min(rows, rows - rowFromBottom.Value - rowSpanFromBottom.Value)); 
     Grid.SetRow((UIElement)d, row); 
     Grid.SetRowSpan((UIElement)d, rowSpanFromBottom.Value); 
    } 

    public static int? GetRowFromBottom(DependencyObject obj) 
    { 
     return (int?) obj.GetValue(RowFromBottomProperty); 
    } 

    public static void SetRowFromBottom(DependencyObject obj, int? value) 
    { 
     obj.SetValue(RowFromBottomProperty, value); 
    } 

    public static int? GetRowSpanFromBottom(DependencyObject obj) 
    { 
     return (int?)obj.GetValue(RowSpanFromBottomProperty); 
    } 

    public static void SetRowSpanFromBottom(DependencyObject obj, int? value) 
    { 
     obj.SetValue(RowSpanFromBottomProperty, value); 
    } 

    private static Grid GetContainingGrid(DependencyObject element) 
    { 
     Grid grid = null; 
     while (grid == null && element != null) 
     { 
      element = LogicalTreeHelper.GetParent(element); 
      grid = element as Grid; 
     } 
     return grid; 
    } 
} 

Если у вас есть вопросы о том, что здесь происходит, не стесняйтесь спрашивать.

1

Вы можете добиться этого, написав собственный пользовательский элемент управления. Вы можете унаследовать от Grid или, наоборот, использовать UserControl с Grid на нем. В любом случае, вы предоставили бы прикрепленные свойства аналогично Grid, и затем вы могли бы манипулировать значениями по своему усмотрению и затем передать их на базовый Grid.

+0

Не могли бы вы более конкретно создать пользовательский элемент управления, который наследуется от Grid? Например, какие методы переопределить, это довольно запутанно. – Robert

+0

Я не думаю, что это должна быть большая проблема. Вы должны исследовать, как реализовать прикрепленные свойства, поскольку именно так Панели выполняют макет. Если вы столкнулись с определенными проблемами, не стесняйтесь задавать еще один вопрос о SO. –

1

Является ли Grid, где вы показываете кубы фиксированного размера? Если это так, вы можете рассмотреть возможность создания ViewModel, который преобразует/изменяет координаты модели, чтобы работать в представлении, то есть куб имел бы значение (0,0), и ViewModel выставит это значение как (0,2).

Просто идея, которая может быть проще, чем опрокинуть собственный контроль.

1

Попробуйте этот преобразователь. XAML выглядеть немного сложнее, но не ViewModel (s) или UserControl (s) не требуется:

конвертер переворачивать строки:

public class UpsideDownRowConverter : IMultiValueConverter 
{ 
    public int RowCount 
    { 
     get; 
     set; 
    } 

    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (values.Length == 2 && values[0] is int && values[1] is int) 
     { 
      var row = (int)values[0]; 
      var rowSpan = (int)values[1]; 

      row = this.RowCount - row - rowSpan; 

      return row; 
     } 

     return DependencyProperty.UnsetValue; 
    } 

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

XAML. Первая сетка является оригинальной, вторым переворачиваются:

<Window.Resources> 
    <local:UpsideDownRowConverter x:Key="UpsideDownRowConverter" 
            RowCount="3"/> 
</Window.Resources> 
<UniformGrid Rows="2"> 
    <Grid Name="Original" 
      Margin="0,0,0,10"> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <Grid.RowDefinitions> 
      <RowDefinition/> 
      <RowDefinition/> 
      <RowDefinition/> 
     </Grid.RowDefinitions> 
     <Rectangle Fill="Green" 
        Grid.Column="0" 
        Grid.Row="2"/> 
     <Rectangle Fill="Red" 
        Grid.Column="1" 
        Grid.Row="1"/> 
     <Rectangle Fill="Blue" 
        Grid.Column="2" 
        Grid.RowSpan="3"/> 
     <Rectangle Fill="Yellow" 
        Grid.Column="3" 
        Grid.RowSpan="2"/> 
    </Grid> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <Grid.RowDefinitions> 
      <RowDefinition/> 
      <RowDefinition/> 
      <RowDefinition/> 
     </Grid.RowDefinitions> 
     <Rectangle Fill="Green" 
        Grid.Column="0"> 
      <Grid.Row> 
       <MultiBinding Converter="{StaticResource UpsideDownRowConverter}"> 
        <Binding Path="Children[0].(Grid.Row)" 
           ElementName="Original"/> 
        <Binding Path="(Grid.RowSpan)" 
           RelativeSource="{RelativeSource Self}"/> 
       </MultiBinding> 
      </Grid.Row> 
     </Rectangle> 
     <Rectangle Fill="Red" 
        Grid.Column="1"> 
      <Grid.Row> 
       <MultiBinding Converter="{StaticResource UpsideDownRowConverter}"> 
        <Binding Path="Children[1].(Grid.Row)" 
           ElementName="Original"/> 
        <Binding Path="(Grid.RowSpan)" 
           RelativeSource="{RelativeSource Self}"/> 
       </MultiBinding> 
      </Grid.Row> 
     </Rectangle> 
     <Rectangle Fill="Blue" 
        Grid.Column="2" 
        Grid.RowSpan="3"> 
      <Grid.Row> 
       <MultiBinding Converter="{StaticResource UpsideDownRowConverter}"> 
        <Binding Path="Children[2].(Grid.Row)" 
           ElementName="Original"/> 
        <Binding Path="(Grid.RowSpan)" 
           RelativeSource="{RelativeSource Self}"/> 
       </MultiBinding> 
      </Grid.Row> 
     </Rectangle> 
     <Rectangle Fill="Yellow" 
        Grid.Column="3" 
        Grid.RowSpan="2"> 
      <Grid.Row> 
       <MultiBinding Converter="{StaticResource UpsideDownRowConverter}"> 
        <Binding Path="Children[3].(Grid.Row)" 
           ElementName="Original"/> 
        <Binding Path="(Grid.RowSpan)" 
           RelativeSource="{RelativeSource Self}"/> 
       </MultiBinding> 
      </Grid.Row> 
     </Rectangle> 
    </Grid> 
</UniformGrid> 

К сожалению, она не в состоянии передать количество строк в качестве третьего значения, поскольку RowDefinitionCollection не уведомляет об изменениях. Вот почему я добавил RowCount как свойство конвертера.

0

Вы можете преобразовать строку в обратный формат, как показано ниже:

private void ReverseRow(Grid grd) 
    { 
     int totalRows = grd.RowDefinitions.Count-1; 
     foreach (UIElement ctl in grd.Children) 
     { 
      int currentRowIndex = Grid.GetRow(ctl); 
      Grid.SetRow(ctl, totalRows - currentRowIndex); 
     } 
    } 

Это отменит строку.

Смежные вопросы