Este artículo proviene de un motor de traducción automática.

Fundamentos

Personalizar los nuevos controles de calendario WPF

Charles Petzold

Descarga de código de la Galería de código de MSDN
Examinar el código en línea

Contenido

Calendario: una orientación breve
Estilos del calendario
Establecer una nueva plantilla de calendario
Reemplazar la plantilla CalendarItem
La plantilla CalendarDayButton eficaz
Derivar de calendario
El control DatePicker

Cuando Microsoft liberado Windows Presentation Foundation (WPF) en 2006, todos los usuarios acordado que fue falta un par de piezas, más forma evidente controles de calendario. Parecía una omisión impar, después de todo, formularios Windows Forms tenía MonthCalendar y DateTimePicker controles, por lo que ¿por qué no sólo puerto los para WPF?

Pero trasladar un control WPF no es una simple cuestión de cambiar sólo los nombres de propiedad y evento. Los controles WPF deben ser "lookless". El aspecto visual del control debe separarse en un árbol visual de otros elementos y controles. Esta plantilla predeterminada se debe estructurada y documentar de tal manera que completamente reemplazable. De esta forma los desarrolladores y diseñadores pueden personalizar los controles proporciona a un aspecto totalmente nuevo.

Diseñar un control con una plantilla reemplazable puede ser intimidating, a medida que descubren cuando intenta diseñar un control MonthCalendar de mi artículo"Las plantillas de controles extraño"en enero de 2008 emitir. Desea que el control como general como sea posible para se amenable a una variedad de aspectos diferentes, aún sin realizar la interfaz entre el código y la plantilla inordinately complejos. Los calendarios presente otro nivel de complejidad porque son mucho que dependen de las convenciones culturales, y no es obvio en qué medida una plantilla personalizada debe poder infringir las convenciones.

Esto es, posiblemente, ¿por qué controles de calendario WPF tardó tanto tiempo. Pero más tarde el año pasado Microsoft lanzado los controles de calendario y DatePicker (con algunas clases subsidiarias) en unKit de herramientas de WPF. (También se incluye una nueva cuadrícula de datos, pero no pueden explicar aquí.) Estos controles son mucho más compatibles con los mismos controles de Silverlight, y probablemente terminará hacia arriba en la siguiente versión de WPF.

Para el resto de este artículo, le suponga que ha descargado el Kit de herramientas de WPF código y los archivos binarios y ejecutar el instalador para registrar WPFToolkit.dll. Una vez este archivo DLL se registra, puede utilizarla en un proyecto WPF en Visual Studio haciendo clic con el botón secundario en la etiqueta de referencias en el proyecto, seleccionando agregar referencia y, a continuación, seleccione la ficha .NET. La DLL del Kit de herramientas de WPF se encuentran hacia el final de la lista.

Calendario: una orientación breve

Las clases de calendario y DatePicker (junto con algunos archivos auxiliares enumeraciones y delegados) tienen un espacio de nombres de Microsoft.Windows.Controls. Algunas clases subsidiarias, principalmente especialmente CalendarItem, CalendarButton CalendarDayButton y DatePickerTextBox, son en el espacio de nombres Microsoft.Windows.Controls.Primitives. Estos no son los espacios de nombres habitual para los controles WPF, por lo que probablemente tendrá que agregar nuevos utilizando directivas en su código de C# y construir las declaraciones de espacio de nombres XML correctas en archivos de XAML. Las clases relacionadas con el administrador de estado Visual nuevo (que trataré más adelante en este artículo) tienen un espacio de nombres más normal de System.Windows.

La clase del calendario se deriva de control. Si simplemente crear una instancia calendario, obtendrá la vista mostrada a la izquierda en la figura 1 . Se muestra sólo un mes, nunca varios meses como MonthCalendar de formularios de Windows. El mes siempre incluye seis semanas, incluso si se requieren sólo cuatro o cinco. Haga clic en el encabezado (la parte en la parte superior que indica el mes y año) y le cambie a la la vista de año en el centro. Puede haga clic en uno de los meses para volver a una vista de dicho mes, o haga clic en el encabezado nuevo para obtener la vista década que se muestra en el lado derecho. Haga clic en uno de los años para volver a la vista de año.

fig01.gif

Figura 1 los modos de presentación de calendario tres )

Estas tres vistas corresponden a la propiedad DisplayMode del calendario, que se establece en uno de los tres miembros de la enumeración CalendarMode: mes, año o diez años.

Los dos botones a cada lado de la cabecera de desplazarse hacia delante o hacia atrás por mes, por año o por diez años.

La propiedad DisplayDate es de tipo fecha y hora y indica que se muestra mes, año o diez años. Propiedades DisplayDateStart y DisplayDateEnd son de tipo DateTime que aceptan valores null y restringir la exploración del calendario a un rango determinado.

La propiedad de IsTodayHighlighted es true de forma predeterminada y hace que un cuadrado oscuro que se dibuje detrás de la fecha de hoy. También se pueden seleccionar las fechas. Establezca la propiedad SelectionMode en un miembro de la enumeración CalendarSelectionMode: SingleDate (el valor predeterminado), SingleRange, MultipleRange, o ninguno. Para el modo de SingleDate, la propiedad SelectedDate (de tipo DateTime que aceptan valores null) indica la selección; de lo contrario, en el que la propiedad de SelectedDates se es de tipo SelectedDatesCollection, que se deriva de ObservableCollection de tipo fecha y hora.

La propiedad BlackoutDates permite desactivar la selección de varios rangos de fechas. La propiedad es de tipo CalendarBlackoutDateCollection, que se deriva de ObservableCollection de tipo CalendarDateRange, una clase que define las propiedades Start y End del tipo de fecha y hora.

La propiedad de FirstDayOfWeek es de tipo DayOfWeek; el valor predeterminado es normalmente el domingo, pero para algunas configuraciones regionales (especialmente Francia) el valor predeterminado es el lunes.

Calendario define cuatro eventos: SelectionModeChanged DisplayModeChanged, DisplayDateChanged, y SelectedDatesChanged.

Estilos del calendario

Las restantes propiedades públicas de la clase de calendario son CalendarItemStyle, CalendarButtonStyle y CalendarDayButtonStyle todo tipo de estilo, pero para comprender estas propiedades necesita saber cómo el control Calendar está integrado y estructurado y para ese conocimiento ayuda a examinar algunas XAML.

Todo el código fuente para el control Calendar está descargable desde el sitio CodePlex, pero si está interesado en personalizar el control, el archivo más importante es sin duda Generic.xaml, ubicados en el subdirectorio release\Calendar\Themes del Kit de herramientas. El archivo Generic.xaml contiene los estilos predeterminados (incluidas las plantillas) para el propio control de calendario y los otros controles que componen el control Calendar. Si va a ser templating calendario, se desea estudiar Generic.xaml muy estrechamente.

Aunque la clase del calendario define todas las propiedades y eventos para el control y también realiza parte el proceso de entrada del usuario, la plantilla de calendario en Generic.xaml revela que calendario consta sólo de un StackPanel invisible y un control CalendarItem.

La clase de CalendarItem también se deriva de control y abarca el todo aspecto visual del control. Esto incluye los tres botones en la parte superior (que hacen se referencia a colectivamente como los botones de exploración). También incluye dos cuadrículas. Una cuadrícula muestra los días del mes (incluidos los encabezados para los días de la semana). La otra cuadrícula muestra en 12 meses o años 12. En cualquier momento sólo uno de estas dos cuadrículas es visible.

CalendarItem también se encarga de rellenar las dos cuadrículas. Al mostrar los días del mes, se crean objetos de tipo CalendarDayButton; los 12 meses o años son de tipo CalendarButton. Ambas clases derivan de botón.

El Generic.xaml incluye las plantillas y estilos predeterminados para calendario, CalendarItem, CalendarButton y CalendarDayButton. Para estos últimos elementos de tres, se pueden establecer nuevos estilos (incluidos las plantillas) estableciendo las propiedades CalendarItemStyle, CalendarButtonStyle o CalendarDayButtonStyle definidas por el calendario, ya sea directamente o a través de la propiedad Style del calendario.

Establecer un estilo para calendario propio es útil para calendario de específico propiedades (como DisplayMode o SelectionMode) o propiedades heredan por el calendario (como HorizontalAlignment o margen), pero desea centrarse en las clases más problemáticas de propiedades: los relativos a las fuentes y los pinceles.

El control Calendar está integrado muy con precisión de sus controles secundarios, y si inicias pruebe alrededor con fuentes, el control puede convertirse rápidamente en lopsided visualmente. Por ejemplo, se puede establecer la propiedad FontFamily en calendario y afectará a todo el texto excepto los encabezados de día de la semana. (El FontFamily para los títulos está codificado en un DateTemplate dentro de la plantilla predeterminada para CalendarItem.) Sin embargo, si se convierte el texto de los días del mes en un poco más pequeño como resultado, el mes completo se parece se desplazan a la izquierda.

Establecer la propiedad FontSize en calendario no tiene ningún efecto. Si realmente desea cambiar el TamañoDeFuente (FontSize) deberá hacer que cambiarlo en el estilo CalendarButton, el estilo CalendarDayButton y dos posiciones dentro de la plantilla CalendarItem. Olvídese de él. Si desea realizar el control Calendar un poco mayor o menor que la normal, no realizar con TamañoDeFuente (FontSize). Sugerir definir un ScaleTransform y establecer propiedad de LayoutTransform el calendario o, quizás, poner el calendario en un Viewbox.

Los colores que calendario se utiliza para marcar el día actual, las fechas seleccionadas y las fechas de desactivación son codificadas en las diversas plantillas. Los pinceles sólo que se puede cambiar fácilmente son la propiedad Background del control de calendario y el BorderBrush que rodea el control. Estos son tanto establecer en los pinceles de degradado lineales en el estilo predeterminado del calendario y, a continuación, se pasa a CalendarItem a través de enlaces de plantilla. Si desea reemplazar el pincel de fondo predeterminado, que le muestran más adelante en este artículo, tenga en cuenta que es responsable el sombreado de fondo diferente detrás de los botones de exploración que puede ver en la figura 1 .

Establecer una nueva plantilla de calendario

El primer paso para reemplazar una plantilla para un control se ve en la definición de clase, en la documentación o el código de origen reales (si está disponible). No existe, verá una colección de atributos TemplatePart. Estos atributos TemplatePart indican con controles o elementos que la nueva plantilla debe tener para ser totalmente funcional. Para que el control Calendar, una nueva plantilla debe tener al menos dos partes: un CalendarItem con el nombre PART_CalendarItem y un panel con el nombre PART_Root. (En la plantilla de calendario predeterminada, el panel de PART_Root es un StackPanel.)

Es improbable que se va se reemplazando la plantilla para el calendario, pero no debería intentar adivinar en segundo lugar sus necesidades. Quizá gustaría agregar algo al árbol visual que componen el calendario, como un desplazamiento ItemsControl que muestra todas las fechas seleccionadas.

Dicha aplicación se muestra en el proyecto SelectedDateLister incluido con el código fuente descargable para esta columna. la figura 2 muestra un extracto del archivo MainWindow.xaml de ese proyecto con una nueva plantilla de calendario y figura 3 muestra lo que parece.

Figura 2 un nueva plantilla de calendario

<ControlTemplate TargetType="toolkit:Calendar">
    <StackPanel Name="PART_Root" 
                Orientation="Horizontal">
        <primitives:CalendarItem 
            Name="PART_CalendarItem" 
            Style="{TemplateBinding CalendarItemStyle}"
            Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}"
            VerticalAlignment="Center" />
        <Border BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Margin="4 4 0 4">
            <ScrollViewer 
                    VerticalScrollBarVisibility="Auto"
                    Height="{Binding ElementName=PART_CalendarItem,
                                     Path=ActualHeight}"
                    Width="100">
                <ItemsControl 
                    ItemsSource=
                        "{Binding RelativeSource={RelativeSource 
                                      AncestorType=toolkit:Calendar}, 
                                  Path=SelectedDates}" />
            </ScrollViewer>
        </Border>
    </StackPanel>
</ControlTemplate>

fig03.gif

La figura 3 calendario con lista de las fechas seleccionadas

Observe el prefijo del espacio de nombres de elementos primitivos necesarios para hacer referencia al control CalendarItem XML. En el elemento raíz del archivo XAML, dicho prefijo se asocia con el espacio de nombres .NET de Microsoft.Windows.Controls.Primitives y el ensamblado WPFToolkit. De forma similar, el Kit de herramientas de prefijo se asocia con el espacio de nombres Microsoft.Windows.Controls en el mismo ensamblado. El calendario sólo se crean instancias en el XAML así:

<toolkit:Calendar />

El estilo de que contiene la plantilla nueva se aplicará implícitamente.

Tenga en cuenta también en la figura 2 el uso de un enlace con RelativeSource para conectar la propiedad ItemsSource de la ItemsControl con la propiedad SelectedDates del calendario. TemplateBinding no puede utilizarse para este trabajo porque SelectedDates no se ha realizado una seguridad con una propiedad de dependencia.

¿Se ha Cómo se sabe qué propiedades de CalendarItem debe establecen con TemplateBinding? Examinó en la plantilla de calendario predeterminada de Generic.xaml. De hecho, no sólo examinó se: se copia y pegado hacer en mi código de origen. Copiar y pegar es, tengo miedo, la mejor manera de hacer modificaciones en las plantillas existentes.

Reemplazar la plantilla CalendarItem

Para disponer de más control sobre el aspecto interno del control Calendar, es necesario reemplazar la plantilla para CalendarItem. La plantilla predeterminada es un trozo considerable del XAML, casi 250 líneas en el archivo Generic.xaml, e incluye tres plantillas incrustadas de los botones de exploración que aparecen en la parte superior.

Los programadores WPF con experiencia que aún no ha delved en Silverlight sorprenderá ver algo familiarizado en las tres plantillas de botón incrustado: las referencias a clases denominadas VisualStateManager, VisualStateGroup y VisualState. Estas clases son componentes principales del administrador de estado Visual que se encuentra ya en Silverlight y espera que forman parte de WPF en la siguiente versión principal. (El código fuente de este administrador de estado Visual preliminar se incluye en el Kit de herramientas de WPF.)

El administrador de estado Visual está pensado para reemplazar muchos de los desencadenadores que utiliza actualmente en las plantillas. Desencadenadores de plantilla se basan customarily en los valores de determinadas propiedades, tales como IsEnabled, IsMouseOver y IsSelected. El administrador de estado Visual se basa en su lugar alrededor de un concepto más estructurado de estados visuales, con nombres como deshabilitado, MouseOver y seleccionado. A primera vista, parecen que tienen una correspondencia uno a uno con desencadenadores estos estados visuales, pero que no es realmente es así. En el uso práctico, los desencadenadores pueden como resultado apariencia visual diferente según el orden en el que aparecen, y a menudo resulta clumsy definir varios desencadenadores cuando necesita trabajar conjuntamente con cada uno de los otros desencadenadores. La identificación de alternativa de estados visuales hace que intención el programador mucho más evidente. Se produce la asociación entre valores de propiedad y estados visuales en código.

Para hacer referencia a las clases de administrador de estado Visual en XAML, deberá definir un prefijo XML adicional (por ejemplo, vsm) asociado con el espacio de nombres System.Windows en el ensamblado WPFToolkit.

Una plantilla personalizada para CalendarItem debe tener ocho partes con nombre, tal como se muestra en La figura 4 .

La figura 4 con elementos de la plantilla CalendarItem
Parte Tipo
PART_Root FrameworkElement
PART_HeaderButton Botón
PART_PreviousButton Botón
PART_NextButton Botón
DayTitleTemplate Nombre de clave de DataTemplate
PART_MonthView Cuadrícula
PART_YearView Cuadrícula
PART_DisabledVisual FrameworkElement

Observe que uno de estos nombres es realmente un recurso Nombre de clave. La sección de recursos de la plantilla CalendarItem debe incluir un DataTemplate con un nombre de "DayTitleTemplate" que contiene un elemento TextBlock con la propiedad de texto establecida como clave para:

Text="{Binding}"

Este DateTemplate se utiliza para mostrar los títulos con los días de la semana. Los elementos de CalendarDayButton utilizados para estos encabezados tenga su establecer en null, pero su conjunto DataContext al encabezado de texto (su, Lu, Ma etc.), por tanto, el enlace extraño. (Tendrá más para indicar en la propiedad DataContext de la CalendarDayButton más adelante.)

El árbol visual predeterminado de la plantilla CalendarItem consta de varios cuadrículas anidadas. Una cuadrícula sola celda externa se denomina PART_Root. Esta cuadrícula contiene un borde y un rectángulo denominado PART_DisabledVisual destinado a proporcionar un semi-opaque cobertura cuando el control está deshabilitado.

El borde contiene otra cuadrícula, esta otra con tres columnas y dos filas que proporciona la estructura principal para el control. Los botones de exploración con tres ocupan la fila superior. La fila inferior contiene dos cuadrículas (PART_MonthView y PART_YearView) con las tres columnas.

Se espera que la cuadrícula PART_MonthView con nombre se espera que tienen siete columnas disponibles (para los siete días de la semana) y siete filas (uno de los encabezados de día de la semana) y seis de los días del mes, mientras PART_YearView tiene cuatro columnas y tres filas (por 12 meses de un año o 12 años en una vista "década"). Cuando la parte de código de CalenderItem crea los secundarios CalendarDayButton y CalendarButton para rellenar estas cuadrículas, establece explícitamente la Grid.Row y Grid.Column adjunta propiedades a cada elemento secundario.

fig05.gif

La figura 5 una nueva plantilla de CalendarItem básica-básica

Por este motivo, es improbable que una plantilla personalizada de CalendarItem serán diferentes considerablemente del diseño existente. Si desea tomar una captura en él, el archivo MainWindow.xaml en el proyecto BareBonesCalendarItem tiene prácticamente todo el formato mínimo que deberá obtener algo completo y funcional. La visual resultante se muestra en figura 5 . Sólo para que sea un poco diferente, genera la estructura general desde un DockPanel, de forma que los botones de mes anterior y siguiente mes estén en los lados. No aplicar plantillas a estos botones de exploración, por qué aspecto botones reales. Incluso con estos Simplificaciones, la plantilla es casi 80 líneas de longitud. Parece un poco poco frecuente, pero funciona.

Un enfoque más seguro y más racional para definir una nueva plantilla CalendarItem es simplemente copie la plantilla predeterminada para CalendarItem de Generic.xaml en su propio archivo XAML y, a continuación, modificar sólo determinadas partes de ella.

Eso es lo que hice en el proyecto ColorfulCalendarItem. El archivo MainWindow.xaml define un prefijo de espacio de nombres XML de local en vez de Kit de herramientas para hacer referencia a la clase de calendario para que sea coherente con las declaraciones de espacio de nombres en Generic.xaml.

En la plantilla CalendarItem agregar un nuevo pincel de fondo para el borde exterior:

<LinearGradientBrush StartPoint="0 0"   EndPoint="0 1">
    <GradientStop Offset="0"   Color="#FFFFC0" />
    <GradientStop Offset="0.5" Color="#FFE0B0" />
    <GradientStop Offset="1"   Color="#FFD0A8" />
</LinearGradientBrush>

También agrega un borde alrededor de la ContentPresenter en el botón encabezado de centro de nuevo y asignó un fondo, así:

<Border Padding="12 0"
        CornerRadius="6">
    <Border.Background>
        <LinearGradientBrush StartPoint="0 0" EndPoint="0 1">
            <GradientStop Offset="0" Color="#FFC4A0" />
            <GradientStop Offset="1" Color="#FF9450" />
        </LinearGradientBrush>
    </Border.Background>

El resultado se muestra en Figura 6 .

fig06.gif

Figura 6 nuevos colores definida por el de la plantilla CalendarItem

La plantilla CalendarDayButton eficaz

La plantilla CalendarItem contiene incrustadas plantillas para los tres botones de exploración aparece en la parte superior del calendario, pero la plantilla no tiene referencias a los objetos CalendarButton y CalendarDayButton que llenan las cuadrículas inferiores. Estos botones se crean instancias en el código, como se tenga en cuenta al examinar el archivo CalendarItem.cs. Sin embargo, puede definir nuevas plantillas para estos botones estableciendo un objeto Style en las propiedades CalendarButtonStyle y CalendarDayButtonStyle del calendario.

Una plantilla personalizada para CalendarDayButton es potencialmente muy eficaz. Aquí puede mostrar información adicional para cada día más allá del número de dos dígitos habitual entre 1 y 31. En primer lugar no parece como si puede hacerlo de manera inteligente, ya que todo lo que agregado al contenido de la CalendarDayButton probablemente tendría que dependen de la fecha exacta representada por ese botón, y esa información no parece estar fácilmente disponible.

No se preocupe. Aunque el contenido de la CalendarDayButton es establecer en una cadena de texto simple, como 24, la propiedad DataContext de cada CalendarDayButton se establece en el objeto de fecha y hora real para esa fecha, incluida la correcta mes y año. (Esta característica ayuda distinguir los objetos CalendarDayButton utilizados para los títulos de día de la semana; los botones que un contenido null pero un DataContext está establecido en el su, lu, las cadenas de texto y así sucesivamente. Ésta es la razón por un DataTemplate es necesaria en CalendarItem de estos encabezados.)

La disponibilidad de un objeto DateTime abre un área completo de posibles mejoras que se pueden implementar haciendo referencia a alguna clase personalizada en una nueva plantilla CalendarDayButton. Dado que la plantilla de CalendarDayButton predeterminada es aproximadamente 120 líneas del XAML, probablemente no se escribir dicha una plantilla desde el principio. Como con la plantilla de CalendarItem, tendrá probablemente desea copiar la plantilla de CalendarDayButton predeterminada Generic.xaml en su propio proyecto y, a continuación, modificarla.

Una posibilidad es mostrar las festividades y otros días importantes en una información sobre herramientas aparece cuando se pase el puntero del mouse (ratón) por encima de cada día, como se ilustra en el proyecto RedLetterDays. Parecía razonable implementar esta característica como un convertidor de enlace: en efecto, en que el convertidor convierte un objeto de fecha y hora en un objeto de valor de tipo String.

la figura 7 muestra las partes importantes de este convertidor de enlace. El constructor establece los elementos de fechas entre marzo (cuando escribo en este artículo) y junio (al se publicarán). Obtener acceso en un programa real, podrían probablemente a estas fechas y cadenas de texto desde un archivo.

Figura 7 más de un convertidor de enlace para RedLetterDays

public class RedLetterDayConverter : IValueConverter {
    static Dictionary<DateTime, string> dict = 
        new Dictionary<DateTime, string>();

    static RedLetterDayConverter() {
        dict.Add(new DateTime(2009, 3, 17), "St. Patrick's Day");
        dict.Add(new DateTime(2009, 3, 20), "First day of spring");
        dict.Add(new DateTime(2009, 4, 1), "April Fools");
        dict.Add(new DateTime(2009, 4, 22), "Earth Day");
        dict.Add(new DateTime(2009, 5, 1), "May Day");
        dict.Add(new DateTime(2009, 5, 10), "Mother's Day");
        dict.Add(new DateTime(2009, 6, 21), "First Day of Summer");
    }

    public object Convert(object value, Type targetType, 
        object parameter, CultureInfo culture) {

        string text;
        if (!dict.TryGetValue((DateTime)value, out text))
            text = null;
        return text;
    }

    ...

}

El archivo MainWindow.xaml contiene la plantilla para CalendarDayButton con unas cuantas adiciones. El primero es el convertidor de enlace almacenado como un recurso:

<ControlTemplate.Resources>
    <src:RedLetterDayConverter x:Key="conv" />
</ControlTemplate.Resources>

La cuadrícula en la plantilla CalendarDayButton externa, a continuación, hace referencia a ese convertidor de enlace para mostrar una información sobre herramientas:

<Grid ToolTip="{Binding Converter={StaticResource conv}, 
Mode=OneWay}">

Si enlace parece falta una configuración de ruta de acceso, tenga en cuenta que la DataContext del botón es un objeto de fecha y hora y que DataContext es heredada a través del árbol visual. Si desea mostrar la fecha y hora en la información sobre herramientas, deberá utilizar simplemente:

<Grid ToolTip="{Binding}">

La plantilla también tiene un objeto de rectángulo adicional entre los otros utilizado para resaltar el día actual y los días seleccionados:

<Rectangle x:Name="RedLetterDayBackground" 
    IsHitTestVisible="False" 
    Fill="#80FF0000" />

Un DataTrigger hace este rectángulo invisible cuando el convertidor de enlace devuelve null:

<DataTrigger 
    Binding="{Binding 
        Converter={StaticResource conv}}"
    Value="{x:Null}">
    <Setter TargetName="RedLetterDayBackground" 
            Property="Visibility" 
            Value="Hidden" />
</DataTrigger>

Figura 8 muestra los tres días no laborables en el mes de marzo.

fig08.gif

Figura 8 la pantalla RedLetterDays

En el caso más general, el árbol visual de plantilla CalendarDayButton puede incluir cualquier derivado de FrameworkElement que tiene una propiedad dependencia del tipo de fecha y hora, o incluso cualquier derivado de FrameworkElement que se supone que sus DataContext es de tipo fecha y hora.

Los lectores algunos de esta columna pueden recuperar el calendario que se ha creado para el problema de enero de 2008 con las fases de la luna. La clase MoonDisk original deriva de Viewport3D para simular una vista de la luna illuminated por el sol. Ha eliminado la propiedad de dependencia de fecha y hora de dicha clase y modifica ligeramente para convertir su DataContext a una fecha y hora. El proyecto MoonPhaseCalendar incluye esa clase MoonDisk y hace referencia en el árbol visual de la plantilla CalendarDayButton así:

<Grid Width="48" Height="48">
    <ContentPresenter 
        x:Name="NormalText"
        HorizontalAlignment="Left"
        VerticalAlignment="Top">
        <TextElement.Foreground>
            <SolidColorBrush x:Name="selectedText" 
                Color="#FF333333"/>
        </TextElement.Foreground>
    </ContentPresenter>
    <src:MoonDisk Margin="6" />
</Grid>

Observe que no hay ningún enlace de MoonDisk porque hereda el DataContext de tipo fecha y hora. El resultado en el mes de julio de 2009 se muestra en la figura 9 .

fig09.gif

Figura 9 el mostrar MoonPhaseCalendar

Derivar de calendario

CalendarItem, CalendarButton y CalendarDayButton son clases sealed todo, no se puede derivar los y incluso si puede deriva de CalendarButton y CalendarDayButton, desea deberá volver a escribir partes de CalendarItem para crear instancias de los nuevos botones en lugar de los antiguos.

Sin embargo, se puede, derivar desde el calendario propio para proporcionar alguna funcionalidad adicional. Por ejemplo, es posible que desee mejorar el control Calendar para que al hacer clic en un día determinado, aparece un cuadro de diálogo no modal, como se muestra en la figura 10 , para permitirle introducir y ver un plan para el día. Esto es la idea detrás del proyecto DailyReminders.

fig10.gif

Figura 10, el cuadro de diálogo de DailyReminders

Para implementar esta característica, la clase que deriva de calendario (que denominan DailyRemindersCalendar) debe ser capaz de detectar al usuario es hacer clic una CalendarDayButton. Normalmente esto sería muy sencillo: el constructor de la clase que deriva de calendario llamaría AddHandler con los argumentos que indica la Mouse.ClickEvent y un método para controlar estos eventos. Como sabe, WPF implementa eventos enrutados, por lo que el evento haga clic en cualquier botón secundarios viaja por el árbol visual. El controlador podría discriminate entre los botones de secundarios distintos por el argumento Source del objeto RoutedEvent de comprobación y comprobando si la DataContext es un objeto de tipo fecha y hora.

Sin embargo, esto demostrado no sea posible. La clase CalendarItem instala controladores para los eventos CalendarDayButton button-down y botón de arriba y deshabilita el evento Click.

En lugar de ello, escribí DailyRemindersCalendar reemplazar el método OnMouseDoubleClick. Sin embargo, que provoca otro problema: para ese evento en un derivado de calendario, propiedades ni el origen ni OriginalSource de MouseButtonEventArgs es cualquier tipo de botón. Origen es null y OriginalSource es un TextBlock o una ruta de acceso o rectángulo.

Una vez más, sin embargo, la herencia de la DataContext a través del árbol visual incluye al rescate. En el reemplazo OnMouseDoubleClick, si el objeto OriginalSource tiene un DataContext de tipo fecha y hora, el método sabe que un CalendarDayButton se hace clic, y el objeto de fecha y hora indica el método exactamente lo que se. El método, a continuación, puede crear un DailyRemindersDialog, que genera su interfaz de usuario (una cuadrícula que contiene controles TextBlock y TextBox completamente en código. (De hecho, el programa de DailyReminders todo consta por completo de código con ningún formato.)

El DailyRemindersDialog se invoca modelessly, para que pueda mostrar simultáneamente cuadros de diálogo de diferentes días. Sin embargo, la DailyRemindersDialog prohíbe mostrar varios cuadros de diálogo para el mismo día. Que podría provocar problemas relacionados con correctamente almacenar y recuperar estos avisos diarios. La clase DailyRemindersStorage controla esa parte del programa al hacer referencia a un archivo XML en el almacenamiento aislado. Cuando se cierra un DailyRemindersDialog, hace que ese archivo se guarden con la información actualizada.

El control DatePicker

Han centrada exclusivamente en el nuevo control de calendario en lugar de la DatePicker debido DatePicker consiste en gran medida un DatePickerTextBox y un lista desplegable que invoca el control Calendar.

Este DatePickerTextBox es extremadamente grosero. Ciertamente, no implementa la característica me gusta tanto en el control DateTimePicker de formularios Windows Forms, haciendo clic en los componentes individuales de la fecha u hora (año, mes, día, hora etc.) y utilizando las teclas numéricas o las claves de cursor para cambiar los valores.

Pero todos los estilos y plantillas que se puede aplicar al independiente control Calendar pueden también aplicar al control Calendario invoca desde la lista desplegable en DatePicker. El control DatePicker tiene una propiedad denominada CalendarStyle de estilo de tipo, y el objeto de estilo que establecida para esta propiedad puede contener setters para cualquier propiedad definida por el calendario, incluyendo las propiedades CalendarItemStyle, CalendarButtonStyle y CalendarDayButtonStyle.

Aunque una serie de estilos incrustados y plantillas, en efecto alcance en el control de calendario y el control DatePicker y a adaptar a sus propias necesidades y preferencias.

Envíe sus preguntas y comentarios a mmnet30@Microsoft.com.

Charles Petzold es un editor colaborador longtime para MSDN Magazine. Su libro más reciente es The Annotated Turing: A Guided Tour through Alan Turing's Historic Paper on Computability and the Turing Machine (Wiley, 2008). Su sitio Web es www.charlespetzold.com.