nprogram’s blog

気ままに、プログラミングのトピックについて書いていきます

WPFで登録・削除可能なリストを作成する [C#][WPF][Prism][MVVM]

はじめに

WPFで登録・削除可能なリストを作成します。
MVVM(Model・View・ViewModel)のプロジェクトにしました。

MVVMの形にするため、以下のようにフォルダ分けしています。

  • Modelsフォルダ
  • ViewModelsフォルダ
  • Viewsフォルダ

Prismでは、以下の機能を使用しています。

  • BindableBase
  • DelegateCommand

環境

  • OS : Windows 10
  • IDE : Visual Studio Community 2017 (Version 15.71)
  • Prism : 6.3.0

[アプリイメージ]

f:id:nprogram:20180617151748p:plain

[クラス図]

f:id:nprogram:20180617163408p:plain

[コードについて]

データコンテキストには、ViewModel.xaml.csで、MainViewModelクラスのインスタンスを設定しています。 これにより、MainViewModelが持つプロパティを画面(MainView.xaml)からアクセスできるようにしています。

[App.xaml.cs]

    /// <summary>
    /// App.xaml の相互作用ロジック
    /// </summary>
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            var w = new MainView();
            var vm = new MainViewModel();

            w.DataContext = vm;
            w.Show();
        }
    }

追加ボタンを押したときは、追加コマンドが実行され、AddCommandExecuteメソッドが呼び出されます。

[ViewModel.xaml]

            <Button Content="データ追加"
                    Command ="{Binding AddCommand, Mode=OneWay}"></Button>

[MainViewModel.cs]

        private DelegateCommand _AddComamnd;
        public DelegateCommand AddCommand
        {
            get { return _AddComamnd = _AddComamnd ?? new DelegateCommand(AddCommandExecute); }
        }

[MainViewModel.cs]

        private void AddCommandExecute()
        {
            Book addBook = new Book(books.GetList().Count + 1, Book.Title, Book.Author, Book.Price);
            books.Add(addBook);

            CollectionList.Add(addBook);
        }

削除ボタンを押したときも、基本的な動作は同じです。削除コマンドが実行され、DelCommandExecuteメソッドが呼び出されます。
なお、削除ボタンは、リスト選択時のみ、有効となるように、IsEnabledプロパティをDataGridSelectedItems.Countにバインドしています。 また、CommandParameterには、選択したアイテム(Bookクラス)を渡しています。
これにより、DelCommandExecuteメソッド内で、IDの値で削除対象のアイテムを特定して削除しています。

全削除ボタンは、DataGridの要素が一つ以上ある場合のみ、有効になるようにしています。

[MainView.xaml]

        <StackPanel Orientation="Vertical" DockPanel.Dock="Top">
            <Button Content="データ追加"
                    Command ="{Binding AddCommand, Mode=OneWay}"></Button>
            <Button Content="データ削除"
                    Command ="{Binding DelComamnd}"
                    CommandParameter="{Binding ElementName=nameBookList, Path=SelectedItem}"
                    IsEnabled="{Binding ElementName=nameBookList, Path=SelectedItems.Count}"/>
            <Button Content="データ全削除"
                    Command ="{Binding DelAllComamnd, Mode=OneWay}"
                    IsEnabled="{Binding ElementName=nameBookList, Path=Items.Count}"/>
        </StackPanel>

ソースコード

Gitにコードをアップロードしました。
よろしければ、ダウンロードしてみてください。

github.com

あとがき

WPFで登録・削除可能なリストを作成しました。
次は、アプリ終了時のリスト状態を記憶できるように、SQLiteを使用して、データ永続化を取り入れたいと思います。

なお、別の記事でMVVMでないWPFで登録・削除可能なリストを作成する記事
(WPFで登録と削除が可能なリストを作成します [C#][WPF] - nprogram’s blog) があるので、よろしければ見てください。

WPF SQLiteを用いたデータ永続化 [C#][WPF]

はじめに

Visual Studio 2017SQLiteを使用したいときは、プロジェクトにSQLiteを追加する必要があります。
SQLiteは、NuGetパッケージ マネージャーからプロジェクトに追加するのが簡単だと思います。
https://www.nuget.org/packages/System.Data.SQLite/

以下はコマンドです。
Install-Package System.Data.SQLite -Version 1.0.108

参考リンク

garafu.blogspot.com

https://www.doraxdora.com/blog/2017/06/09/post-1184/

データベースを特定の独自クラス専用にしないようにするためには、ジェネリッククラスを活用する必要がありあmす。

Astah Professionalの便利な点まとめ

はじめに

今回、astah professionalを購入したので、使っていて便利な点をまとめてみました。

<プロジェクトの言語設定が便利>

[f:id:nprogram:2080601020203p:plain]

C#のプロジェクトをUMLに使用としたとき、クラスのメンバ変数なのか、それとも、プロパティなのかを表現ができるのがとてもいいと思います。

f:id:nprogram:20180601020313p:plain

他にもあれば、今後載せていきますね。

C#の勉強サイトまとめ

codezine.jp

[C# / WPF] 最新のC# 6.0でMVVMパターンを実装する https://qiita.com/nia_tn1012/items/de5c8f83f9a638f6e44eqiita.com

上のRSSリーダーは、下記内容を使用しています。いきなり、こったRSSリーダーを作ろうとするのではなく、要素技術の確認を行うために、以下のリンクを試してみたい。

と思ったのですが、UML図を使って、理解しやすい形にして、要素を分解して学習することにしました。

INotifyPropertyChanged, ICommand, ObeservableCollection, DataTemplate, Delegateなど要素はあります。これは、別記事にする予定です。

f:id:nprogram:20180601021322p:plain

System.ServiceModel.Syndication.SyndicationFeedクラスを使うために、System.ServiceModel.Web.dllを参照に追加しておく。 SyndicationFeedクラスの使い方はとっても簡単。SyndicationFeed.Load(XmlReader)みたいにして、XmlReaderから作成できる。

C#からC++のDLLを呼び出す (構造体編) [C#]

はじめに

以下の場合も、実現可能です。

  • C++のDLLのAPIに対して、構造体のデータを渡す場合
  • C++のDLLのAPIから、構造体のデータを受け取る場合

環境

  • IDE : Visual Studio Community 2017 (Version 15.7.1)

コード

#include <string>

#ifdef __cplusplus
#define DLLEXPORT extern "C" __declspec(dllexport)
#else
#define DLLEXPORT __declspec(dllexport)
#endif

const static int INIT_HP = 300;
const static int INIT_MP = 200;
const static int MAX_NAME_LENGTH = 256;
const static int BIG_DATA_LENGTH = 100000;

struct MainCharacter_t
{
    char name[MAX_NAME_LENGTH];
    int hp;
    int mp;
    unsigned char bigData[BIG_DATA_LENGTH];
};

MainCharacter_t MainCharacter = { "Ichiro", INIT_HP, INIT_MP, 0 };


DLLEXPORT void SetCharaData(MainCharacter_t *someData_t)
{
    memcpy_s(&MainCharacter, sizeof(MainCharacter_t), someData_t, sizeof(MainCharacter_t));
}

DLLEXPORT void GetCharaData(MainCharacter_t *someData_t)
{
    memcpy_s(someData_t, sizeof(MainCharacter_t), &MainCharacter, sizeof(MainCharacter_t));
}

// テストデータセット用[f:id:nprogram:20180522230541p:plain]
DLLEXPORT void SetBigData()
{
    unsigned char tempBigData[BIG_DATA_LENGTH] = { 0 };

    for (int i = 0; i < BIG_DATA_LENGTH; i++)
    {
        tempBigData[i] = i % 256;
    }

    memcpy_s(MainCharacter.bigData, BIG_DATA_LENGTH, tempBigData, BIG_DATA_LENGTH);
}

[C#のコード]

using System;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        public const int MAX_NAME_LENGTH = 256;
        public const int BIG_DATA_LENGTH = 100000;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
        public struct MainCharacter_t
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_NAME_LENGTH)]
            public string imageFileName;
            public int hp;
            public int mp;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = BIG_DATA_LENGTH)]
            public byte[] bigData;
        }

        // テストデータセット用
        [DllImport("CPlusDLL", EntryPoint = "SetBigData", CallingConvention = CallingConvention.Cdecl)]
        static extern void _SetBigData();

        [DllImport("CPlusDLL", EntryPoint = "SetCharaData", CallingConvention = CallingConvention.Cdecl)]
        static extern void _SetCharaData(IntPtr someCharaData);

        [DllImport("CPlusDLL", EntryPoint = "GetCharaData", CallingConvention = CallingConvention.Cdecl)]
        static extern void _GetCharaData(IntPtr someCharaData);

        static void Main(string[] args)
        {
            // テストデータセット
            _SetBigData();

            ShowCharaData(GetCurrentCharaData());

            MainCharacter_t setData1 = new MainCharacter_t();
            setData1.hp = 10;
            setData1.mp = 0;
            setData1.imageFileName = "Jiro";
            SetCurrentCharaData(setData1);

            // テストデータセット
            _SetBigData();
            ShowCharaData(GetCurrentCharaData());
        }

        static public void SetCurrentCharaData(MainCharacter_t someCharaData)
        {
            // COM タスク メモリ アロケーターから、C#の構造体のサイズ分、メモリ ブロックを割り当てる
            IntPtr someCharaDataPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(someCharaData));

            // マネージ オブジェクトからアンマネージ メモリ ブロックにデータをマーシャリングする
            Marshal.StructureToPtr(someCharaData, someCharaDataPtr, false);

            _SetCharaData(someCharaDataPtr);

            Marshal.FreeCoTaskMem(someCharaDataPtr);
        }

        static public MainCharacter_t GetCurrentCharaData()
        {
            MainCharacter_t mainCharacter = new MainCharacter_t();


            // COM タスク メモリ アロケーターから、C#の構造体のサイズ分、メモリ ブロックを割り当てる
            IntPtr mainCharacterPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(mainCharacter));

            _GetCharaData(mainCharacterPtr);

            // IntPtr変数が示すメモリに格納された情報を、Marshal.PtrToStructure()で、C#の構造体にコピーする
            mainCharacter = (MainCharacter_t)Marshal.PtrToStructure(mainCharacterPtr, mainCharacter.GetType());

            Marshal.FreeCoTaskMem(mainCharacterPtr);

            return mainCharacter;
        }

        static public void ShowCharaData(MainCharacter_t someCharaData)
        {
            Console.WriteLine("MainChara Name : " + someCharaData.imageFileName);
            Console.WriteLine("MainChara HP : " + someCharaData.hp);
            Console.WriteLine("MainChara MP : " + someCharaData.mp);

            Console.WriteLine("[Big Data First Data (5 count)]");
            for ( int i = 0; i < 5; i++)
            {
                Console.WriteLine("[" + i + "] : " + someCharaData.bigData[i]);
            }

            Console.WriteLine("[Big Data Last Data (5 count)]");
            for (int i = BIG_DATA_LENGTH - 5; i < BIG_DATA_LENGTH; i++)
            {
                Console.WriteLine("[" + i + "] : " + someCharaData.bigData[i]);
            }
        }
    }
}

実行結果

f:id:nprogram:20180522230541p:plain

参考リンク

d.hatena.ne.jp