Фишки XAML-разработчика: динамический Grid

от автора

В статье рассмотрим несколько полезных усовершенствований для контрола Grid.
image
Классический сценарий использования Grid предполагает следующий синтаксис

<Grid>     <Grid.RowDefinitions>         <RowDefinition Height="*"/>         <RowDefinition MinHeight="20" Height="Auto"/>         <RowDefinition Height="*"/>         <RowDefinition Height="2*"/>         <RowDefinition Height="*" MaxHeight="100"/>         <RowDefinition Height="*"/>         <RowDefinition Height="*"/>     </Grid.RowDefinitions>          <Grid.ColumnDefinitions>         <ColumnDefinition Width="*"/>         <ColumnDefinition MinWidth="100" Width="*" MaxWidth="300"/>         <ColumnDefinition Width="*"/>     </Grid.ColumnDefinitions>          <TextBlock     	Grid.Row="1"     	Grid.Column="1"     	Grid.RowSpan="1"     	Grid.ColumnSpan="2">     	     <!--...--> </Grid> 

У него имеется ряд недостатков:
1. Падение лаконичности кода при усложнении интерфейса
2. Случается, при временной cмене типа контрола с Grid на StackPanel, например, необходимо удалять либо комментировать блоки декларации колонок и столбцов, что не всегда удобно
3. Такой Grid достаточно статичен и видоизменять его колонки со столбцами во время работы приложения не слишком сподручно и красиво в контексте паттерна MVVM

Однако существует весьма остроумный способ устранения этих недостатков. Взгляните на следующее расширение Rack (руск. «Стеллаж»)

<Grid Rack.Rows="* 20\Auto * 2* */100 * *" Rack.Columns="* 50\*/100 *">     <TextBlock Rack.Set="R1 C1 RS1 CS2">     <!--...--> </Grid> 

1. Код лаконичен
2. При замене типа контрола ничего не нужно комментировать или удалять
3. Доступна высокая степень динамичности интерфейса

<Grid      Rack.Rows="{Binding Property1, Converter={StaticResource RowsConverter}}"      Rack.Columns="{Binding Property1, Converter={StaticResource ColumnsConverter}}" >          <TextBlock Rack.Set="{Binding Property1, Converter={StaticResource TextPositionConverter}}">     <!--...--> </Grid> 

По началу такой синтаксис выглядит непривычно, но на деле он не сложнее, чем, скажем, объявление векторной геометрии для Path. В строке [Rack.Rows="* 20\Auto * 2* */100 * *"] через запятую либо пробел происходит декларация колонок [столбцов], а опциональные параметры «20\» и «/100» задают минимальные и максимальные размеры соответственно. В свою очередь [Rack.Set=«R1 C1 RS1 CS2»] означает присваивание значений свойствам Grid.Row, Grid.Column, Grid.RowSpan, Grid.ColumnSpan, причём все значения указывать не обязательно, то есть запись [Rack.Set=«R1 C1»] также верна.

Реализовано расширение через вложенные свойства (atteched properties) и включено в библиотеку Aero Framework. Исходный код открыт, поэтому, если вам не нравится, к примеру, предложенный синтаксис, то вы запросто можете видоизменить его по своему усмотрению. Если вы скачаете библиотеку и запустите проект HelloAero, то воочию убедитесь, каким динамичным может стать обычный Grid с применением такого способа декларации. На всякий случай приведу пару скриншотов и исходный код ниже.

Спасибо за Ваше внимание!

Screenshots

Source Code

using System; using System.Diagnostics; using System.Linq; using System.Windows; using System.Windows.Controls;  namespace Aero.Markup {     public static class Rack     {         #region Declarations          public static readonly DependencyProperty ShowLinesProperty = DependencyProperty.RegisterAttached(             "ShowLines", typeof (bool), typeof (Rack), new PropertyMetadata(default(bool), (o, args) =>             {                 var grid = o as Grid;                 if (grid == null) return;                 grid.ShowGridLines = Equals(args.NewValue, true);             }));          public static void SetShowLines(DependencyObject element, bool value)         {             element.SetValue(ShowLinesProperty, value);         }          public static bool GetShowLines(DependencyObject element)         {             return (bool) element.GetValue(ShowLinesProperty);         }          public static readonly DependencyProperty RowsProperty = DependencyProperty.RegisterAttached(             "Rows", typeof (string), typeof (Rack), new PropertyMetadata("", OnRowsPropertyChanged));          public static readonly DependencyProperty ColumnsProperty = DependencyProperty.RegisterAttached(             "Columns", typeof (string), typeof (Rack), new PropertyMetadata("", OnColumnsPropertyChanged));          public static string GetRows(DependencyObject d)         {             return (string) d.GetValue(RowsProperty);         }          public static void SetRows(DependencyObject d, string value)         {             d.SetValue(RowsProperty, value);         }          public static string GetColumns(DependencyObject d)         {             return (string) d.GetValue(ColumnsProperty);         }          public static void SetColumns(DependencyObject d, string value)         {             d.SetValue(ColumnsProperty, value);         }          public static readonly DependencyProperty SetProperty = DependencyProperty.RegisterAttached(             "Set", typeof (string), typeof (Rack), new PropertyMetadata("", OnSetChangedCallback));          public static void SetSet(DependencyObject element, string value)         {             element.SetValue(SetProperty, value);         }          public static string GetSet(DependencyObject element)         {             return (string) element.GetValue(SetProperty);         }          #endregion          private static void OnRowsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)         {             var grid = o as Grid;             if (grid == null) return;              grid.RowDefinitions.Clear();             var patterns = (e.NewValue as string ?? "").Split(new[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);             foreach (var pattern in patterns)             {                 var indexMin = pattern.IndexOf(@"\", StringComparison.Ordinal);                 var indexMax = pattern.IndexOf(@"/", StringComparison.Ordinal);                 var hasMin = indexMin >= 0;                 var hasMax = indexMax >= 0;                 var valueMin = hasMin ? pattern.Substring(0, indexMin) : "";                 var valueMax = hasMax ? pattern.Substring(indexMax + 1, pattern.Length - indexMax - 1) : "";                 var start = hasMin ? indexMin + 1 : 0;                 var finish = hasMax ? indexMax : pattern.Length;                 var value = pattern.Substring(start, finish - start);                 var definition = new RowDefinition {Height = value.ToGridLength()};                 if (valueMin != "") definition.MinHeight = double.Parse(valueMin);                 if (valueMax != "") definition.MaxHeight = double.Parse(valueMax);                 grid.RowDefinitions.Add(definition);             }         }          private static void OnColumnsPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)         {             var grid = o as Grid;             if (grid == null) return;              grid.ColumnDefinitions.Clear();             var patterns = (e.NewValue as string ?? "").Split(new[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);             foreach (var pattern in patterns)             {                 var indexMin = pattern.IndexOf(@"\", StringComparison.Ordinal);                 var indexMax = pattern.IndexOf(@"/", StringComparison.Ordinal);                 var hasMin = indexMin >= 0;                 var hasMax = indexMax >= 0;                 var valueMin = hasMin ? pattern.Substring(0, indexMin) : "";                 var valueMax = hasMax ? pattern.Substring(indexMax + 1, pattern.Length - indexMax - 1) : "";                 var start = hasMin ? indexMin + 1 : 0;                 var finish = hasMax ? indexMax : pattern.Length;                 var value = pattern.Substring(start, finish - start);                 var definition = new ColumnDefinition {Width = value.ToGridLength()};                 if (valueMin != "") definition.MinWidth = double.Parse(valueMin);                 if (valueMax != "") definition.MaxWidth = double.Parse(valueMax);                 grid.ColumnDefinitions.Add(definition);             }         }          private static void OnSetChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)         {             var element = o as FrameworkElement;             if (element == null) return;             var patterns = (e.NewValue as string ?? "").Split(new[] {' ', ','}, StringSplitOptions.RemoveEmptyEntries);             var columnPattern = patterns.FirstOrDefault(p => p.StartsWith("C") && !p.StartsWith("CS")).With(p => p.Replace("C", ""));             var rowPattern = patterns.FirstOrDefault(p => p.StartsWith("R") && !p.StartsWith("RS")).With(p => p.Replace("R", ""));             var columnSpanPattern = patterns.FirstOrDefault(p => p.StartsWith("CS")).With(p => p.Replace("CS", ""));             var rowSpanPattern = patterns.FirstOrDefault(p => p.StartsWith("RS")).With(p => p.Replace("RS", ""));             int column, row, columnSpan, rowSpan;             if (int.TryParse(columnSpanPattern, out columnSpan)) Grid.SetColumnSpan(element, columnSpan);             if (int.TryParse(rowSpanPattern, out rowSpan)) Grid.SetRowSpan(element, rowSpan);             if (int.TryParse(columnPattern, out column)) Grid.SetColumn(element, column);             if (int.TryParse(rowPattern, out row)) Grid.SetRow(element, row);         }          private static GridLength ToGridLength(this string length)         {             try             {                 length = length.Trim();                 if (length.ToLowerInvariant().Equals("auto")) return new GridLength(0, GridUnitType.Auto);                 if (!length.Contains("*")) return new GridLength(double.Parse(length), GridUnitType.Pixel);                 length = length.Replace("*", "");                 if (string.IsNullOrEmpty(length)) length = "1";                 return new GridLength(double.Parse(length), GridUnitType.Star);             }             catch (Exception exception)             {                 Debug.WriteLine(exception.Message);                 return new GridLength();             }         }     } } 

ссылка на оригинал статьи https://habrahabr.ru/post/276077/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *