using Grpc.Core; using HotelPms.Client.Blazor.Dialog; using HotelPms.Client.Blazor.Models; using HotelPms.Client.Blazor.Util; using HotelPms.Data.Common; using HotelPms.Share.IO; using HotelPms.Share.Util; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Web; using MudBlazor; using System.Data; using static HotelPms.Client.Blazor.Util.SystemEnum; using static System.Net.Mime.MediaTypeNames; namespace HotelPms.Client.Blazor.Pages.UseDetail; public partial class RoomTypeInput : ComponentBase { #region ★★★★★ Parameter ★★★★★ [Parameter] public ViewModel.UseInput? Data { get; set; } /// /// 親Pageの再表示 /// [Parameter] public Action OnRefresh { get; set; } #endregion #region ★★★★★ Declare ★★★★★ /// /// 异步锁 /// ■ロック追加前 /// Leave⇒Start /// Leave⇒Row:1 Col:0 enterPush:False, orgText:, text:1 /// 【10:07:06 046】SendAsync begin:10:07:06 046 /// 【10:07:06 047】SendAsync Url⇒/HotelPms.Data.GrpcTableCore/GetData /// Enter⇒Row:0 Col:0 Text:1 /// 【10:07:06 095】SendAsync End:10:07:06 095 /// 該当部屋タイプが既に存在します。 /// Leave⇒End /// /// ■追加後 /// Leave⇒Start /// Leave⇒Row:1 Col:0 enterPush:False, text:1 /// 【10:26:02 811】SendAsync begin:10:26:02 811 /// 【10:26:02 811】SendAsync Url⇒/HotelPms.Data.GrpcTableCore/GetData /// 【10:26:02 860】SendAsync End:10:26:02 860 /// 該当部屋タイプが既に存在します。 /// Leave⇒End /// Enter⇒Row:0 Col:0 /// /// Semaphore(int initialCount, int maximumCount); /// initialCount代表还分配几个线程,比如是1,那就是还能允许一个线程继续跑锁起来的代码 /// maximumCount代表最大允许数, 比如是1, 那就是进去1个线程, 就会锁起来 /// private System.Threading.SemaphoreSlim slimlock = new SemaphoreSlim(1, 1); private int selRow = 0; private int selectedRowNumber = -1; private MudTable mudRoomTypeInputTable; private bool keyDownPreventDefault = false; private bool keyPressPreventDefault = false; private bool enterPush = false; private bool arrowUpPush = false; private EditGridFocusEventArgs m_CurrentFocusData = new EditGridFocusEventArgs(); #endregion #region ★★★★★ Property ★★★★★ private RoomTypeInputRow m_SelRoomTypeInputRow = null; /// /// 選択されている行 /// public RoomTypeInputRow SelRoomTypeInputRow { get { return m_SelRoomTypeInputRow; } set { m_SelRoomTypeInputRow = value; EnvironmentSetting.Debug($"Change:{m_SelRoomTypeInputRow.ID}"); } } #endregion #region ★★★★★ Class Event ★★★★★ protected override void OnInitialized() { //EnvironmentSetting.Debug($"【{DateTime.Now.ToString("HH:mm:ss fff")}】RoomTypeInput.OnInitialized開始:"); base.OnInitialized(); } protected override void OnAfterRender(bool firstRender) { //EnvironmentSetting.Debug($"【{DateTime.Now.ToString("HH:mm:ss fff")}】RoomTypeInput.OnAfterRender開始:{firstRender}"); base.OnAfterRender(firstRender); } protected override async Task OnAfterRenderAsync(bool firstRender) { await slimlock.WaitAsync(); EnvironmentSetting.Debug($"RoomTypeInput.OnAfterRenderAsync開始:{firstRender}"); await ShowFocus(); EnvironmentSetting.Debug($"RoomTypeInput.OnAfterRenderAsync終了:{firstRender}"); slimlock.Release(); } protected override void OnParametersSet() { //EnvironmentSetting.Debug($"【{DateTime.Now.ToString("HH:mm:ss fff")}】RoomTypeInput.OnParametersSet開始:"); base.OnParametersSet(); } protected override bool ShouldRender() { EnvironmentSetting.Debug($"RoomTypeInput.ShouldRender開始:"); return base.ShouldRender(); } #endregion #region ★★★★★ Control Event ★★★★★ private async Task Enter(int index, RoomTypeInputRow data, FocusEventArgs e) { await slimlock.WaitAsync(); int rowIdx = GetRowIndex(data); Console.WriteLine($"Enter⇒Row:{rowIdx} Col:{index}"); slimlock.Release(); } private async Task Leave(int index, RoomTypeInputRow data, FocusEventArgs e) { string text = string.Empty; try { await slimlock.WaitAsync(); Console.WriteLine($"Leave⇒Start"); int rowIdx = GetRowIndex(data); text = data.GetCellText(index); Console.WriteLine($"Leave⇒Row:{rowIdx} Col:{index} enterPush:{enterPush}, text:{text}"); if (enterPush) { //Enterキーを押した時にも既にチェック済 enterPush = false; } else { //値変更したら、イベント発生 if (!data.IsValueChanged(index, text)) { //必須判断:初期表示時:OrgTextは空白のため、値変更しないまま return; } //通常チェックを行う if (!await IsValid(index, text, data, false)) { return; } } } catch (Exception ex) { OperationLog.Instance.WriteLog(ex.Message); } finally { //Refresh(); //UI更新(binding値変更通知) Console.WriteLine($"Leave⇒End"); slimlock.Release(); } } /// /// ※Backspaceここに来る /// /// /// /// /// private async Task KeyDown(int index, RoomTypeInputRow data, KeyboardEventArgs e) { if (e.Key == "ArrowUp") { try { EnvironmentSetting.Debug($"ArrowUp:{index}"); if (SetAutoNextFocus(index, false, data)) { //BlazorのBugでEnterのパラメータ変数できない! //Uncaught Error: System.ArgumentException: There is no event handler associated with this event. EventId: '290'. (Parameter 'eventHandlerId') //mkArtakMSFT modified the milestones: Next sprint planning, 6.0-preview4 on 20 Mar ← Net6.0-preview4対応だそう keyDownPreventDefault = true; arrowUpPush = true; } else { keyDownPreventDefault = false; arrowUpPush = false; } } catch (Exception ex) { OperationLog.Instance.WriteLog($"KeyDown:{ex.Message}"); } finally { Refresh("KeyDown⇒↑"); //UI更新(binding値変更通知) } } else if (e.Key == "End") { if (data.Cells[index].ShowStyle == EShowStyle.ShowList) { data.ActiveCol = index; if (index == (int)RoomTypeInputRow.ColType.ID) { using DataTable dt = await MasterCore.GetRoomTypeList(); var parameters = new DialogParameters { ["Data"] = dt }; var dialog = DialogService.Show($"部屋タイプ一覧", parameters); var ret = await dialog.Result; if (!ret.Cancelled) { var row = ret.Data as DataRow; EnvironmentSetting.Debug(row[0].ToString()); data.Cells[index].Text = row[0].ToString(); data.Cells[(int)RoomTypeInputRow.ColType.Name].Text = row[1].ToString(); await KeyPress(index, data, new KeyboardEventArgs { Key = "Enter" }); //Refresh("KeyDown⇒End"); } } } } else if (e.Key == "Delete") { if (index == (int)RoomTypeInputRow.ColType.ID) { int rowIdx = GetRowIndex(data); List list = (Data.RoomTypeList as List); bool isLastRow = rowIdx == (list.Count - 1); if (isLastRow) { return; } SetFocus(rowIdx, (int)RoomTypeInputRow.ColType.ID); EnvironmentSetting.Debug($"削除前件数:{list.Count}"); list.Remove(data); EnvironmentSetting.Debug($"削除後件数:{list.Count}"); keyDownPreventDefault = false; arrowUpPush = false; } } else { keyDownPreventDefault = false; arrowUpPush = false; } } /// /// ※Backspaceここに来ない /// /// /// /// /// private async Task KeyPress(int index, RoomTypeInputRow data, KeyboardEventArgs e) { if (e.Key == "Enter") { try { int rowIdx = GetRowIndex(data); Console.WriteLine($"Row:{rowIdx} Col:{index}"); Console.WriteLine("Data:" + CConvert.ToJsonText(data)); //※その時、Fields[index].Textの値まだ変っていない!!!!! string text = await data.GetInputValue(index, JSRuntime); EnvironmentSetting.Debug($"Return:{index}⇒{text}"); keyPressPreventDefault = true; //イベント中止 enterPush = true; //Enterを押した知らせ //値変更したら、イベント発生 if (data.IsValueChanged(index, text)) { //通常チェックを行う if (!await IsValid(index, text, data, true)) { return; } } else { //必須判断:初期表示時:OrgTextは空白のため、値変更しないまま if (text.Trim().Length == 0) { //最終行となるはず } } if (!SetAutoNextFocus(index, true, data)) { return; } } catch (Exception ex) { EnvironmentSetting.Debug(ex.Message); } finally { Refresh("KeyPress⇒Enter"); //UI更新(binding値変更通知) } } else { if (CConvert.IsNumPress(e.Key)) { keyPressPreventDefault = false; } else { keyPressPreventDefault = true; //入力禁止(※:IMEモードで入力したものをここでは通らないため、防げない) } enterPush = false; } } #endregion #region ★★★★★ Private Method ★★★★★ private RenderFragment RenderWidget(string msg) => builder => { builder.OpenComponent(0, typeof(MessageContext)); builder.AddAttribute(1, "Text", msg); builder.CloseComponent(); }; /// /// フォーカス移動準備 /// /// /// /// EditModeの場合、自動で次のフォーカス移動する private void SetFocus(int row, int col, EditGridFocusEventArgs.ActionType action = EditGridFocusEventArgs.ActionType.Focus) { EnvironmentSetting.Debug($"フォーカス移動準備:開始⇒Row:{row},Col:{col},Action:{action},Enabled:{m_CurrentFocusData.Enabled}"); m_CurrentFocusData.Row = row; m_CurrentFocusData.Col = col; m_CurrentFocusData.Action = action; m_CurrentFocusData.Enabled = true; EnvironmentSetting.Debug($"フォーカス移動準備:完了⇒Row:{row},Col:{col},Action:{action},Enabled:{m_CurrentFocusData.Enabled}"); } /// /// OnAfterRenderAsync時フォーカスを設定 /// private async Task ShowFocus() { try { EnvironmentSetting.Debug($"SaleInput.FocusAsync行Before:Row={m_CurrentFocusData.Row},Col={m_CurrentFocusData.Col},Action={m_CurrentFocusData.Action},Enabled={m_CurrentFocusData.Enabled}"); if (!m_CurrentFocusData.Enabled) { EnvironmentSetting.Debug($"OnAfterRenderAsync⇒return false"); return false; } m_CurrentFocusData.Enabled = false; EnvironmentSetting.Debug($"ShowFocus処理:Row={m_CurrentFocusData.Row},Col={m_CurrentFocusData.Col},Action={m_CurrentFocusData.Action},Enabled={m_CurrentFocusData.Enabled}"); if (m_CurrentFocusData.Action == EditGridFocusEventArgs.ActionType.Focus && m_CurrentFocusData.Row != -1 && m_CurrentFocusData.Col != -1) { //この時点Grid編集状態ですので、フォーカス可能になる EnvironmentSetting.Debug($"フォーカスへ開始:Row = {m_CurrentFocusData.Row}, Col = {m_CurrentFocusData.Col}"); await (Data.RoomTypeList as List)[m_CurrentFocusData.Row].Cells[m_CurrentFocusData.Col].Ref.FocusAsync(); //ここに再度OnAfterRenderAsyncに入るが、EnabledはFalseのため、ShowFocusの動作がしない EnvironmentSetting.Debug($"フォーカスへ完了:Row = {m_CurrentFocusData.Row}, Col = {m_CurrentFocusData.Col}"); } return true; } catch (Exception ex) { EnvironmentSetting.Debug(ex.Message); return false; } } private int GetRowIndex(RoomTypeInputRow data) { return (Data.RoomTypeList as List).IndexOf(data); } /// /// フォーカス移動 /// /// 列Index /// /// private bool SetAutoNextFocus(int index, bool isEnter, RoomTypeInputRow data) { try { int rowIdx = GetRowIndex(data); List list = (Data.RoomTypeList as List); if (isEnter) { if (index == (int)RoomTypeInputRow.ColType.ID) { SetFocus(rowIdx, (int)RoomTypeInputRow.ColType.Count); } else { if (rowIdx == -1) { //削除された場合 //await list[list.Count - 1].RefID.FocusAsync(); } else { if (rowIdx < (list.Count - 1)) { SetFocus(rowIdx + 1, (int)RoomTypeInputRow.ColType.ID); } } } } else { if (index == (int)RoomTypeInputRow.ColType.ID) { if (rowIdx > 0) { SetFocus(rowIdx - 1, (int)RoomTypeInputRow.ColType.Count); } } else { SetFocus(rowIdx, (int)RoomTypeInputRow.ColType.ID); } } return true; } catch { return false; } } /// /// 入力されたIDが存在したかどうか /// /// /// /// private bool IsExistsID(int rowIdx, int id) { List list = (Data.RoomTypeList as List); int i = 0; foreach (RoomTypeInputRow row in list) { if (i == rowIdx) { continue; } if (CConvert.ToInt(row.ID) == id) { return true; } i++; } return false; } /// /// 業務チェック /// /// /// /// /// /// private async Task IsValid(int index, string inputText, RoomTypeInputRow data, bool isEnter) { int rowIdx = GetRowIndex(data); //最終行判断 List list = (Data.RoomTypeList as List); bool isLastRow = rowIdx == (list.Count - 1); if (index == (int)RoomTypeInputRow.ColType.ID) { int id = CConvert.ToInt(inputText); RoomTypeBase typeBase = await MasterCore.GetRoomTypeBase(id); if (typeBase == null) { if (isEnter) { Message.Show(RenderMessage("該当データが存在しません。")); } else { Console.WriteLine("該当データが存在しません。"); } data.RestoreText(RoomTypeInputRow.ColType.ID); return false; } if (IsExistsID(rowIdx, id)) { if (isEnter) { Message.Show(RenderMessage("該当部屋タイプが既に存在します。")); } else { Console.WriteLine("該当部屋タイプが既に存在します。"); } data.RestoreText(RoomTypeInputRow.ColType.ID); return false; } data.SetCellText((int)RoomTypeInputRow.ColType.Name, typeBase.Name); if (isLastRow) { //新規 data.SetCellText((int)RoomTypeInputRow.ColType.Count, "1"); //UseRoomを追加:当日 var useRoom = await Data.CreateUseRoom(id, typeBase.Kind); data.DataList.Add(useRoom); //一番したの新規行表示 list.Add(new RoomTypeInputRow()); } else { //部屋タイプ変更(データ更新) foreach (var item in data.DataList) { item.RoomTypeID = id; item.RoomKind = typeBase.Kind; } //画面表示更新 data.SetCellText((int)RoomTypeInputRow.ColType.ID, id.ToString()); } } else if (index == (int)RoomTypeInputRow.ColType.Count) { if (isLastRow) { return false; } //0の場合、完全に削除する int orgCount = data.DataList.Count; int newCount = CConvert.ToInt(inputText); //UseRoomの± int count = newCount - orgCount; if (count > 0) { for (int i = 0; i < count; i++) { var useRoom = await Data.CreateUseRoom(data.DataList[0].RoomTypeID, data.DataList[0].RoomKind); data.DataList.Add(useRoom); } } else { Data.RemoveUseRoom(data.DataList, -count); } if (newCount == 0) { list.Remove(data); if (rowIdx >= list.Count) { SetFocus(list.Count - 1, (int)RoomTypeInputRow.ColType.ID); } else { SetFocus(rowIdx, (int)RoomTypeInputRow.ColType.ID); } return false; } data.SetCellText(index, CConvert.ToInt(inputText).ToString()); } return true; } #endregion #region ★★★★★ Public Method ★★★★★ /// /// コントロール再表示 /// public void Refresh(string type) { EnvironmentSetting.Debug($"再表示へ開始:{type}"); StateHasChanged(); EnvironmentSetting.Debug($"再表示へ終了:{type}"); if(OnRefresh != null) { OnRefresh(); } } #endregion }