2010-09-08 4 views

risposta

24

Ho preso l'idea di Matt e lo ha esteso un po '; Ho implementato un comportamento associato che consente di definire la filigrana per ciascun controllo DatePicker tramite XAML. Ecco il codice:

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; 
     } 
    } 
} 

Ecco qualche esempio 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, questa grande opera, ma non funziona quando DatePicker è parte di DataGridColumnHeader e, a volte, quando DatePicker è sul controllo che viene prima nascosto e quindi visibile. La soluzione di Matt Hamilton funziona solo su Load, e quando si modifica selectedDate c'è di nuovo fastidioso Selezionare una filigrana della data. La soluzione più semplice è solo per sostituire l'evento OnRender in una classe personalizzata. Se si imposta la proprietà filigrana e non un contenuto filigrana che è necessario ignorare anche l'evento onload. La classe completa è qui:

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; 
       } 
      } 
     } 
    } 

}