This article has been localized into Russian by the community.
Элемент Calendar
WPF имеет в своём составе встроенный элемент для отображения календаря. Применение его очень простое: вы помещаете элемент в окно и получаете полный вид календаря, как показано ниже:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarControlSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarControlSample" Height="250" Width="300">
<Grid>
<Calendar />
</Grid>
</Window>
Обратите внимание, как выводятся все возможные дни выбранного месяца, включая возможность перемещения в предыдущие или следующие месяцы, используя стрелки в верхней части элемента. По умолчанию, выбран текущий месяц и отмечен текущий день, если не была указана конкретная дата.
Размеры календаря
Вы, наверно, заметите, что в нашем первом примере календарь не занимает всего свободного пространства. Даже, в случае если Вы назначите его ширине и высоте большие размеры, настоящий размер календаря не изменится, а если одно из них укажете намного меньше, то будет видна только его часть.
Это поведение фиксированных размеров не очень типично для WPF, где всё обычно растягивается для заполнения всего свободного пространства, и может немного раздражать, если Вы запланировали область, которую хотели бы заполнить календарём. К счастью для нас, всё в WPF масштабируемо, но в данном случае с календарём, ему требуется маленькая помощь. Для этого Мы воспользуемся элементом Viewbox:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarViewboxSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarViewboxSample" Height="350" Width="300">
<Viewbox>
<Calendar />
</Viewbox>
</Window>
Обратите внимание, как сейчас элемент Calendar масштабируется до того момента, как заполнит всё свободное пространство по ширине. Масштабирование производится ко всем составляющим элемента, включая размеры шрифта и ширины границ.
Вы, возможно, ещё заметите, что элемент Calendar не занимает всё пространство по высоте. Это заметно, так как окно больше по высоте чем по ширине, и по умолчанию, элемент Viewbox растянется учитывая соотношение сторон. Вы можете его с лёгкостью растянуть с заполнением всего доступного пространства в обоих направлениях - просто измените свойство Stretch, которое по умолчанию является Uniform, на Fill:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarViewboxSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarViewboxSample" Height="350" Width="300">
<Viewbox Stretch="Fill" StretchDirection="UpOnly">
<Calendar />
</Viewbox>
</Window>
Сейчас он занимает всё возможное пространство в обоих направлениях. Обычно, это нежелательно, так как большинство элементов, в особенности этот, будет выглядеть неуместно, если получит необычный набор размеров, например 800 пикселей по высоте и 300 пикселей по ширине. Установка режима Stretch в значение Uniform(или оставленное как есть, так оно по умолчанию) обычно является оптимальным решением.
Я бы порекомендовал добавления свойства StretchDirection, так как показано в этом примере. Это свойство позволяет нам указывать когда содержимое должно быть масштабировано вверх или вниз, что может быть полезно. Например, элемент Calendar становится почти бесполезным меньше определённого размера, где Вы больше не можете различить чем он является, и чтобы этого избежать, Вы можете назначит свойству StretchDirection значение UpOnly - масштабирование элемента Calendar больше не опустится его изначальных размеров по умолчанию.
Установка изначального внешнего вида, используя DisplayDate
Элемент Calendar по умолчанию будет показывать текущий месяц, но Вы можете это изменить используя свойство DisplayDate. Просто установите его значение на дату с желаемым начальным месяцем и это отразится на элементе:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarDisplayDateSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarDisplayDateSample" Height="300" Width="300">
<Viewbox>
<Calendar DisplayDate="01.01.2014" />
</Viewbox>
</Window>
Свойство календаря SelectionMode
Интересным является свойство SelectionMode. Изменяя его значение по умолчанию, SingleDate, Вы можете выбрать несколько дат или их промежуток. Вот пример:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarSelectionModeSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarSelectionModeSample" Height="300" Width="300">
<Viewbox>
<Calendar SelectionMode="SingleRange" />
</Viewbox>
</Window>
При значении SingleRange свойства SelectionMode Вы можете выбрать весь промежуток дат, удерживая левую кнопку мыши и перетаскивая с одной даты на другую или удерживая клавиши Ctrl или Shift одновременно помечая несколько дат также, как работает множественный выбор во всём Windows. На снимке экрана я выбрал всю неделю с Воскресенья по Понедельник, но Вы можете также легко выбрать даты в середине недели и промежутки превышающие область одной недели.
Режим SingleRange разрешает выбрать только один промежуток дат, что исходит из названия. Это означает, что Вы не можете выбрать две несоседние даты и более одного промежутка. Если Вы этого хотите, Вам нужно переключить режим выделения в MultipleRange:
<Calendar SelectionMode="MultipleRange" />
С этим свойством нет никаких ограничений в выборе дат. В этом случае я выбрал все субботы, воскресенья и некоторые дни недели между.
Конечно, если Вы не хотите допускать выделения одной или более дат, Вы можете установить SelectionMode в None.
Теперь давайте обсудим как мы можем работать с выбранным(-ыми) датой(-ами) в элементе Calendar.
Работа с выбранной датой
Если Вы разрешаете только одинарное выделение(смотрите объяснение режимов выделения выше), то всё что Вам нужно - это свойство SelectedDate. Оно позволяет Вам как назначать, так и получать текущую выделенную дату из программной части или через привязку данных.
Вот пример где мы назначаем выбранную дату на завтрашнюю из программного части и потом используем привязку данных для считывания выбранной даты в элемент TextBox:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarSelectionSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarSelectionSample" Height="280" Width="220">
<StackPanel Margin="10">
<Calendar Name="cldSample" SelectionMode="MultipleRange" SelectedDate="10.10.2013" />
<Label>Selected date:</Label>
<TextBox Text="{Binding ElementName=cldSample, Path=SelectedDate, StringFormat=d, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Window>
using System;
using System.Windows;
namespace WpfTutorialSamples.Misc_controls
{
public partial class CalendarSelectionSample : Window
{
public CalendarSelectionSample()
{
InitializeComponent();
cldSample.SelectedDate = DateTime.Now.AddDays(1);
}
}
}
В программной части мы просто назначаем свойство SelectedDate на текущую дату плюс один день, что означает завтра. Пользователь потом может изменить это выделение нажимая на элемент Calendar, что автоматически отразится через привязку данных к свойству Text элемента TextBox.
В качестве бонуса, используя магию привязки данных, Вы также можете изменять значение из элемента TextBox. Просто введите верное значение даты и изменение тут же отразится в элементе Calendar. Как только Вы введёте неверную дату, автоматическая проверка привязки оповестит Вас о проблеме:
Работа с несколькими выбранными датами
Если Вы разрешаете выделение одновременно более одной даты, Вы не обнаружите свойство SelectedDate таким же полезным. Наоборот, вы должны использовать свойство SelectedDates, которое является коллекцией с уже выбранными датами в элементе Calendar. Это свойство может быть доступно из программной части или использовано привязкой, как мы делаем здесь:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarSelectedDatesSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarSelectedDatesSample" Height="420" Width="220">
<StackPanel Margin="10">
<Calendar Name="cldSample" SelectionMode="MultipleRange" />
<Label>Selected dates:</Label>
<ListBox ItemsSource="{Binding ElementName=cldSample, Path=SelectedDates}" MinHeight="150" />
</StackPanel>
</Window>
С такой простой привязкой, как эта, теперь мы может отображать список текущих выбранных дат.
Если Вы хотите реагировать на изменение дат в программной части, Вы можете присоединится к событию SelectedDatesChanged элемента Calendar.
Недоступные даты
В зависимости от способа использования календаря может возникнуть необходимость запрета/ограничения возможности использования некоторых дат. Это может быть необходимо, например, в программе бронирования, где Вы хотите запретить выбор уже недоступных для бронирования дат. Элемент Calendar поддерживает это путём использование коллекции BlackoutDates, использование которой, разумеется, возможно как в XAML, так и в программной части класса:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarBlockedoutDatesSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarBlockedoutDatesSample" Height="300" Width="300">
<Viewbox>
<Calendar Name="cldSample" SelectionMode="MultipleRange">
<Calendar.BlackoutDates>
<CalendarDateRange Start="10.13.2013" End="10.19.2013" />
<CalendarDateRange Start="10.27.2013" End="10.31.2013" />
</Calendar.BlackoutDates>
</Calendar>
</Viewbox>
</Window>
using System;
using System.Windows;
using System.Windows.Controls;
namespace WpfTutorialSamples.Misc_controls
{
public partial class CalendarBlockedoutDatesSample : Window
{
public CalendarBlockedoutDatesSample()
{
InitializeComponent();
cldSample.BlackoutDates.AddDatesInPast();
cldSample.BlackoutDates.Add(new CalendarDateRange(DateTime.Today, DateTime.Today.AddDays(1)));
}
}
}
В этом примере я демонстрирую оба способа добавления вычеркнутых дней - через XAML и через программную часть. Оба способа работают через добавление экземпляров CalendarDateRange в коллекцию BlackedoutDates collection.
В XAML я жёстко прописываю промежутки дат(по большей части, чтобы показать Вам, что это возможно выполнить и таким образом), одновременно совершая что-то более умное в программной части, путём изначального добавления всех прошедших дней в коллекцию одним вызовом метода AddDatesInPast() и дальнейшим добавлением промежутка содержащего текущий и завтрашний даты.
DisplayMode - отображение месяцев или лет
Свойство DisplayMode может изменять элемент Calendar таким образом, что в одном случае вы сможете выбирать дату, а в другом месяц или, даже, год. Это совершается через свойство DisplayMode, по умолчанию хранящее значение Month, которые мы использовали во всех предыдущих примерах. Здесь показано, что происходит в случае, если мы его поменяем:
<Window x:Class="WpfTutorialSamples.Misc_controls.CalendarDisplayModeSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="CalendarDisplayModeSample" Height="300" Width="300">
<Viewbox>
<Calendar DisplayMode="Year" />
</Viewbox>
</Window>
Назначив DisplayMode в значение Year, мы теперь сможем выбрать месяц определённого года. Вы можете изменять год сверху, используя стрелки.
Элемент Calendar также позволяет выбор целого года, используя значение Decade в свойстве DisplayMode:
<Calendar DisplayMode="Decade" />
Заключение
Как Вы можете видеть, элемент Calendar является очень гибким с большим количеством опции и функционала, требуя минимум конфигурации для использования. Если Вы собираете приложение с функционалом связанным любым образом с датой, то, возможно, сможете использовать элемент Calendar в том или ином случае.