午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

WindowsFormのNumericUpDownみたいなのをWPFのユーザーコントロールでDLLで作ってみた

2年後もっといいのできた
なので↓以下の記事は簡易版って感じ
 
 
以前のTextBoxとScrollBarの組み合わせで作っていたWindowsFormのNumericUpDownみたいなのをWPFのユーザーコントロールでDLLで作った
 
扱える数値は整数だけ
設定できるのは
Value 		値(整数)
Max			上限値
Min			下限値
SmallChange	変更値小
LargeChange	変更値大
IntegerFontSize	フォントサイズ
 
 
 
イメージ 1
-1000から1000
SmallChangeが1、LargeChangeが10
フォントサイズ30
幅100,縦70
 
SmallChange
	スクロールバーのボタンクリック
	テキストボックスでマウスホイール
LargeChange
	スクロールバークリック
	スクロールバーでマウスホイール
 
 
ユーザーコントロールをDLLで作成するのはこの前の
ユーザーコントロール(WPF)のDLLを作ってアプリで使うまでの手順メモ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15436544.html
この手順で作って
 
NumericUpDownみたいのは
WPFでもNumericUpDownが使いたい、簡単に作りたい ( パソコン ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15313853.html
この時のと同じ動き
 
 
デザイン画面

f:id:gogowaten:20191212140218p:plain

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Text.RegularExpressions;
using System.Windows.Controls.Primitives;
using System.Globalization;

namespace IntegerUpDown
{
    public partial class IntegerUpDown : UserControl
    {
        public int Value
        {
            get { return (int)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }
        public static readonly DependencyProperty ValueProperty =
            DependencyProperty.Register(nameof(Value), typeof(int), typeof(IntegerUpDown));
        public int Max
        {
            get { return (int)GetValue(MaxProperty); }
            set { SetValue(MaxProperty, value); }
        }
        public static readonly DependencyProperty MaxProperty =
            DependencyProperty.Register(nameof(Max), typeof(int), typeof(IntegerUpDown));
        public int Min
        {
            get { return (int)GetValue(MinProperty); }
            set { SetValue(MinProperty, value); }
        }
        public static readonly DependencyProperty MinProperty =
            DependencyProperty.Register(nameof(Min), typeof(int), typeof(IntegerUpDown));
        public int SmallChange
        {
            get { return (int)GetValue(SmallChangeProperty); }
            set { SetValue(SmallChangeProperty, value); }
        }
        public static readonly DependencyProperty SmallChangeProperty =
            DependencyProperty.Register(nameof(SmallChange), typeof(int), typeof(IntegerUpDown));

        public int LargeChange
        {
            get { return (int)GetValue(LargeChangeProperty); }
            set { SetValue(LargeChangeProperty, value); }
        }
        public static readonly DependencyProperty LargeChangeProperty =
            DependencyProperty.Register(nameof(LargeChange), typeof(int), typeof(IntegerUpDown));

        public double IntegerFontSize
        {
            get { return (double)GetValue(IntegerFontSizeProperty); }
            set { SetValue(IntegerFontSizeProperty, value); }
        }
        public static readonly DependencyProperty IntegerFontSizeProperty =
            DependencyProperty.Register(nameof(IntegerFontSize), typeof(double), typeof(IntegerUpDown));
        public IntegerUpDown()
        {
            InitializeComponent();
            SmallChange = 1;//ここで指定するとデフォルトの数値にできる、ユーザーコントロールを使う側のXAMLから指定されていた場合はそちらが適用される
            LargeChange = 1;
            //Height = 30;
            Width = 50;
            Max = 10;
            Min = 0;
            Value = 0;
            IntegerFontSize = 14f;

            MyInitialize();
            this.Loaded += IntegerUpDown_Loaded;//レイアウト用、起動しないと初期サイズがわからないので
        }
        private void IntegerUpDown_Loaded(object sender, RoutedEventArgs e)
        {//テキストボックスの横幅は全体の横幅-スクロールバーの横幅
            nTextBox.Width = Width - nScrollBar.Width;
        }
        private void MyInitialize()
        {
            nScrollBar.RenderTransform = new RotateTransform(180);
            nScrollBar.RenderTransformOrigin = new Point(0.5, 0.5);

            nTextBox.TextAlignment = TextAlignment.Right;
            nTextBox.VerticalContentAlignment = VerticalAlignment.Center;

            nStackPanel.Height = Height;

            MySetBinding();
            MySetEvents();

        }


       #region バインディング関連


        private void MySetBinding()
        {
            var b = new Binding();
            b.Source = this;
            b.Path = new PropertyPath(IntegerUpDown.ValueProperty);
            b.Converter = new ConverterInteger2Double();
            nScrollBar.SetBinding(ScrollBar.ValueProperty, b);

            b = new Binding();
            b.Source = nScrollBar;
            b.Path = new PropertyPath(ScrollBar.ValueProperty);
            b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
            nTextBox.SetBinding(TextBox.TextProperty, b);

            b = new Binding();
            b.Source = this;
            b.Path = new PropertyPath(MaxProperty);
            b.Converter = new ConverterInteger2Double();
            nScrollBar.SetBinding(ScrollBar.MaximumProperty, b);

            b = new Binding();
            b.Source = this;
            b.Path = new PropertyPath(MinProperty);
            b.Converter = new ConverterInteger2Double();
            nScrollBar.SetBinding(ScrollBar.MinimumProperty, b);

            b = new Binding();
            b.Source = this;
            b.Path = new PropertyPath(SmallChangeProperty);
            b.Mode = BindingMode.OneWay;
            nScrollBar.SetBinding(ScrollBar.SmallChangeProperty, b);

            b = new Binding();
            b.Source = this;
            b.Path = new PropertyPath(LargeChangeProperty);
            b.Mode = BindingMode.OneWay;
            nScrollBar.SetBinding(ScrollBar.LargeChangeProperty, b);

            b = new Binding();
            b.Source = this;
            b.Path = new PropertyPath(IntegerFontSizeProperty);
            b.Mode = BindingMode.OneWay;
            nTextBox.SetBinding(TextBox.FontSizeProperty, b);

        }
       #endregion


       #region イベント関連
        private void MySetEvents()
        {
            nTextBox.TextChanged += NTextBox_TextChanged;
            nTextBox.LostFocus += NTextBox_LostFocus;
            nTextBox.GotFocus += NTextBox_GotFocus;
            nTextBox.MouseWheel += NTextBox_MouseWheel;

            nScrollBar.MouseWheel += NScrollBar_MouseWheel;
        }
        private void NTextBox_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (e.Delta > 0) { nScrollBar.Value += SmallChange; }
            else { nScrollBar.Value -= SmallChange; }
        }
        //スクロールバーの上でマウスホイール回転でLargeChange分の数値を上下
        private void NScrollBar_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            if (e.Delta > 0) { nScrollBar.Value += LargeChange; }
            else { nScrollBar.Value -= LargeChange; }
        }
        //テキストボックスクリック時に文字全体を選択状態にする
        private void NTextBox_GotFocus(object sender, RoutedEventArgs e)
        {
            TextBox box = (TextBox)sender;
            this.Dispatcher.InvokeAsync(() =>
            {
                Task.Delay(10);
                box.SelectAll();
            });
        }
        //テキストボックスのロストフォーカス時
        //にテキストをValueに入れる
        //これがないとValueに範囲外の数値が入ったりする
        private void NTextBox_LostFocus(object sender, RoutedEventArgs e)
        {
            TextBox box = (TextBox)sender;
            int i;
            if (int.TryParse(box.Text, out i) == true)
            {
                Value = i;
            }
            else
            {
                Value = Min;
                box.Text = Min.ToString();
            }
            //Value = int.Parse(box.Text);
        }
        //数値以外は入力できないように
        private void NTextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            TextBox box = (TextBox)sender;
            double d;
            if (!double.TryParse(box.Text, out d))
            {
                box.Text = Regex.Replace(box.Text, "[^0-9-]", "");//数値以外を""に置換
            }
        }
       #endregion
    }

//intとdoubleの変換用、バインディングで使う
    internal class ConverterInteger2Double : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            int i = (int)value;
            return (double)i;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            double d = (double)value;
            return (int)d;
        }
    }
}
 
 
 
作ったユーザーコントロールのIntegerUpDown.dllを使うアプリ方の
デザイン画面

f:id:gogowaten:20191212140342p:plain

上は何も設定していない状態
下はいろいろ設定
 

f:id:gogowaten:20191212140428p:plain

コードは何もなし初期状態で参照にDLLを追加しただけ
 
 
イメージ 5
ツールボックスにアイテムの選択からDLLを追加して使えばいいはず
 

 
 
 

f:id:gogowaten:20191212140443p:plain

よくわからんのがデザイン画面のXAMLの7行目
ツールボックスからユーザーコントロールを追加すると最初の1個めのときだけ、これが自動で追加される
半角スペースで前半と後半に分かれているみたいで
xmlns:IntegerUpDown="clr-namespace:IntegerUpDown;assembly=IntegerUpDown"
ここまでが前半で半角スペースのあとに
x:Class="_20180402_IntegerUpDown_dll.MainWindow"
 
前半のはnamespaceってあるから名前空間は参照に追加したDLLの名前空間かなあ
イメージ 6
DLLを作っている方のコード、namespaceってあるこれに合わせる必要があるんだと思う
assemblyはDLLのファイル名かなあ
 
後半の
x:Class="_20180402_IntegerUpDown_dll.MainWindow"
これは使う方のプロジェクト名とXAMLの名前を指定しているみたいで
 

f:id:gogowaten:20191212140502p:plain

プロジェクトの名前の後に.(ピリオド)のあとに使っているXAMLのファイル名になっている
ソリューションエクスプローラーでみると
イメージ 8
 
なので使うプロジェクトやXAMLが変わるとここも変わる
プロジェクト名が20180402_ユーザーコントロールNumeric使うテストだと

f:id:gogowaten:20191212140527p:plain

xmlns:IntegerUpDown="clr-namespace:IntegerUpDown;assembly=IntegerUpDown" x:Class="_20180402_ユーザーコントロールNumeric使うテスト.MainWindow"
前半のnamespaceとassemblyは同じだけど後半部分が変わっている
 
 
 
 
WPFVBで使ってみる

f:id:gogowaten:20191212140541p:plain

WPFアプリをVisual Basicで新規作成
 

f:id:gogowaten:20191212140553p:plain

DLLを参照に追加
 

f:id:gogowaten:20191212140604p:plain

参照に追加できた
 

f:id:gogowaten:20191212140615p:plain

XAMLにも追加できた
7行目がいつもと違う、x:Classがないけど
デバッグ実行してみる
 
イメージ 15
使える!
同じWPFならC#でもVBでも使えるんだなあ
使えるはずだとは思っていたけど実際に動くと面白い
これで毎回作らなくても済むから楽ちん
DLLってのは便利だなあ
 
関連記事
 
続き、2018/04/04は翌日、イベントを追加してみた
イベントの追加、ユーザーコントロールの依存関係プロパティの変更時にイベント発生させたい ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15444457.html
 
 

 2年後、もう少しマシなNumericUpDownできた

gogowaten.hatenablog.com

 
2018/04/06は3日後
カラーピッカーのdll(ライブラリ)作った、WPFユーザーコントロール ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15447603.html
 
 
 
2018/3/30は3日前
ユーザーコントロール(WPF)のDLLを作ってアプリで使うまでの手順メモ ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15436544.html
 
 
2018/1/7は3ヶ月前
WPFでもNumericUpDownが使いたい、簡単に作りたい ( パソコン ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15313853.html