Change grid coordinate system

The mesh in WPF currently has this mesh system:

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

Is there a way to make him behave like this:

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

Ideally, I would like RowSpan to expand the element up rather than down.

Example:

My data source stores the cube on the map as 0.0 in order to display it in the lower left corner. However, the grid in WPF will place this cube in the upper left corner. Another problem is that the data source gives me 2x2 with the position of the lower left "anchor" position with width and height. Width and height are associated with ColSpan and RowSpan. RowSpan is a problem because it will expand on the grid, not up.

+6
source share
5 answers

You must do this without creating a user or user control using the attached properties.

Here is a class that I think should do what you want. Instead of binding the values โ€‹โ€‹of Grid.Row and Grid.RowSpan to your row and height, bind GridEx.RowFromBottom and GridEx.RowSpanFromBottom to them. Property change handlers for these properties will calculate the new Grid.Row value based on the values โ€‹โ€‹of these properties and the number of rows in the grid.

One potential problem is that it may not update correctly if you add or subtract rows from the grid at run time.

 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; } } "RowFromBottom", typeof (int), typeof (GridEx), new FrameworkPropertyMetadata (default (int), FrameworkPropertyMetadataOptions.AffectsMeasure |?? FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, 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; } } "RowSpanFromBottom", typeof (int), typeof (GridEx), new FrameworkPropertyMetadata (default (int), FrameworkPropertyMetadataOptions.AffectsMeasure |?? FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsParentMeasure, 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; } } 

If you have any questions about what's going on here, feel free to ask.

+1
source

You can achieve this by writing your own custom control. You can inherit from Grid or, alternatively, use UserControl with Grid on it. In any case, you would provide attached properties in the same way as the Grid , and then you could manipulate the values โ€‹โ€‹as you like, and then transfer them to the base Grid .

+1
source

Is a Grid where you show cubes of a fixed size? If so, you might consider creating a ViewModel that transforms / changes the model coordinates to work in the view, i.e. The cube will have a value of (0,0) , and the ViewModel will show this value as (0,2) .

Just an idea that could be simpler than your own control.

+1
source

Try this converter. XAML looks a little more complicated, but ViewModel or UserControl (s) is not required:

Converter for line feeds:

 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. The first grid is original, the second is upside down:

 <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> 

Unfortunately, it cannot pass the number of rows as the third value, because the RowDefinitionCollection does not notify of changes. So I added RowCount as a converter property.

+1
source

You can convert the string to the inverse format as shown below:

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

This will return a string.

0
source

Source: https://habr.com/ru/post/912534/


All Articles