diff --git a/HaCreator/CustomControls/MapBrowser.cs b/HaCreator/CustomControls/MapBrowser.cs index 55bb34a7..921ad7d2 100644 --- a/HaCreator/CustomControls/MapBrowser.cs +++ b/HaCreator/CustomControls/MapBrowser.cs @@ -18,6 +18,7 @@ using HaSharedLibrary.Wz; using MapleLib.WzLib.WzStructure; using System.Data.SQLite; +using HaCreator.GUI.InstanceEditor; namespace HaCreator.CustomControls { @@ -32,6 +33,13 @@ public partial class MapBrowser : UserControl private bool _bTownOnlyFilter = false; private bool _bIsHistoryMapBrowser = false; + private LoadSearchHelper _search; + public LoadSearchHelper Search + { + get { return _search; } + private set { } + } + /// /// Constructor /// @@ -39,6 +47,8 @@ public MapBrowser() { InitializeComponent(); + this._search = new LoadSearchHelper(mapNamesBox, maps); + this.minimapBox.SizeMode = PictureBoxSizeMode.Zoom; } @@ -232,114 +242,6 @@ public void ClearLoadedMapHistory() { #endregion #region UI - private string _previousSeachText = string.Empty; - private CancellationTokenSource _existingSearchTaskToken = null; - /// - /// On search box text changed - /// - /// - /// May be null - public void searchBox_TextChanged(object sender, EventArgs e) - { - TextBox searchBox = (TextBox)sender; - string searchText = searchBox.Text.ToLower(); - - if (_previousSeachText == searchText) - return; - _previousSeachText = searchText; // set - - // start searching - searchMapsInternal(searchText); - } - - /// - /// Search and filters map according to the user's query - /// - /// - public void searchMapsInternal(string searchText) { - if (!_bMapsLoaded) - return; - - // Cancel existing task if any - if (_existingSearchTaskToken != null && !_existingSearchTaskToken.IsCancellationRequested) { - _existingSearchTaskToken.Cancel(); - } - - // Clear - mapNamesBox.Items.Clear(); - if (searchText == string.Empty) { - var filteredItems = maps.Where(kvp => { - MapInfo mapInfo = null; - if (mapsMapInfo.ContainsKey(kvp)) { - mapInfo = mapsMapInfo[kvp].Item2; - - if (this._bTownOnlyFilter) { - if (!mapInfo.town) - return false; - } - } - return true; - }).Select(kvp => kvp) // or kvp.Value or any transformation you need - .Cast() - .ToArray(); - - - mapNamesBox.Items.AddRange(filteredItems); - - mapNamesBox_SelectedIndexChanged(null, null); - } - else { - - Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher; - - // new task - _existingSearchTaskToken = new CancellationTokenSource(); - var cancellationToken = _existingSearchTaskToken.Token; - - Task t = Task.Run(() => { - Thread.Sleep(500); // average key typing speed - - List mapsFiltered = new List(); - foreach (string map in maps) { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - MapInfo mapInfo = null; - if (mapsMapInfo.ContainsKey(map)) { - mapInfo = mapsMapInfo[map].Item2; - } - - // Filter by string first - if (map.ToLower().Contains(searchText)) { - - // Filter again by 'town' if mapInfo is not null. - if (mapInfo != null) { - if (this._bTownOnlyFilter) { - if (!mapInfo.town) - continue; - } - } - mapsFiltered.Add(map); - } - } - - currentDispatcher.BeginInvoke(new Action(() => { - foreach (string map in mapsFiltered) { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - mapNamesBox.Items.Add(map); - } - - if (mapNamesBox.Items.Count > 0) { - mapNamesBox.SelectedIndex = 0; // set default selection to reduce clicks - } - })); - }, cancellationToken); - - } - } - /// /// On map selection changed /// diff --git a/HaCreator/GUI/InstanceEditor/LoadItemSelector.Designer.cs b/HaCreator/GUI/InstanceEditor/LoadItemSelector.Designer.cs index aa5afb7b..78645061 100644 --- a/HaCreator/GUI/InstanceEditor/LoadItemSelector.Designer.cs +++ b/HaCreator/GUI/InstanceEditor/LoadItemSelector.Designer.cs @@ -138,7 +138,6 @@ private void InitializeComponent() this.searchBox.Text = "Type here to search"; this.searchBox.WatermarkActive = true; this.searchBox.WatermarkText = "Type here"; - this.searchBox.TextChanged += new System.EventHandler(this.searchBox_TextChanged); // // LoadItemSelector // diff --git a/HaCreator/GUI/InstanceEditor/LoadItemSelector.cs b/HaCreator/GUI/InstanceEditor/LoadItemSelector.cs index e9dcfed9..82075cb4 100644 --- a/HaCreator/GUI/InstanceEditor/LoadItemSelector.cs +++ b/HaCreator/GUI/InstanceEditor/LoadItemSelector.cs @@ -71,6 +71,9 @@ public LoadItemSelector(int filterItemId) { InitializeComponent(); + LoadSearchHelper searchBox = new LoadSearchHelper(listBox_itemList, itemNames); + this.searchBox.TextChanged += searchBox.TextChanged; + this.FormClosing += LoadQuestSelector_FormClosing; this._filterItemId = filterItemId; @@ -159,105 +162,6 @@ private void LoadQuestSelector_FormClosing(object sender, FormClosingEventArgs e } #endregion - #region Search box - private string _previousSeachText = string.Empty; - private CancellationTokenSource _existingSearchTaskToken = null; - - /// - /// Searchbox text changed - /// - /// - /// - private void searchBox_TextChanged(object sender, EventArgs e) - { - TextBox searchBox = (TextBox)sender; - string searchText = searchBox.Text.ToLower(); - - if (_previousSeachText == searchText) - return; - _previousSeachText = searchText; // set - - // start searching - searchItemInternal(searchText); - } - - /// - /// Search and filters map according to the user's query - /// - /// - public void searchItemInternal(string searchText) - { - if (!_bItemsLoaded) - return; - - // Cancel existing task if any - if (_existingSearchTaskToken != null && !_existingSearchTaskToken.IsCancellationRequested) - { - _existingSearchTaskToken.Cancel(); - } - - // Clear - listBox_itemList.Items.Clear(); - if (searchText == string.Empty) - { - var filteredItems = itemNames.Where(kvp => - { - return true; - }).Select(kvp => kvp) // or kvp.Value or any transformation you need - .Cast() - .ToArray(); - - listBox_itemList.Items.AddRange(filteredItems); - - listBox_itemList_SelectedIndexChanged(null, null); - } - else - { - - Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher; - - // new task - _existingSearchTaskToken = new CancellationTokenSource(); - var cancellationToken = _existingSearchTaskToken.Token; - - Task t = Task.Run(() => - { - Thread.Sleep(500); // average key typing speed - - List itemsFiltered = new List(); - foreach (string map in itemNames) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - // Filter by string first - if (map.ToLower().Contains(searchText)) - { - itemsFiltered.Add(map); - } - } - - currentDispatcher.BeginInvoke(new Action(() => - { - foreach (string map in itemsFiltered) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - listBox_itemList.Items.Add(map); - } - - if (listBox_itemList.Items.Count > 0) - { - listBox_itemList.SelectedIndex = 0; // set default selection to reduce clicks - } - })); - }, cancellationToken); - - } - } - #endregion - /// /// On list box selection changed /// diff --git a/HaCreator/GUI/InstanceEditor/LoadJobSelector.Designer.cs b/HaCreator/GUI/InstanceEditor/LoadJobSelector.Designer.cs index c48d6a96..6d51e776 100644 --- a/HaCreator/GUI/InstanceEditor/LoadJobSelector.Designer.cs +++ b/HaCreator/GUI/InstanceEditor/LoadJobSelector.Designer.cs @@ -138,7 +138,6 @@ private void InitializeComponent() this.searchBox.Text = "Type here to search"; this.searchBox.WatermarkActive = true; this.searchBox.WatermarkText = "Type here"; - this.searchBox.TextChanged += new System.EventHandler(this.searchBox_TextChanged); // // LoadJobSelector // diff --git a/HaCreator/GUI/InstanceEditor/LoadJobSelector.cs b/HaCreator/GUI/InstanceEditor/LoadJobSelector.cs index 670aa79c..444b8e39 100644 --- a/HaCreator/GUI/InstanceEditor/LoadJobSelector.cs +++ b/HaCreator/GUI/InstanceEditor/LoadJobSelector.cs @@ -67,6 +67,9 @@ public LoadJobSelector() { InitializeComponent(); + LoadSearchHelper searchBox = new LoadSearchHelper(listBox_npcList, jobNames); + this.searchBox.TextChanged += searchBox.TextChanged; + this.FormClosing += LoadQuestSelector_FormClosing; // load items @@ -131,105 +134,6 @@ private void LoadQuestSelector_FormClosing(object sender, FormClosingEventArgs e } #endregion - #region Search box - private string _previousSeachText = string.Empty; - private CancellationTokenSource _existingSearchTaskToken = null; - - /// - /// Searchbox text changed - /// - /// - /// - private void searchBox_TextChanged(object sender, EventArgs e) - { - TextBox searchBox = (TextBox)sender; - string searchText = searchBox.Text.ToLower(); - - if (_previousSeachText == searchText) - return; - _previousSeachText = searchText; // set - - // start searching - searchItemInternal(searchText); - } - - /// - /// Search and filters map according to the user's query - /// - /// - public void searchItemInternal(string searchText) - { - if (!_bJobsLoaded) - return; - - // Cancel existing task if any - if (_existingSearchTaskToken != null && !_existingSearchTaskToken.IsCancellationRequested) - { - _existingSearchTaskToken.Cancel(); - } - - // Clear - listBox_npcList.Items.Clear(); - if (searchText == string.Empty) - { - var filteredItems = jobNames.Where(kvp => - { - return true; - }).Select(kvp => kvp) // or kvp.Value or any transformation you need - .Cast() - .ToArray(); - - listBox_npcList.Items.AddRange(filteredItems); - - listBox_itemList_SelectedIndexChanged(null, null); - } - else - { - - Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher; - - // new task - _existingSearchTaskToken = new CancellationTokenSource(); - var cancellationToken = _existingSearchTaskToken.Token; - - Task t = Task.Run(() => - { - Thread.Sleep(500); // average key typing speed - - List itemsFiltered = new List(); - foreach (string map in jobNames) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - // Filter by string first - if (map.ToLower().Contains(searchText)) - { - itemsFiltered.Add(map); - } - } - - currentDispatcher.BeginInvoke(new Action(() => - { - foreach (string map in itemsFiltered) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - listBox_npcList.Items.Add(map); - } - - if (listBox_npcList.Items.Count > 0) - { - listBox_npcList.SelectedIndex = 0; // set default selection to reduce clicks - } - })); - }, cancellationToken); - - } - } - #endregion - /// /// On list box selection changed /// diff --git a/HaCreator/GUI/InstanceEditor/LoadMapSelector.cs b/HaCreator/GUI/InstanceEditor/LoadMapSelector.cs index 6e56dfff..fc33cb43 100644 --- a/HaCreator/GUI/InstanceEditor/LoadMapSelector.cs +++ b/HaCreator/GUI/InstanceEditor/LoadMapSelector.cs @@ -32,7 +32,7 @@ public LoadMapSelector() DialogResult = DialogResult.Cancel; - this.searchBox.TextChanged += this.mapBrowser.searchBox_TextChanged; + this.searchBox.TextChanged += this.mapBrowser.Search.TextChanged; } /// @@ -47,7 +47,7 @@ public LoadMapSelector(NumericUpDown numericUpDown) this.numericUpDown = numericUpDown; - this.searchBox.TextChanged += this.mapBrowser.searchBox_TextChanged; + this.searchBox.TextChanged += this.mapBrowser.Search.TextChanged; } /// @@ -60,7 +60,7 @@ public LoadMapSelector(TextBox textbox) { DialogResult = DialogResult.Cancel; this.textBox = textbox; - this.searchBox.TextChanged += this.mapBrowser.searchBox_TextChanged; + this.searchBox.TextChanged += this.mapBrowser.Search.TextChanged; } /// diff --git a/HaCreator/GUI/InstanceEditor/LoadNpcSelector.Designer.cs b/HaCreator/GUI/InstanceEditor/LoadNpcSelector.Designer.cs index 186d05c7..cffaddbc 100644 --- a/HaCreator/GUI/InstanceEditor/LoadNpcSelector.Designer.cs +++ b/HaCreator/GUI/InstanceEditor/LoadNpcSelector.Designer.cs @@ -138,7 +138,6 @@ private void InitializeComponent() this.searchBox.Text = "Type here to search"; this.searchBox.WatermarkActive = true; this.searchBox.WatermarkText = "Type here"; - this.searchBox.TextChanged += new System.EventHandler(this.searchBox_TextChanged); // // LoadNpcSelector // diff --git a/HaCreator/GUI/InstanceEditor/LoadNpcSelector.cs b/HaCreator/GUI/InstanceEditor/LoadNpcSelector.cs index 52d26c54..bf5ea597 100644 --- a/HaCreator/GUI/InstanceEditor/LoadNpcSelector.cs +++ b/HaCreator/GUI/InstanceEditor/LoadNpcSelector.cs @@ -65,6 +65,9 @@ public LoadNpcSelector() { InitializeComponent(); + LoadSearchHelper searchBox = new LoadSearchHelper(listBox_npcList, itemNames); + this.searchBox.TextChanged += searchBox.TextChanged; + this.FormClosing += LoadQuestSelector_FormClosing; // load items @@ -129,105 +132,6 @@ private void LoadQuestSelector_FormClosing(object sender, FormClosingEventArgs e } #endregion - #region Search box - private string _previousSeachText = string.Empty; - private CancellationTokenSource _existingSearchTaskToken = null; - - /// - /// Searchbox text changed - /// - /// - /// - private void searchBox_TextChanged(object sender, EventArgs e) - { - TextBox searchBox = (TextBox)sender; - string searchText = searchBox.Text.ToLower(); - - if (_previousSeachText == searchText) - return; - _previousSeachText = searchText; // set - - // start searching - searchItemInternal(searchText); - } - - /// - /// Search and filters map according to the user's query - /// - /// - public void searchItemInternal(string searchText) - { - if (!_bItemsLoaded) - return; - - // Cancel existing task if any - if (_existingSearchTaskToken != null && !_existingSearchTaskToken.IsCancellationRequested) - { - _existingSearchTaskToken.Cancel(); - } - - // Clear - listBox_npcList.Items.Clear(); - if (searchText == string.Empty) - { - var filteredItems = itemNames.Where(kvp => - { - return true; - }).Select(kvp => kvp) // or kvp.Value or any transformation you need - .Cast() - .ToArray(); - - listBox_npcList.Items.AddRange(filteredItems); - - listBox_itemList_SelectedIndexChanged(null, null); - } - else - { - - Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher; - - // new task - _existingSearchTaskToken = new CancellationTokenSource(); - var cancellationToken = _existingSearchTaskToken.Token; - - Task t = Task.Run(() => - { - Thread.Sleep(500); // average key typing speed - - List itemsFiltered = new List(); - foreach (string map in itemNames) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - // Filter by string first - if (map.ToLower().Contains(searchText)) - { - itemsFiltered.Add(map); - } - } - - currentDispatcher.BeginInvoke(new Action(() => - { - foreach (string map in itemsFiltered) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - listBox_npcList.Items.Add(map); - } - - if (listBox_npcList.Items.Count > 0) - { - listBox_npcList.SelectedIndex = 0; // set default selection to reduce clicks - } - })); - }, cancellationToken); - - } - } - #endregion - /// /// On list box selection changed /// diff --git a/HaCreator/GUI/InstanceEditor/LoadQuestSelector.Designer.cs b/HaCreator/GUI/InstanceEditor/LoadQuestSelector.Designer.cs index 043c30a6..b9d5f5df 100644 --- a/HaCreator/GUI/InstanceEditor/LoadQuestSelector.Designer.cs +++ b/HaCreator/GUI/InstanceEditor/LoadQuestSelector.Designer.cs @@ -157,7 +157,6 @@ private void InitializeComponent() this.searchBox.Text = "Type here to search"; this.searchBox.WatermarkActive = true; this.searchBox.WatermarkText = "Type here"; - this.searchBox.TextChanged += new System.EventHandler(this.searchBox_TextChanged); // // LoadQuestSelector // diff --git a/HaCreator/GUI/InstanceEditor/LoadQuestSelector.cs b/HaCreator/GUI/InstanceEditor/LoadQuestSelector.cs index ce9c0fa2..24e80b2b 100644 --- a/HaCreator/GUI/InstanceEditor/LoadQuestSelector.cs +++ b/HaCreator/GUI/InstanceEditor/LoadQuestSelector.cs @@ -66,6 +66,9 @@ public LoadQuestSelector() { InitializeComponent(); + LoadSearchHelper searchBox = new LoadSearchHelper(listBox_npcList, questNames); + this.searchBox.TextChanged += searchBox.TextChanged; + this.FormClosing += LoadQuestSelector_FormClosing; // load items @@ -133,105 +136,6 @@ private void LoadQuestSelector_FormClosing(object sender, FormClosingEventArgs e } #endregion - #region Search box - private string _previousSeachText = string.Empty; - private CancellationTokenSource _existingSearchTaskToken = null; - - /// - /// Searchbox text changed - /// - /// - /// - private void searchBox_TextChanged(object sender, EventArgs e) - { - TextBox searchBox = (TextBox)sender; - string searchText = searchBox.Text.ToLower(); - - if (_previousSeachText == searchText) - return; - _previousSeachText = searchText; // set - - // start searching - searchItemInternal(searchText); - } - - /// - /// Search and filters map according to the user's query - /// - /// - public void searchItemInternal(string searchText) - { - if (!_bItemsLoaded) - return; - - // Cancel existing task if any - if (_existingSearchTaskToken != null && !_existingSearchTaskToken.IsCancellationRequested) - { - _existingSearchTaskToken.Cancel(); - } - - // Clear - listBox_npcList.Items.Clear(); - if (searchText == string.Empty) - { - var filteredItems = questNames.Where(kvp => - { - return true; - }).Select(kvp => kvp) // or kvp.Value or any transformation you need - .Cast() - .ToArray(); - - listBox_npcList.Items.AddRange(filteredItems); - - listBox_itemList_SelectedIndexChanged(null, null); - } - else - { - - Dispatcher currentDispatcher = Dispatcher.CurrentDispatcher; - - // new task - _existingSearchTaskToken = new CancellationTokenSource(); - var cancellationToken = _existingSearchTaskToken.Token; - - Task t = Task.Run(() => - { - Thread.Sleep(500); // average key typing speed - - List itemsFiltered = new List(); - foreach (string map in questNames) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - // Filter by string first - if (map.ToLower().Contains(searchText)) - { - itemsFiltered.Add(map); - } - } - - currentDispatcher.BeginInvoke(new Action(() => - { - foreach (string map in itemsFiltered) - { - if (_existingSearchTaskToken.IsCancellationRequested) - return; // stop immediately - - listBox_npcList.Items.Add(map); - } - - if (listBox_npcList.Items.Count > 0) - { - listBox_npcList.SelectedIndex = 0; // set default selection to reduce clicks - } - })); - }, cancellationToken); - - } - } - #endregion - /// /// On list box selection changed /// diff --git a/HaCreator/GUI/InstanceEditor/LoadSearchHelper.cs b/HaCreator/GUI/InstanceEditor/LoadSearchHelper.cs new file mode 100644 index 00000000..766459db --- /dev/null +++ b/HaCreator/GUI/InstanceEditor/LoadSearchHelper.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using System.Threading; +using System.Windows.Threading; + +namespace HaCreator.GUI.InstanceEditor +{ + /// + /// Filters the ListBox selection according to the user's input in the TextBox + /// It cancels the prior task when the user types a new character before sorting and searching is completed. + /// + public class LoadSearchHelper + { + private string _previousSearchText = string.Empty; + private CancellationTokenSource _existingSearchTaskToken = null; + private bool _bItemsLoaded = false; + private readonly List _itemNames; + private readonly ListBox _listBox; + private readonly Dispatcher _dispatcher; + + /// + /// Constructor for the search helper + /// + /// + /// + public LoadSearchHelper(ListBox listBox, List itemNames) + { + _listBox = listBox; + _itemNames = itemNames; + _dispatcher = Dispatcher.CurrentDispatcher; + _bItemsLoaded = true; + } + + /// + /// On text change by the user + /// + /// + /// + public async void TextChanged(object sender, EventArgs e) + { + TextBox searchBox = (TextBox)sender; + string searchText = searchBox.Text.ToLower(); + if (_previousSearchText == searchText) + return; + _previousSearchText = searchText; + + // Cancel the existing task before starting a new one + if (_existingSearchTaskToken != null) + { + _existingSearchTaskToken.Cancel(); + _existingSearchTaskToken = null; + } + + await SearchItemInternal(searchText); + } + + private async Task SearchItemInternal(string searchText) + { + if (!_bItemsLoaded) + return; + + _listBox.Items.Clear(); + + if (string.IsNullOrEmpty(searchText)) + { + var filteredItems = _itemNames.Cast().ToArray(); + _listBox.Items.AddRange(filteredItems); + OnListBoxSelectionChanged(); + } + else + { + _existingSearchTaskToken = new CancellationTokenSource(); + var cancellationToken = _existingSearchTaskToken.Token; + + try + { + await Task.Delay(500, cancellationToken); // Delay for 500ms or until cancelled + + List itemsFiltered = _itemNames + .Where(item => item.ToLower().Contains(searchText)) + .ToList(); + + await _dispatcher.InvokeAsync(() => + { + foreach (string item in itemsFiltered) + { + if (cancellationToken.IsCancellationRequested) + return; + _listBox.Items.Add(item); + } + if (_listBox.Items.Count > 0) + { + _listBox.SelectedIndex = 0; + } + }, DispatcherPriority.Normal, cancellationToken); + } + catch (TaskCanceledException) + { + // Task was cancelled, do nothing + } + finally + { + _existingSearchTaskToken = null; + } + } + } + + private void OnListBoxSelectionChanged() + { + // Implement the logic for listBox_itemList_SelectedIndexChanged here + // or provide a way to set an external event handler + } + } +} \ No newline at end of file diff --git a/HaCreator/GUI/Load.cs b/HaCreator/GUI/Load.cs index ba35248f..15b9cc3a 100644 --- a/HaCreator/GUI/Load.cs +++ b/HaCreator/GUI/Load.cs @@ -50,7 +50,7 @@ public FieldSelector(MultiBoard board, System.Windows.Controls.TabControl Tabs, this._bAutoCloseUponSelection = bAutoCloseUponSelection; this.defaultMapNameFilter = defaultMapNameFilter; - this.searchBox.TextChanged += this.mapBrowser.searchBox_TextChanged; + this.searchBox.TextChanged += this.mapBrowser.Search.TextChanged; } /// @@ -86,7 +86,7 @@ private void Load_Load(object sender, EventArgs e) this.searchBox.Focus(); this.searchBox.Text = defaultMapNameFilter; - this.mapBrowser.searchBox_TextChanged(this.searchBox, null); + this.mapBrowser.Search.TextChanged(this.searchBox, null); } } @@ -100,7 +100,7 @@ private void checkBox_townOnly_CheckedChanged(object sender, EventArgs e) { this.mapBrowser.TownOnlyFilter = checkBox_townOnly.Checked; // search again - this.mapBrowser.searchMapsInternal(this.searchBox.Text == this.searchBox.WatermarkText ? "" : this.searchBox.Text); + this.mapBrowser.Search.TextChanged(this.searchBox.Text == this.searchBox.WatermarkText ? "" : this.searchBox.Text, null); } /// diff --git a/HaCreator/HaCreator.csproj b/HaCreator/HaCreator.csproj index 5ff963fd..8874a38c 100644 --- a/HaCreator/HaCreator.csproj +++ b/HaCreator/HaCreator.csproj @@ -255,6 +255,7 @@ LoadJobSelector.cs + Form