C#で作るプロジェクト管理アプリケーション(その1)

その2

https://qiita.com/sumiyoshi01/items/3c124c9e2fd01d9eff1b

## 1.制作の動機
4年生になり、卒業研究が始まったのでモチベを高めるために自分だけのオリジナルアプリを作ろうと思いました。

## 2.アプリケーションの開発と実行の環境
* 実行環境
    Windows11
* 統合開発環境
    Visual Studio 2022
* 開発言語
    C#
* FW
    .NET

## 3.概要と機能
詳しい概要と機能はこちらの動画でまとめています。(時系列バラバラで編集も下手で申し訳ないです。)

<iframe width="560" height="315" src="https://www.youtube.com/embed/X-sNzqluxGs?si=g0I1tiXRM3e5P-0q" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## 4.クラス図
クラスは以下の6つで構成されています。
* MainWindow
* Project
* TaskItem
* ScheduleItem
* ReferenceItem
* IdeaItem
![クラス図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3290653/b1922dd3-c33d-8fe9-650c-644d18fc8a2d.png)

:::note info
以下の5つはMainWindow内で呼び出せるように先に宣言していたものです。(MainWindowの上に位置している要素のことです。)
* string TaskPlaceholder
* string SchedulePlaceholder
* string ReferencePlaceholder
* List<Project> projects;
* ListView taskListView;
:::

## 5.ディレクトリ構成
プロジェクトファイルのディレクトリ構成です。制作の経過によってライブラリの数などは変わると思います。
```
- App.xaml
- App.xaml.cs
- AssemblyInfo.cs
- EditTaskDialog.xaml
- EditTaskDialog.xaml.cs
- IdeaItem.cs
- InputDialog.xaml
- InputDialog.xaml.cs
- MainWindow.xaml
- MainWindow.xaml.cs
- Project.cs
- ProjectManager.cs
- README.md
- ReferenceItem.cs
- ReviewDialog.xaml
- ReviewDialog.xaml.cs
- ScheduleItem.cs
- SelectProjectDialog.xaml
- SelectProjectDialog.xaml.cs
- task manager for research.csproj
- task manager for research.csproj.user
- TaskItem.cs
+ bin
  + Debug
    + net6.0-windows
      - chromedriver.exe
      - credentials.txt
      - f9223b0baed6c85359886b13b7487364_xxo.ico
      - HtmlAgilityPack.dll
      - MaterialDesignColors.dll
      - MaterialDesignThemes.Wpf.dll
      - Microsoft.Xaml.Behaviors.dll
      - Newtonsoft.Json.dll
      - projects.txt
      - task manager for research.deps.json
      - task manager for research.dll
      - task manager for research.exe
      - task manager for research.exe - ショートカット.lnk
      - task manager for research.pdb
      - task manager for research.runtimeconfig.json
      - WebDriver.dll
      + selenium-manager
        + linux
          - selenium-manager
        + macos
          - selenium-manager
        + windows
          - selenium-manager.exe
      + 卒業研究
        - completed_tasks.csv
        - ideas.csv
        - references.csv
        - schedules.csv
        - tasks.csv
+ obj
  - project.assets.json
  - project.nuget.cache
  - task manager for research.csproj.nuget.dgspec.json
  - task manager for research.csproj.nuget.g.props
  - task manager for research.csproj.nuget.g.targets
  + Debug
    + net6.0-windows
      - .NETCoreApp,Version=v6.0.AssemblyAttributes.cs
      - App.baml
      - App.g.cs
      - App.g.i.cs
      - apphost.exe
      - EditTaskDialog.baml
      - EditTaskDialog.g.cs
      - EditTaskDialog.g.i.cs
      - InputDialog.baml
      - InputDialog.g.cs
      - InputDialog.g.i.cs
      - MainWindow.baml
      - MainWindow.g.cs
      - MainWindow.g.i.cs
      - ReviewDialog.baml
      - ReviewDialog.g.cs
      - ReviewDialog.g.i.cs
      - SelectProjectDialog.baml
      - SelectProjectDialog.g.cs
      - SelectProjectDialog.g.i.cs
      - task manager for research.AssemblyInfo.cs
      - task manager for research.AssemblyInfoInputs.cache
      - task manager for research.assets.cache
      - task manager for research.csproj.AssemblyReference.cache
      - task manager for research.csproj.BuildWithSkipAnalyzers
      - task manager for research.csproj.CopyComplete
      - task manager for research.csproj.CoreCompileInputs.cache
      - task manager for research.csproj.FileListAbsolute.txt
      - task manager for research.designer.deps.json
      - task manager for research.designer.runtimeconfig.json
      - task manager for research.dll
      - task manager for research.g.resources
      - task manager for research.GeneratedMSBuildEditorConfig.editorconfig
      - task manager for research.genruntimeconfig.cache
      - task manager for research.pdb
      - task manager for research_Content.g.cs
      - task manager for research_Content.g.i.cs
      - task manager for research_MarkupCompile.cache
      - task manager for research_MarkupCompile.i.cache
      + ref
        - task manager for research.dll
      + refint
        - task manager for research.dll
```

## 6.プロジェクトファイルの作成方法
1. Visual Studio 2022を開く
1. 新しいプロジェクトの作成
1. WPFアプリケーション(C#)
1. プロジェクト名を入力

:::note alert
もし必要なパッケージをインストールしてなかったら作れないので、C#のWPFアプリケーションが無かったらパッケージのインストールから始めてください。
:::

## 7.各コード(その1)

```diff_c_sharp:MainWindow.cs
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Diagnostics;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using HtmlAgilityPack;
using System.Threading.Tasks;

namespace task_manager_for_research
{
    public partial class MainWindow : Window
    {
        private const string TaskPlaceholder = "タスクを入力してね♡";
        private const string SchedulePlaceholder = "イベントを入力してね♡";
        private const string ReferencePlaceholder = "参考文献を入力してね♡";

        private List<Project> projects;
        private ListView taskListView;

        public MainWindow()
        {
            InitializeComponent();
            projects = ProjectManager.LoadProjects();
            InitializeProjectTabs();
        }

        private async void FetchTasksButton_Click(object sender, RoutedEventArgs e)
        {
            var tasks = await GetTasksFromyourschool();
            DisplayTasks(tasks);
        }

        private async Task<List<Tuple<string, string>>> GetTasksFromyourschool()
        {
            var tasks = new List<Tuple<string, string>>();
            string chromeDriverPath = @"~\chromedriver.exe"; // ChromeDriverのパスに変更
            var credentials = LoadCredentials("credentials.txt");

            var options = new ChromeOptions();
            options.AddArgument("--headless");

            using (var driver = new ChromeDriver(chromeDriverPath, options))
            {
                try
                {
                    driver.Navigate().GoToUrl("");// 自分の学校の掲示板のURLに変更してください

                    driver.FindElement(By.Name("")).SendKeys(credentials.Item1);
                    driver.FindElement(By.Name("")).SendKeys(credentials.Item2);
                    driver.FindElement(By.Name("")).Click();

                    driver.Navigate().GoToUrl("");

                    var pageSource = driver.PageSource;
                    var htmlDoc = new HtmlDocument();
                    htmlDoc.LoadHtml(pageSource);

                    var taskNodes = htmlDoc.DocumentNode.SelectNodes("");
                    var dateNodes = htmlDoc.DocumentNode.SelectNodes("");

                    if (taskNodes != null && dateNodes != null && taskNodes.Count == dateNodes.Count)
                    {
                        for (int i = 0; i < taskNodes.Count; i++)
                        {
                            var taskName = taskNodes[i].InnerText.Trim();
                            var taskDate = dateNodes[i].InnerText.Trim();
                            if (!string.IsNullOrEmpty(taskName) && !string.IsNullOrEmpty(taskDate))
                            {
                                tasks.Add(new Tuple<string, string>(taskName, taskDate));
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show($"課題の取得中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            return tasks;
        }
        private Tuple<string, string> LoadCredentials(string filePath)
        {
            try
            {
                var lines = File.ReadAllLines(filePath);
                var username = lines.FirstOrDefault(l => l.StartsWith("username:"))?.Split(':')[1].Trim();
                var password = lines.FirstOrDefault(l => l.StartsWith("password:"))?.Split(':')[1].Trim();

                if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
                {
                    throw new Exception("ユーザーネームまたはパスワードが設定ファイルに見つかりませんでした。");
                }

                return new Tuple<string, string>(username, password);
            }
            catch (Exception ex)
            {
                throw new Exception($"設定ファイルの読み込み中にエラーが発生しました: {ex.Message}");
            }
        }

        private void DisplayTasks(List<Tuple<string, string>> tasks)
        {
            var taskWindow = new Window
            {
                Title = "未提出課題一覧",
                Width = 400,
                Height = 300
            };

            var taskListView = new ListView();
            foreach (var task in tasks)
            {
                var item = new ListViewItem { Content = $"{task.Item1} - {task.Item2}" };
                taskListView.Items.Add(item);
            }

            taskWindow.Content = taskListView;
            taskWindow.ShowDialog();
        }

        private void InitializeProjectTabs()
        {
            // 現在選択されているタブのインデックスを保存
            int selectedIndex = ProjectTabControl.SelectedIndex;

            ProjectTabControl.Items.Clear();
            foreach (var project in projects)
            {
                var tabItem = new TabItem
                {
                    Header = project.ProjectName,
                    Content = CreateProjectTabContent(project)
                };
                ProjectTabControl.Items.Add(tabItem);
                Console.WriteLine($"Tab added: {project.ProjectName}");
            }

            // 保存したインデックスを再度設定
            if (selectedIndex >= 0 && selectedIndex < ProjectTabControl.Items.Count)
            {
                ProjectTabControl.SelectedIndex = selectedIndex;
                var selectedProject = projects[selectedIndex];
                RefreshTaskListView(selectedProject);
            }
            else if (ProjectTabControl.Items.Count > 0)
            {
                ProjectTabControl.SelectedIndex = 0;
                var selectedProject = projects[0];
                RefreshTaskListView(selectedProject);
            }
        }

        private TabControl CreateProjectTabContent(Project project)
        {
            var projectTabControl = new TabControl();

            var taskTab = new TabItem { Header = "タスク管理", Content = CreateTaskContent(project) };
            var scheduleTab = new TabItem { Header = "スケジュール", Content = CreateScheduleContent(project) };
            var referenceTab = new TabItem { Header = "参考文献管理", Content = CreateReferenceContent(project) };
            var feedbackTab = new TabItem { Header = "フィードバック", Content = CreateFeedbackContent(project) };
            var ideaTab = new TabItem { Header = "アイデア", Content = CreateIdeaContent(project) };

            projectTabControl.Items.Add(taskTab);
            projectTabControl.Items.Add(scheduleTab);
            projectTabControl.Items.Add(referenceTab);
            projectTabControl.Items.Add(feedbackTab);
            projectTabControl.Items.Add(ideaTab);

            return projectTabControl;
        }


        private StackPanel CreateTaskContent(Project project)
        {
            var taskNameTextBox = new TextBox { Text = TaskPlaceholder, Foreground = Brushes.Gray };
            SetPlaceholderText(taskNameTextBox, TaskPlaceholder);

            var startDatePicker = new DatePicker();
            var endDatePicker = new DatePicker();

            var addTaskButton = new Button { Content = "タスクを追加してね♡"};
            var taskListView = new ListView { Height = 200, SelectionMode = SelectionMode.Single };

            // タスク表示用のテンプレートを設定
            var dataTemplate = new DataTemplate(typeof(TaskItem));
            var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
            stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

            var taskNameTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            taskNameTextFactory.SetBinding(TextBlock.TextProperty, new Binding("TaskName"));
            taskNameTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(taskNameTextFactory);

            var startDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            startDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("StartDate"));
            startDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(startDateTextFactory);

            var endDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            endDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("EndDate"));
            endDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(endDateTextFactory);

            var editButtonFactory = new FrameworkElementFactory(typeof(Button));
            editButtonFactory.SetValue(Button.ContentProperty, "編集");
            editButtonFactory.AddHandler(Button.ClickEvent, new RoutedEventHandler((s, e) =>
            {
                var task = ((FrameworkElement)s).DataContext as TaskItem;
                if (task != null)
                {
                    Debug.WriteLine($"Edit button clicked for task: {task.TaskName}");
                    EditTask(task);
                }
            }));
            stackPanelFactory.AppendChild(editButtonFactory);

            var completeButtonFactory = new FrameworkElementFactory(typeof(Button));
            completeButtonFactory.SetValue(Button.ContentProperty, "完了");
            completeButtonFactory.AddHandler(Button.ClickEvent, new RoutedEventHandler((s, e) =>
            {
                var task = ((FrameworkElement)s).DataContext as TaskItem;
                if (task != null)
                {
                    CompleteTask(task, project);
                }
            }));
            stackPanelFactory.AppendChild(completeButtonFactory);

            dataTemplate.VisualTree = stackPanelFactory;
            taskListView.ItemTemplate = dataTemplate;

            var completedTaskListView = new ListView { Height = 200, SelectionMode = SelectionMode.Single };

            addTaskButton.Click += (s, e) => AddTaskButton_Click(s, e, project, taskNameTextBox, startDatePicker, endDatePicker, taskListView);

            LoadTasks(project, taskListView, completedTaskListView);

            var stackPanel = new StackPanel();
            stackPanel.Children.Add(taskNameTextBox);
            stackPanel.Children.Add(startDatePicker);
            stackPanel.Children.Add(endDatePicker);
            stackPanel.Children.Add(addTaskButton);
            stackPanel.Children.Add(taskListView);

            return stackPanel;
        }

        private StackPanel CreateScheduleContent(Project project)
        {
            var calendar = new Calendar
            {
                Name = "ProjectCalendar",
                DisplayMode = CalendarMode.Month
            };

            calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;

            var scheduleListView = new ListView { Height = 200, SelectionMode = SelectionMode.Single };

            var dataTemplate = new DataTemplate(typeof(ScheduleItem));
            var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
            stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

            var scheduleNameTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            scheduleNameTextFactory.SetBinding(TextBlock.TextProperty, new Binding("ScheduleName"));
            scheduleNameTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(scheduleNameTextFactory);

            var scheduleDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            scheduleDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("ScheduleDate"));
            scheduleDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(scheduleDateTextFactory);

            dataTemplate.VisualTree = stackPanelFactory;
            scheduleListView.ItemTemplate = dataTemplate;

            var scheduleNameTextBox = new TextBox { Text = "イベントを入力してね♡", Foreground = Brushes.Gray };
            SetPlaceholderText(scheduleNameTextBox, "イベントを入力してね♡");

            var addScheduleButton = new Button { Content = "イベントを追加してね♡" };
            addScheduleButton.Click += (s, e) => AddScheduleButton_Click(s, e, project, scheduleNameTextBox, calendar, scheduleListView);

            LoadSchedules(project, scheduleListView);

            var stackPanel = new StackPanel();
            stackPanel.Children.Add(calendar);
            stackPanel.Children.Add(scheduleNameTextBox);
            stackPanel.Children.Add(addScheduleButton);
            stackPanel.Children.Add(scheduleListView);

            return stackPanel;
        }

        private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
        {
            var calendar = sender as Calendar;
            DateTime? selectedDate = calendar.SelectedDate;
            if (selectedDate.HasValue)
            {
                MessageBox.Show($"選択された日付: {selectedDate.Value.ToShortDateString()}");
            }
        }

        private void LoadSchedules(Project project, ListView scheduleListView)
        {
            scheduleListView.Items.Clear();
            project.Schedules.Clear();

            try
            {
                if (File.Exists(project.ScheduleFilePath))
                {
                    using (StreamReader reader = new StreamReader(project.ScheduleFilePath))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            var data = line.Split(',');
                            ScheduleItem schedule = new ScheduleItem { ScheduleName = data[0], ScheduleDate = data[1] };
                            project.Schedules.Add(schedule);
                            scheduleListView.Items.Add(schedule);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"スケジュールの読み込み中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void AddScheduleButton_Click(object sender, RoutedEventArgs e, Project project, TextBox scheduleNameTextBox, Calendar calendar, ListView scheduleListView)
        {
            string scheduleName = scheduleNameTextBox.Text;
            DateTime? selectedDate = calendar.SelectedDate;

            if (!string.IsNullOrEmpty(scheduleName) && scheduleName != "イベントを入力してね♡" && selectedDate.HasValue)
            {
                ScheduleItem newSchedule = new ScheduleItem { ScheduleName = scheduleName, ScheduleDate = selectedDate.Value.ToShortDateString() };
                scheduleListView.Items.Add(newSchedule);
                project.Schedules.Add(newSchedule);

                SaveSchedules(project);

                // イベントハンドラを一時的に削除してから日付をクリア
                calendar.SelectedDatesChanged -= Calendar_SelectedDatesChanged;
                ClearScheduleInputs(scheduleNameTextBox, calendar);
                calendar.SelectedDatesChanged += Calendar_SelectedDatesChanged;
            }
            else
            {
                MessageBox.Show("すべてのフィールドに入力してください。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void ClearScheduleInputs(TextBox scheduleNameTextBox, Calendar calendar)
        {
            scheduleNameTextBox.Text = "イベントを入力してね♡";
            scheduleNameTextBox.Foreground = Brushes.Gray;
            calendar.SelectedDate = null;
        }

        private void SaveSchedules(Project project)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project), "プロジェクトがnullです。");
            }

            if (string.IsNullOrEmpty(project.ScheduleFilePath))
            {
                throw new ArgumentNullException(nameof(project.ScheduleFilePath), "スケジュールファイルパスが設定されていません。");
            }

            try
            {
                using (StreamWriter writer = new StreamWriter(project.ScheduleFilePath, false))
                {
                    foreach (ScheduleItem item in project.Schedules)
                    {
                        writer.WriteLine($"{item.ScheduleName},{item.ScheduleDate}");
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"スケジュールの保存中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private StackPanel CreateReferenceContent(Project project)
        {
            var referenceTypeComboBox = new ComboBox
            {
                ItemsSource = new List<string> { "Link", "Book" },
                SelectedIndex = 0
            };

            var referenceTextBox = new TextBox { Text = "参考文献を入力してね♡", Foreground = Brushes.Gray };
            SetPlaceholderText(referenceTextBox, "参考文献を入力してね♡");

            var authorTextBox = new TextBox { Text = "著者を入力してね♡", Foreground = Brushes.Gray, Visibility = Visibility.Collapsed };
            SetPlaceholderText(authorTextBox, "著者を入力してね♡");

            referenceTypeComboBox.SelectionChanged += (s, e) =>
            {
                if (referenceTypeComboBox.SelectedItem.ToString() == "Book")
                {
                    authorTextBox.Visibility = Visibility.Visible;
                }
                else
                {
                    authorTextBox.Visibility = Visibility.Collapsed;
                }
            };

            var dateTextBox = new TextBox { Text = "日付を入力してね♡", Foreground = Brushes.Gray };
            SetPlaceholderText(dateTextBox, "日付を入力してね♡");

            var contentTextBox = new TextBox { Text = "内容を入力してね♡", Foreground = Brushes.Gray };
            SetPlaceholderText(contentTextBox, "内容を入力してね♡");

            var addReferenceButton = new Button { Content = "参考文献を追加してね♡" };

            var referenceListView = new ListView { Height = 200, SelectionMode = SelectionMode.Single };

            // 参考文献表示用のテンプレートを設定
            var dataTemplate = new DataTemplate(typeof(ReferenceItem));
            var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
            stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

            var referenceTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            referenceTextFactory.SetBinding(TextBlock.TextProperty, new Binding("Display"));
            referenceTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(referenceTextFactory);

            var dateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            dateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("Date"));
            dateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(dateTextFactory);

            var contentTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            contentTextFactory.SetBinding(TextBlock.TextProperty, new Binding("Content"));
            contentTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(contentTextFactory);

            var openButtonFactory = new FrameworkElementFactory(typeof(Button));
            openButtonFactory.SetValue(Button.ContentProperty, "開く");
            openButtonFactory.AddHandler(Button.ClickEvent, new RoutedEventHandler((s, e) =>
            {
                var referenceItem = ((FrameworkElement)s).DataContext as ReferenceItem;
                if (referenceItem != null && referenceItem.ReferenceType == "Link")
                {
                    Process.Start(new ProcessStartInfo(referenceItem.Reference) { UseShellExecute = true });
                }
            }));
            stackPanelFactory.AppendChild(openButtonFactory);

            dataTemplate.VisualTree = stackPanelFactory;
            referenceListView.ItemTemplate = dataTemplate;

            addReferenceButton.Click += (s, e) => AddReferenceButton_Click(s, e, project, referenceTypeComboBox, referenceTextBox, authorTextBox, dateTextBox, contentTextBox, referenceListView);

            var stackPanel = new StackPanel();
            stackPanel.Children.Add(referenceTypeComboBox);
            stackPanel.Children.Add(referenceTextBox);
            stackPanel.Children.Add(authorTextBox);
            stackPanel.Children.Add(dateTextBox);
            stackPanel.Children.Add(contentTextBox);
            stackPanel.Children.Add(addReferenceButton);
            stackPanel.Children.Add(referenceListView);

            LoadReferences(project, referenceListView);

            return stackPanel;
        }

        private void AddReferenceButton_Click(object sender, RoutedEventArgs e, Project project, ComboBox referenceTypeComboBox, TextBox referenceTextBox, TextBox authorTextBox, TextBox dateTextBox, TextBox contentTextBox, ListView referenceListView)
        {
            string referenceType = referenceTypeComboBox.SelectedItem.ToString();
            string reference = referenceTextBox.Text;
            string author = authorTextBox.Text;
            string date = dateTextBox.Text;
            string content = contentTextBox.Text;

            if (!string.IsNullOrEmpty(reference) && reference != "参考文献を入力してね♡" && !string.IsNullOrEmpty(date) && date != "日付を入力してね♡" && !string.IsNullOrEmpty(content) && content != "内容を入力してね♡")
            {
                var newReference = new ReferenceItem
                {
                    ReferenceType = referenceType,
                    Reference = reference,
                    Author = referenceType == "Book" ? author : null,
                    Date = date,
                    Content = content
                };
                referenceListView.Items.Add(newReference);
                project.References.Add(newReference);

                SaveReferences(project);
                ClearReferenceInputs(referenceTextBox, authorTextBox, dateTextBox, contentTextBox);
            }
            else
            {
                MessageBox.Show("すべてのフィールドに入力してください。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void SaveReferences(Project project)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project), "プロジェクトがnullです。");
            }

            if (string.IsNullOrEmpty(project.ReferenceFilePath))
            {
                project.ReferenceFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{project.ProjectName}_references.csv");
            }

            try
            {
                using (StreamWriter writer = new StreamWriter(project.ReferenceFilePath, false))
                {
                    foreach (ReferenceItem item in project.References)
                    {
                        writer.WriteLine($"{item.ReferenceType},{item.Reference},{item.Author},{item.Date},{item.Content}");
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"参考文献の保存中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void LoadReferences(Project project, ListView referenceListView)
        {
            referenceListView.Items.Clear();
            project.References.Clear();

            try
            {
                if (File.Exists(project.ReferenceFilePath))
                {
                    using (StreamReader reader = new StreamReader(project.ReferenceFilePath))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            var data = line.Split(',');
                            var referenceItem = new ReferenceItem
                            {
                                ReferenceType = data[0],
                                Reference = data[1],
                                Author = data[2],
                                Date = data[3],
                                Content = data[4]
                            };
                            project.References.Add(referenceItem);
                            referenceListView.Items.Add(referenceItem);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"参考文献の読み込み中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void ClearReferenceInputs(TextBox referenceTextBox, TextBox authorTextBox, TextBox dateTextBox, TextBox contentTextBox)
        {
            referenceTextBox.Text = "参考文献を入力してね♡";
            referenceTextBox.Foreground = Brushes.Gray;
            authorTextBox.Text = "著者を入力してね♡";
            authorTextBox.Foreground = Brushes.Gray;
            dateTextBox.Text = "日付を入力してね♡";
            dateTextBox.Foreground = Brushes.Gray;
            contentTextBox.Text = "内容を入力してね♡";
            contentTextBox.Foreground = Brushes.Gray;
        }

        private StackPanel CreateFeedbackContent(Project project)
        {
            var completedTaskListView = new ListView { Height = 400, SelectionMode = SelectionMode.Single };

            // 完了タスク表示用のテンプレートを設定
            var dataTemplate = new DataTemplate(typeof(TaskItem));
            var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
            stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

            var taskNameTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            taskNameTextFactory.SetBinding(TextBlock.TextProperty, new Binding("TaskName"));
            taskNameTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(taskNameTextFactory);

            var startDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            startDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("StartDate"));
            startDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(startDateTextFactory);

            var endDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            endDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("EndDate"));
            endDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(endDateTextFactory);

            var completionDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            completionDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("CompletionDate"));
            completionDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(completionDateTextFactory);

            var reviewTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            reviewTextFactory.SetBinding(TextBlock.TextProperty, new Binding("Review"));
            reviewTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(reviewTextFactory);

            var reviewButtonFactory = new FrameworkElementFactory(typeof(Button));
            reviewButtonFactory.SetValue(Button.ContentProperty, "レビューを書く");
            reviewButtonFactory.AddHandler(Button.ClickEvent, new RoutedEventHandler((s, e) =>
            {
                // レビューボタンのクリックイベント
                var task = ((FrameworkElement)s).DataContext as TaskItem;
                if (task != null)
                {
                    WriteReview(task, project);
                }
            }));
            stackPanelFactory.AppendChild(reviewButtonFactory);

            dataTemplate.VisualTree = stackPanelFactory;
            completedTaskListView.ItemTemplate = dataTemplate;

            var stackPanel = new StackPanel();
            stackPanel.Children.Add(new TextBlock { Text = "✅完了したタスク", FontSize = 20, FontWeight = FontWeights.Bold, Margin = new Thickness(0, 0, 0, 20), FontFamily = new FontFamily("HGP創英角ポップ体") });
            stackPanel.Children.Add(completedTaskListView);

            LoadTasks(project, null, completedTaskListView);

            return stackPanel;
        }

        private void SetPlaceholderText(TextBox textBox, string placeholder)
        {
            textBox.GotFocus += (s, e) =>
            {
                if (textBox.Text == placeholder)
                {
                    textBox.Text = "";
                    textBox.Foreground = Brushes.Black;
                }
            };

            textBox.LostFocus += (s, e) =>
            {
                if (string.IsNullOrWhiteSpace(textBox.Text))
                {
                    textBox.Text = placeholder;
                    textBox.Foreground = Brushes.Gray;
                }
            };
        }

        private void LoadTasks(Project project, ListView taskListView, ListView completedTaskListView)
        {
            Debug.WriteLine("LoadTasks: Loading tasks");

            taskListView?.Items.Clear();
            completedTaskListView?.Items.Clear();
            project.Tasks.Clear();
            project.CompletedTasks.Clear();

            try
            {
                if (File.Exists(project.TaskFilePath))
                {
                    using (var reader = new StreamReader(project.TaskFilePath))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            var parts = line.Split(',');
                            if (parts.Length >= 3)
                            {
                                var task = new TaskItem
                                {
                                    TaskName = parts[0],
                                    StartDate = parts[1],
                                    EndDate = parts[2]
                                };
                                if (!project.Tasks.Any(t => t.TaskName == task.TaskName && t.StartDate == task.StartDate && t.EndDate == task.EndDate))
                                {
                                    project.Tasks.Add(task);
                                    taskListView?.Items.Add(task);
                                    Debug.WriteLine($"LoadTasks: Task added - {task.TaskName}");
                                }
                            }
                        }
                    }
                }

                if (File.Exists(project.CompletedTaskFilePath))
                {
                    using (var reader = new StreamReader(project.CompletedTaskFilePath))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            var parts = line.Split(',');
                            if (parts.Length >= 4)
                            {
                                var task = new TaskItem
                                {
                                    TaskName = parts[0],
                                    StartDate = parts[1],
                                    EndDate = parts[2],
                                    CompletionDate = parts[3],
                                    Review = parts.Length > 4 ? parts[4] : string.Empty
                                };
                                if (!project.CompletedTasks.Any(t => t.TaskName == task.TaskName && t.StartDate == task.StartDate && t.EndDate == task.EndDate && t.CompletionDate == task.CompletionDate))
                                {
                                    project.CompletedTasks.Add(task);
                                    completedTaskListView?.Items.Add(task);
                                    Debug.WriteLine($"LoadTasks: Completed task added - {task.TaskName}");
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"LoadTasks: Error loading tasks - {ex.Message}");
                MessageBox.Show($"タスクの読み込み中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void AddTaskButton_Click(object sender, RoutedEventArgs e, Project project, TextBox taskNameTextBox, DatePicker startDatePicker, DatePicker endDatePicker, ListView taskListView)
        {
            string taskName = taskNameTextBox.Text;
            DateTime? startDate = startDatePicker.SelectedDate;
            DateTime? endDate = endDatePicker.SelectedDate;

            if (!string.IsNullOrEmpty(taskName) && taskName != TaskPlaceholder && startDate.HasValue && endDate.HasValue)
            {
                TaskItem newTask = new TaskItem { TaskName = taskName, StartDate = startDate.Value.ToShortDateString(), EndDate = endDate.Value.ToShortDateString() };
                if (!project.Tasks.Any(t => t.TaskName == newTask.TaskName && t.StartDate == newTask.StartDate && t.EndDate == newTask.EndDate))
                {
                    taskListView.Items.Add(newTask);
                    project.Tasks.Add(newTask); // プロジェクトにタスクを追加

                    SaveTasks(project); // タスクを保存
                    ClearTaskInputs(taskNameTextBox, startDatePicker, endDatePicker);

                    // アイデアタブのタスクリストビューを更新
                    var ideaTaskListView = GetIdeaTaskListView(project);
                    if (ideaTaskListView != null)
                    {
                        ideaTaskListView.Items.Add(newTask);
                    }
                }
            }
            else
            {
                MessageBox.Show("すべてのフィールドに入力してください。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }


        private void ClearTaskInputs(TextBox taskNameTextBox, DatePicker startDatePicker, DatePicker endDatePicker)
        {
            taskNameTextBox.Text = TaskPlaceholder;
            taskNameTextBox.Foreground = Brushes.Gray;
            startDatePicker.SelectedDate = null;
            endDatePicker.SelectedDate = null;
        }

        private void ProjectTabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (ProjectTabControl.SelectedItem is TabItem selectedTab)
            {
                var projectName = selectedTab.Header.ToString();
                var selectedProject = projects.FirstOrDefault(p => p.ProjectName == projectName);

                if (selectedProject != null)
                {
                    // タブ内のタスクリストビューと完了タスクリストビューを取得してリロード
                    RefreshTaskListView(selectedProject);
                }
            }
        }

        private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            foreach (var project in projects)
            {
                SaveTasks(project);
                SaveSchedules(project);
            }
        }

        private void SaveTasks(Project project)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project), "プロジェクトがnullです。");
            }

            if (string.IsNullOrEmpty(project.TaskFilePath))
            {
                throw new ArgumentNullException(nameof(project.TaskFilePath), "タスクファイルパスが設定されていません。");
            }

            if (string.IsNullOrEmpty(project.CompletedTaskFilePath))
            {
                throw new ArgumentNullException(nameof(project.CompletedTaskFilePath), "完了タスクファイルパスが設定されていません。");
            }

            try
            {
                // タスクファイルの書き込み
                using (StreamWriter writer = new StreamWriter(project.TaskFilePath, false)) // 'false'で上書きモード
                {
                    foreach (TaskItem item in project.Tasks)
                    {
                        writer.WriteLine($"{item.TaskName},{item.StartDate},{item.EndDate}");
                    }
                }

                // 完了タスクファイルの書き込み
                using (StreamWriter writer = new StreamWriter(project.CompletedTaskFilePath, false)) // 'false'で上書きモード
                {
                    foreach (TaskItem item in project.CompletedTasks)
                    {
                        writer.WriteLine($"{item.TaskName},{item.StartDate},{item.EndDate},{item.CompletionDate},{item.Review}");
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"タスクの保存中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void AddProjectButton_Click(object sender, RoutedEventArgs e)
        {
            var projectName = PromptForProjectName("新しいプロジェクト名を入力してください");
            if (!string.IsNullOrEmpty(projectName))
            {
                string projectDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, projectName);
                Directory.CreateDirectory(projectDirectory);

                var newProject = new Project
                {
                    ProjectName = projectName,
                    TaskFilePath = Path.Combine(projectDirectory, "tasks.csv"),
                    CompletedTaskFilePath = Path.Combine(projectDirectory, "completed_tasks.csv"),
                    ScheduleFilePath = Path.Combine(projectDirectory, "schedules.csv"),
                    ReferenceFilePath = Path.Combine(projectDirectory, "references.csv"),
                    IdeaFilePath = Path.Combine(projectDirectory, "ideas.csv")
                };

                CreateFileIfNotExists(newProject.TaskFilePath);
                CreateFileIfNotExists(newProject.CompletedTaskFilePath);
                CreateFileIfNotExists(newProject.ScheduleFilePath);
                CreateFileIfNotExists(newProject.ReferenceFilePath);
                CreateFileIfNotExists(newProject.IdeaFilePath);

                projects.Add(newProject);
                InitializeProjectTabs();
                ProjectManager.SaveProjects(projects);
            }
        }

        private void EditProjectButton_Click(object sender, RoutedEventArgs e)
        {
            var selectProjectDialog = new SelectProjectDialog(projects);
            if (selectProjectDialog.ShowDialog() == true)
            {
                var projectName = selectProjectDialog.SelectedProjectName;
                var project = projects.FirstOrDefault(p => p.ProjectName == projectName);
                if (project != null)
                {
                    var newProjectName = PromptForProjectName("新しいプロジェクト名を入力してください", project.ProjectName);
                    if (!string.IsNullOrEmpty(newProjectName) && newProjectName != project.ProjectName)
                    {
                        string newProjectDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, newProjectName);
                        Directory.CreateDirectory(newProjectDirectory);

                        project.ProjectName = newProjectName;
                        project.TaskFilePath = Path.Combine(newProjectDirectory, "tasks.csv");
                        project.CompletedTaskFilePath = Path.Combine(newProjectDirectory, "completed_tasks.csv");
                        project.ScheduleFilePath = Path.Combine(newProjectDirectory, "schedules.csv");
                        project.ReferenceFilePath = Path.Combine(newProjectDirectory, "references.csv");
                        project.IdeaFilePath = Path.Combine(newProjectDirectory, "ideas.csv");

                        CreateFileIfNotExists(project.TaskFilePath);
                        CreateFileIfNotExists(project.CompletedTaskFilePath);
                        CreateFileIfNotExists(project.ScheduleFilePath);
                        CreateFileIfNotExists(project.ReferenceFilePath);
                        CreateFileIfNotExists(project.IdeaFilePath);

                        InitializeProjectTabs();
                        ProjectManager.SaveProjects(projects);
                    }
                }
            }
        }


        private void CreateFileIfNotExists(string filePath)
        {
            if (!File.Exists(filePath))
            {
                File.Create(filePath).Dispose();
            }
        }


        private void DeleteProjectButton_Click(object sender, RoutedEventArgs e)
        {
            var selectProjectDialog = new SelectProjectDialog(projects);
            if (selectProjectDialog.ShowDialog() == true)
            {
                var projectName = selectProjectDialog.SelectedProjectName;
                var project = projects.FirstOrDefault(p => p.ProjectName == projectName);
                if (project != null)
                {
                    projects.Remove(project);
                    InitializeProjectTabs();
                    ProjectManager.SaveProjects(projects);
                    File.Delete(project.TaskFilePath);
                    File.Delete(project.CompletedTaskFilePath);
                }
            }
        }

        private string PromptForProjectName(string message, string defaultName = "")
        {
            var inputDialog = new InputDialog(message, defaultName);
            if (inputDialog.ShowDialog() == true)
            {
                return inputDialog.ResponseText;
            }
            return null;
        }

        private void EditTask(TaskItem task)
        {
            var originalTaskName = task.TaskName;
            var originalStartDate = task.StartDate;
            var originalEndDate = task.EndDate;

            var editDialog = new EditTaskDialog(task);
            if (editDialog.ShowDialog() == true)
            {
                var project = projects.FirstOrDefault(p =>
                    p.Tasks.Any(t => t.TaskName == originalTaskName && t.StartDate == originalStartDate && t.EndDate == originalEndDate) ||
                    p.CompletedTasks.Any(t => t.TaskName == originalTaskName && t.StartDate == originalStartDate && t.EndDate == originalEndDate)
                );

                if (project != null)
                {
                    var existingTask = project.Tasks.FirstOrDefault(t => t.TaskName == originalTaskName && t.StartDate == originalStartDate && t.EndDate == originalEndDate);
                    if (existingTask != null)
                    {
                        existingTask.TaskName = editDialog.TaskName;
                        existingTask.StartDate = editDialog.StartDate;
                        existingTask.EndDate = editDialog.EndDate;
                    }
                    else
                    {
                        var existingCompletedTask = project.CompletedTasks.FirstOrDefault(t => t.TaskName == originalTaskName && t.StartDate == originalStartDate && t.EndDate == originalEndDate);
                        if (existingCompletedTask != null)
                        {
                            existingCompletedTask.TaskName = editDialog.TaskName;
                            existingCompletedTask.StartDate = editDialog.StartDate;
                            existingCompletedTask.EndDate = editDialog.EndDate;
                        }
                    }

                    SaveTasks(project);
                    InitializeProjectTabs();
                    RefreshTaskListView(project);

                    // アイデアタブのタスクリストを更新
                    var ideaTaskListView = GetIdeaTaskListView(project);
                    if (ideaTaskListView != null)
                    {
                        RefreshIdeaTaskListView(ideaTaskListView, project);
                    }
                }
                else
                {
                    MessageBox.Show("プロジェクトが見つかりませんでした。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
        }


        private void CompleteTask(TaskItem task, Project project)
        {
            var taskToRemove = project.Tasks.FirstOrDefault(t => t.TaskName == task.TaskName && t.StartDate == task.StartDate && t.EndDate == task.EndDate);
            if (taskToRemove != null)
            {
                project.Tasks.Remove(taskToRemove);
                task.CompletionDate = DateTime.Now.ToShortDateString();
                project.CompletedTasks.Add(task);
                SaveTasks(project);
                RefreshTaskListView(project);

                // アイデアタブのタスクリストを更新
                var ideaTaskListView = GetIdeaTaskListView(project);
                if (ideaTaskListView != null)
                {
                    RefreshIdeaTaskListView(ideaTaskListView, project);
                }
            }
            else
            {
                MessageBox.Show("タスクが見つかりませんでした。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }


        private void RefreshTaskListView(Project project)
        {
            var selectedTab = ProjectTabControl.Items
                .Cast<TabItem>()
                .FirstOrDefault(tab => (tab.Header as string) == project.ProjectName);

            if (selectedTab != null)
            {
                var projectTabControl = selectedTab.Content as TabControl;
                if (projectTabControl != null)
                {
                    var taskTab = projectTabControl.Items
                        .Cast<TabItem>()
                        .FirstOrDefault(tab => tab.Header as string == "タスク管理");

                    if (taskTab != null)
                    {
                        var taskListView = (taskTab.Content as StackPanel)?.Children
                            .OfType<ListView>()
                            .FirstOrDefault();

                        if (taskListView != null)
                        {
                            taskListView.Items.Clear();
                            foreach (var task in project.Tasks)
                            {
                                taskListView.Items.Add(task);
                            }
                        }
                        else
                        {
                            MessageBox.Show("タスクリストビューが見つかりませんでした。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
                        }

                        // 完了タスクリストビューの更新
                        var feedbackTab = projectTabControl.Items
                            .Cast<TabItem>()
                            .FirstOrDefault(tab => tab.Header as string == "フィードバック");

                        if (feedbackTab != null)
                        {
                            var completedTaskListView = (feedbackTab.Content as StackPanel)?.Children
                                .OfType<ListView>()
                                .FirstOrDefault();

                            if (completedTaskListView != null)
                            {
                                completedTaskListView.Items.Clear();
                                foreach (var completedTask in project.CompletedTasks)
                                {
                                    completedTaskListView.Items.Add(completedTask);
                                }
                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("タスク管理タブが見つかりませんでした。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
                    }
                }
                else
                {
                    MessageBox.Show("プロジェクトタブコントロールが見つかりませんでした。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
                }
            }
            else
            {
                MessageBox.Show("選択されたプロジェクトタブが見つかりませんでした。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void WriteReview(TaskItem task, Project project)
        {
            var reviewDialog = new ReviewDialog(task);
            if (reviewDialog.ShowDialog() == true)
            {
                // レビューを更新
                task.Review = reviewDialog.ReviewText;

                // レビューが正しく設定されたことを確認
                Debug.WriteLine($"Review written: {task.Review}");

                // プロジェクトの完了タスクリストを更新
                var existingTask = project.CompletedTasks.FirstOrDefault(t => t.TaskName == task.TaskName && t.StartDate == task.StartDate && t.EndDate == task.EndDate);
                if (existingTask != null)
                {
                    existingTask.Review = task.Review;
                    Debug.WriteLine($"Updated task in project.CompletedTasks: {existingTask.TaskName}, Review: {existingTask.Review}");
                }

                // タスクの保存を行う
                SaveTasks(project);

                // 完了タスクビューをリフレッシュ
                RefreshTaskListView(project);
            }
        }

        private StackPanel CreateIdeaContent(Project project)
        {
            var ideaTaskListView = new ListView { Height = 200, SelectionMode = SelectionMode.Single };

            var dataTemplate = new DataTemplate(typeof(TaskItem));
            var stackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
            stackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);

            var taskNameTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            taskNameTextFactory.SetBinding(TextBlock.TextProperty, new Binding("TaskName"));
            taskNameTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(taskNameTextFactory);

            var startDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            startDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("StartDate"));
            startDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(startDateTextFactory);

            var endDateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            endDateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("EndDate"));
            endDateTextFactory.SetValue(TextBlock.MarginProperty, new Thickness(5, 0, 5, 0));
            stackPanelFactory.AppendChild(endDateTextFactory);

            dataTemplate.VisualTree = stackPanelFactory;
            ideaTaskListView.ItemTemplate = dataTemplate;

            var ideaTextBox = new TextBox { Text = "アイデアを入力してね♡", Foreground = Brushes.Gray };
            SetPlaceholderText(ideaTextBox, "アイデアを入力してね♡");

            var saveIdeaButton = new Button { Content = "アイデアを保存してね♡" };
            saveIdeaButton.Click += (s, e) => SaveIdeaButton_Click(s, e, project, ideaTaskListView, ideaTextBox);

            var ideaListView = new ListView { Height = 200, SelectionMode = SelectionMode.Single };

            var ideaDataTemplate = new DataTemplate(typeof(IdeaItem));
            var ideaStackPanelFactory = new FrameworkElementFactory(typeof(StackPanel));
            ideaStackPanelFactory.SetValue(StackPanel.OrientationProperty, Orientation.Vertical);

            var taskTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            taskTextFactory.SetBinding(TextBlock.TextProperty, new Binding("TaskName"));
            ideaStackPanelFactory.AppendChild(taskTextFactory);

            var ideaTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            ideaTextFactory.SetBinding(TextBlock.TextProperty, new Binding("Idea"));
            ideaStackPanelFactory.AppendChild(ideaTextFactory);

            var dateTextFactory = new FrameworkElementFactory(typeof(TextBlock));
            dateTextFactory.SetBinding(TextBlock.TextProperty, new Binding("Date"));
            ideaStackPanelFactory.AppendChild(dateTextFactory);

            ideaDataTemplate.VisualTree = ideaStackPanelFactory;
            ideaListView.ItemTemplate = ideaDataTemplate;

            var stackPanel = new StackPanel();
            stackPanel.Children.Add(ideaTaskListView);
            stackPanel.Children.Add(ideaTextBox);
            stackPanel.Children.Add(saveIdeaButton);
            stackPanel.Children.Add(ideaListView);

            LoadIdeas(project, ideaListView);

            return stackPanel;
        }
        private void SaveIdeaButton_Click(object sender, RoutedEventArgs e, Project project, ListView ideaTaskListView, TextBox ideaTextBox)
        {
            if (ideaTaskListView.SelectedItem is TaskItem selectedTask && !string.IsNullOrWhiteSpace(ideaTextBox.Text) && ideaTextBox.Text != "アイデアを入力してね♡")
            {
                var newIdea = new IdeaItem
                {
                    TaskName = selectedTask.TaskName,
                    Idea = ideaTextBox.Text,
                    Date = DateTime.Now.ToShortDateString()
                };

                project.Ideas.Add(newIdea);
                SaveIdeas(project);
                ideaTextBox.Text = "アイデアを入力してね♡";
                ideaTextBox.Foreground = Brushes.Gray;
                LoadIdeas(project, (ideaTaskListView.Parent as StackPanel)?.Children.OfType<ListView>().Last());
            }
            else
            {
                MessageBox.Show("タスクを選択し、アイデアを入力してください。", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
        private void LoadIdeas(Project project, ListView ideaListView)
        {
            ideaListView.Items.Clear();
            project.Ideas.Clear();

            try
            {
                if (File.Exists(project.IdeaFilePath))
                {
                    using (StreamReader reader = new StreamReader(project.IdeaFilePath))
                    {
                        string line;
                        while ((line = reader.ReadLine()) != null)
                        {
                            var data = line.Split(',');
                            IdeaItem idea = new IdeaItem { TaskName = data[0], Idea = data[1], Date = data[2] };
                            project.Ideas.Add(idea);
                            ideaListView.Items.Add(idea);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"アイデアの読み込み中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private void SaveIdeas(Project project)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project), "プロジェクトがnullです。");
            }

            if (string.IsNullOrEmpty(project.IdeaFilePath))
            {
                project.IdeaFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{project.ProjectName}_ideas.csv");
            }

            try
            {
                using (StreamWriter writer = new StreamWriter(project.IdeaFilePath, false))
                {
                    foreach (IdeaItem item in project.Ideas)
                    {
                        writer.WriteLine($"{item.TaskName},{item.Idea},{item.Date}");
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"アイデアの保存中にエラーが発生しました: {ex.Message}", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        private ListView GetIdeaTaskListView(Project project)
        {
            var selectedTab = ProjectTabControl.Items
                .Cast<TabItem>()
                .FirstOrDefault(tab => (tab.Header as string) == project.ProjectName);

            if (selectedTab != null)
            {
                var projectTabControl = selectedTab.Content as TabControl;
                if (projectTabControl != null)
                {
                    var ideaTab = projectTabControl.Items
                        .Cast<TabItem>()
                        .FirstOrDefault(tab => tab.Header as string == "アイデア");

                    if (ideaTab != null)
                    {
                        var ideaTaskListView = (ideaTab.Content as StackPanel)?.Children
                            .OfType<ListView>()
                            .FirstOrDefault();
                        return ideaTaskListView;
                    }
                }
            }
            return null;
        }

        private void RefreshIdeaTaskListView(ListView ideaTaskListView, Project project)
        {
            ideaTaskListView.Items.Clear();
            foreach (var task in project.Tasks)
            {
                ideaTaskListView.Items.Add(task);
            }
        }


    }
}

```
```diff_xml:MainWindow.xaml
<Window x:Class="task_manager_for_research.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
        Title="きゅん" Height="700" Width="800"
        Background="White"
        Closing="MainWindow_Closing">

    <Window.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="LightBlue" SecondaryColor="Lime" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

    <Grid>
        <StackPanel Margin="10">
            <TextBlock Text="😸プロジェクト進捗管理😸" FontSize="20" FontWeight="Bold" Margin="0,0,0,20" FontFamily="HGP創英角ポップ体"/>
            <StackPanel Orientation="Horizontal" Margin="0,0,0,10">
                <Button Content="プロジェクト追加" Click="AddProjectButton_Click" Style="{StaticResource MaterialDesignRaisedButton}" Margin="0,0,10,0"/>
                <Button Content="プロジェクト編集" Click="EditProjectButton_Click" Style="{StaticResource MaterialDesignRaisedButton}" Margin="0,0,10,0"/>
                <Button Content="プロジェクト削除" Click="DeleteProjectButton_Click" Style="{StaticResource MaterialDesignRaisedButton}" Margin="0,0,10,0"/>
                <Button Content="未提出課題一覧" Click="FetchTasksButton_Click" Style="{StaticResource MaterialDesignRaisedButton}"/>
            </StackPanel>
        </StackPanel>

        <TabControl Name="ProjectTabControl" Margin="10,100,10,10" SelectionChanged="ProjectTabControl_SelectionChanged">
            <!-- 動的にタブを追加する -->
        </TabControl>
    </Grid>
</Window>

```

```diff_xml:App.xaml
<Application x:Class="task_manager_for_research.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="OnStartup">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
                <ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesign3.Defaults.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

```
UIやWebページのクローラーなどのインストールはツール->NuGet パッケージマネージャーから行うことができます。

:::note warn
MaterialDesignThemesはアップグレードに伴って参照先が若干変わっています。
:::