2010-09-08 11 views

Respuesta

24

he tomado la idea de Matt , y lo extendió un poco; Implementé un comportamiento adjunto que le permite definir la marca de agua para cada control DatePicker a través de XAML. Aquí está el código:

namespace DatePickerWatermark 
{ 
    using System.Windows; 
    using System.Windows.Controls; 
    using System.Windows.Controls.Primitives; 
    using System.Windows.Media; 

    public static class DatePickerWatermarkBehaviour 
    { 
     public static readonly DependencyProperty WatermarkProperty = 
      DependencyProperty.RegisterAttached(
       "Watermark", 
       typeof(string), 
       typeof(DatePickerWatermarkBehaviour), 
       new UIPropertyMetadata(null, OnWatermarkChanged)); 

     public static string GetWatermark(Control control) 
     { 
      return (string)control.GetValue(WatermarkProperty); 
     } 

     public static void SetWatermark(Control control, string value) 
     { 
      control.SetValue(WatermarkProperty, value); 
     } 

     private static void OnWatermarkChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) 
     { 
      var datePicker = dependencyObject as DatePicker; 
      if (datePicker == null) 
       return; 

      if ((e.NewValue != null) && (e.OldValue == null)) 
       datePicker.Loaded += DatePickerLoaded; 
      else if ((e.NewValue == null) && (e.OldValue != null)) 
       datePicker.Loaded -= DatePickerLoaded; 
     } 

     private static void DatePickerLoaded(object sender, RoutedEventArgs e) 
     { 
      var datePicker = sender as DatePicker; 
      if (datePicker == null) 
       return; 

      var datePickerTextBox = GetFirstChildOfType<DatePickerTextBox>(datePicker); 
      if (datePickerTextBox == null) 
       return; 

      var partWatermark = datePickerTextBox.Template.FindName("PART_Watermark", datePickerTextBox) as ContentControl; 
      if (partWatermark == null) 
       return; 

      partWatermark.Content = GetWatermark(datePicker); 
     } 

     private static T GetFirstChildOfType<T>(DependencyObject dependencyObject) where T : DependencyObject 
     { 
      if (dependencyObject == null) 
       return null; 

      for (var i = 0; i < VisualTreeHelper.GetChildrenCount(dependencyObject); i++) 
      { 
       var child = VisualTreeHelper.GetChild(dependencyObject, i); 
       var result = (child as T) ?? GetFirstChildOfType<T>(child); 
       if (result != null) 
        return result; 
      } 

      return null; 
     } 
    } 
} 

He aquí algunos ejemplo de XAML:

<Window x:Class="DatePickerWatermark.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:b="clr-namespace:DatePickerWatermark" Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.Resources> 
      <Style TargetType="DatePicker"> 
       <Setter Property="Margin" Value="10,5"/> 
      </Style> 
      <Style x:Key="EnglishDatePicker" TargetType="DatePicker" BasedOn="{StaticResource {x:Type DatePicker}}"> 
       <Setter Property="b:DatePickerWatermarkBehaviour.Watermark" Value="Please select a date:"/> 
      </Style> 
     </Grid.Resources> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition Width="200"/> 
     </Grid.ColumnDefinitions> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
      <RowDefinition Height="Auto"/> 
     </Grid.RowDefinitions> 
     <DatePicker Grid.Row="0" b:DatePickerWatermarkBehaviour.Watermark=""/> 
     <DatePicker Grid.Row="1" b:DatePickerWatermarkBehaviour.Watermark="Wählen Sie ein Datum"/> 
     <DatePicker Grid.Row="2" b:DatePickerWatermarkBehaviour.Watermark="Sélectionner une date"/> 
     <DatePicker Grid.Row="3" Style="{StaticResource EnglishDatePicker}"/> 
     <DatePicker Grid.Row="4" Style="{StaticResource EnglishDatePicker}"/> 
    </Grid> 
</Window> 
1

Wayne, esto funciona muy bien, pero no funciona cuando DatePicker es parte de DataGridColumnHeader ya veces cuando DatePicker es el control que se oculta primera y luego visible. La solución de Matt Hamilton solo funciona en carga, y cuando cambias el día seleccionado hay otra vez una marca de agua molesta. La solución más sencilla es anular el evento OnRender en clase personalizada. Si establece una propiedad de marca de agua y no un contenido de marca de agua, debe anular el evento de carga también. La clase completa está aquí:

public class myDateTimePicker : DatePicker 
{ 

    public string Watermark { get; set; } 

    protected override void OnSelectedDateChanged(SelectionChangedEventArgs e) 
    { 
     base.OnSelectedDateChanged(e); 
     //SetWatermark(); 
    } 

    protected override void OnRender(System.Windows.Media.DrawingContext drawingContext) 
    { 
     base.OnRender(drawingContext); 
     SetWatermark(); 
    } 

    private void SetWatermark() 
    { 
     FieldInfo fiTextBox = typeof(DatePicker).GetField("_textBox", BindingFlags.Instance | BindingFlags.NonPublic); 
     if (fiTextBox != null) 
     { 
      DatePickerTextBox dateTextBox = (DatePickerTextBox)fiTextBox.GetValue(this); 
      if (dateTextBox != null) 
      { 
       if (string.IsNullOrWhiteSpace(this.Watermark)) 
       { 
        this.Watermark = "Custom watermark"; 
       } 

       // if you set property this way then you need to override OnSelectedDateChanged event 
       //PropertyInfo piWatermark = typeof(DatePickerTextBox).GetProperty("Watermark", BindingFlags.Instance | BindingFlags.NonPublic); 
       //if (piWatermark != null) 
       //{ 
       // piWatermark.SetValue(dateTextBox, this.Watermark, null); 
       //} 

       var partWatermark = dateTextBox.Template.FindName("PART_Watermark", dateTextBox) as ContentControl; 
       if (partWatermark != null) 
       { 
        partWatermark.Foreground = new SolidColorBrush(Colors.Gray); 
        partWatermark.Content = this.Watermark; 
       } 
      } 
     } 
    } 

} 
Cuestiones relacionadas