2016-04-13 21 views
2

Ich versuche, die Kontrollschablone der DateTimePicker von neu zu gestalten. Hier ist ein Bild von dem, was es sollte wie folgt aussehen:
Style ComparisonRestyle Kontrollschablone mit vorhandenen Teilen

Hier ist der relevante Teil des ursprünglichen Code:

DateTimePicker.cs

[TemplatePart(Name = PART_Calendar, Type = typeof(Calendar))] 
[TemplatePart(Name = PART_TimeUpDown, Type = typeof(TimePicker))] 
public class DateTimePicker : DateTimePickerBase 
{ 
    private const string PART_Calendar = "PART_Calendar"; 
    private const string PART_TimeUpDown = "PART_TimeUpDown"; 

    public override void OnApplyTemplate() 
    { 
     base.OnApplyTemplate(); 

     if(_calendar != null) 
      _calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged; 

     _calendar = GetTemplateChild(PART_Calendar) as Calendar; 

     if(_calendar != null) 
     { 
      _calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged; 
      _calendar.SelectedDate = Value ?? null; 
      _calendar.DisplayDate = Value ?? this.ContextNow; 
      this.SetBlackOutDates(); 
     } 

     _timePicker = GetTemplateChild(PART_TimeUpDown) as TimePicker; 
    } 
} 

Generic.xaml

<Style TargetType="{x:Type local:DateTimePicker}"> 
    ... 
    <Setter Property="Template"> 
    <Setter.Value> 
     <ControlTemplate TargetType="{x:Type local:DateTimePicker}"> 
     ... 
     <StackPanel> 
      <Calendar x:Name="PART_Calendar" BorderThickness="0" /> 
      <local:TimePicker x:Name="PART_TimeUpDown" ... /> 
     </StackPanel> 
     ... 
     </ControlTemplate> 
    </Setter.Value> 
    <Setter> 
</Style> 

Jetzt da die DateTimePicker hat eine ziemlich gute Menge an Logik im Code hinter für die Anpassung der Eigenschaften des Kalenders Teil auf bestimmte Ereignisse, ich würde es hassen, das Rad neu zu erfinden. Im Idealfall würde ich einfach in der Lage sein mag, um die Steuerung wie folgt restyle:

CustomStyles.xaml

<Style x:Key="MetroDateTimePicker" TargetType="{x:Type xctk:DateTimePicker}"> 
    <Setter Property="Foreground" Value="{DynamicResource TextBrush}"/> 
    <Setter Property="Background" Value="{DynamicResource ControlBackgroundBrush}"/> 
    <Setter Property="BorderThickness" Value="1"/> 
    <Setter Property="BorderBrush" Value="{DynamicResource TextBoxBorderBrush}"/> 
    <Setter Property="FocusVisualStyle" Value="{x:Null}"/> 
    <Setter Property="FontFamily" Value="{DynamicResource ContentFontFamily}"/> 
    <Setter Property="FontSize" Value="{DynamicResource ContentFontSize}"/> 
    <Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/> 
    <Setter Property="Stylus.IsFlicksEnabled" Value="False"/> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type xctk:DateTimePicker}"> 
       <Grid> 
        <Border x:Name="Base" 
          Background="{TemplateBinding Background}" 
          BorderBrush="{TemplateBinding BorderBrush}" 
          BorderThickness="{TemplateBinding BorderThickness}" 
          SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/> 
        <Grid Margin="5"> 
         <Grid.ColumnDefinitions> 
          <ColumnDefinition Width="*" /> 
          <ColumnDefinition Width="Auto" /> 
         </Grid.ColumnDefinitions> 
         <xctk:ButtonSpinner x:Name="PART_Spinner" 
              Grid.Column="0" 
              BorderThickness="0" 
              IsTabStop="False" 
              Background="Transparent" 
              Style="{StaticResource MetroButtonSpinner}" 
              AllowSpin="{TemplateBinding AllowSpin}" 
              ShowButtonSpinner="{TemplateBinding ShowButtonSpinner}"> 
          <xctk:WatermarkTextBox x:Name="PART_TextBox" 
                BorderThickness="0" 
                Background="Transparent" 
                FontFamily="{TemplateBinding FontFamily}" 
                FontSize="{TemplateBinding FontSize}" 
                FontStretch="{TemplateBinding FontStretch}" 
                FontStyle="{TemplateBinding FontStyle}" 
                FontWeight="{TemplateBinding FontWeight}" 
                Foreground="{TemplateBinding Foreground}" 
                HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" 
                IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" 
                MinWidth="20" 
                AcceptsReturn="False" 
                Padding="0" 
                TextAlignment="{TemplateBinding TextAlignment}" 
                TextWrapping="NoWrap" 
                Text="{Binding Text, RelativeSource={RelativeSource TemplatedParent}}" 
                TabIndex="{TemplateBinding TabIndex}" 
                Watermark="{TemplateBinding Watermark}" 
                WatermarkTemplate="{TemplateBinding WatermarkTemplate}" /> 
         </xctk:ButtonSpinner> 
         <ToggleButton x:Name="_calendarToggleButton" 
             Background="{TemplateBinding Background}" 
             Grid.Column="1" 
             IsChecked="{Binding IsOpen, RelativeSource={RelativeSource TemplatedParent}}" 
             Style="{DynamicResource ChromelessButtonStyle}" 
             Foreground="{TemplateBinding Foreground}" 
             IsTabStop="False"> 
          <Path Fill="{TemplateBinding Foreground}" 
            Data="..." 
            Stretch="Uniform"> 
           <Path.Width> 
            <Binding RelativeSource="{RelativeSource TemplatedParent}" 
              Path="FontSize" 
              Converter="{x:Static shared:FontSizeOffsetConverter.Instance}"> 
             <Binding.ConverterParameter> 
              <sys:Double>4</sys:Double> 
             </Binding.ConverterParameter> 
            </Binding> 
           </Path.Width> 
           <Path.Height> 
            <Binding RelativeSource="{RelativeSource TemplatedParent}" 
              Path="FontSize" 
              Converter="{x:Static shared:FontSizeOffsetConverter.Instance}"> 
             <Binding.ConverterParameter> 
              <sys:Double>4</sys:Double> 
             </Binding.ConverterParameter> 
            </Binding> 
           </Path.Height> 
          </Path> 
         </ToggleButton> 
        </Grid> 
        <Popup x:Name="PART_Popup" 
          AllowsTransparency="True" 
          IsOpen="{Binding IsChecked, ElementName=_calendarToggleButton}" 
          PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" 
          StaysOpen="False"> 
         <Border Padding="3" 
           Background="{DynamicResource WhiteBrush}" 
           BorderBrush="{DynamicResource ComboBoxPopupBrush}" 
           BorderThickness="{TemplateBinding BorderThickness}" 
           Effect="{DynamicResource DropShadowBrush}"> 
          <StackPanel> 
           <Calendar x:Name="Part_Calendar" 
              BorderThickness="0" 
              MinWidth="115" 
              DisplayDateStart="{Binding Minimum, RelativeSource={RelativeSource TemplatedParent}}" 
              DisplayDateEnd="{Binding Maximum, RelativeSource={RelativeSource TemplatedParent}}" 
              IsTodayHighlighted="False"/> 
           <xctk:TimePicker x:Name="PART_TimeUpDown" 
               Style="{StaticResource MetroTimePicker}" 
               Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" 
               Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}" 
               Format="{TemplateBinding TimeFormat}" 
               FormatString="{TemplateBinding TimeFormatString}" 
               Value="{Binding Value, RelativeSource={RelativeSource TemplatedParent}}" 
               Minimum="{Binding Minimum, RelativeSource={RelativeSource TemplatedParent}}" 
               Maximum="{Binding Maximum, RelativeSource={RelativeSource TemplatedParent}}" 
               ClipValueToMinMax="{Binding ClipValueToMinMax, RelativeSource={RelativeSource TemplatedParent}}" 
               IsUndoEnabled="{Binding IsUndoEnabled, RelativeSource={RelativeSource TemplatedParent}}" 
               AllowSpin="{TemplateBinding TimePickerAllowSpin}" 
               ShowButtonSpinner="{TemplateBinding TimePickerShowButtonSpinner}" 
               Watermark="{TemplateBinding TimeWatermark}" 
               WatermarkTemplate="{TemplateBinding TimeWatermarkTemplate}" 
               Visibility="{TemplateBinding TimePickerVisibility}" 
               Margin="3 0 3 3"/> 
          </StackPanel> 
         </Border> 
        </Popup> 
       </Grid> 
       <ControlTemplate.Triggers> 
        <MultiDataTrigger> 
         <MultiDataTrigger.Conditions> 
          <Condition Binding="{Binding IsReadOnly, RelativeSource={RelativeSource Self}}" Value="False" /> 
          <Condition Binding="{Binding AllowTextInput, RelativeSource={RelativeSource Self}}" Value="False" /> 
         </MultiDataTrigger.Conditions> 
         <Setter Property="IsReadOnly" Value="True" TargetName="PART_TextBox" /> 
        </MultiDataTrigger> 
        <DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource Self}}" Value="True"> 
         <Setter Property="IsReadOnly" Value="True" TargetName="PART_TextBox" /> 
        </DataTrigger> 
        <Trigger Property="IsEnabled" Value="False"> 
         <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}" /> 
        </Trigger> 
       </ControlTemplate.Triggers> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

Hinweis: Dies ist nicht die einzige Sache, die ich geändert habe; Es gibt tatsächlich zwei weitere Stile, die ich weggelassen habe (MetroTimePicker und MetroButtonSpinner). Alles sieht gut aus, es verhält sich nicht richtig. Immer wenn ich in meinem Code einen Unterbrechungspunkt einstelle (nachdem ApplyTemplate genannt wird), kann ich sehen, dass das private Feld _calendar Null ist. Der direkte Aufruf von myPicker.GetTemplateChild("PART_Calendar") gibt ebenfalls null zurück (dies ist im Watch- oder Direkt-Fenster möglich).

Es scheint, dass wenn ich einen benutzerdefinierten Stil anwendete, war es nicht mehr in der Lage, die benannten Elemente in der Vorlage aufzunehmen. Ich muss etwas vermissen, weil ich dachte, dass ich fast jede Kontrollschablone anwenden könnte, solange alle genannten Teile dort waren (und den entsprechenden Typ hatten). Meine Frage ist also, wie kann ich eine benutzerdefinierte Vorlage auf ein WPF-Steuerelement anwenden und sicherstellen, dass die mit den benannten Teilen verknüpfte Logik weiterhin wie erwartet funktioniert?

+0

Das ist ein ziemlich spezifisches Adhoc-Szenario. Wäre es notwendig, diese Sache speziell zu durchforschen, es sei denn, Sie haben einige Informationen/Fehler, die für die Freigabe spezifisch sind, die der ganzen Reihenfolge der deduktiven Argumentation bei der Fehlersuche helfen könnten? –

+0

@ChrisW. Ja, tut mir Leid, ich habe mich schon eine Weile mit diesem Problem beschäftigt. Der ganze Stil ist ziemlich groß (und beinhaltet tatsächlich 3 verschiedene Stile), also möchte ich mich hier nicht zu sehr einmischen. Ich glaube jedoch, das Kernproblem ist, dass das '_calendar'-Feld null ist, und ich kann mir keinen Grund vorstellen, der passieren würde, da die Gesamtstruktur meines Stils dem Original ziemlich ähnlich ist. Es gibt keine Ausnahmen. –

+0

... und Sie sind sicher, dass Sie alle PART_ * s verwenden? Wenn Sie sehen, dass alles hilft, wetten Sie, dass es ein kleines Nuance-Detail ist, wenn Sie die Kontrollschablone überschreiben. –

Antwort

0

In Ordnung, ich bin ein Idiot. Ich weiß nicht, wie lange ich in dieser sah, Doppel- und jeden Namen Triple-geprüft, aber irgendwie habe ich nie gesehen diese dummen kleinen Tippfehler:

<Calendar x:Name="Part_Calendar" … /> 

gewesen wäre:

<Calendar x:Name="PART_Calendar" … />