【问题标题】:Change Grid Coordinate System更改网格坐标系
【发布时间】:2012-04-05 20:29:32
【问题描述】:

WPF 中的网格目前有一个这样的网格系统:

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

有没有办法让它表现得像这样:

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

理想情况下,我希望 RowSpan 向上而不是向下扩展项目。

例子:

我的数据源在地图上将一个立方体存储为 0,0,以便将其显示在左下角。然而,WPF 中的网格会将该立方体放在左上角。另一个问题是数据源给了我一个 2x2 的位置,左下角的“锚点”位置的宽度和高度。宽度和高度绑定到 ColSpan 和 RowSpan。 RowSpan 是一个问题,因为它将在网格中向下而不是向上扩展。

【问题讨论】:

  • 你能举一个例子来说明你为什么需要它吗?

标签: c# wpf


【解决方案1】:

您应该能够做到这一点,而无需使用附加属性创建自定义或用户控件。

这是一个我认为应该能够做你想做的事的课程。不要将Grid.RowGrid.RowSpan 的值绑定到您的行和高度,而是将GridEx.RowFromBottomGridEx.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;
    }
}

如果您对这里发生的事情有任何疑问,请随时提问。

【讨论】:

    【解决方案2】:

    您可以通过编写自己的自定义控件来实现这一点。您可以从 Grid 继承,或者使用带有 GridUserControl。无论哪种方式,您都可以提供类似于Grid 的附加属性,然后您可以根据需要操作这些值,然后将它们传递给底层Grid

    【讨论】:

    • 您能否更具体地说明如何创建一个继承自 Grid 的自定义控件?例如要覆盖哪些方法,这是相当混乱的。
    • 我认为这应该不是什么大问题。您应该研究如何实现附加属性,因为这就是面板执行布局的方式。如果您遇到具体问题,请随时在 SO 上提出另一个问题。
    【解决方案3】:

    显示立方体的Grid 是固定大小的吗?如果是这样,您可以考虑编写一个转换/反转模型坐标以便在视图中工作的 ViewModel,即,立方体将具有值 (0,0),而 ViewModel 将将该值公开为 (0,2)

    只是一个可能比滚动您自己的控件更容易的想法。

    【讨论】:

      【解决方案4】:

      试试这个转换器。 XAML 看起来有点复杂,但不需要 ViewModel 或 UserControl:

      转换器翻转行:

      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。第一个Grid是原始的,第二个是翻转的:

      <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 添加为转换器的属性。

      【讨论】:

        【解决方案5】:

        您可以将行转换为反向格式,如下所示:

            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);
                }
            }
        

        这将恢复该行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多