Calender

Visual Studio を使って TextBox から記念日をタイプして経過日数を計算します。

前田稔の超初心者のプログラム入門

年月日を切り分ける

  1. Windows 10 & Visual Studio 2019 の環境で TextBox から記念日を取得して経過日数を計算します。
    C# の string や ArrayList は強力ですが、C++ の文字列や Array は非力です。
    そこで STL の wstring や vector を使うことになります。
    最初のステップとして「年月日を切り分ける」MySplit Class から始めます。
  2. Visual Studio 2019 を立ち上げて[ファイル][新規作成][プロジェクト]を開き、[新しいプロジェクトの作成]を選択します。
    [Blank App(Universal Windows - C++/CX)]を選択して下さい。
    そのままコンパイル&実行すると空のウインドウが表示されます。
    生年月日をタイプする TextBox と Button と生存日数を表示する TextBox を貼り付けて下さい。
    Button にはボタンクリックのイベントハンドラー(Button_Click)を設定しています。
        <Grid>
            <TextBox x:Name="textBox" HorizontalAlignment="Left" Margin="40,60,0,0" Text="TextBox"
                VerticalAlignment="Top" Height="40" Width="130"/>
            <Button x:Name="button" Content="Button" Margin="230,60,0,0" VerticalAlignment="Top" Click="Button_Click"/>
            <TextBox x:Name="textBox1" HorizontalAlignment="Left" Margin="360,60,0,0" Text="TextBox"
                VerticalAlignment="Top" Height="40" Width="120"/>
        </Grid>
    
  3. 年月日を切り分ける MySplit Class から説明します。
    C# の Split() 関数を使えば実質的に1行なのですが、C++ にはこれに相当する API が見つかりません。
    その上 C++ の文字列や Array は非力で、STL に頼ることになります。
  4. MySplit.h のソースコードです。
    STL の vector を使うときは <vector> を #include します。
    今回は ref class では無くネイティブクラスが適しているようです。
    split() が年月日を切り分けて数値に変換した vector をリターンする関数です。
    Debug() はデバッグに使用する関数ですが、無くてもかまいません。
    #include <vector>
    
    namespace App1
    {
        class MySplit
        {
        public:
            std::vector<int> split(Platform::String^ str);
            void Debug(Platform::String^ msg, int v);
        };
    }
    
  5. MySplit.cpp のソースコードです。
    OutputDebugString を使うときは <windows.h> を #include して下さい。
    STL の String を使うときは <string> を #include して下さい。
    String^ str で渡された文字列を STL の wstring にコピーして作業します。
    s.find_first_not_of(L" ,/", Col); で Col の位置から[ ,/]以外の文字を検出します。
    この値が文字列の左端になります。
    つぎに s.find_first_of(L" ,/", Col); で Col の位置から[ ,/]の文字を検出します。
    この値が文字列の右端になります。
    Word.assign(s, Col, wk - Col); で左端から wk-Col の長さで切り出して、数値に変換して vector にプッシュします。
    #pragma once
    
    #include "pch.h"
    #include "MySplit.h"
    #include <windows.h>    // OutputDebugString
    #include <string>       // STL の String を使う
    using namespace App1;
    
    // '/' で区切られた文字列を数値に変換して vector でリターン
    std::vector<int> MySplit::split(Platform::String^ str)
    {
        std::vector<int> vt;
        std::wstring s;
        std::wstring Word;
        int    Size, Col, wk;
        s = str->Data();
        s += L"/";
        OutputDebugString(s.data());
        Size = s.length() - 1;
        vt.clear();
        for (Col = 0; Col < Size;)
        {
            wk = s.find_first_not_of(L" ,/", Col);
            Col = wk;
            wk = s.find_first_of(L" ,/", Col);
            if (wk==Col)        break;
            Word = Word.assign(s, Col, wk - Col);
            vt.push_back(_wtoi(Word.data()));
            Col = wk + 1;
        }
        return vt;
    }
    
    void  MySplit::Debug(Platform::String^ msg, int v)
    {
        Platform::String^ str = msg + v.ToString() + "\r\n";
        OutputDebugString(str->Data());
    }
    
  6. MainPage.xaml.h のソースコードです。
    Button_Click() のイベントハンドラが宣言されています。
    #pragma once
    
    #include "MainPage.g.h"
    #include "MySplit.h"
    
    namespace App1
    {
        public ref class MainPage sealed
        {
        public:
            MainPage();
    
        private:
            void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e);
        };
    }
    
  7. MainPage.xaml.cpp のソースコードです。
    MySplit* mysplit; を定義して mysplit = new MySplit(); でクラスを生成して下さい。
    Button_Click() のイベントハンドラで TextBox から誕生日(YYYY/MM/DD)を取得して、TextBox1 に表示します。
    取得した誕生日を vect = mysplit->split(str); で切り分けて出力ウインドウに印字します。
    #pragma once
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include <windows.h>    // OutputDebugString
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    using namespace App1;
    
    MySplit* mysplit;
    
    MainPage::MainPage()
    {
        InitializeComponent();
        mysplit = new MySplit();
    }
    
    void App1::MainPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        std::vector<int> vect;
        String^ str;
        str = ref new String(textBox->Text->Data());
        textBox1->Text = str;
        vect = mysplit->split(str);
        for(int i=0; i<3; i++)
            mysplit->Debug("\nvect: ",vect[i]);
    }
    
  8. MySplit.h と MySplit.cpp をプロジェクトに組み込んで下さい。
    ボタンをクリックすると TextBox にタイプした yyyy/mm/dd が切り分けられて出力ウインドウに印字されます。

通算日数を調べる

  1. 現実には西暦元年1月1日は存在しませんが、あるものとして指定された年月日までの通算日数を調べるクラス MyDays Class を作成します。
    MyDays.h のソースコードです。
    Days() が西暦元年1月1日から yyyy, mm, dd までの通算日数を計算する関数です。
    Uruu() が閏歳を判定する関数です。
    MTBL[12] は1月~12月までの累積日数の配列です。
    namespace App1
    {
        class MyDays
        {
        public:
            int Days(int yy, int mm, int dd);
            bool    Uruu(int year);
            int     ToDay;
        private:
            int MTBL[12] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
            int cnt;
        };
    }
    
  2. MyDays.cpp のソースコードです。
    Days(int yy, int mm, int dd) 関数で通算日数を計算します。
    yy が閏年で3月以前のときは1日減らします。
    #pragma once
    
    #include "pch.h"
    #include "MyDays.h"
    using namespace App1;
    
    // 西暦1年1月1日からの通算日数を調べる関数
    int  MyDays::Days(int yy, int mm, int dd)
    {   int w;
        w = (yy-1)*365;     //年*365
        w += (yy/4 - yy/100 + yy/400 + MTBL[mm-1] + dd);
        if (mm<3 && Uruu(yy))  w--;
        return(w);
    }
    
    // 閏年を調べる関数
    bool  MyDays::Uruu(int year)
    {   if (year%400L==0L)  return(true);
        if (year%100L==0L)  return(false);
        if (year%4L==0L)    return(true);
        return(false);
    }
    
  3. Main.xaml.h は Button_Click() 関数が追加されていることを除いて変更はありません。
    Main.xaml.cpp のソースコードです。
    Main.xaml の Constructor で MySplit Class と MyDays Class を生成します。
    本日の日付を取得して西暦元年1月1日からの通算日数を計算して mydays->ToDay に格納します。
    Button_Click() で生存日数を表示するのですが、今回は誕生日(yyyy/mm/dd)を取得して年月日を int に変換して印字します。
    #pragma once
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include "MySplit.h"
    #include "MyDays.h"
    #include <windows.h>    // OutputDebugString
        ・・・
        MySplit *mysplit;
        MyDays  *mydays;
    
    MainPage::MainPage()
    {
        int yy,mm,dd;
        InitializeComponent();
        mysplit = new MySplit();
        mydays = new MyDays();
        Windows::Globalization::Calendar^ cal = ref new Windows::Globalization::Calendar();
        yy = cal->Year;
        mm = cal->Month;
        dd = cal->Day;
        mydays->ToDay = mydays->Days(yy, mm, dd);
        mysplit->Debug("*ToDay: ", mydays->ToDay);
    }
    
    void App1::MainPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        std::vector<int> vect;
        String^ str;
        str = ref new String(textBox->Text->Data());
        textBox1->Text = str;
        vect = mysplit->split(str);
        for(int i=0; i<3; i++)
            mysplit->Debug("vect: ",vect[i]);
    }
    
  4. MySplit.h と MySplit.cpp と MyDays.h と MyDays.cpp をプロジェクトに組み込んで下さい。
    プログラムを実行すると西暦元年1月1日から本日までの通算日数と TextBox にタイプした yyyy/mm/dd が出力ウインドウに印字されます。

生存日数を計算する

  1. MySplit Class と MyDays Class を継承して、アプリの処理を行う MyApp Class を作成します。
    C# では partial class(兄弟 Class) を使ったのですが、今回はネイティブクラスでプログラムしたので C# に比べて解り難いかも知れません。
    クラスを継承すると、継承したクラスに含まれる領域や関数を直接利用することが出来ます。
  2. MyApp.h のソースコードです。
    MyApp Class は MySplit Class と MyDays Class を継承しています。
    MyApp(); が Constructor で、西暦元年1月1日から今日までの通算日数を計算します。
    DayCount() が誕生日から今日までの生存日数を計算する関数です。
    ToDay は西暦元年1月1日から今日までの通算日数を記録する領域です。
    他の Class で定義されているときは、そちらの領域を削除して下さい。
    #include "MySplit.h"
    #include "MyDays.h"
    
    namespace App1
    {
        class MyApp : public MySplit, MyDays
        {
        public:
            MyApp();        //Constructor
            int     DayCount(Platform::String^ ymd);
            int     ToDay;
        };
    }
    
  3. MyApp.cpp のソースコードです。
    Constructor で西暦元年1月1日から今日までの日数を計算して ToDay に格納します。
    DayCount() 関数で生年月日と今日の日付から生存日数を調べます。
    #pragma once
    
    #include "pch.h"
    #include "MyApp.h"
    #include <string>       // STL の String を使う
    using namespace App1;
    
    // Constructor
    MyApp::MyApp() 
    {
        int yy,mm,dd;
        Windows::Globalization::Calendar^ cal =
            ref new Windows::Globalization::Calendar();
        yy = cal->Year;
        mm = cal->Month;
        dd = cal->Day;
        ToDay = Days(yy, mm, dd);
        Debug("ToDay: ", ToDay);
    }
    
    // 生年月日と今日の日付から生存日数を調べる関数
    int  MyApp::DayCount(Platform::String^ ymd)
    {
        std::vector<int> vect;
        int wk;
        vect = split(ymd);
        wk = Days(vect[0], vect[1], vect[2]);
        return (ToDay - wk + 1);
    }
    
  4. Main.xaml.h は Button_Click() 関数が追加されていることを除いて変更はありません。
    Main.xaml.cpp のソースコードです。
    Main.xaml の Constructor で MyApp Class を生成します。
    MyApp Class は MySplit Class と MyDays Class を継承しているので、これらに含まれる関数を使用することが出来ます。
    Button_Click() で生存日数を計算して、textBox1 に表示します。
    #pragma once
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include "MyApp.h"
    #include <windows.h>    // OutputDebugString
        ・・・
    using namespace App1;
    
        MyApp   *myapp;
    
    MainPage::MainPage()
    {
        InitializeComponent();
        myapp = new MyApp();
    }
    
    void App1::MainPage::Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
        int ans;
        String^ str;
        str = ref new String(textBox->Text->Data());
        ans = myapp->DayCount(str);
        textBox1->Text = ans.ToString();
    }
    
  5. MySplit.h と MySplit.cpp と MyDays.h と MyDays.cpp と MyApp.h と MyApp.cpp をプロジェクトに組み込んで下さい。
    プログラムを実行すると TextBox にタイプした誕生日から本日までの生存日数が TextBox1 に表示されます。

【NOTE】

C# の string や ArrayList は強力ですが、C++ の文字列や Array は非力です。
そこで STL の wstring や vector に頼ることになります。
さらに Store C++ には Class の種類が幾通りかある上に、public: protected: private: internal: の使い分けも面倒で、 エラーメッセージを参照しながら悪戦苦闘の連続です。
エラーメッセージも解りにくく、初心者では太刀打ち出来ないかも知れません。
Windows10 Calender では Class 分けをしないで MainPage に全てのソースコードを組み込んで、解り易くプログラミングしています。
C# の方が簡単で解り易いので VS 2019 Calender も参考にして下さい。

[Next Chapter ↓] Windows10 Calender(クラス分けしない)

超初心者のプログラム入門(C/C++)