wpfのNumericUpDown

NumericUpDownは、wpfのカスタムコントロールの、定番ですよね。

それも、作ってみました。

 

まずは、コードです。

 

using System;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

using System.Windows.Input;

using System.Windows.Controls.Primitives;

namespace OkaSharp
{

[TemplatePart(Name = "PART_UpButton", Type = typeof(RepeatButton))]
[TemplatePart(Name = "PART_DownButton", Type = typeof(RepeatButton))]
[TemplateVisualState(Name = "Positive", GroupName = "ValueStates")]
[TemplateVisualState(Name = "Negative", GroupName = "ValueStates")]
[TemplateVisualState(Name = "Focused", GroupName = "FocusedStates")]
[TemplateVisualState(Name = "Unfocused", GroupName = "FocusedStates")]
public class NumericUpDown : Control
{
static NumericUpDown()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(NumericUpDown), new FrameworkPropertyMetadata(typeof(NumericUpDown)));
}

public NumericUpDown()
{
DefaultStyleKey = typeof(NumericUpDown);
this.IsTabStop = true;
}


public static readonly DependencyProperty MaxValueProperty =
DependencyProperty.Register(
"MaxValue", typeof(double), typeof(NumericUpDown),
new PropertyMetadata(100.0,
new PropertyChangedCallback(ValueChangedCallback)));

public double MaxValue
{
get
{
return (double)GetValue(MaxValueProperty);
}

set
{
SetValue(MaxValueProperty, value);

}
}

public static readonly DependencyProperty MinValueProperty =
DependencyProperty.Register(
"MinValue", typeof(double), typeof(NumericUpDown),
new PropertyMetadata(-100.0,
new PropertyChangedCallback(ValueChangedCallback)));

public double MinValue
{
get
{
return (double)GetValue(MinValueProperty);
}

set
{
SetValue(MinValueProperty, value);

}
}

 

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value", typeof(double), typeof(NumericUpDown),
new PropertyMetadata(0.0,
new PropertyChangedCallback(ValueChangedCallback)));

public double Value
{
get
{
return (double)GetValue(ValueProperty);
}

set
{
if *1
{

SetValue(ValueProperty, value);

}
else if(value > MaxValue)
{

SetValue(ValueProperty, MaxValue);
}
else if(value < MinValue)
{
SetValue(ValueProperty, MinValue);
}

}
}

private static void ValueChangedCallback(DependencyObject obj,
DependencyPropertyChangedEventArgs args)
{
NumericUpDown ctl = (NumericUpDown)obj;
double newValue = (double)args.NewValue;

// Call UpdateStates because the Value might have caused the
// control to change ValueStates.
ctl.UpdateStates(true);

// Call OnValueChanged to raise the ValueChanged event.
ctl.OnValueChanged(
new System.Windows.RoutedPropertyChangedEventArgs<double>*2;
}

public static readonly RoutedEvent ValueChangedEvent =
EventManager.RegisterRoutedEvent("ValueChanged", RoutingStrategy.Direct,
typeof(System.Windows.RoutedPropertyChangedEventHandler<double>), typeof(NumericUpDown));

public event System.Windows.RoutedPropertyChangedEventHandler<double> ValueChanged
{
add { AddHandler(ValueChangedEvent, value); }
remove { RemoveHandler(ValueChangedEvent, value); }
}


protected virtual void OnValueChanged(System.Windows.RoutedPropertyChangedEventArgs<double> e)
{
// Raise the ValueChanged event so applications can be alerted
// when Value changes.
RaiseEvent(e);
}


private void UpdateStates(bool useTransitions)
{
if (Value >= 0)
{
VisualStateManager.GoToState(this, "Positive", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Negative", useTransitions);
}

if (IsFocused)
{
VisualStateManager.GoToState(this, "Focused", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Unfocused", useTransitions);
}

}

public override void OnApplyTemplate()
{
UpButtonElement = GetTemplateChild("PART_UpButton") as RepeatButton;
DownButtonElement = GetTemplateChild("PART_DownButton") as RepeatButton;
//TextElement = GetTemplateChild("TextBlock") as TextBlock;

UpdateStates(false);
}

private RepeatButton downButtonElement;

private RepeatButton DownButtonElement
{
get
{
return downButtonElement;
}

set
{
if (downButtonElement != null)
{
downButtonElement.Click -=
new RoutedEventHandler(downButtonElement_Click);
}
downButtonElement = value;

if (downButtonElement != null)
{
downButtonElement.Click +=
new RoutedEventHandler(downButtonElement_Click);
}
}
}

void downButtonElement_Click(object sender, RoutedEventArgs e)
{
Value--;
}

private RepeatButton upButtonElement;

private RepeatButton UpButtonElement
{
get
{
return upButtonElement;
}

set
{
if (upButtonElement != null)
{
upButtonElement.Click -=
new RoutedEventHandler(upButtonElement_Click);
}
upButtonElement = value;

if (upButtonElement != null)
{
upButtonElement.Click +=
new RoutedEventHandler(upButtonElement_Click);
}
}
}

void upButtonElement_Click(object sender, RoutedEventArgs e)
{
Value++;
}

protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
Focus();
}


protected override void OnGotFocus(RoutedEventArgs e)
{
base.OnGotFocus(e);
UpdateStates(true);
}

protected override void OnLostFocus(RoutedEventArgs e)
{
base.OnLostFocus(e);
UpdateStates(true);
}
}

}

 

次に、スタイルです。

 

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:OkaSharp"
>

 

<Style TargetType="{x:Type local:NumericUpDown}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:NumericUpDown">
<Grid Background="{TemplateBinding Background}">


<VisualStateManager.VisualStateGroups>

 

<VisualStateGroup Name="FocusStates">

<!--
Add a focus rectangle to highlight the entire control
when it has focus.
-->
<VisualState Name="Focused">
<Storyboard>
<ObjectAnimationUsingKeyFrames Duration="0"
Storyboard.TargetName="FocusVisual"
Storyboard.TargetProperty="Visibility">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<Visibility>Visible</Visibility>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>

<!--
Return the control to its initial state by
hiding the focus rectangle.
-->
<VisualState Name="Unfocused" />
</VisualStateGroup>

</VisualStateManager.VisualStateGroups>

<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="25" />
</Grid.ColumnDefinitions>

<Border Grid.RowSpan="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Background="#E0FFFFFF"
BorderBrush="Gray"
BorderThickness="1">
<!-- Bind the TextBlock to the Value property -->
<TextBlock Name="TextBlock"
Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type local:NumericUpDown}},
Path=Value}"
TextAlignment="Right" />
</Border>

<Rectangle Name="FocusVisual"
Grid.RowSpan="2"
Grid.ColumnSpan="2"
Stroke="Black"
StrokeDashArray="0,1"
StrokeThickness="1"
Visibility="Collapsed" />
<RepeatButton Name="PART_UpButton"
Grid.Row="0"
Grid.Column="1"
Content="▲"
FontSize="8" />
<RepeatButton Name="PART_DownButton"
Grid.Row="1"
Grid.Column="1"
Content="▼"
FontSize="8" />

</Grid>

</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

 

 

</ResourceDictionary>

 

*1:value <= MaxValue) && (value >= MinValue

*2:double)(args.OldValue),
newValue, ValueChangedEvent