<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [Winform]在Form里顯示模態對話框ModalDialog

      問題

      如何在WinForm的一個Form里面彈出一個模態Dialog?

      背景

      程序的框架是Winform,只有一個窗口MainForm。MainForm里面是一個TabControl,每個TabPage是一個Form,每個TabPage的Form相互獨立,互不干擾,TabPage間可以隨時切換。由于有某些需求,TabPage需要接受用戶輸入,并等待輸入完成,才能執行后面的代碼,此時,程序是需要阻塞等待輸入的,所以需要彈出一個模態Dialog。

      1. 為什么不用MessageBox呢?因為MessageBox是直接彈出一個模態對話框且該對話框是一個新的窗口,這時候整個MainForm是偽阻塞狀態,用戶無法通過與MainForm的其他區域交互,包括點擊標簽頁切換到其他TabPage。所以,我需要該對畫框只在Form里顯示。
      2. 為什么不用MDI呢? 最主要的原因是TabControl里的Form,其TopLevel屬性是false的,如果想在Form里面添加MDI窗口,需要將Form的TopLevel屬性設置為true,這時我將無法使用TabControl。

      代碼實現

      創建一個CustomDialog類,繼承Form

      public class CustomDialog : Form{
      
      }
      

      創建CustomDialog成員變量

      1. 這里使用到了兩個類, PanelControlContainer。其中Panel充當CustomDialog的容器。ControlContainer則是Panel的容器。
      public class CustomDialog : Form{
          private Panel? _panelContainer;
          private ControlContainer? _parentContainer;
          private Form? _parentForm;
          // 聲明Panel,ControlContainer和Form
      }
      

      定義一個ShowDialog方法

      要顯示模態Dialog,當然要是實現ShowDialog方法啦!這里定義了一個ShowDialog方法,和其他ShowDialog方法有些許不同,該方法的參數是ControlContainer類型, 用于接收一個控件作為父控件

      public class CustomDialog : Form{
          public void ShowDialog(ControlContainer parentControl){
              //TODO
          }
      

      設置CustomDialog.PaneContainer的屬性和內容

      這部分代碼最主要實現了CustomDialog在它的父控件Form中顯示的功能,PS:有點簡單粗暴,但是有效(_)

      public class CustomDialog : Form{
          private void AddDialogToTheView(){
              if(ContainerControl is null){
                  throw new NullReferenceException(nameof(_parentContainer));
              }
              //panel的高度
              int panelHeight = 350;
              int panelWidth = 500;
              
              //panel顯示的位置
              int startUpLocationX = (_parentContainer.ClientSize.Width - panelWidth) / 2;
              int startUpLocationY = (_parentContainer.ClientSize.Height - panelHeight) / 2;
              
              // 設置_panelContainer的屬性
              _panelContainer = new Panel(){
                  Height = panelHeight,
                  Width = panelWidth,
                  Location = new Point(startUpLocationX, startUpLocationY),
              };
      
              // 設置Dialog的屬性
              TopLevel = false;
              DockStyle = DockStyle.Fill;
              //添加進Panel里面
              _panelContainer.Controls.Add(this);
              Contianer.Controls.Add(_panelContainer);
              // 顯示Dialog
              Show();
              PanelControl.BringToFront();
              
          }
      
      }
      

      實現偽阻塞

      要說實現這個CustomDialog哪里最難,應該是這個偽阻塞功能最難。前面的View相關的方案,一般人稍微思考一下都可以想出來。但是想優雅的實現CustomDialog的偽阻塞功能,確實不易

      • 如何阻塞一段代碼?
        我最初的做法是這樣的:
      public void WaitForExit(Cancellationtoken token){
          while(!toke.IsCancellationRequested){
              Application.DoEvents();
          }
      }
      CancellationTokenSource source = new CancellationTokenSource();
      WaitForExit(source.Token);
      
      //user cancel
      source.Cancel();
      

      這個寫法有效,但還是不夠優雅 ?? ps:切記請勿在UI線程中直接使用while(true){}

      • 最后我的寫法是這樣的:
        在這里我使用了一個Winform中默認沒有的命名空間:System.Windows.Threading
        在csproj里開啟wpf的命名空間
      <enableWpf>true</enableWpf>
      

      這里其實是借鑒了wpf的模態Dialog的實現方式,具體可以參考wpf的源碼;有現成的輪子?直接偷!??

      public class CustomDialog : Form{
          private DispatcherFrame? CurrentDispatcherFrame;
          private void WaitForExit(){
              try{
                  
              ComponentDispatcher.PushModal();
              CurrentDispatcherFrame = new DispatcherFrame(true);
              Dispatcher.PushFrame(CurrentDispatcherFrame);
              }
              finally{
                  ComponentDispatcher.PopModal();
              }
          }
      }
      

      當調用WaitForExit()方法后,程序就進入偽阻塞狀態了,此時UI線程仍然能繪制UI;直到調用CurrentDispatcherFrame?.Continue = false;,WaitForExit才會退出偽阻塞狀態。

      細節優化

      • 這個時候,整個CustomDialog的大體實現基本完成了,下一步就是優化細節

      重寫Form的Closed事件

      當調用CustomDialog的Close()方法時,會觸發Form.OnClosed事件,此時阻塞狀態將會退出

      protected override void OnClosed(EventArgs e){
          base.OnClosed(e);
          if(CurrentDispatcherFrame is not null){
              CurrentDispatcherFrame.Continue = false;
              CurrentDispatcherFrame = null;
          }
      }
      

      在ParentForm中注冊關閉事件

      在CustomDialog彈出的狀態,如果用戶想退出程序,點擊MainForm的關閉按鈕,此時是關閉不了的。MainForm是需要等CustomDialog關閉后才能關閉的,而CustomDialog需要等待用戶關閉才能關閉。此時需要將MainForm的關閉事件注冊到CustomDialog的關閉事件上。

      1. ParentForm_Closing事件
      private void ParentForm_Closing(object? sender, CancelEventArgs e){
          this.Close();//ParentForm關閉時,關閉CustomDialog
      }
      
      1. ParentForm訂閱關閉事件
      public void ShowDialog(ContainerControl _parentContainer){
      
          if(_parentContainer is Form containerForm && containerForm.TopLevel){
              this.Owner = containerForm;
          }
          _parentForm = _parentContainer.ParentForm;
          _parentForm.Closing += ParentForm_Closing;//訂閱關閉事件
          
          //TO DO
      }
      

      重寫Form的Closing事件

      訂閱了closing事件記得也要取消訂閱

      protected override void OnClosing(CancelEventArgs e){
          base.OnClosing(e);
          if(_parentForm is not null){
              _parentForm.Closing -= ParentForm_Closing;
          }
      }
      

      完整的ShowDialog方法

      public IAsyncResult ShowDialogAsync(ContainerControl _parentContainer){
          var asyncResult = _parentContainer.BeginInvoke(new Action(() => 
          {
              if(_parentContainer is Form containerForm && containerForm.TopLevel){
                  this.Owner = containerForm;
              }
              _parentForm = _parentContainer.ParentForm;
              _parentForm.Closing += ParentForm_Closing;//訂閱關閉事件
      
              AddDialogToTheView(); //已完成
              WaitForExit(); //已完成
              RemoveTheDialogFromTheView();//TODO 這里懶得寫了
          }));
          return asyncResult;
          }
      

      同步方法

      public void ShowDialog(ContainerControl _parentContainer){
          var asyncResult = ShowDialogAsync(_parentContainer);
          asyncResult.AsyncWaitHandle.WaitOne();
      }
      

      結語

      • 至此,CustomDialog已經可以使用了。定制的DialogForm,只需要繼承CustomDialog即可。其他交互邏輯在子類中實現即可

      其他細節

      當Form改變的時候,自動調整CustomDialog到Form中間:向_parentContainer訂閱SizeChanged事件

      protected void _parentContainer_SizeChanged(object sender, EventArgs e){
          if (sender is not ContainerControl control || _containerPanel is null)
          {
              return;
          }
      
          _panelContainer.Location = new Point((control.ClientSize.Width - _containerPanel.Width) / 2, (control.ClientSize.Height - _containerPanel.Height) / 2);
      }
      
      posted @ 2023-12-28 14:57  Echo_HR910  閱讀(844)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 又大又粗又硬又爽黄毛少妇| 十八禁在线观看视频播放免费| 久久99精品国产麻豆婷婷| 熟妇好大好深好满好爽| 国产精品一区二区三区三级| 国产成人一区二区三区免费| 国产午夜福利免费入口| 国内精品大秀视频日韩精品| 国产99在线 | 亚洲| 国产精品老熟女一区二区| 国产AV大陆精品一区二区三区| 无码无需播放器av网站| 青龙| 蜜臀人妻精品一区二区免费| 久久久久香蕉国产线看观看伊| 中文字幕人妻日韩精品| 国产亚洲精品成人av久| 国产精品高清国产三级囯产AV| 内射干少妇亚洲69XXX| 色多多性虎精品无码av| 亚洲乱色一区二区三区丝袜| 亚洲一区二区精品极品| 又粗又大又黄又硬又爽免费看| 久久久久久综合网天天| 成在线人永久免费视频播放| 亚洲成人av在线高清| 夜夜躁狠狠躁日日躁| 亚洲欧洲日产国码久在线| 亚洲熟妇AV午夜无码不卡| 麻豆亚洲精品一区二区| 日韩精品不卡一区二区三区| 国产色无码精品视频免费| 免费无码又爽又刺激网站| 亚洲精品香蕉一区二区| 亚洲国产精品日韩专区av| 少妇人妻无码专区在线视频| 国产丝袜肉丝视频在线| 一区二区三区精品偷拍| 亚洲中文字幕无码永久在线| 九九热精品在线视频观看| 日韩av日韩av在线|