Keven M
Keven M

Reputation: 992

Make standard property update along with DependencyProperty

I may be completely off on this, but I swear I remember reading somewhere how to do this, I just can't seem to find it. I have a DependencyProperty Minutes as part of a TimePicker custom control. It's data type is int, as it was the only way to get the increase/decrease button to work. The problem with this is that the minutes from 1-9 display without the leading zero. To circumvent this, I added a standard property MinuteZeros, which converts the value of Minutes to a string and employs ToString("00"). This property is bound to the display content. Now when I hit the increase/decrease buttons though, it doesn't update. How do I bind Minutes and MinuteZeros so that they update each other automatically?

Code:

public class TimePicker : Control
{
    public static DependencyProperty HourProperty = DependencyProperty.Register("Hour", typeof(int), typeof(TimePicker),
        new FrameworkPropertyMetadata((int)12, new PropertyChangedCallback(OnHourChanged)));

    public static DependencyProperty MinuteProperty = DependencyProperty.Register("Minute", typeof(int), typeof(TimePicker),
        new FrameworkPropertyMetadata((int)00, new PropertyChangedCallback(OnMinuteChanged)));

    public int Hour
    {
        get { return (int)GetValue(HourProperty); }
        set { SetValue(HourProperty, value); }
    }

    public string MinuteZeros
    {
        get { return Minute.ToString("00"); }
        set { value = Minute.ToString("00"); }
    }

    public int Minute
    {
        get { return (int)GetValue(MinuteProperty); }
        set { SetValue(MinuteProperty, value); }
    }
    #endregion

    #region Events

    public override void OnApplyTemplate()
    {
        var upButton = GetTemplateChild("PART_IncreaseTime") as RepeatButton;
        upButton.Click += IncreaseClick;

        var downButton = GetTemplateChild("PART_DecreaseTime") as RepeatButton;
        downButton.Click += DecreaseClick;

        var hourButton = GetTemplateChild("PART_Hour") as ToggleButton;
        hourButton.Checked += HourSelected;

        var minuteButton = GetTemplateChild("PART_Minute") as ToggleButton;
        minuteButton.Checked += MinuteSelected;
    }

    private void HourSelected(object sender, RoutedEventArgs e)
    {
        var minuteButton = GetTemplateChild("PART_Minute") as ToggleButton;            
        minuteButton.IsChecked = false;
    }

    private void MinuteSelected(object sender, RoutedEventArgs e)
    {
        var hourButton = GetTemplateChild("PART_Hour") as ToggleButton;
        hourButton.IsChecked = false;
    }

    private void IncreaseClick(object sender, RoutedEventArgs e)
    {
        var hourButton = GetTemplateChild("PART_Hour") as ToggleButton;
        var minuteButton = GetTemplateChild("PART_Minute") as ToggleButton;

        if (hourButton.IsChecked == true)
        {
            if (Hour == 12)
            {
                Hour = 1;
            }
            else
            {
                Hour += 1;
            }
        }
        else if (minuteButton.IsChecked == true)
        {
            if (Minute == 59)
            {
                Minute = 00;
            }
            else
            {
                Minute += 1;
            }
        }
    }

    private void DecreaseClick(object sender, RoutedEventArgs e)
    {
        var hourButton = GetTemplateChild("PART_Hour") as ToggleButton;
        var minuteButton = GetTemplateChild("PART_Minute") as ToggleButton;

        if (hourButton.IsChecked == true)
        {
            if (Hour == 1)
            {
                Hour = 12;
            }
            else
            {
                Hour -= 1;
            }
        }
        else if (minuteButton.IsChecked == true)
        {
            if (Minute == 00)
            {
                Minute = 59;
            }
            else
            {
                Minute -= 1;
            }
        }
    }

    private static void OnHourChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {

    }

    private static void OnMinuteChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {

    }

    static TimePicker()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TimePicker), new FrameworkPropertyMetadata(typeof(TimePicker)));
    }
}

public partial class TimePickerEvents : ResourceDictionary
{
    TimePicker time = new TimePicker();

    void PART_IncreaseTime_Click(object sender, RoutedEventArgs e)
    {
        time.Hour += 1;

    }

}   

XAML:

<ToggleButton x:Name="PART_Minute"
                                      VerticalAlignment="{TemplateBinding VerticalAlignment}"
                                      Margin="0"
                                      MinWidth="25"
                                      BorderBrush="Transparent"
                                      Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=MinuteZeros}">
                            <ToggleButton.Template>
                                <ControlTemplate TargetType="{x:Type ToggleButton}">
                                        <Grid>
                                            <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                            <ContentPresenter x:Name="ContentPart"
                                                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                                              TextBlock.Foreground="#FF605c" Visibility="Collapsed">
                                                <ContentPresenter.Effect>
                                                    <BlurEffect />
                                                </ContentPresenter.Effect>
                                            </ContentPresenter>
                                        </Grid>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter TargetName="ContentPart" Property="Visibility" Value="Visible" />
                                        </Trigger>
                                        <Trigger Property="IsChecked" Value="True">
                                            <Setter Property="FontWeight" Value="Bold" />
                                            <Setter TargetName="ContentPart" Property="Visibility" Value="Visible" />
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </ToggleButton.Template>
                            <ToggleButton.Style>
                                <Style TargetType="{x:Type ToggleButton}">
                                    <Setter Property="Background" Value="Transparent" />
                                </Style>
                            </ToggleButton.Style>
                        </ToggleButton>

Upvotes: 0

Views: 205

Answers (2)

Keven M
Keven M

Reputation: 992

Just wanted to post the final changes that made this work.

    public static DependencyProperty SelectedTimeProperty = DependencyProperty.Register("SelectedTime", typeof(TimeSpan), typeof(TimePicker),
        new FrameworkPropertyMetadata((TimeSpan.Zero), new PropertyChangedCallback(OnSelectedTimeChanged)));

    public TimeSpan SelectedTime
    {
        get { return (TimeSpan)GetValue(SelectedTimeProperty); }
        set { SetValue(SelectedTimeProperty, value); }
    }

    private static void OnHourChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        TimePicker tp = (TimePicker)sender;

        //TimePicker tp = new TimePicker();
        var amButton = tp.GetTemplateChild("PART_AmIndicator") as RadioButton;
        var pmButton = tp.GetTemplateChild("PART_PmIndicator") as RadioButton;

        if (pmButton.IsChecked != false)
        {
            tp.SelectedTime = new TimeSpan((int.Parse(tp.Hour) + 12), int.Parse(tp.Minute), 0);
        }
        else if (amButton.IsChecked != false && (tp.Hour == "12"))
        {
            tp.SelectedTime = new TimeSpan(0, int.Parse(tp.Minute), 0);
        }
        else
        {
            tp.SelectedTime = new TimeSpan(int.Parse(tp.Hour), int.Parse(tp.Minute), 0);
        }
    }

    private static void OnMinuteChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        //TimePicker tp = new TimePicker();
        TimePicker tp = (TimePicker)sender;

        var amButton = tp.GetTemplateChild("PART_AmIndicator") as RadioButton;
        var pmButton = tp.GetTemplateChild("PART_PmIndicator") as RadioButton;

        if (pmButton.IsChecked != false)
        {
            tp.SelectedTime = new TimeSpan((int.Parse(tp.Hour) + 12), int.Parse(tp.Minute), 0);
        }
        else if (amButton.IsChecked != false && (tp.Hour == "12"))
        {
            tp.SelectedTime = new TimeSpan(0, int.Parse(tp.Minute), 0);
        }
        else
        {
            tp.SelectedTime = new TimeSpan(int.Parse(tp.Hour), int.Parse(tp.Minute), 0);
        }
    }

Upvotes: 0

Phil
Phil

Reputation: 42991

Option 1: use StringFormat - doesn't work, see SO article

Option 2: use a StringFormatConverter - which does work

Get rid of MinuteZeros and use

<Converters:StringFormatConverter x:Key="StringFormatConverter" />

{Binding 
     RelativeSource={RelativeSource TemplatedParent}, 
     Path=Minutes, 
     StringFormatConverter={StaticResource StringFormatConverter},
     ConverterParameter=D2
}

public class StringFormatConverter : IValueConverter
{
    #region Implementation of IValueConverter

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
        {
            throw new ArgumentNullException("value");
        }

        var fmt = string.Format("{{0:{0}}}", parameter);
        return string.Format(fmt, value);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }

    #endregion
}

Option 3: which I haven't implemented

Put logic in your OnHourChanged and OnMinuteChanged handlers to synchronize Minutes and MinutesZeros.

Upvotes: 1

Related Questions