Как сделать так, чтобы элемент управления расширителем наводился на следующий элемент?

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

<StackPanel>
  <StackPanel Orientation="Horizontal">
    <StackPanel>
      <Expander>...</Expander>
    </StackPanel>
    <StackPanel>
      <Button ... /><Button ... /><Button ... />
    </StackPanel>
  </StackPanel>
  <StackPanel Orientation="Horizontal">
    <DataGrid ... />
  </StackPanel>
</StackPanel>

Один из способов исправить это — поместить все в одну ячейку сетки и добавить расширитель последним. Однако это кажется мне неуместным на нескольких уровнях. Вместо этого я бы предпочел заставить расширитель бесконтактно расширяться над другими элементами управления. Это должно повлиять на макет, но только на его размер из сложенного состояния. Расширение никак не должно повлиять на макет.

Как я могу сказать тупому эспандеру, чтобы он не был таким настойчивым?


person Konrad Viltersten    schedule 04.02.2015    source источник
comment
Рассматривали ли вы возможность использования Popup или ComboBox вместо расширителя?   -  person Sphinxxx    schedule 04.02.2015
comment
@Сфинххх Да. Это должно быть управление расширителем по нескольким причинам. На самом деле, на данный момент проще сделать это уродливым способом (поместить все в одну ячейку сетки), чем возиться с заменой расширителя. У меня есть некоторые функции, запеченные там, и от начала повторной разработки у меня мурашки по коже ... Слишком боюсь упс и ловушек.   -  person Konrad Viltersten    schedule 04.02.2015


Ответы (1)


Для справки, давайте пронумеруем элементы StackPanel из вашего исходного XAML:

<StackPanel #1>
    <StackPanel #2 Orientation="Horizontal">
        <StackPanel #3>
            <Expander>...</Expander>
        </StackPanel>
        <StackPanel #4>
            <Button ... /><Button ... /><Button ... />
        </StackPanel>
    </StackPanel>
    <StackPanel #5 Orientation="Horizontal">
        <DataGrid ... />
    </StackPanel>
</StackPanel>

StackPanel при горизонтальной ориентации устанавливает свою высоту равной высоте своего самого высокого дочернего элемента, а при вертикальной ориентации устанавливает свою высоту равной сумме всех высот его дочерних элементов.

Когда вы расширяете элемент управления Expander, вы увеличиваете его высоту и, следовательно, увеличиваете высоту его контейнера (#3). Это, в свою очередь, может увеличить или не увеличить высоту родительского контейнера №3 (№2) в зависимости от того, станет ли расширитель больше по высоте, чем панель стека, содержащая кнопки (№4).

Чтобы добиться эффекта, который вам, кажется, нужен, вы можете либо использовать Grid, как уже обсуждалось в вопросе и другом ответе, либо вы можете использовать элемент Canvas следующим образом:

<Window x:Class="..."
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <StackPanel Orientation="Horizontal" Panel.ZIndex="1">
            <Canvas Width="{Binding ActualWidth, ElementName=Expander}">
                <Expander x:Name="Expander" Header="Header" Background="Yellow">
                    <StackPanel Background="Aqua">
                        <TextBlock Text="Some text" />
                        <TextBlock Text="Some text" />
                        <TextBlock Text="Some text" />
                        <TextBlock Text="Some text" />
                        <TextBlock Text="Some text" />
                        <TextBlock Text="Some text" />
                    </StackPanel>
                </Expander>
            </Canvas>
            <StackPanel>
                <Button Content="Button1" />
                <Button Content="Button2" />
                <Button Content="Button3" />
            </StackPanel>
        </StackPanel>
        <TextBlock Text="Some more text" Background="LimeGreen" />
    </StackPanel>
</Window>

Используемый здесь холст позволяет элементу управления расширения «выходить» за границы контейнера. Чтобы элемент управления расширения «плавал» над элементом, находящимся непосредственно под ним (в данном примере это TextBlock), используется присоединенное свойство Panel.ZIndex. Нам также нужно привязать ширину холста к ширине расширителя, так как холст не будет изменять свой размер в зависимости от своих дочерних элементов.

Вот как это выглядит, когда расширитель свернут:

И как это выглядит в развернутом виде:

(Простите за ужасные цвета, они только для того, чтобы вы могли видеть, где находятся контрольные границы).

person Steven Rands    schedule 05.02.2015
comment
Цвета отличные! Сразу видно что к чему. Интересно, является ли это предполагаемой целью управления холстом. У меня сложилось впечатление, что его целью было привлечь... - person Konrad Viltersten; 06.02.2015
comment
@KonradViltersten Цель WPF Canvas состоит в том, чтобы разместить своих дочерних элементов в определенных координатах, на самом деле это вообще не имеет ничего общего с рисованием. У него также есть полезное свойство, позволяющее своим дочерним элементам выходить за его границы по умолчанию (т. е. он не обрезает своих дочерних элементов до своих собственных границ). Поэтому его часто используют, когда нужен эффект перекрытия. Единственный другой встроенный элемент управления, который имеет аналогичные возможности наложения слоев, — это Grid, но это более тяжелая панель. - person Steven Rands; 06.02.2015
comment
@KonradViltersten Думайте о Canvas в этом случае как о чем-то похожем на position: relative; в CSS, поскольку холст является слотом макета, а его дочерние элементы позиционируются относительно холста, но в конечном итоге могут перекрывать другие элементы за пределами границы. холст. - person Steven Rands; 06.02.2015
comment
Крутая вещь :) Есть ли способ, чтобы расширитель также перетекал через границы содержащего окна? Я упаковал свой Expander в Usercontrol, надеясь, что Panel.ZIndex, установленный в UC, будет достаточно, как кажется, мне также пришлось установить ZIndexes в содержащем Grid/Panel, чтобы достичь вершины. - person dba; 07.06.2017
comment
@dba Я думаю, что все элементы управления WPF обрезаются до границ своего родительского окна. Вы можете попробовать использовать элемент управления Popup, который по сути является отдельным окном, но тогда вы можете столкнуться с проблемами фокуса. - person Steven Rands; 07.06.2017