[UWP]用畫中畫模式(CompactOverlay Mode)讓用總在最前端顯示

来源:https://www.cnblogs.com/dino623/archive/2020/01/17/uwp_CompactOverlay_mode.html

1. 什麼是,以及怎麼用畫中畫 Windows 10 Creators Update以後UWP提供了一個新的視圖模式CompactOverlay,中文翻譯成 緊湊的覆蓋層 ?反正大部分時間我們都會稱它為 畫中畫模式 。 上圖中右上角即為進入畫中畫模式的微軟“電影和電視”應用。 可以調用 "Appli ...


1. 什麼是,以及怎麼用畫中畫

Windows 10 Creators Update以後UWP提供了一個新的視圖模式CompactOverlay,中文翻譯成 緊湊的覆蓋層?反正大部分時間我們都會稱它為畫中畫模式

上圖中右上角即為進入畫中畫模式的微軟“電影和電視”應用。

可以調用ApplicationView.TryEnterViewModeAsync函數進入或退出CompactOverlayer模式:

//進入CompactOverlay模式
await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.CompactOverlay);

//返回預設模式
await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.Default);

還可以使用ViewModePreferences控制進入CompactOverlay時視窗的大小:

//進入CompactOverlay模式並將窗體設置為 200 x 200 像素
var preferences = ViewModePreferences.CreateDefault(ApplicationViewMode.CompactOverlay);
preferences.CustomSize = new Windows.Foundation.Size(200, 200);
await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.CompactOverlay, preferences);

//返回預設模式
var preferences = ViewModePreferences.CreateDefault(ApplicationViewMode.Default);
await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.Default, preferences);

進入CompactOverlay模式後,窗體首先縮小並移動到屏幕右上方,並且有以下行為:

• 視窗置於頂層;
• 最大化、最小化按鈕消失;
• 標題欄會在失去焦點並且滑鼠離開後幾秒鐘消失;
• 使用`Window.Current.SetTitleBar`設置為標題欄元素的內容也會在滑鼠離開後消失;
• 可以改變視窗大小,但只能在 150 x 150 到 500 x 500 之間改變;
• 雖然標題欄消失,但左下右三個邊框仍在;

因為尺寸有限制,所以超過 150 x 150 到 500 x 500 這個範圍的ViewModePreferences.CustomSize不會生效,會取最接近範圍的值。例如使用 700 x 500 會出現 500 x 500 的視窗。

2. 通過自定義StateTrigger響應畫中畫模式

上一篇文章介紹過如何使用AdaptiveTrigger實現響應式佈局,CompactOverlay的情況更加極端,畢竟有可能從1920 x 1050突然變成150 x 150。為了應對這種情況,我自定義了一個StateTrigger,根據ApplicationView.ViewMode的值判斷是否激活當前的State。這個類繼承自StateTriggerBase,在監視的FrameworkElement的SizeChanged事件中調用SetActive改變State的激活狀態。具體代碼及使用方式如下:

public class IsCompactOverlayModeTrigger : StateTriggerBase
{
    private FrameworkElement _targetElement;

    public FrameworkElement TargetElement
    {
        get
        {
            return _targetElement;
        }
        set
        {
            if (_targetElement != null)
            {
                _targetElement.SizeChanged -= OnSizeChanged;
            }
            _targetElement = value;
            _targetElement.SizeChanged += OnSizeChanged;
        }
    }

    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        var view = ApplicationView.GetForCurrentView();
        SetActive(view.ViewMode == ApplicationViewMode.CompactOverlay);
    }
}
<VisualStateManager.VisualStateGroups>
    <VisualStateGroup>
        <VisualState>
            <VisualState.StateTriggers>
                <helpers:IsCompactOverlayModeTrigger TargetElement="{x:Bind}" />
            </VisualState.StateTriggers>
            <VisualState.Setters>
                <Setter Target="CompactView.Opacity"
                        Value="1" />
                <Setter Target="CompactView.IsHitTestVisible"
                        Value="True" />
                <Setter Target="NormalView.Opacity"
                        Value="0" />
                <Setter Target="NormalView.IsHitTestVisible"
                        Value="False" />
            </VisualState.Setters>
        </VisualState>
    </VisualStateGroup>
</VisualStateManager.VisualStateGroups>

雖然前一篇文章介紹了使用Visibility改變視圖,但使用了ImplicitAnimation的話改變Visibility會觸發動畫,所以有時我會使用Opacity和IsHitTestVisible來顯示/隱藏UI元素。

3. 或者索性導航到新的頁面

使用 StateTrigger畢竟還是有些繁瑣,大部分情況下需要用到畫中畫模式的應用,CompactOverlay的視圖都是固定的那幾個,所以可以直接導航到一個新頁面。

private async void OnEnterDefault(object sender, RoutedEventArgs e)
{
    var preferences = ViewModePreferences.CreateDefault(ApplicationViewMode.Default);
    await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.Default, preferences);
}

private async void OnEnterCompactOverlay(object sender, RoutedEventArgs e)
{
    var preferences = ViewModePreferences.CreateDefault(ApplicationViewMode.CompactOverlay);
    preferences.CustomSize = new Windows.Foundation.Size(150, 150);
    await ApplicationView.GetForCurrentView().TryEnterViewModeAsync(ApplicationViewMode.CompactOverlay, preferences);
    Frame.Navigate(typeof(CompactPage), null, new SuppressNavigationTransitionInfo());
}

這時候記得要使用SuppressNavigationTransitionInfo暫停導航的過渡動畫,否則會很難看。

4. 結語

CompactOverlay mode – aka Picture-in-Picture

上面這篇文章還給出了更多有用的代碼:如何判斷是否支持CompactOverlay及如何在多視圖模式下使用。但我沒有用到就不再贅述了,有興趣可以參考這個文章。

畫中畫模式對我的番茄鐘應用很重要。雖然我很喜歡在第二個屏幕上使用番茄鐘,一來不占用我的工作區域,二來可以提醒別人我正在專註工作不要打擾,但對很多只有一個屏幕的用戶來說畫中畫模式更加實用。關於畫中畫模式的更多信息可以參考下麵給出的網站。

5. 參考

ApplicationView Class (Windows.UI.ViewManagement) - Windows UWP applications Microsoft Docs

ApplicationViewMode Enum (Windows.UI.ViewManagement) - Windows UWP applications Microsoft Docs


您的分享是我們最大的動力!

更多相關文章
  • [TOC] MVC "MVC 維基百科,自由的百科全書" MVC 是軟體工程的一種軟體架構模式,它不是具體的技術,而是一種代碼分層的理念,主要體現了職責分離原則。 M Model 模型 V View 視圖 C Controller 控制器 對 MVC 的誤解及緣由 誤解:頁面視圖 = View ,E ...
  • 簡介 Orleans用起來的確很爽,更爽的是咱們有能監控它的工具。 " OrleansDashboard " 這個工具是一個可視化的Silo監控工具,Silo和Grain的活躍狀態一目瞭然,各個介面的響應速度也可以很清晰的統計到。 安裝 使用NuGet引用 然後修改 的配置,添加Dashboard: ...
  • Teigha中實體旋轉 代碼: using (var trans = database.TransactionManager.StartTransaction()) { Entity ent = trans.GetObject(entityId, OpenMode.ForWrite) asEntit ...
  • 什麼是AutoMapper?AutoMapper是一個簡單的小型庫,用於解決一個看似複雜的問題 - 擺脫將一個對象映射到另一個對象的代碼。這種類型的代碼是相當沉悶和無聊的寫,所以為什麼不發明一個工具來為我們做? 我們來看看在.netcore3.1中怎樣使用AutoMapper9.0。 Profile ...
  • 程式處理存放圖片的幾種方式 我的理解有4個: 1:放在項目本身得文件夾中,直接部署到伺服器上 2:存放在磁碟中,然後資料庫中存放路徑,讀取得時候傳路徑。這個適合小項目 3:將圖片轉換成二進位文件,但是不建議這樣做,因為會給資料庫造成壓力。 4:存放在雲存儲器上,也是在資料庫上存地址,不過是雲地址,使 ...
  • 第一部分:面向對象 封裝 繼承(里氏轉換) 多態第二部分:值類型、引用類型、字元串操作第三部分:集合文件操作第四部分:正則表達式第五部分:XML操作第六部分:委托、事件第七部分:反射。 //存儲著我們當前正在運行的進程//Process[] pro = Process.GetProcesses(); ...
  • 在很多時候,我們做一些非常規化的界面的時候,往往需要創建一些用戶控制項,在其中繪製好一些基礎的界面塊,作為後續重覆使用的一個單元,用戶控制項同時也可以封裝處理一些簡單的邏輯。在開發Winform各種類型項目,我都時不時需要定製一些特殊的用戶控制項,以方便在界面模塊中反覆使用。我們一般是在自定義的用戶控制項里... ...
  • C#實現的對兩個Table進行Merge,兩表必須存在至少一個公共欄位作為連接項,否則連接就失去了意義。如下是對兩個table進行Merge的詳細代碼: private void button1_Click(object sender, EventArgs e)//Button點擊觸發事件 { #r ...
一周排行
  • 1. 泛型Generic 1.1 引入泛型:延遲聲明 泛型方法聲明時,並未寫死類型,在調用的時候再指定類型。 延遲聲明:推遲一切可以推遲的。 1.2 如何聲明和使用泛型 泛型方法:方法名稱後面加上尖括弧,裡面是類型參數 類型參數實際上就是一個類型T聲明,方法就可以用這個類型T了。 如下所示: pub ...
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7903617.html,記錄一下學習過程以備後續查用。 一、引言 今天我們要講行為型設計模式的第三個模式--迭代器模式,先從名字上來看。迭代是遍歷的意思,迭代器可以理解為是遍歷某某的工具,遍歷什麼呢?在軟 件設 ...
  • 本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/7928521.html,記錄一下學習過程以備後續查用。 一、引言 今天我們要講行為型設計模式的第四個模式--觀察者模式,先從名字上來看。觀察者模式可以理解為既然有“觀察者”,那肯定就有“被觀察者”了。“觀察者” ...
  • 先看核心代碼: public List<DataEntity> SearchShopSalesReport(DateTimeOffset? dateFrom, DateTimeOffset? dateTo,string groupBy) { var query = data.DataEntity / ...
  • 首先新建一個項目,名稱叫Caliburn.Micro.ActionConvertions 然後刪掉MainWindow.xaml 然後去app.xaml刪掉StartupUri這行代碼 其次,安裝Caliburn.Micro,Caliburn.Micro.Core,這兩個Nuget包,如下圖 然後新 ...
  • 一文帶你瞭解 C DLR 的世界 在很久之前,我寫了一片文章 "dynamic結合匿名類型 匿名對象傳參" ,裡面我以為DLR內部是用反射實現的。因為那時候是心中想當然的認為只有反射能夠在運行時解析對象的成員信息並調用成員方法。後來也是因為其他的事一直都沒有回過頭來把這一節知識給補上,正所謂亡羊補牢 ...
  • ​ 在C#8.0中,針對介面引入了一項新特性,就是可以指定預設實現,方便對已有實現進行擴展,也對面向Android和Swift的Api進行互操作提供了可能性。下麵我們來看看該特性的具體規則與實現。 一、主要應用場景: 在不破壞影響已有實現的情況下,可以添加新成員。這解決了在第三方已經大量使用了的介面 ...
  • 前言 通常在應用程式開發到正式上線,在這個過程中我們會分為多個階段,通常會有 開發、測試、以及正式環境等。每個環境的參數配置我們會使用不同的參數,因此呢,在ASP.NET Core中就提供了相關的環境API,方便我們更好的去做這些事情。 環境 ASP.NET Core使用ASPNETCORE_ENV ...
  • 擱置了幾天,工作忙的一塌糊塗,今天終於抽空來繼續看看MVC的知識。先來看看MVC的路由是如何處理的。以下為替代的路由: app.UseEndpoints(endpoints => { endpoints.MapControllerRoute( name: "default", pattern: "{ ...
  • 多用www.bing.com國際版解決代碼報錯 代碼運行的時候,報異常,國內的搜索引擎一搜, 浮誇的廣告太多,解決方案准確性不足, 盜版又很嚴重(導致一錯皆錯),方案未及時更新等詬病。 www.bing.com國際版可以關聯到: (1). 外國官網,可以獲得官方的解決方案。 (2). stackov ...
x