WPF: TabContol: Управление табуляцией

Язык C#: программирование на C#, портирование кода C# на VB и VB на C#.

Модератор: Ramzes

burik
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 514
Зарегистрирован: 03.11.2005 (Чт) 22:04
Откуда: Беларусь, Рогачев

WPF: TabContol: Управление табуляцией

Сообщение burik » 28.12.2011 (Ср) 4:50

Всем привет!

Есть проект на WPF (.NET Framework 4.0), который разрабатывается по паттерну MVVM с использованием Caliburn Micro.
Есть окно, которое содержит вкладки, как минимум две. View-Model окна наследуется от Caliburn'овского Conductor<Screen>.Collection.OneActive. Каждый таб - отдельная вьюшка + вью-моделька.
Есть следующая задача: сделать так, чтобы после последнего поля первой вкладки нажатием Tab пользователь переходил к первому полю второй вкладки.

На .NET весьма недавно, может быть по этому о простом решении такой задачи не знаю. Пробовал играться с KeyboardNavigation, но заставить вкладки переключаться не удалось. Может плохо искал?

Поскольку аналогичная возможность предвидится еще в нескольких местах (да и просто из спортивного интереса), захотелось сделать более-менее универсальное решение. Решил написать Behavior для FrameworkElement'а, который принимает параметр вроде TargetControl (контрол, на который нужно перейти по нажатии Tab) и при, собственно, нажатии Tab будет производить все необходимые манипуляции, чтобы требуемый контрол стал доступен пользователю и получил фокус (активировал нужное окно, открывал вкладку... ну и все на данный момент).

Принцип работы следующий - рекурсивно вызываем функцию активации элемента для визуального родителя (начиная с родителя TargetControl и пока родитель не окажется null) и выполняем операции по активации этого элемента в соответствии с его типом (например, если это окно, то вызываем .Activate()).
Вот тут получается проблема с TabControl. У него в чайлдах нету какого-либо элемента, который содержал бы весь контент таба и который можно было бы как-нибудь активировать. И вообще, он всегда содержит только контент активного таба. Т.е. пройтись по всем табам, найти тот, который содержит требуемый элемент, и активировать его не представляется возможным. :(
Да и вообще, чтобы активировать таб, нужно изменить значение свойства SelectedItem на одно из значений коллекции Items, которая содержит вью-модели. Т.е. даже если найти нужный UI-элемент с нужным содержимым, необходимо как-то получить соответствующую ViewModel.

Есть какие-нибудь идеи?
Между слухов, сказок, мифов,
просто лжи, легенд сомнений
мы враждуем жарче скифов
за несходство заблуждений
Игорь Губерман

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: WPF: TabContol: Управление табуляцией

Сообщение FireFenix » 28.12.2011 (Ср) 13:50

burik писал(а):Принцип работы следующий - рекурсивно вызываем функцию активации элемента для визуального родителя
Вот тут получается проблема с TabControl. У него в чайлдах нету какого-либо элемента, который содержал бы весь контент таба и который можно было бы как-нибудь активировать.

Всё там есть, вкладка имеет класс TabPage.
Если нету в визуальном древе, то используй обычное логическое

Если у тебя вкладки привязаны к каким-то элементам и динамически создаются/удаляются, то можно сделать, так чтобы твой последний элемент по Tab'y слал сообщение вышестоящему модулю, который в свою очередь инкриминировал индекс элемент соответствующей вкладки, активировал её и выделял первый элемент
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

burik
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 514
Зарегистрирован: 03.11.2005 (Чт) 22:04
Откуда: Беларусь, Рогачев

Re: WPF: TabContol: Управление табуляцией

Сообщение burik » 28.12.2011 (Ср) 18:05

FireFenix писал(а):Всё там есть, вкладка имеет класс TabPage.
Если нету в визуальном древе, то используй обычное логическое

Ни в логическом, ни в визуальном деревьях нет TabPage. И вообще нашел только TabPage из System.Windows.Forms, а у меня, как я писал, WPF.

FireFenix писал(а):Если у тебя вкладки привязаны к каким-то элементам и динамически создаются/удаляются, то можно сделать, так чтобы твой последний элемент по Tab'y слал сообщение вышестоящему модулю, который в свою очередь инкриминировал индекс элемент соответствующей вкладки, активировал её и выделял первый элемент

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

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: WPF: TabContol: Управление табуляцией

Сообщение FireFenix » 28.12.2011 (Ср) 19:03

burik писал(а):TabPage

Да, ошибся, прошу прощения. TabControl содержит коллекцию TabItem, а не TabPage

burik писал(а):Ни в логическом, ни в визуальном деревьях нет TabPage... а у меня, как я писал, WPF

Я это внимательно прочитал. Совет остаётся тотжий.

Код: Выделить всё
<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>
        <TabControl Height="278" HorizontalAlignment="Left" Margin="20,22,0,0" Name="TabControl1" VerticalAlignment="Top" Width="471">
            <TabItem Header="TabItem1" Name="TabItem1">
                <Grid />
            </TabItem>
            <TabItem Header="TabItem2" Name="TabItem2">
                <Grid>
                    <Button Content="Button" Height="87" HorizontalAlignment="Left" Margin="71,76,0,0" Name="Button1" VerticalAlignment="Top" Width="279" />
                </Grid>
            </TabItem>
        </TabControl>
    </Grid>
</Window>

Код: Выделить всё
Class MainWindow
    Private Sub Button1_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles Button1.Click
        Dim Parent As DependencyObject = Button1

        While Not Parent Is Nothing
            Console.WriteLine(Parent.GetType.ToString)
            Parent = LogicalTreeHelper.GetParent(Parent)
        End While
    End Sub
End Class

Вывод:
Код: Выделить всё
System.Windows.Controls.Button
System.Windows.Controls.Grid
System.Windows.Controls.TabItem
System.Windows.Controls.TabControl
System.Windows.Controls.Grid
WpfApplication1.MainWindow

Вот как раз 3ий элемент и есть TabItem, который содержит контент текущей страницы
А если меньше париться, то у TabControl есть свойства SelectedItem и SelectedIndex, которые соответственно возвращают текущий TabItem и его позицию в массиве TabControl.Items


burik писал(а):Так-то оно так, но как-то мне это решение не нравится - оно и паттерну MVVM не соответствует

Ну... Иногда бывает такая оптимальная структура программы или задачи, которую по нормально сложно сделать не обойдя паттерн :)
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる

burik
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 514
Зарегистрирован: 03.11.2005 (Чт) 22:04
Откуда: Беларусь, Рогачев

Re: WPF: TabContol: Управление табуляцией

Сообщение burik » 28.12.2011 (Ср) 19:21

Все так, но, видимо, не всегда. У меня коллекция Items фактически биндится на коллекцию объектов наследованных от Screen (это из Caliburn). Т.е. там получается не коллекция TabItem'ом, а коллекция объектов различного типа.

Хотелось бы, конечно, получить что-то совсем универсальное, однако, вижу что это нереально. В моем случае все табы наследуют Screen, у которого есть метод GetView, который уже возвращает вьюшку по которой можно искать контрол. В общем-же случае, там может быть любой объект и заранее никак невозможно узнать как этот объект связан с UI-элементами.. Хотя.. Ведь TabControl способен из этого объекта получить вьюшку.. Но как?

Для начала сделаю костыль, нацеленный именно на наследника Screen, но надо еще подумать..
Между слухов, сказок, мифов,
просто лжи, легенд сомнений
мы враждуем жарче скифов
за несходство заблуждений
Игорь Губерман

FireFenix
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1640
Зарегистрирован: 25.05.2007 (Пт) 10:24
Откуда: Mugen no Sora

Re: WPF: TabContol: Управление табуляцией

Сообщение FireFenix » 28.12.2011 (Ср) 20:36

burik писал(а):Все так, но, видимо, не всегда. У меня коллекция Items фактически биндится на коллекцию объектов наследованных от Screen (это из Caliburn). Т.е. там получается не коллекция TabItem'ом, а коллекция объектов различного типа.

Не знаю как устроен твой фреймворк, но думаю что в его и библиотек изучении поможет Reflector, или его взломанная версия - Deflector

burik писал(а):Хотелось бы, конечно, получить что-то совсем универсальное, однако, вижу что это нереально. В моем случае все табы наследуют Screen, у которого есть метод GetView, который уже возвращает вьюшку по которой можно искать контрол. В общем-же случае, там может быть любой объект и заранее никак невозможно узнать как этот объект связан с UI-элементами.. Хотя.. Ведь TabControl способен из этого объекта получить вьюшку.. Но как?

Посмотри логическое древо как я делал.
Попробуй переключать чрез Item'ы TabControl'a и отслживай тип переключаемого элемента. Наверняка там такая же хрень как TabItem, только по другому обёрнута и почти так же работает
Птицей Гермеса меня называют, свои крылья пожирая... сам себя я укрощаю
私はヘルメスの鳥 私は自らの羽根を喰らい 飼い慣らされる


Вернуться в C#

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 8

    TopList