none
WPF XAML - панель навигации с раскрывающимися узлами RRS feed

  • Вопрос

  • Здравствуйте!

    Кто-нибудь знает, как можно реализовать панель навигации (слева), как на картинке ниже?

    Пытался стилизовать TabControl, но не получается вставить Expander вместо TabItem для пунктов DEVICES, SHARED и т.д. В гугле вычитал, что это TreeView + ContetnPanel от Tabcontrol. Но для меня редька хрена не слаще. Accordion еще немного похож.

    24 февраля 2017 г. 22:24

Ответы

  • Возможно я иду куда-то в лес и вовсе не нужно использовать TabControl. А просто нужно создать для каждого TreeViewItem свои формы (страницы, UserControl) с элементами управления и их подтягивать...

    Если у вас для разных элементов дерева разные компоненты в правой части, то да. При использовании паттерна MVVM вы просто указываете разные DataTemplate для разных ViewModel которые подгружаете в правую часть и WPF за вас все сделает сам. TabControl вам точно не нужен.

    28 февраля 2017 г. 6:33
    Отвечающий
  • В проводнике "правая часть" - банальный ListView из CommonControls. Не используйте проводник Windows как образец в проектировании WPF-приложений.

    Поправка: тут я наврал, на самом деле проводник начиная с Win7 использует DirectUI, а не CommonControls, но сути дела это не меняет.

    Естественно, вам не нужен никакой скрытый TabControl. Думаю, вы можете просто исользовать связку Frame + несколько Page.


    • Изменено VadimTagil 27 февраля 2017 г. 14:19 поправка
    • Помечено в качестве ответа kremlinbot 28 февраля 2017 г. 6:46
    27 февраля 2017 г. 13:10

Все ответы

  • Добрый день.

    Не понятно в чем проблема. Вы хотите чтобы у вас на вкладке был некий список, перетаскивая drag&drop-ом элементы из которого чтобы создавались новые вкладки?

    27 февраля 2017 г. 10:56
    Отвечающий
  • Нет, нет, все на много проще. Наверно, надо было по лучше картинку поискать. Желтая стрелка не моя - уже была на картинке.

    В windows есть та же панель навигации - Компьютер, Сеть, Домашняя группа. По сути это TabControl, только TabItem у него может быть: обычный, expander. Вот TabItem-Expander не знаю как реализовать. Drag and Drop и прочие примочки не надо. Просто раскрывающийся узел в панели навигации.

    P.S. если коротко: реализация TabItem у TabControl в виде Expander. Или иными элементами управления решить эту задачу

    • Изменено kremlinbot 27 февраля 2017 г. 12:21
    27 февраля 2017 г. 12:17
  • Это не TabControl, это TreeView. У которого к разным типам элементов применяется разный визуальный стиль. Если вам нужно как левая часть в проводнике, то это именно TreeView. Пример можете здесь посмотреть.
    27 февраля 2017 г. 12:26
    Отвечающий
  • У меня больше вызывает вопросы правая часть - контент TreeViewItem (правая область, где отображаются значки Видео, Документы, Изображения). В TabControl это была так сказать "Content Panel". В ней размещались для каждой вкладки (TabItem) свои элементы управления. 

    Т.е. мне надо 2 контрола: TreeView со списком навигации и TabControl со скрытыми TabItem, но отображаемой областью "Content Panel". Как сделать привязку TreeViewItem и TabItem? Чтобы по щелчку на TreeViewItem отображалось нужное содержимое TabControl. 

    Возможно я иду куда-то в лес и вовсе не нужно использовать TabControl. А просто нужно создать для каждого TreeViewItem свои формы (страницы, UserControl) с элементами управления и их подтягивать...

    27 февраля 2017 г. 12:59
  • В проводнике "правая часть" - банальный ListView из CommonControls. Не используйте проводник Windows как образец в проектировании WPF-приложений.

    Поправка: тут я наврал, на самом деле проводник начиная с Win7 использует DirectUI, а не CommonControls, но сути дела это не меняет.

    Естественно, вам не нужен никакой скрытый TabControl. Думаю, вы можете просто исользовать связку Frame + несколько Page.


    • Изменено VadimTagil 27 февраля 2017 г. 14:19 поправка
    • Помечено в качестве ответа kremlinbot 28 февраля 2017 г. 6:46
    27 февраля 2017 г. 13:10
  • Возможно я иду куда-то в лес и вовсе не нужно использовать TabControl. А просто нужно создать для каждого TreeViewItem свои формы (страницы, UserControl) с элементами управления и их подтягивать...

    Если у вас для разных элементов дерева разные компоненты в правой части, то да. При использовании паттерна MVVM вы просто указываете разные DataTemplate для разных ViewModel которые подгружаете в правую часть и WPF за вас все сделает сам. TabControl вам точно не нужен.

    28 февраля 2017 г. 6:33
    Отвечающий
  • Здравствуйте! Алексей, я не смог разобраться с тем, как можно реализовать ваш совет.

    Пока получается подтягивать страницы только при обычном использовании TreeView. Т.е.  у TreeViewItem есть свойство Tag, в которое записано имя страницы, которую необходимо загружать. Также есть обработчик SelecteItemChanged у TreeView, в котором мы получаем Tag и для Frame устанавливаем свойство Source. 

    Взято от сюда: codeproject

    Сейчас обрисую, как у меня ситуация складывается:


    Это главная форма (MainView). Слева - treeView, справа - frame, снизу кнопки управления.

    Для MainView в качестве DataContext выступает MainViewModel. Для подпункта 1.1 и 1.2 должны соответственно загрузиться страницы Page1 и Page2, которые берут данные из MainViewModel. А в подпункт 1.3, должен брать данные из другой ViewModel - SomeViewModel. 

    Код-XAML:

    <TreeView  x:Name="treeView" Grid.Row="0" Grid.Column="0">
                <TreeViewItem IsExpanded="True">
                    <TreeViewItem.Header>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Пункт 1" VerticalAlignment="Center" Padding="5" />
                        </StackPanel>
                    </TreeViewItem.Header>
                    <TreeViewItem>
                        <TreeViewItem.Header>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{StaticResource ResourceKey=default-image}"/>
                                <TextBlock Text="Подпункт 1.1" VerticalAlignment="Center" Padding="5" />
                            </StackPanel>
                        </TreeViewItem.Header>
                    </TreeViewItem>
                    <TreeViewItem>
                        <TreeViewItem.Header>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{StaticResource ResourceKey=default-image}"/>
                                <TextBlock Text="Подпункт 1.2" VerticalAlignment="Center" Padding="5" />
                            </StackPanel>
                        </TreeViewItem.Header>
                    </TreeViewItem>
                    <TreeViewItem>
                        <TreeViewItem.Header>
                            <StackPanel Orientation="Horizontal">
                                <Image Source="{StaticResource ResourceKey=default-image}"/>
                                <TextBlock Text="Подпункт 1.3" VerticalAlignment="Center" Padding="5" />
                            </StackPanel>
                        </TreeViewItem.Header>
                    </TreeViewItem>
                </TreeViewItem>
            </TreeView>
    
            <Frame x:Name="ContentFrame" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" JournalOwnership="OwnsJournal" NavigationUIVisibility="Hidden" Width="Auto" 
                    Source="{Binding SelectedItem, Converter={converter:UriConverter}, ElementName=treeView}" />

    Вы писали:

    вы просто указываете разные DataTemplate для разных ViewModel которые подгружаете в правую часть и WPF за вас все сделает сам. 

    Честно, не понял как это реализовать. Искал что-то похожее и натнулся на статью habr. Но решение немного не подходит, т.к. вместо TreeViewItem используется Button + Command, а у TreeViewItem Command нет. 

    Не могли бы поподробнее разъяснить (с кодом) как решить эту проблему, используя MVVM?


    • Изменено kremlinbot 8 марта 2017 г. 20:49
    8 марта 2017 г. 20:48
  • Содержимым TreeViewItem могут быть любые элементы, в том числе Button
    9 марта 2017 г. 7:48
  • Здравствуйте! Я не стал использовать button в качестве TreeViewItem (лишние заморочки по стилизации). Вместо этого, чтобы вызывать команду для TreeViewItem использовал:

    <TreeViewItem.InputBindings>
         <MouseBinding Command="{Binding CmdShowMessage}" MouseAction="LeftClick"/>
    </TreeViewItem.InputBindings>

    Таким образом мое дерево выглядит так: 

     <TreeView  x:Name="treeView"  Grid.Row="0" Grid.Column="0"  ItemTemplateSelector="{StaticResource myDataTemplateSelector}">
                <TreeViewItem IsExpanded="True">
                    <TreeViewItem.InputBindings>
                        <MouseBinding Command="{Binding CmdShowMessage}" MouseAction="LeftClick"/>
                    </TreeViewItem.InputBindings>
                    <TreeViewItem.Header>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="Пункт 1" VerticalAlignment="Center" Padding="5" />
                        </StackPanel>
                    </TreeViewItem.Header>
                    <TreeViewItem Header="Подпункт 1.1" Uid="1">
                        <TreeViewItem.InputBindings>
                            <MouseBinding Command="{Binding  GoPage1Command}" MouseAction="LeftClick"/>
                        </TreeViewItem.InputBindings>
                    </TreeViewItem>
                    <TreeViewItem Header="Подпункт 1.2" Uid="2">
                        <TreeViewItem.InputBindings>
                            <MouseBinding Command="{Binding GoPage2Command}" MouseAction="LeftClick"/>
                        </TreeViewItem.InputBindings>
                    </TreeViewItem>
                </TreeViewItem>
    </TreeView>

    Мои шаблоны данных:

    <Window.Resources>
               <selector:NavBarDataTemplateSelector x:Key="myDataTemplateSelector"/>
    
            <DataTemplate x:Key="firstTemplate" DataType="{x:Type viewmodel:FirstVM}">
                <view:FirstView/>
            </DataTemplate>
          
            <DataTemplate x:Key="secondTemplate" DataType="{x:Type viewmodel:SecondVM}">
                <view:SecondView/>
            </DataTemplate>
    
    </Window.Resources>

    Таким образом я создал привязку ViewModel и View. Для DataTemplate использовал x:key, т.к. могут быть разные View, использующие данные одной ViewModel. Далее по команде GoPage1 или GoPage2 мы устанавливаем в качестве текущей ViewModel нужную нам. 

    public ICommand GoPage1Command { get { return new BaseCommand(GoToPage, null, true); } }
    private void GoToPage()
    
            {
                SelectedViewModel = new FirstVM();
    
            }

    Содержимое должно подтягиваться в ContentControl для выбранной ViewModel (определяли выше DataTemplate):

    <ContentControl Content="{Binding SelectedViewModel}"  Grid.Row="0" Grid.Column="1" HorizontalAlignment="Stretch" Grid.ColumnSpan="2"/>

    Но т.к. у нас можем быть несколько View привязанных к одной и той же ViewModel, то необходимо различать что необходимо подтягивать. Для этого в TreeView устанавливаем Selector (см. выше Windows.Resources):

     public class NavBarDataTemplateSelector : DataTemplateSelector
        {
            public override DataTemplate SelectTemplate(object item, DependencyObject container)
            {
               
                FrameworkElement element = container as FrameworkElement;  // получаем вызывающий контейнер
    
                if (element != null && item != null && item is TreeViewItem)
                {
                    TreeViewItem taskitem = item as TreeViewItem;
    
                    if (taskitem.Uid == "1")
                        return element.FindResource("firstTemplate") as DataTemplate;
                    else if (taskitem.Uid == "2")
                        return element.FindResource("secondTemplate") as DataTemplate;
                }
                return null;
            }
        }
     

    Для каждого TreeViewItem устанавливаем свойство (Uid или Tag), для которого вернем нужный шаблон...

    Но это все не работает - не отображается содержимое (контролы) First и SecondView в области ContentControl. Подскажите, где я свернул не туда? 

    18 марта 2017 г. 8:01
  • Первое, проверьте, что у вас заходит в команду.

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

    21 марта 2017 г. 6:00
    Отвечающий