C# 更改栅格坐标系

C# 更改栅格坐标系,c#,wpf,C#,Wpf,WPF中的网格当前具有如下网格系统: Cols + + + + + | 0 | 1 | 2 | 3 | +--+---|---|---|---|--- 0 | | | | | +--+---|---|---|---|--- Rows 1 | | | | | +--+---|---|---|---|--- 2 | | | | | +--+---|---|---|---|--- Cols

WPF中的网格当前具有如下网格系统:

    Cols
   +   +   +   +   +
   | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|---
 0 |   |   |   |   |
+--+---|---|---|---|---  Rows
 1 |   |   |   |   |   
+--+---|---|---|---|---
 2 |   |   |   |   | 
+--+---|---|---|---|---
    Cols
   +   +   +   +   +
   | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|---
 2 |   |   |   |   |
+--+---|---|---|---|---  Rows
 1 |   |   |   |   |   
+--+---|---|---|---|---
 0 |   |   |   |   | 
+--+---|---|---|---|---
有没有办法让它表现得像这样:

    Cols
   +   +   +   +   +
   | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|---
 0 |   |   |   |   |
+--+---|---|---|---|---  Rows
 1 |   |   |   |   |   
+--+---|---|---|---|---
 2 |   |   |   |   | 
+--+---|---|---|---|---
    Cols
   +   +   +   +   +
   | 0 | 1 | 2 | 3 | 
+--+---|---|---|---|---
 2 |   |   |   |   |
+--+---|---|---|---|---  Rows
 1 |   |   |   |   |   
+--+---|---|---|---|---
 0 |   |   |   |   | 
+--+---|---|---|---|---
理想情况下,我希望行跨度向上延伸项目,而不是向下延伸项目

例如:


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

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

显示多维数据集的
网格是否为固定大小?如果是这样,您可以考虑编写一个视图模型来转换/反转模型坐标,以便在视图中工作,即立方体将具有值<代码>(0,0)< /代码>,并且VIEW模型会将该值暴露为<代码>(0,2)< /代码> .<

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

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

这是一个我认为应该可以做你想做的事情的课程。与其将
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;
    }
}

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

试试这个转换器。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。第一个栅格为原始栅格,第二个栅格为翻转栅格:

<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作为converter属性的原因。

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

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

这将还原该行。

您能否给出一个示例,说明为什么需要该行?您能否更具体地说明如何创建从网格继承的自定义控件?例如,覆盖哪些方法,这相当混乱。我认为这不应该是一个大问题。您应该研究如何实现附加属性,因为面板就是这样执行布局的。如果你遇到了具体的问题,请随便问另一个问题。