2014-02-06 11 views
3

이미 존재하는 DateTimePicker 컨트롤을 중심으로 해킹을 생성하도록 요청 받았습니다. 일반적으로 날짜/시간 선택기에는 달력의 훌륭한 이미지가 있고 그 다음에 실제 날짜를 표시하는 텍스트 상자가 있습니다. 사용자는 이미지를 클릭하고 팝업 캘린더를 표시 할 수 있으며, 선택하면 날짜가 텍스트 상자 영역으로 새로 고쳐집니다.제어 템플릿 스토리 보드, 같은 템플릿 내에서 다른 컨트롤의 값을 설정하십시오.

다른 디자이너는 일정 그래픽이 마음에 들지 않고 일반 텍스트 상자 컨트롤 만 원하지만 사용자가 두 번 클릭하여 팝업 달력을 열면 날짜를 가져 와서 새로 고칩니다. 나는 S/O에서 여기에서 찾아낸 다른 도움 덕분에 이것에 아주 가깝다.

그래서, 내 컨트롤 템플릿을 설명하고 평범한 (비 사용자 지정 컨트롤을 제안하지 않는 한)로 유지. 컨트롤 템플릿은 텍스트 상자 컨트롤을 기반으로합니다. 우리는 텍스트 상자의 PART_ContentHost와 테두리를 가지고 표준 달력 컨트롤의 팝업을 가지고 있습니다.

컨트롤 템플릿 트리거의 경우, ScrollViewer (텍스트 입력 영역)에 연결된 MouseDoubleClick 이벤트가 있습니다. 방아쇠되었을 경우, 팝업의 IsOpen을 true로 설정해, 달력을 공개합니다. 이것은 잘 작동합니다.

이제 끝내십시오. 사용자가 달력에서 날짜를 선택하면 다음 트리거가 팝업을 닫습니다 (IsOpen을 false로 설정). 이것도 작동합니다.

내 문제. 또한 선택한 날짜를 가지고 자사의 ToString() 날짜 표현이 ScrollViewer.Content (X : 이름 = "PART_ContentHost)에 넣어 얻을 선택에 따라 할 수 있습니다.

<ControlTemplate TargetType="{x:Type TextBox}" x:Key="CTTextBox" > 
    <StackPanel> 
     <Border x:Name="targetBorder" 
     BorderBrush="{TemplateBinding BorderBrush}" 
     SnapsToDevicePixels="true"> 

     <ScrollViewer x:Name="PART_ContentHost" 
      Background="{TemplateBinding Background}" 
      BorderBrush="{TemplateBinding BorderBrush}" 
      Foreground="{TemplateBinding Foreground}" /> 
     </Border> 
     <Popup PlacementTarget="{Binding ElementName=PART_ContentHost}" x:Name="PopCal"> 
     <Calendar x:Name="ActualCalendar"/> 
     </Popup> 
    </StackPanel> 

    <ControlTemplate.Triggers> 
     <EventTrigger RoutedEvent="ScrollViewer.MouseDoubleClick" SourceName="PART_ContentHost"> 
     <BeginStoryboard> 
      <Storyboard> 
       <BooleanAnimationUsingKeyFrames 
        Storyboard.TargetName="PopCal" 
        Storyboard.TargetProperty="(Popup.IsOpen)"> 
        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="True"/> 
       </BooleanAnimationUsingKeyFrames> 
      </Storyboard> 
     </BeginStoryboard> 
     </EventTrigger> 

     <EventTrigger RoutedEvent="Calendar.SelectedDatesChanged" SourceName="ActualCalendar"> 
     <BeginStoryboard> 
      <Storyboard> 
       <BooleanAnimationUsingKeyFrames 
        Storyboard.TargetName="PopCal" 
        Storyboard.TargetProperty="(Popup.IsOpen)"> 
        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value="False"/> 
       </BooleanAnimationUsingKeyFrames> 
      </Storyboard> 


      WHAT WOULD I PUT HERE to have the selected date of the popup calendar 
      inserted into the content of the PART_ContentHost... 
      <Storyboard> 
       <BooleanAnimationUsingKeyFrames 
        Storyboard.TargetName="PART_ContentHost" 
        Storyboard.TargetProperty="(Content)"> 
        <DiscreteBooleanKeyFrame KeyTime="00:00:00" Value=" ????? "/> 
       </BooleanAnimationUsingKeyFrames> 
      </Storyboard> 

     </BeginStoryboard> 
     </EventTrigger> 
    </ControlTemplate.Triggers> 
</ControlTemplate> 

<Style TargetType="{x:Type TextBox}" x:Key="STextBox" > 
    <!--<Setter Property="OverridesDefaultStyle" Value="True"/>--> 
    <Setter Property="FontFamily" Value="Arial" /> 
    <Setter Property="FontSize" Value="12" /> 
    <Setter Property="Height" Value="20" /> 
    <Setter Property="Width" Value="100" /> 
    <Setter Property="VerticalContentAlignment" Value="Bottom" /> 
    <Setter Property="HorizontalContentAlignment" Value="Left" /> 
    <Setter Property="HorizontalAlignment" Value="Left" /> 
    <!-- Padding is Left, Top, Right, Bottom --> 
    <Setter Property="Padding" Value="2,0,0,2" /> 
    <Setter Property="Margin" Value="0,0,0,0" /> 

    <Setter Property="Visibility" Value="Visible" /> 
    <Setter Property="IsEnabled" Value="True" /> 

    <Setter Property="CharacterCasing" Value="Upper" /> 
    <Setter Property="BorderThickness" Value="1" /> 

    <Setter Property="BorderBrush" Value="Black" /> 
    <Setter Property="Background" Value="White" /> 
    <Setter Property="Foreground" Value="Black" /> 

    <Setter Property="Template" Value="{StaticResource CTTextBox}" /> 
</Style> 

답변

2

나는 문제가 해결 될 수 있다는 확신 몇 가지 방법으로,하지만 이러한 상황에서 나는 보통 행동을 첨부 할 수 있습니다. 그러나 상황이 꽤 정상적인 것은 아니지만, 컨트롤을위한 템플릿이 실현 되었기 때문에. 어쨌든, 나는이 상황에 맞는 행동을 취할 것이라고 생각합니다.

첨부 된 동작은 MVVM 패턴을 완전히 충족시키는 매우 강력하고 편리한 솔루션으로 Blend (사전 정의 된 인터페이스 사용)에서도 사용할 수 있습니다. 첨부 된 동작은 att입니다. ached 속성에는이 속성을 변경하기위한 이벤트 처리기가 있으며 모든 논리가이 처리기에서 구현됩니다.

동작을 시작하기 전에 템플릿에 적용한 몇 가지 변경 사항을 고려해야합니다.

왜 내가 PART_ContentHost ScrollViewer 컨트롤로 사용하고 있는지 이해가되지 않습니다. 아마 여러 날짜가 있고 스크롤로 표시해야 할 것입니다.

  1. ContentPresenter에 그들의 주요 목표입니다
  2. ContentControl을

: WPF에서 내용을 표시하는 데 필요한 두 개의 컨트롤이 있습니다. 가장 가벼운 첫 번째 파일은 대개 템플릿에서 항상 사용되지만 작업을 조정하는 데 필요한 이벤트를 지원하지 않으므로 ContentControl을 선택했습니다. 작은 것들에 Popup에 설정 한 템플릿 바인딩 속성을 추가 :

AllowsTransparency="True" 
VerticalOffset="4" 
HorizontalOffset="-5" 

더 나은 시각화를 위해. 이제 행동의 예를 살펴 보겠습니다.

XAML

<Window.Resources> 
    <ControlTemplate x:Key="CTTextBox" TargetType="{x:Type TextBox}"> 
     <StackPanel AttachedBehaviors:SelectDateBehavior.IsEnabled="True"> <!-- Here is determined behaviour --> 
      <Border x:Name="targetBorder" 
        Width="{TemplateBinding Width}" 
        Height="{TemplateBinding Height}" 
        BorderBrush="{TemplateBinding BorderBrush}" 
        BorderThickness="{TemplateBinding BorderThickness}" 
        Background="{TemplateBinding Background}" 
        TextBlock.Foreground="{TemplateBinding Foreground}" 
        SnapsToDevicePixels="True"> 

       <ContentControl x:Name="ContentHost" 
           Content="{TemplateBinding Text}" 
           HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
           VerticalAlignment="{TemplateBinding VerticalContentAlignment}" 
           Margin="4,0,0,0" /> 
      </Border> 

      <Popup x:Name="PopCal" 
        AllowsTransparency="True" 
        VerticalOffset="4" 
        HorizontalOffset="-5" 
        PlacementTarget="{Binding ElementName=ContentHost}"> 

       <Calendar x:Name="ActualCalendar" /> 
      </Popup> 
     </StackPanel> 
    </ControlTemplate> 

    <Style TargetType="{x:Type TextBox}" x:Key="STextBox"> 
     <Setter Property="FontFamily" Value="Arial" /> 
     <Setter Property="FontSize" Value="12" /> 
     <Setter Property="Height" Value="25" /> 
     <Setter Property="Width" Value="100" /> 
     <Setter Property="VerticalContentAlignment" Value="Center" /> 
     <Setter Property="HorizontalContentAlignment" Value="Left" /> 
     <Setter Property="HorizontalAlignment" Value="Center" />  
     <Setter Property="CharacterCasing" Value="Upper" /> 
     <Setter Property="BorderThickness" Value="1" /> 
     <Setter Property="BorderBrush" Value="Gray" /> 
     <Setter Property="Background" Value="AliceBlue" /> 
     <Setter Property="Foreground" Value="Black" /> 
     <Setter Property="Template" Value="{StaticResource CTTextBox}" /> 
    </Style> 
</Window.Resources> 

<Grid> 
    <TextBox Style="{StaticResource STextBox}" 
      Text="Select date" /> 
</Grid> 

Atatched Behavior

public class SelectDateBehavior 
{ 
    #region IsEnabled Dependency Property 

    public static readonly DependencyProperty IsEnabledProperty; 

    public static void SetIsEnabled(DependencyObject DepObject, bool value) 
    { 
     DepObject.SetValue(IsEnabledProperty, value); 
    } 

    public static bool GetIsEnabled(DependencyObject DepObject) 
    { 
     return (bool)DepObject.GetValue(IsEnabledProperty); 
    } 

    static SelectDateBehavior() 
    { 
     IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", 
                  typeof(bool), 
                  typeof(SelectDateBehavior), 
                  new UIPropertyMetadata(false, IsEnabledChanged)); 
    } 

    #endregion 

    #region IsEnabledChanged Handler 

    private static void IsEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     Panel panel = sender as Panel; 

     if (panel != null) 
     { 
      if (e.NewValue is bool && ((bool)e.NewValue) == true) 
      { 
       panel.Loaded += new RoutedEventHandler(panelLoaded); 
      } 
      else 
      { 
       panel.Loaded -= new RoutedEventHandler(panelLoaded); 
      } 
     } 
    } 

    #endregion 

    #region Panel Loaded Handler 

    private static void panelLoaded(object sender, RoutedEventArgs e) 
    { 
     Panel panel = sender as Panel; 
     Border border = panel.FindName("targetBorder") as Border; 
     ContentControl contentHost = border.FindName("ContentHost") as ContentControl; 
     Popup popup = panel.FindName("PopCal") as Popup; 

     if (popup != null) 
     { 
      Calendar calendar = popup.FindName("ActualCalendar") as Calendar;     
      calendar.SelectedDatesChanged += new EventHandler<SelectionChangedEventArgs>(calendarSelectedDatesChanged); 
     } 

     if (contentHost != null) 
     { 
      contentHost.MouseDoubleClick += new MouseButtonEventHandler(contentHostMouseDoubleClick); 
     }   
    } 

    #endregion 

    #region ContentHost MouseDoubleClick Handler 

    private static void contentHostMouseDoubleClick(object sender, MouseButtonEventArgs e) 
    { 
     ContentControl contentHost = sender as ContentControl; 
     Border border = contentHost.Parent as Border; 
     Panel panel = border.Parent as Panel; 
     Popup popup = panel.FindName("PopCal") as Popup; 

     if (popup != null) 
     { 
      popup.IsOpen = true; 
     } 
    } 

    #endregion 

    #region Calendar SelectedDatesChanged Handler 

    private static void calendarSelectedDatesChanged(object sender, SelectionChangedEventArgs e) 
    { 
     Calendar calendar = sender as Calendar; 
     Popup popup = calendar.Parent as Popup; 
     Panel panel = popup.Parent as Panel; 
     Border border = panel.FindName("targetBorder") as Border; 
     ContentControl contentHost = border.FindName("ContentHost") as ContentControl; 

     if (popup != null) 
     { 
      contentHost.Content = calendar.SelectedDate; 
      popup.IsOpen = false; 
     } 
    } 

    #endregion 
} 

Output

enter image description here

현재 날짜를 설정는

여기에 수행됩니다 :

private static void calendarSelectedDatesChanged(object sender, SelectionChangedEventArgs e) 
{ 
    // Skipped a few lines of code 
    if (popup != null) 
    { 
     contentHost.Content = calendar.SelectedDate; 
     popup.IsOpen = false; 
    } 
} 

Some notes

날 일부 기능에 주목하자. 우선, 가장 우선 순위가 높은 설정 값에서 WPF 애니메이션에서 애니메이션에 IsOpen 값을 설정하면 다른 소스 (코드 등)에서 액세스 할 수 없기 때문에 EvenTrigger Storyboard를 제거해야했습니다. . 그래서 나는 모든 행동/이벤트가 옆의 행동을 떠난다.

둘째, 솔루션은 강력하게 템플릿과 컨트롤의 구조에 묶여 있습니다. 즉, 템플릿의 구조를 변경해야하는 경우 변경 및 동작 (많지는 않음)을 의미합니다.

이 예제는 here입니다.

+0

몇 가지 좋은 점을 지적하고 클래스 정의에 대한 다른 대안을 가지고 있습니다 ... – DRapp

+0

@DRapp : 왜 클래스 정의에 대한 대안을 찾고 있습니까? 나를 위해, 클래스의 동작은 독립적입니다, 그것은 단지 하나의 네임 스페이스에 있습니다. –

+1

우리가 적용해야 할 물건의 프레임 워크에 다른 기본 요소가 있기 때문에 수업을 통해 처리하고 있습니다. 나는 그것을 거기에 놓았을 것이고, 내가 여기에서 여러분의 피드백을 기반으로 대부분을 감쌌다 고 생각합니다. 감사 – DRapp