Создайте элемент управления DockPanel со второстепенным содержимым

Я хотел бы создать настраиваемый элемент управления, который предоставляет все функции DockPanel, но также предоставляет дополнительный Overlay, который находится «вне» DockPanel < / em>. Было бы свойство зависимости, которое будет управлять видимостью панели наложения, так что, когда для свойства установлено значение true / visible, панель будет отображаться поверх всего в DockPanel.

В идеале потребитель сможет поместить элемент управления в ту же ситуацию, что и обычная панель DockPanel, и без каких-либо других изменений он будет вести себя так же, как обычная панель DockPanel:

<DockPanelWithOverlay LastChildFill="True" >
    <Button DockPanel.Dock="Bottom".../>
    <Button DockPanel.Dock="Top".../>          
    <Grid>
        <Grid controls.../>
    </Grid>
</DockPanelWithOverlay>

Тем не менее, будет доступна дополнительная область, в которую они могут поместить дополнительный контент и вызывать при необходимости.

<DockPanelWithOverlay LastChildFill="True" >
    <Button DockPanel.Dock="Bottom".../>
    <Button DockPanel.Dock="Top".../>   
    <Grid>
        <Grid controls.../>
    </Grid>  
    <DockPanel.Overlay>
        <whatever controls for the overlay>
    </DockPanel.Overlay>     
</DockPanelWithOverlay>

Но это было бы неверно, поскольку Content устанавливается дважды? Итак, чтобы справиться, при использовании наложения, я думаю, мне нужно было бы явно указать, что и где ?:

<DockPanelWithOverlay LastChildFill="True" >
    <DockPanel.Children>
        <Button DockPanel.Dock="Bottom".../>
        <Button DockPanel.Dock="Top".../>   
        <Grid>
            <Grid controls.../>
        </Grid>    
    </DockPanel.Children> 
    <DockPanel.Overlay  Visibility="{Binding IsVisible}">
        <whatever controls for the overlay>
    </DockPanel.Overlay>  
</DockPanelWithOverlay>

Я не совсем уверен, как лучше с этим справиться: создавать ли CustomControl или UserControl, наследовать непосредственно от DockPanel и пытаться предоставить отдельный ContentControl или, возможно, унаследовать от Panel и делегировать MeasureOverride и ArrangeOverride DockPanel .

Как мне с этим справиться?


person Jason    schedule 08.10.2015    source источник


Ответы (2)


Интересный вопрос. Я написал компонент DockPanelWithOverlay, который выполняет эту работу:

DockPanel с полупрозрачным оверлеем

Я выбрал CustomControl, потому что хотел наследовать Panel. Но у Panel нет шаблона, который можно изменить. Итак, я написал настраиваемый элемент управления, наследующий элемент управления с настраиваемым шаблоном, но пользовательский элемент управления, я думаю, отлично работал бы (я не пытался быть честным)

Edit UserControl не так хорош, потому что он наследует ContentControl. Таким образом, у него может быть только один дочерний элемент.
Цель DockPanelWithOverlay - иметь много детей. Поэтому я считаю, что UserControl - не самое лучшее наследование, как это часто бывает. UserControl лучше, если вы хотите предоставить некоторый контент в xaml, в основном статический, не настраиваемый пользователем элемента управления.

Конец редактирования

Чтобы организовать контент внутри tempalte, я использовал сетку.
Порядок двух компонентов имеет значение. Это порядок рисования.

Grid позволяет разместить два компонента в одном месте:
Внутри будет элемент управления Overlay и базовая DockPanel.

DockPanelWithOverlay
.. |
.. | -ControlTemplate
...... |
...... | -Grid
.......... |
.......... | --DockPanel
.......... | --OverlayControl

Имея шаблон, проще сделать некоторую привязку из DockPanelWithOverlay к свойствам элементов управления шаблона. (Чтобы создать CustomControl, создайте проект библиотеки пользовательских элементов управления WPFC)

Выдержка из themes \ generic.xaml в библиотеке:

<Style TargetType="{x:Type local:DockPanelWithOverlay}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:DockPanelWithOverlay}">
                <!-- the grid allows to put two components at the same place -->
                <Grid >
                    <DockPanel x:Name="dockPanel" />
                    <ContentControl x:Name="overlayControl" Visibility="{TemplateBinding OverlayVisibility}" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Наследование управления позволяет использовать шаблон для создания небольшой иерархии UIElements.

Чтобы разрешить привязку, необходимо добавить некоторые свойства зависимостей:

  1. Наложение для предоставления некоторых элементов UIElements или строки для наложения содержимого
  2. OverlayVisibility для скрытия / отображения наложения

Вот код для DockPanelWithOverlay:
(обратите внимание на OnApplytemplate, вызываемый сразу после вызова компонентов шаблонов)

 // Children is the property that will be valued with the content inside the tag of the control 
[ContentProperty("Children")]
public class DockPanelWithOverlay : Control
{
    static DockPanelWithOverlay()
    {
        // Associate the control with its template in themes/generic.xaml
        DefaultStyleKeyProperty.OverrideMetadata(typeof(DockPanelWithOverlay), new FrameworkPropertyMetadata(typeof(DockPanelWithOverlay)));
    }
    public DockPanelWithOverlay()
    {
        Children = new UIElementCollection(this, this);
    }
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        // once the template is instanciated, the dockPanel and overlayCOntrol can be found from the template
        // and the children of DockPanelWithOverlay can be put in the DockPanel
        var dockPanel = this.GetTemplateChild("dockPanel") as DockPanel;
        if (dockPanel != null)
            for (int i = 0; i < Children.Count; )
            {
                UIElement elt = Children[0];
                Children.RemoveAt(0);
                dockPanel.Children.Add(elt);
            }
    }   
    // Here is the property to show or not the overlay
    public Visibility OverlayVisibility
    {
        get { return (Visibility)GetValue(OverlayVisibilityProperty); }
        set { SetValue(OverlayVisibilityProperty, value); }
    }
    // Here is the overlay. Tipically it could be a Texblock, 
    // or like in our example a Grid holding a TextBlock so that we could put a semi transparent backround
    public Object Overlay
    {
        get { return (Object)GetValue(OverlayProperty); }
        set { SetValue(OverlayProperty, value); }
    }
    // Using a DependencyProperty as the backing store for OverlayProperty. 
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty OverlayProperty =
        DependencyProperty.Register("Overlay", typeof(Object), typeof(DockPanelWithOverlay), new PropertyMetadata(null));
    public static readonly DependencyProperty OverlayVisibilityProperty =
        DependencyProperty.Register("OverlayVisibility", typeof(Visibility), typeof(DockPanelWithOverlay), new PropertyMetadata(Visibility.Visible));
    [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
    public UIElementCollection Children
    {
        get { return (UIElementCollection)GetValue(ChildrenProperty); }
        set { SetValue(ChildrenProperty, value); }
    }
    public static readonly DependencyProperty ChildrenProperty =
        DependencyProperty.Register("Children", typeof(UIElementCollection), typeof(DockPanelWithOverlay), new PropertyMetadata(null));
}

Используя DockPanelWithOverlay:

<lib:DockPanelWithOverlay x:Name="dockPanelWithOverlay1" 
                                OverlayVisibility="Visible"              
                                HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
    <Button Content="Top" Height="50" DockPanel.Dock="Top" Background="Red"/>
    <Button Content="Bottom" Height="50" DockPanel.Dock="Bottom" Background="Yellow"/>
    <Button Content="Left" Width="50" DockPanel.Dock="Left" Background="Pink"/>
    <Button Content="Right" Width="50" DockPanel.Dock="Right" Background="Bisque"/>
    <Button Content="Center" Background="Azure"/>
    <lib:DockPanelWithOverlay.Overlay>
        <Grid Background="#80404080">
            <TextBlock Text="Overlay" FontSize="80" Foreground="#FF444444" HorizontalAlignment="Center" VerticalAlignment="Center" RenderTransformOrigin="0.5,0.5">
                <TextBlock.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform/>
                        <SkewTransform/>
                        <RotateTransform Angle="-15"/>
                        <TranslateTransform/>
                    </TransformGroup>
                </TextBlock.RenderTransform>
            </TextBlock>
        </Grid>
    </lib:DockPanelWithOverlay.Overlay>
</lib:DockPanelWithOverlay>

Оверлей можно легко включить или отключить привязку, например, с помощью свойства CheckBox.IsChecked.

Вот полный рабочий код: http://1drv.ms/1NfCl9z

Думаю, это действительно ответ на ваш вопрос. С Уважением

person Emmanuel DURIN    schedule 09.10.2015
comment
Спасибо, это именно то, что я искал. - person Jason; 10.10.2015
comment
Мне это нравится, и я буду внимательно его изучать. Спасибо, Эммануэль. - person PScr; 11.10.2015

Я предлагаю, чтобы мы попытались прояснить, как, по вашему мнению, это работает. Я предполагаю, что вторичная панель тоже будет DockPanel и полностью закроет основную панель. То есть вы видите либо одно, либо другое, но никогда оба сразу. Каким вы видите переключение между ними? ToggleButton возможно? Или только под контролем, скажем, какого-нибудь Trigger?

Моя первая мысль относительно реализации заключается в том, что вам, кажется, нравится, как DockPanel? выкладывает вещи, так зачем трогать методы верстки? Один из способов - иметь только одну докпанель, но две коллекции дочерних элементов, которые вы устанавливаете, в соответствии с которыми вы хотите отображать. Или вторичная панель во всплывающем окне?

Вы хотите написать что-то вроде этого:

<DockPanelWithAlternative
    AlternativeVisibility="{Binding somethingHere}" >
    <TextBlock Dock.Top ... />
    <TextBlock Dock.Alternative.Top ... />

</DockPanelWithAlternative>

Я думаю о чем-то вроде:

<UserControl>
   <Grid>
      <DockPanel x:Name="MainPanel" ZIndex="0"/>
      <DockPanel x:Name="AlternativePanel" Visbility=... ZIndex="1"/>
   </Grid>
</UserControl>
person PScr    schedule 08.10.2015
comment
Спасибо за ответ @PScr. Я ожидал, что вторичная панель потенциально может разместить что-либо, после чего потребитель может вставить DockPanel, если потребуется. Обычно он используется для передачи сообщения пользователю, но в большей степени сосредоточен на этом элементе управления, а не на использовании стандартного диалогового окна приложения. Он может переходить внутрь или иметь некоторую тонкую прозрачность, позволяющую просвечивать часть основной панели DockPanel. - person Jason; 08.10.2015
comment
Да я вижу. Возможно, нам следует подумать о UserControl с DockPanel и ContentControl, который находится сверху. Затем у вас может быть DataTemplate для ContentControl, который пользователь может установить: ‹DockPanelWithX XTempleate = ... XContent = ... XVisibility = ... /› - person PScr; 09.10.2015
comment
Я только что прочитал это: codeproject.com/Articles/820324 /. Возможно, есть идеи, которые вы могли бы адаптировать. - person PScr; 09.10.2015
comment
Спасибо за комментарии @Pscr, я обновил вопрос, чтобы включить немного больше деталей. Я не пытаюсь создать всплывающее диалоговое окно, я больше пытаюсь понять лучший или эффективный способ создания / расширения настраиваемого элемента управления. Спасибо - person Jason; 09.10.2015