Wpf вкладки как в браузере
Библиотека управления WPF-TabControl с перетаскиваемыми вкладками
Размер и положение вкладки
Элементы управления, представленные на этот раз, являются наиболее часто используемыми элементами TabControl.В Интернете есть много распространенных стилей TabControl, некоторые из которых также поддерживают перетаскивание вкладок, но анимации встречаются редко. Это также по причине, потому что это не строка кода, чтобы создать элемент управления, который не теряет исходную функцию, но также нуждается в добавлении эффектов анимации. Чтобы добиться эффекта, показанного на картинке выше, мы не можем сделать это за одну ночь. Самая запретная вещь - это желание достичь всех эффектов, как только они появятся.
В начале нам лучше было использовать Blend, чтобы увидеть, как реализована нативная часть шаблона стиля TabControl, поэтому мы можем иметь ссылку. Давайте перетащим TabControl из панели ресурсов в форму и скорректируем соответствующий размер:
Затем щелкните правой кнопкой мыши по нему и отредактируйте шаблон-> edit copy-> OK, ключевая часть в автоматически сгенерированном коде xaml находится здесь:
Вы можете видеть, что все вкладки (то есть TabItems) фактически размещены в TabPanel, поддерживаемом TabControl. Достаточно знать это, мы можем создать собственную TabPanel. Чтобы заменить его: public class TabPanel : Panel , Так как этот TabPanel является контейнером, он должен отвечать за расчет размера TabItem и организацию его позиции. Мы можем переопределить родительский Panel MeasureOverride Методы для обработки этой логики: protected override Size MeasureOverride(Size constraint) , В этом методе мы передаем InternalChildren Это свойство только для чтения, чтобы получить вкладку, высота вкладки у нас есть TabItemHeight Обозначение свойства. Поскольку TabPanel прозрачна для пользователя, нам также необходимо настроить TabControl с помощью TabItemHeight Свойство, пусть оно привязывается к TabPanel. После того, как TabItemWidth и IsEnableTabFill То же самое верно. Ширина вкладок обсуждается отдельно, если IsEnableTabFill = true Мы должны разделить ширину на равные. Например, ширина контейнера равна 100, а вкладок 10, поэтому ширина каждой вкладки равна 10. Здесь важно отметить, что ширина вкладок предпочтительно не должна иметь десятичной точки, хотя UseLayoutRounding С помощью этой функции можно в некоторой степени удалить размытие, но на вкладках, расположенных одна за другой, будет контрпродуктивно. Вы обнаружите, что ширина разделительной линии между ними не согласована. Лучший способ - это «несправедливо разделить». "Вставьте кусок кода, чтобы объяснить:
Предположим, что текущая ширина контейнера равна 108, а количество вкладок равно 10. После обработки методом MeasureOverride ширина первых восьми равна 11, а ширина двух последних равна 10. Если IsEnableTabFill = false, не делите его поровну, просто поместите его в контейнер.
Теперь размер вкладки фиксирован, где позиция? Слишком просто, цикл for продолжает накладывать ширину каждой вкладки: size.Width + = tabItem.ItemWidth ;. Наконец, вы можете упорядочить положение вкладок, вызвав Element.Arrange:
Поскольку левое и правое поля вкладки вычитают левое поле, интервал между ними является полем.
Логическая обработка размера и положения вкладки примерно соответствует описанному выше процессу. Из-за ограниченного пространства, и я не люблю размещать большой кусок кода, я сосредоточусь только на обсуждении. Полный код также должен учитывать различные ситуации. Я повторяю
1. Обработка анимации
В этой части мы фокусируемся на мышке: для вкладок, мыши, движения мыши и мыши мы должны обращать на них внимание, поэтому подписывайтесь на них отдельно. Соответственно, нам также нужно добавить несколько личных полей на вкладку для записи статуса, таких как _isDragging 、 _isDragged 、 _dragPoint 、 _isWaiting Я не говорю, что первые два, они все буквальные, третий используется для временного сохранения положения мыши при перемещении, каждый раз, когда вы входите во вкладку OnMouseMove Событие будет _isDragged Отличие от его старого значения заключается в том, чтобы узнать, насколько далеко должна двигаться текущая вкладка. _isWaiting Это совершенно особая цель: когда пользователь перетаскивает вкладку, нам лучше подождать, пока не достигнет фиксированного расстояния, например, ширины 20 единиц, то есть вкладка не будет запущена, пока мышь не переместится более чем на 20 пикселей в горизонтальном направлении. сопротивление.
В первом GIF вы можете видеть, что, когда перетаскиваемая вкладка меняет положение, остальные вкладки также динамически меняют положение. Так как определяется время изменения положения? Очень просто, просто разделите это расстояние от перетаскиваемой вкладки до левой границы TabPanel на ItemWidth Результатом округления является текущая позиция вкладки, а следующим шагом является изменение позиции вкладки и текущей позиции перетаскивания. На данный момент мы наконец можем использовать анимацию для достижения, потому что в этой серии статей много раз говорилось о коде анимации, поэтому я не буду их повторять.
Предыдущий абзац был об изменении позиций. Как насчет добавления вкладок и удаления вкладок? На самом деле, есть ярлык для использования, который должен использовать FluidMoveBehavior Поставь его в стиль, ну и эффект получится!
Но здесь есть одна заметка, FluidMoveBehavior Хотя он может упростить некоторую логику анимации, он немного переопределен, он также выполняет логику движения вашей позиции. Вы обнаружите, что если вы не обработаете его, он будет повторять свою анимацию после окончания вашей собственной анимации. , Может быть FluidMoveBehavior из Duration Свойство временно обнуляется для решения этой проблемы: FluidMoveDuration = new Duration(TimeSpan.FromSeconds(0)); 。
Эта статья только описывает процесс реализации и идеи: те, кто заинтересован, могут скачать исходный код, много общаться и вместе совершенствоваться.
The WPF TabControl allows you to split your interface up into different areas, each accessible by clicking on the tab header, usually positioned at the top of the control. Tab controls are commonly used in Windows applications and even within Windows' own interfaces, like the properties dialog for files/folders etc.
Just like with most other WPF controls, the TabControl is very easy to get started with. Here's a very basic example:
As you can see, each tab is represented with a TabItem element, where the text shown on it is controlled by the Header property. The TabItem element comes from the ContentControl class, which means that you may define a single element inside of it that will be shown if the tab is active (like on the screenshot). I used a Label in this example, but if you want to place more than one control inside of the tab, just use one of the panels with child controls inside of it.
Customized headers
Once again, WPF proves to be extremely flexible when you want to customize the look of your tabs. Obviously the content can be rendered any way you like it, but so can the tab headers! The Header property can be filled with anything you like, which we'll take advantage of in the next example:
The amount of markup might be a bit overwhelming, but as you can probably see once you dig into it, it's all very simple. Each of the tabs now has a TabControl.Header element, which contains a StackPanel, which in turn contains an Image and a TextBlock control. This allows us to have an image on each of the tabs as well as customize the color of the text (we could have made it bold, italic or another size as well).
Controlling the TabControl
Sometimes you may wish to control which tab is selected programmatically or perhaps get some information about the selected tab. The WPF TabControl has several properties which makes this possible, including SelectedIndex and SelectedItem. In the next example, I've added a couple of buttons to the first example which allows us to control the TabControl:
As you can see, I've simply added a set of buttons in the lower part of the interface. The first two allows will select the previous or next tab on the control, while the last one will display information about the currently selected tab, as demonstrated on the screenshot.
The first two buttons uses the SelectedIndex property to determine where we are and then either subtracts or adds one to that value, making sure that the new index doesn't fall below or above the amount of available items. The third button uses the SelectedItem property to get a reference to the selected tab. As you can see, I have to typecast it into the TabItem class to get a hold of the header property, since the SelectedProperty is of the object type by default.
Summary
The TabControl is great when you need a clear separation in a dialog or when there's simply not enough space for all the controls you want in it. In the next couple of chapters, we'll look into some of the possibilites there are when using the TabControl for various purposes.
WPF-адаптивная закрываемая вкладка TabControl в виде браузера
Несмотря на то, что это TabControl с адаптивным закрытием, TabControl не нужно изменять. Лучше вызвать адаптивно закрываемый TabItem.
Общая идея: создать пользовательский элемент управления, унаследованный от TabItem, поместить в него кнопку и удалить себя из TabControl при нажатии.При добавлении или удалении изменений размера TabItem и TabControl рассчитайте соответствующую ширину по количеству элементов.
Новый пользовательский элемент управления
Создайте новый пользовательский элемент управления и унаследуйте его от TabItem, чтобы у него были все свойства и события TabItem. Эта функция не требует настраиваемых зависимых свойств и событий. Его использование точно такое же, как и TabItem.
После сборки замените UserControl на TabItem и удалите лишнюю часть
Бэкэнд, унаследованный от UserControl, изменен на унаследованный от TabItem
Измените стиль, чтобы добавить кнопку закрытия
Добавьте понравившийся стиль в Xmal. Самое главное - добавить кнопку в Шаблон и зарегистрировать событие Click для закрытия.
Фоновая логика
Найдите родительский TabControl
Обратите внимание, что TabItem не может закрыться. Упомянутое здесь закрытие фактически удаляется из коллекции Items его родительского элемента TabControl. И при изменении размера родительского элемента TabControl необходимо зарегистрировать событие, чтобы изменить ширину каждого элемента. Поэтому я решил найти Его родительский элемент, TabControl, объявляет частную переменную, чтобы добавить ссылку на родительский элемент.
Вы можете найти его родительский элемент TabControl через вспомогательный класс VisualTreeHelper визуального дерева. Конечно, дело не в том, что его родительский элемент является непосредственно TabControl, и вам нужно найти его рекурсивно.
Расчетный размер
Поскольку он адаптивный, должен быть нормальный размер, и только тогда, когда недостаточно места для сжатия каждого элемента. Самый простой способ, который я думаю, - это договориться о том, чтобы поместить этот размер в тег родительского элемента TabControl, чтобы вы могли Вы можете легко получить этот размер, обратившись к родительскому TabControl.
Метод расчета состоит в том, чтобы разделить фактическую ширину родительского элемента TabControl во время выполнения на согласованный размер и взять целое число int. Это критическое значение для поддержания количества элементов с согласованной шириной.
Если оно меньше или равно этому значению, используйте согласованную ширину. Если оно больше этого значения, разделите родительскую ширину бега на количество Items, чтобы найти среднюю ширину, а затем просмотрите элементы родительского TabControl и присвойте это среднее значение.
Следует отметить, что если размер всех суммируемых элементов больше или равен размеру родительского элемента, элементы будут перенесены, что выглядит немного некрасиво. Поэтому я взял операцию родительской ширины бега -5, так что он никогда не достигнет Бордюр, заворачивать не буду.
Однако вы также можете переписать шаблон элемента управления TabControl и заменить контейнер Hrader на Stackpanel, и он не будет переноситься. Я просто думаю, что описанный выше метод относительно прост.
Изменение родительского размера
Это можно отслеживать с помощью события SizeChanged элемента управления TabControl.То, что необходимо сделать, это пересчитать размер.
Кнопка закрытия
После удаления самого себя в коллекции Items родительского элемента TabControl обратите внимание на пересчет размера и удаление метода регистрации события SizeChanged.
Этот эффект довольно распространен, возможно, у вас уже получилось, и у вас есть идеи получше. Я надеюсь, вы сможете поделиться ими и вместе добиться прогресса.
1. Стиль кнопки закрытия TabItem.Resources добавляет Key, а кнопка закрытия в шаблоне добавляет ссылку на ресурс.
2. Удалите стиль TabItem HorizontalContentAlignment = "Left", VerticalContentAlignment = "Center". Макет содержимого заголовка изменен на HorizontalAlignment = "" VerticalAlignment = "".
У меня есть TabControl в моем приложении WPF. Я хочу, чтобы мое приложение в основном поддерживало несколько "экземпляров" в рамках одной программы. Например, подумайте о веб-браузерах, они позволяют иметь несколько экземпляров веб-сайтов на разных вкладках, я хочу достичь аналогичной функциональности, где мое приложение содержит несколько экземпляров "sub-приложений".
проблема, с которой я сейчас сталкиваюсь, заключается в том, что мне нужно скопировать-вставить один и тот же XAML на каждую вкладку, потому что каждая вкладка имеет точно та же разметка и пользовательский интерфейс, но разные данные. Другая проблема заключается в том, что мне нужна функциональность для динамического создания этих вкладок.
вот скриншот моего приложения в его текущем состоянии. Как вы можете видеть, в верхней части есть 2 вкладки, а вторая имеет прозрачный фон, поскольку она неактивна.
Итак, как создать систему с вкладками, где пользовательский интерфейс вкладки остается неизменным для каждой вкладки, и мне нужно только разработать с одним пользовательским интерфейсом XAML и дублировать это для каждой вкладки?
- каждая вкладка имеет тот же пользовательский интерфейс.
- каждая вкладка имеет разные данные для элементов пользовательского интерфейса.
- как разработчик я хочу работать над XAML вкладки только один раз и прямо в Visual Studio.
В идеале мне бы понравился простой пример проекта / кода, где есть один неустановленный элемент управления вкладками, и приложение при запуске динамически создает 2-n вкладок, которые все имеют тот же интерфейс, но с разными данными.
Как отмечено в другом ответе, вероятно, есть много способов сделать это, но вот мой простой способ:
определите DataTemplate, который определяет содержимое каждой из ваших одинаковых вкладок. Элементы управления в шаблоне данных будут привязаны к модели представления текущей выбранной вкладки. Я поместил один TextBlock в свой пример, но вы можете легко расширить это.
используя этот Xaml:
и этот вид модель код:
мне это очень нравится учебник при стилизации элемента управления tab. Вы можете легко поместить более сложный контент в заголовки вкладок, а также контент.
вы должны изучить полный шаблон вкладку Управление, чтобы получить представление о том, как он работает. Используйте Blend или vs11 beta для извлечения шаблона.
чтобы динамически добавлять / удалять вкладки, теперь все, что вам нужно сделать, это добавить / удалить элементы в ObservableCollection моделей вида.
Это может превратиться в довольно большой ответ, потому что есть много различных путей, которые вы могли бы предпринять.
важно понимать, что TabControl является ItemsControl, что означает, что он может содержать коллекцию объектов, таких как UserControls, которые составляют каждый модуль. Таким образом, вы можете начать с привязки свойства Items к ObservableCollection некоторых объектов UserControl, которые живут внутри некоторого проекта модуля, который реализует интерфейс типа IModule из Prism. Интерфейс определяет начальную точку любого модуля, который может быть загружен в пределах вкладки. По мере запроса модулей вы просто загружаете сборку и добавляете вкладку, содержащую ссылку на область, определенную в модуле.
Это на самом деле не очень сложно, но я рекомендую вам прочитать о Присм, потому что он будет обрабатывать много тяжелой работы для вас. Я недавно прошел через строительство интерфейс точно такой же, как тот, который вы описываете с помощью Prism.
взгляните на Caliburn.Микро для MVVM основа-решение. Точный вопрос, который вы задаете, решается в одном из примеров решений.
без дополнительной информации мой подход будет заключаться в запуске поставщика MVVM внутри ApplicationDomain для каждого экземпляра или вкладки. Когда вы убьете/закроете вкладку, выгрузите домен приложения.
Читайте также: