【问题标题】:Numeric UpDown control using dependency properties使用依赖属性的数值 UpDown 控件
【发布时间】:2012-03-14 14:21:16
【问题描述】:

所以我不明白这一点。我有一个自定义控件,它使用向上/向下按钮来增加/减少切换按钮上显示的时间。我有切换的Content 属性显示HourMinute 属性的正确值,我现在正在尝试设置代码隐藏以增加值。根据 VS2010 调试器,它增加了 Hour 属性的值,但并没有改变 Toggles 的内容来反映这一点。我将绑定模式设置为TwoWay,并且我正在使用{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Hour, Mode=TwoWay} 绑定到该值,因为它位于自定义控件的generic.xaml 文件中。关于如何正确更新显示值的任何想法?

XAML:(删除样式模板以节省空间)

    <Style TargetType="{x:Type local:TimePicker}">
    <Setter Property="Height" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Height}" />
    <Setter Property="Width" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Width}" />
    <Setter Property="HorizontalAlignment" Value="Center" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="FontSize" Value="14" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate>
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="Transparent"
                        BorderThickness="1">
                    <StackPanel  x:Name="PART_Root"
                                 Orientation="Horizontal"
                                 HorizontalAlignment="Center"
                                 VerticalAlignment="{TemplateBinding VerticalAlignment}">
                        <!--Region Hour Button-->
                        <ToggleButton x:Name="PART_Hour"
                                      VerticalAlignment="{TemplateBinding VerticalAlignment}"
                                      Margin="0"
                                      BorderBrush="Transparent"
                                      BorderThickness="0"
                                      Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Hour, Mode=TwoWay, BindsDirectlyToSource=True}">                                
                        </ToggleButton>
                        <!--EndRegion-->
                        <Label HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
                               VerticalContentAlignment="{TemplateBinding VerticalAlignment}"
                               FontSize="14"
                               Content=":"/>
                        <!--Region Minute Button-->
                        <ToggleButton x:Name="PART_Minute"
                                      VerticalAlignment="{TemplateBinding VerticalAlignment}"
                                      Margin="0"
                                      BorderBrush="Transparent"
                                      Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Minute}">                                
                        </ToggleButton>
                        <!--EndRegion-->
                        <StackPanel x:Name="PART_IncDecPanel"
                                    HorizontalAlignment="Center"
                                    VerticalAlignment="Center"
                                    Orientation="Vertical">
                            <Grid Height="{Binding ElementName=PART_Hour, Path=ActualHeight}">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="*"/>
                                    <RowDefinition Height="*"/>
                                </Grid.RowDefinitions>
                                <Viewbox Stretch="Fill" Grid.Row="0">
                                    <!--Region Increase Button-->
                                    <Button x:Name="PART_IncreaseTime"
                                            HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
                                            VerticalContentAlignment="{TemplateBinding VerticalAlignment}"
                                            BorderBrush="Transparent"
                                            BorderThickness="0"
                                            FontFamily="Marlett"
                                            Foreground="DimGray"
                                            Content="5"
                                            Padding="0"
                                            Click="PART_IncreaseTime_Click">                                            
                                    </Button>
                                    <!--EndRegion-->
                                </Viewbox >
                                <Viewbox Stretch="Fill" Grid.Row="1">
                                    <!--Region Decrease Button-->
                                    <Button x:Name="PART_DecreaseTime"
                                            HorizontalContentAlignment="{TemplateBinding HorizontalAlignment}"
                                            VerticalContentAlignment="{TemplateBinding VerticalAlignment}"
                                            BorderBrush="Transparent"
                                            BorderThickness="0"
                                            FontFamily="Marlett"
                                            Foreground="DimGray"
                                            Content="6"
                                            Padding="0">                                            
                                    </Button>
                                    <!--EndRegion-->
                                </Viewbox>
                            </Grid>

                        </StackPanel>

                    </StackPanel>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

代码:

public class TimePicker : Control
{
    #region Dependency Property Declarations
    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(string), typeof(TimePicker),
        new FrameworkPropertyMetadata((string)"00", new PropertyChangedCallback(OnMinuteChanged)));        

    #endregion

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

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

    #region Events
    private static void OnHourChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        TimePicker time = new TimePicker();
        time.Hour = (int)e.NewValue;
        MessageBox.Show("Hour changed");
    }

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

    }
    #endregion

    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;

    }
}   

【问题讨论】:

  • 为什么要在OnHourChanged 中创建new TimerPicker
  • 这是我可以访问Hour 而不是HourProperty 的唯一方法。
  • TimePicker timePicker = (TimePicker)sender;

标签: c# wpf binding callback dependency-properties


【解决方案1】:

不确定为什么要使用切换按钮来显示小时/分钟,我认为 Label/TextBlock 会更适合。

将 ToggleButtons 的绑定更改为

Content="{TemplateBinding Hour}"

然后在代码中,覆盖OnApplyTemplate

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

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

private void IncreaseClick(object sender, RoutedEventArgs e)
{
    // Here would be a place to see what toggle button is checked 
    // (or which TextBlock last had focus) and increase Hour/Minute
    // based on that info
    Hour += 1;
}

private void DecreaseClick(object sender, RoutedEventArgs e)
{
    // Here would be a place to see what toggle button is checked 
    // (or which TextBlock last had focus) and decreaseHour/Minute
    // based on that info
    Hour -= 1;
}

这应该可以帮助您入门。 仅供参考:在您的 OnHourChanged 和 OnMinuteChanged 中,发件人是您的控制(TimePicker)。因此,您可以将发件人投射到 TimePicker 并访问您的所有属性。甚至是您的私人财产。

【讨论】:

  • 使用 ToggleButton 背后的逻辑是它具有保持选中状态的内置功能,而 Label 没有。至于TextBlock,我真的不希望光标在它上面闪烁,我只是希望它被选中。我确信我可以覆盖光标,但这似乎更自然。我强烈考虑改用 RadioButton 作为基础,但我想先让它工作。 :) 我确实尝试了您的建议,并且在将 GetTemplatePart 更改为 GetTemplateChild 后它起作用了。谢谢!
  • 糟糕,抱歉那个错字。我更改为正确的方法名称。我可以向您保证,客户希望手动更改时间以及上/下。这就是为什么 TextBox 最好的原因。然后,您可以使用 Tab 键在控件中切换,并使用向上/向下按钮或使用滚轮或向上/向下键盘键。现在开始考虑这些。
【解决方案2】:

为什么不使用 WPF 工具包的数字上下控制? http://wpftoolkit.codeplex.com/wikipage?title=NumericUpDown

您所要做的就是通过两种方式将其绑定到您的属性。

【讨论】:

  • 主要是因为这是我学习自定义控件的机会,但也因为我真的不喜欢重用其他内容。我会从其他来源汲取灵感,但我喜欢自己动手提供的挑战和学习机会。
猜你喜欢
  • 1970-01-01
  • 2011-05-25
  • 2021-05-14
  • 1970-01-01
  • 1970-01-01
  • 2013-07-24
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
相关资源
最近更新 更多