初級代碼游戲的專欄介紹與文章目錄-CSDN博客
我的github:codetoys,所有代碼都將會位于ctfc庫中。已經放入庫中我會指出在庫中的位置。
這些代碼大部分以Linux為目標但部分代碼是純C++的,可以在任何平臺上使用。
源碼指引:github源碼指引_初級代碼游戲的博客-CSDN博客
C#是我多年以來的業余愛好,新搞的東西能用C#的就用C#了。
? ? ? ? WinUI3里面有個SplitView,實現了兩塊面板,但是是不帶鼠標拖動改變大小的功能的,其設計目標不是兩塊面板,而是根據需要隱藏一個面板。
????????一般來說,移動設備上確實不需要用戶改變界面比例,但是你是WinUI3啊。所以說微軟腦子里都是些什么啊,不如大家都散了算了。
? ? ? ? 因為寫Winforms程序的時候我非常依賴手動調整界面比例,對于不能自主調整的完全不能忍,所以,我必須實現一個SplitPanel。
目錄
一、設計SplitPanel的基本原理
二、編寫xaml
三、處理鼠標事件
3.1 初始化
3.2 鼠標事件
四、效果
一、設計SplitPanel的基本原理
? ? ? ? 通過鼠標改變界面大小的功能總是涉及到這些技術:
- 動態設置控件/窗口的位置和尺寸,當然這沒什么難度
- 處理鼠標事件,根據鼠標位置計算鼠標移動的距離,從而改變界面比例
- 捕獲鼠標,為什么需要捕獲鼠標?因為鼠標移出控件/窗口之后就無法獲得鼠標事件,這樣能實現縮小但是無法實現變大。而捕獲鼠標就是告訴操作系統即使鼠標移出也仍然要發送鼠標事件
? ? ? ? 結合以上幾點,一般實現鼠標改變界面大小的實現方法是:
- 在鼠標按下時記錄鼠標位置,捕獲鼠標
- 在鼠標移動時根據鼠標移動距離改變界面大小
- 在鼠標釋放時停止捕獲鼠標
? ? ? ? 在WinUI3里面我們可以借助Grid來實現:
左面板 | 分隔條 | 右面板 |
? ? ? ? 分隔條用Border就可以了,寬度2-3像素,1個像素很難點擊,7、8個像素又太粗壯不好看。
? ? ? ? 左面板、右面板任意。
二、編寫xaml
<Grid><Grid.ColumnDefinitions><ColumnDefinition Width="3*" x:Name="col_mainlist" /><ColumnDefinition Width="3" /><ColumnDefinition Width="1*" x:Name="col_preview" /></Grid.ColumnDefinitions><!-- 主列表 --><controls:DataGrid Grid.Column="0" x:Name="MainDataList" BorderBrush="Black" BorderThickness="1" Margin="2" Padding="2"Height="Auto" Background="LightGray"GridLinesVisibility="All" HorizontalGridLinesBrush="Blue" VerticalGridLinesBrush="Green" AutoGenerateColumns="True" /><!-- 分隔條 --><Border Grid.Column="1" x:Name="border_split" BorderBrush="Gray" BorderThickness="3" Margin="0,2,0,2" /><!-- 預覽區 --><Grid Grid.Column="2" BorderBrush="Black" BorderThickness="1" Margin="2" Padding="2">。。。。。。</Grid></Grid>
? ? ? ? ?這個Grid只有一行,所以沒有定義行。有三個列,分別對應左面板、分隔條、右面板。分隔條固定為3個像素,左面板和右面板為3:1分配。左面板是DataGrid,右面板是個子Grid。
? ? ? ? 左右面板都有x:Name,因為我們需要同時改變左右面板的大小。你說改變DataGrid和子Grid的大小,讓總Grid自適應行不行?這種自適應的東西很詭異,我是不太放心的。
? ? ? ? 用作分隔條的Border上也有x:Name,我們設置鼠標事件都在它上面。
? ? ? ? 這段xaml中的主列表和預覽區是我實際的內容,和要實現的SplitPanel功能沒有任何關系。
三、處理鼠標事件
3.1 初始化
//設置拖動手柄border_split.PointerPressed += OnPointerPressed;border_split.PointerReleased += OnPointerReleased;
? ? ? ? 在窗口的構造函數里增加上述代碼。為什么沒有設置PointerMoved事件?因為沒有按下鼠標的時候處理PointerMoved是沒有意義的。
3.2 鼠標事件
private void OnPointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e){FrameworkElement? control = sender as FrameworkElement;if (null == control) return;try{foreach (var pointer in control.PointerCaptures){//control.PointerCaptures.Contains(e.Pointer)是無效的,必須用PointerId來比較if (pointer.PointerId== e.Pointer.PointerId){ChangeControlSize(control, e);}}}catch (Exception ex){_ = ShowMessageBox(ex.Message, "Exception");}}private void OnPointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e){FrameworkElement? control = sender as FrameworkElement;if (null == control) return;try{foreach (var pointer in control.PointerCaptures){//control.PointerCaptures.Contains(e.Pointer)是無效的,必須用PointerId來比較if (pointer.PointerId == e.Pointer.PointerId){control.PointerMoved -= OnPointerMoved; ;this.Title = "PointerReleased";control.ReleasePointerCapture(e.Pointer);}}}catch (Exception ex){_ = ShowMessageBox(ex.Message, "Exception");}}private void OnPointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e){FrameworkElement? control = sender as FrameworkElement;if (null == control) return;if (!control.CapturePointer(e.Pointer)){this.Title = "CapturePointer error";return;}control.PointerMoved += OnPointerMoved; ;pos = e.GetCurrentPoint(control).Position.X;this.Title = "PointerPressed " + pos.ToString() + " - " + control.ActualWidth.ToString()+ " control.PointerCaptures "+ control.PointerCaptures.Count.ToString();}double pos = 0;private void ChangeControlSize(FrameworkElement? control, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e){if (null == control) return;double move = e.GetCurrentPoint(control).Position.X - pos;if (move >= 0){if (col_preview.ActualWidth - move < 0) return;}else{if (col_mainlist.ActualWidth + move < 0) return;}col_preview.Width = new GridLength(col_preview.ActualWidth - move);col_mainlist.Width = new GridLength(col_mainlist.ActualWidth + move);}
? ? ? ? 這段代碼還是比較嚴謹的,左右面板的名字可以隨意替換。唯一不太嚴謹的是記錄鼠標初始位置的pos,一般不建議全局變量用這樣簡單的方式命名。
? ? ? ? 這個代碼里面有一部分沒有完善處理:假設了只有一個鼠標,但仍然判斷了是否正在進行鼠標捕獲(根據這個假設和鼠標按下才處理鼠標移動,并不需要檢查是否是正在捕獲的鼠標)。嚴謹的方式是根據鼠標ID來分別處理,設想,如果有兩個鼠標同時操作會怎么樣?
? ? ? ? 處理是否是正在捕獲的鼠標的時候遇到了一個坑,我已經在代碼里指出。并不能簡單地用鼠標對象是否在集合中來判斷,必須一個一個比較ID。
? ? ? ? 這段代碼只處理了寬度,如果要做縱向的,稍微修改就可以了。
四、效果
? ? ? ? 差不多就是這個意思了。當然,最好能把鼠標改成專門的鼠標形狀。
?
(這里是文檔結束)