Есть ли способ настроить TileBru sh так, чтобы он всегда был 1 плиткой высотой, но переменным количеством плиток в ширину? - PullRequest
1 голос
/ 04 апреля 2020

Я пытаюсь создать TileBrush, который при использовании всегда будет иметь высоту в один элемент мозаичного изображения и переменное количество элементов в ширину (в зависимости от того, сколько места имеет элемент, к которому он применяется).

Чтобы проиллюстрировать мой вопрос, я написал немного кода. В указанном коде есть два TextBlock в Grid. Оба TextBlock имеют одинаковый фон, но разный размер шрифта. В настоящее время фон каждого TextBlock всегда имеет высоту 1 и 4 тайла.

Какие изменения мне нужно сделать, чтобы фон каждого TextBlock всегда имел высоту в 1 плитку, но переменное количество плиток в ширину (в зависимости от размера шрифта TextBlock и ширина содержащего Grid столбца)?

<Window x:Class="WpfTestApp01.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfTestApp01"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>

        <DrawingBrush x:Key="TestBrush"
                      TileMode="Tile"
                      Stretch="Uniform"
                      Viewbox="0,0,1,1"
                      ViewboxUnits="RelativeToBoundingBox"
                      Viewport="0,0,0.25,1"
                      ViewportUnits="RelativeToBoundingBox">
            <DrawingBrush.Drawing>
                <GeometryDrawing>
                    <GeometryDrawing.Geometry>
                        <GeometryGroup>
                            <EllipseGeometry Center="50,50"
                                             RadiusX="20"
                                             RadiusY="45"/>
                            <EllipseGeometry Center="50,50"
                                             RadiusX="45"
                                             RadiusY="20"/>
                        </GeometryGroup>
                    </GeometryDrawing.Geometry>
                    <GeometryDrawing.Brush>
                        <SolidColorBrush Color="Gray" />
                    </GeometryDrawing.Brush>
                    <GeometryDrawing.Pen>
                        <Pen Thickness="2"
                             Brush="Black"/>
                    </GeometryDrawing.Pen>
                </GeometryDrawing>
            </DrawingBrush.Drawing>
        </DrawingBrush>

    </Window.Resources>

    <Grid>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <GridSplitter Grid.Row="0"
                      Grid.Column="1"
                      Grid.RowSpan="2"
                      Width="3"
                      VerticalAlignment="Stretch"
                      HorizontalAlignment="Center"
                      Background="Black"/>

        <TextBlock Text="Left text here."
                   FontSize="12"
                   FontFamily="Consolas"
                   Grid.Row="0"
                   Grid.Column="0"
                   Background="{StaticResource TestBrush}"/>

        <TextBlock Text="Right text here."
                   FontSize="30"
                   FontFamily="Consolas"
                   Grid.Row="1"
                   Grid.Column="2"
                   Background="{StaticResource TestBrush}"/>

    </Grid>
</Window>

А вот что выдает приведенный выше код:

Screenshot

1 Ответ

0 голосов
/ 04 апреля 2020

Ваш bru sh имеет четыре фрагмента, потому что вы установили область просмотра точно на 1/4 относительного размера выходной ограничительной рамки. Пока область просмотра имеет фиксированную связь с ограничительной рамкой, вы не можете получить ничего другого , кроме четырех.

Итак, способ исправить это - удалить эту связь и замените его чем-нибудь более подходящим. Простейший способ сделать это - установить режим просмотра в Absolute вместо RelativeToBoundingBox. Но тогда возникает вопрос, какое абсолютное значение использовать? В коде, который вы разместили, если вы установите абсолютный размер на любое заданное фиксированное значение c, вы получите тот же размер везде, где вы используете bru sh, и он не будет соответствовать желаемому TextBlock .

Вам нужен другой размер в зависимости от TextBlock. Это означает, что разные bru sh для каждого TextBlock. Но вам, вероятно, не нужно объявлять новый bru sh для каждого TextBlock, который вы объявляете, жестко программируя размер, который подходит. В этом отношении, может быть, вы даже не хотите объявлять новый TextBlock. WPF работает намного лучше, когда вы используете шаблоны для организации визуального дерева. Использование шаблона позволяет вам жестко кодировать аспекты, которые вы хотите повторить везде, при этом позволяя контексту визуального элемента управлять определенными c аспектами, которые должны варьироваться. Итак, давайте сделаем это!

Сначала вам понадобится модель представления:

class TextAndSizeModel
{
    public string Text { get; set; }
    public double Size { get; set; }
}

Это вырожденная модель, содержащая только два фрагмента данных, которые различаются в вашем примере, без уведомления об изменении свойства. Мы не собираемся менять значения в этом простом примере, так что это нормально.

Теперь, одна из менее очевидных вещей, которые вам понадобятся, - это конвертер. Это связано с тем, что в каждом случае нам нужен разный размер области просмотра, а размер области просмотра равен Rect, что является простым значением, а не объектом зависимости. Поэтому нам нужен конвертер, чтобы сделать соответствующее Rect значение по мере необходимости:

class SquareRectConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double doubleValue = System.Convert.ToDouble(value);

        return new Rect(0, 0, doubleValue, doubleValue);
    }

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

И теперь мы готовы сделать разметку. Это выглядит так:

<Window x:Class="TestSO61021208TileBrush.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestSO61021208TileBrush"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
  <Window.Resources>
    <GeometryDrawing x:Key="geometryDrawing1">
      <GeometryDrawing.Geometry>
        <GeometryGroup>
          <EllipseGeometry Center="50,50"
                                             RadiusX="20"
                                             RadiusY="45"/>
          <EllipseGeometry Center="50,50"
                                             RadiusX="45"
                                             RadiusY="20"/>
        </GeometryGroup>
      </GeometryDrawing.Geometry>
      <GeometryDrawing.Brush>
        <SolidColorBrush Color="Gray" />
      </GeometryDrawing.Brush>
      <GeometryDrawing.Pen>
        <Pen Thickness="2"
                             Brush="Black"/>
      </GeometryDrawing.Pen>
    </GeometryDrawing>

    <local:SquareRectConverter x:Key="squareRectConverter1"/>

    <DataTemplate DataType="{x:Type local:TextAndSizeModel}">
      <TextBlock Text="{Binding Text}"
                  FontSize="{Binding Size}"
                  FontFamily="Consolas">
        <TextBlock.Background>
          <DrawingBrush
                  TileMode="Tile"
                  Stretch="Uniform"
                  Viewbox="0,0,1,1"
                  ViewboxUnits="RelativeToBoundingBox"
                  Viewport="{Binding ActualHeight, RelativeSource={RelativeSource AncestorType=TextBlock}, Converter={StaticResource squareRectConverter1}}"
                  ViewportUnits="Absolute"
                  Drawing="{StaticResource geometryDrawing1}"/>
        </TextBlock.Background>
      </TextBlock>                   
    </DataTemplate>

  </Window.Resources>

  <Grid>

    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="Auto"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <GridSplitter Grid.Row="0"
                      Grid.Column="1"
                      Grid.RowSpan="2"
                      Width="3"
                      VerticalAlignment="Stretch"
                      HorizontalAlignment="Center"
                      Background="Black"/>

    <ContentControl Grid.Row="0" Grid.Column="0">
      <ContentControl.Content>
        <local:TextAndSizeModel Text="Left text here." Size="12"/>
      </ContentControl.Content>
    </ContentControl>

    <ContentControl Grid.Row="1" Grid.Column="2">
      <ContentControl.Content>
        <local:TextAndSizeModel Text="Right text here." Size="30"/>
      </ContentControl.Content>
    </ContentControl>
  </Grid>
</Window>

ContentControl используется, конечно, для сопоставления объекта модели представления с шаблоном. В шаблоне вы увидите три привязки:

  1. Текстовое значение.
  2. Значение размера шрифта.
  3. ActualHeight содержащего TextBlock элемент к значению размера Viewport, через преобразователь

Это устанавливает высоту области просмотра точно на высоту TextBlock. Но это также позволяет окну просмотра быть квадратом, который не относится к ограничивающей рамке, так что он может правильно располагаться по всему фону.

Результат выглядит следующим образом: enter image description here

Теперь bru sh располагается горизонтально, чтобы соответствовать фону, независимо от фактической ширины TextBlock.

Конечно, в этом конкретном примере bru sh изменяется высота в соответствии с размером TextBlock. Я сделал вывод, что это то, что вы хотели, потому что именно так оно и работает в вашем первоначальном примере, и вы не написали ничего, чтобы предположить, что вы хотели этого иначе. Тем не менее, вы должны быть в состоянии изменить технику basi c выше, чтобы получить то, что вы хотите. Вам нужно будет сделать математику по-другому, так что вместо того, чтобы высота и ширина менялись в точности с высотой содержащего элемента, вы выбираете фиксированную ширину для bru sh, используя переданную высоту только для высота области просмотра. Таким образом, bru sh будет отцентрирован по вертикали в элементе с фиксированным размером для элемента graphi c, но все еще будет располагаться горизонтально.

Это будет выглядеть примерно так:

Преобразователь:

class CenteredRectConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        double height = System.Convert.ToDouble(value);
        double width = System.Convert.ToDouble(parameter);

        return new Rect(0, 0, width, height);
    }

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

Шаблон данных:

<DataTemplate DataType="{x:Type local:TextAndSizeModel}">
  <TextBlock Text="{Binding Text}"
              FontSize="{Binding Size}"
              FontFamily="Consolas">
    <TextBlock.Background>
      <DrawingBrush
              TileMode="Tile"
              Stretch="Uniform"
              Viewbox="0,0,1,1"
              ViewboxUnits="RelativeToBoundingBox"
              Viewport="{Binding ActualHeight,
                RelativeSource={RelativeSource AncestorType=TextBlock},
                Converter={StaticResource centeredRectConverter1},
                ConverterParameter=14}"
              ViewportUnits="Absolute"
              Drawing="{StaticResource geometryDrawing1}"/>
    </TextBlock.Background>
  </TextBlock>
</DataTemplate>

Это выглядит следующим образом: enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...