小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

WPF MVVM 彈框之等待框

 新進(jìn)小設(shè)計(jì) 2021-03-15


獨(dú)立觀察員 2020年10月13日

之前寫(xiě)過(guò)一篇《WPF MVVM 模式下的彈窗》,里面實(shí)現(xiàn)了確認(rèn)框和消息框,經(jīng)過(guò)一段時(shí)間的演化,目前又新增了可顯示自定義內(nèi)容的彈框、可進(jìn)行信息錄入的彈框、以及本文將要介紹的加載等待框。

一、效果

先來(lái)看看效果,首先是其它彈框(動(dòng)圖):

然后是等待彈框(動(dòng)圖):

下面來(lái)看如何實(shí)現(xiàn),當(dāng)然,是在之前的基礎(chǔ)上進(jìn)行的,前一篇文章沒(méi)看的話(huà),需要先看一下,或者直接獲取文末提供的代碼查看。

二、彈框主體改造

首先改造的是,給右上角的 X 和底下的確認(rèn)取消按鈕區(qū)域的是否顯示特性 Visibility 綁定了相關(guān)屬性,可以控制是否顯示,這樣在消息框情況下可以隱藏底部按鈕,在等待框情況下可以都隱藏掉。

然后是中間的主體區(qū)域,圖上看不出什么變化,實(shí)際上變化還是比較大的,代碼如下:

文字版:

<ScrollViewer Grid.Row="2" HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Auto"><StackPanel Margin="5" VerticalAlignment="Center"><TextBlock FontSize="16" Text="{Binding DialogMessage, FallbackValue='是否確認(rèn)操作?是否確認(rèn)操作?是否確認(rèn)操作?是否確認(rèn)操作?是否確認(rèn)操作?', TargetNullValue='是否確認(rèn)操作?'}" TextWrapping="Wrap"   VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="{Binding IsShowText, Converter={StaticResource VisibleConverter}, FallbackValue=Visible}"></TextBlock><ContentControl Visibility="{Binding IsShowCustom, Converter={StaticResource VisibleConverter}, FallbackValue=Collapsed}" Content="{Binding CustomContent}" 
                        HorizontalAlignment="{Binding CustomContentHorizontalAlignment, TargetNullValue=Center, Mode=OneWay}" HorizontalContentAlignment="Center" MinWidth="50"></ContentControl></StackPanel></ScrollViewer>

最外層使用 ScrollViewer 包裹,如果內(nèi)容過(guò)多則可滾動(dòng)。往里一層是 StackPanel,里面有一個(gè) TextBlock 用于顯示文本內(nèi)容,還有一個(gè) ContentControl 用于顯示自定義內(nèi)容(綁定一個(gè) FrameworkElement 類(lèi)型的對(duì)象)。兩種內(nèi)容可以分別控制顯示和隱藏,也可以同時(shí)顯示,本文介紹的等待框就是使用了同時(shí)顯示。

三、等待動(dòng)畫(huà)用戶(hù)控件

按照設(shè)想,等待框的動(dòng)畫(huà)部分作為自定義內(nèi)容放入彈框的 ContentControl 中,所以我們需要新建個(gè)用戶(hù)控件。(此節(jié)參考朝夕教育 Jovan 老師在 B 站發(fā)布的 WPF 教學(xué)視頻的“動(dòng)畫(huà)實(shí)戰(zhàn)”一節(jié))

將一個(gè) Grid 分為四列,每列中放置一個(gè)不同顏色的 Border (以 Grid 包裹)并設(shè)置 LayoutTransform 變換類(lèi)型為 ScaleTransform,并給每個(gè) ScaleTransform 命名:

Border 顯示為圓形并居中的代碼為:

<Grid.Resources><Style TargetType="Border"><Setter Property="Width" Value="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualWidth, Converter={StaticResource DivideConverter}, ConverterParameter=2}"></Setter><Setter Property="Height" Value="{Binding RelativeSource={RelativeSource Self}, Path=Width}"></Setter><Setter Property="CornerRadius" Value="100"></Setter><!--<Setter Property="LayoutTransform">
            <Setter.Value>
                <ScaleTransform ScaleX="1.6" ScaleY="1.6"></ScaleTransform>
            </Setter.Value>
        </Setter>--></Style></Grid.Resources>

也就是設(shè)置寬度為包裹它的 Grid 的寬度的一半,即每列寬度的一半,這個(gè)平分的操作是通過(guò)轉(zhuǎn)換器 DivideConverter 實(shí)現(xiàn)的,具體可下載代碼查看。然后,高度綁定寬度,這樣就是正方形了。最后再設(shè)置圓角,就成圓形了。注釋的部分是設(shè)置 LayoutTransform 變換的,具體的 ScaleTransform 變換有個(gè) ScaleX 和 ScaleY 值,分別設(shè)置 X 和 Y 方向上的變換數(shù)值(變大為 1.6 倍),由于后面需要對(duì)這兩個(gè)值設(shè)置動(dòng)畫(huà),所以此處不能寫(xiě)死,注釋掉。

動(dòng)畫(huà)直接在后臺(tái)設(shè)置:

private void UC_Wait_OnLoaded(object sender, RoutedEventArgs e)
{
    RunAnimation();
}private void RunAnimation()
{//定義動(dòng)畫(huà);DoubleAnimation da = new DoubleAnimation()
    {
        Duration = new Duration(TimeSpan.FromMilliseconds(1000)),
        To = 1.6,
        RepeatBehavior = RepeatBehavior.Forever,
        AutoReverse = true,
    };

    Task.Run(async () =>{for (int i = 0; i < 4; i++)
        {
            Dispatcher.Invoke(() =>{var st = FindName($"ST{i + 1}") as ScaleTransform;
                st?.BeginAnimation(ScaleTransform.ScaleXProperty, da);
                st?.BeginAnimation(ScaleTransform.ScaleYProperty, da);
            });await Task.Delay(300);
        }
    });
}

界面載入后執(zhí)行動(dòng)畫(huà)方法,動(dòng)畫(huà)方法中先定義了一個(gè) DoubleAnimation 類(lèi)型的動(dòng)畫(huà):間隔一秒,目標(biāo)值為 1.6,一直重復(fù),自動(dòng)反轉(zhuǎn)。然后在循環(huán)中按照命名規(guī)則,依次先使用 FindName 方法找到 ScaleTransform 元素對(duì)象,并對(duì)其設(shè)置 X 和 Y 方向上的動(dòng)畫(huà),等待 300 毫秒再設(shè)置下一個(gè),總共四個(gè)。

四、彈窗 ViewModel 和幫助類(lèi)的改造

 彈窗 ViewModel 中添加了一個(gè)標(biāo)識(shí)是否是等待框的屬性 IsWaitDialog,在倒計(jì)時(shí)計(jì)時(shí)器里面,當(dāng)是等待框時(shí)改為正計(jì)時(shí),自然也就不會(huì)觸發(fā)關(guān)閉操作,代碼如下:

/// <summary>/// 是否是等待框/// </summary>public bool IsWaitDialog { get; set; } = false;/// <summary>/// 倒計(jì)時(shí)計(jì)時(shí)器/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{if (IsWaitDialog)
    {
        LeftTime++;
    }else{
        LeftTime--;if (LeftTime <= 0)
        {
            _timer.Stop();
            CloseCommand.Execute(null);
        }
    }
}

在控制彈框顯示隱藏的屬性 IsShowDialog 的 set 方法中,當(dāng)是等待框時(shí),倒計(jì)時(shí)設(shè)為零,方便后面(上面說(shuō)的)直接進(jìn)行正計(jì)時(shí):

關(guān)鍵是幫助方法中,新增一個(gè)彈出等待框方法:

/// <summary>/// 彈出等待框/// </summary>/// <param name="vm">相關(guān)ViewModel</param>/// <param name="message">消息內(nèi)容</param>/// <param name="action">業(yè)務(wù)方法</param>/// <param name="title">彈窗標(biāo)題</param>/// <returns></returns>public static async Task ShowWait(ConfirmBoxViewModel vm, string message, Func<Task> action = null, string title = "請(qǐng)耐心等待")
{
    vm.CustomContent = new UC_Wait();await Task.Run(async () =>{
        vm.IsMessageDialog = false;
        vm.IsWaitDialog = true;
        vm.IsShowDialog = true;
        vm.IsShowText = true;
        vm.IsShowCustom = true;
        vm.IsShowButton = false;
        vm.CustomContentHorizontalAlignment = HorizontalAlignment.Stretch.ToString();if (!string.IsNullOrWhiteSpace(message))
        {
            vm.DialogMessage = message;
        }if (!string.IsNullOrWhiteSpace(title))
        {
            vm.DialogTitle = title;
        }

        Console.WriteLine($"等待框就緒,業(yè)務(wù)操作開(kāi)始執(zhí)行...");await Task.Run(async () =>{await action?.Invoke();

        }).ContinueWith(_ =>{
            vm.IsShowDialog = false;
            Console.WriteLine($"業(yè)務(wù)操作執(zhí)行完畢,等待框關(guān)閉.");
        });
    });
}

先將自定義內(nèi)容設(shè)置為等待動(dòng)畫(huà)用戶(hù)控件,接下來(lái)是一些顯示方面的設(shè)置。

關(guān)鍵是如何在執(zhí)行完業(yè)務(wù)方法后才關(guān)閉彈窗呢?

一開(kāi)始 Func<Task> action 這個(gè)參數(shù)我用的還是 Action action,這樣的話(huà),action?.Invoke() 這里不能 await,然后 .NET Core 3.1 又不支持 action?.BeginInvoke(callback, null) 這種寫(xiě)法。

后來(lái)把參數(shù)類(lèi)型改為 Func<Task> ,就可以 await action?.Invoke() 了,而且神奇的是,調(diào)用的地方不用修改(后面展示)。這樣的話(huà),就可以通過(guò)如下方式(ContinueWith)達(dá)到業(yè)務(wù)方法執(zhí)行完成之后關(guān)閉彈窗了:

Console.WriteLine($"等待框就緒,業(yè)務(wù)操作開(kāi)始執(zhí)行...");await Task.Run(async () =>{await action?.Invoke();

}).ContinueWith(_ =>{
    vm.IsShowDialog = false;
    Console.WriteLine($"業(yè)務(wù)操作執(zhí)行完畢,等待框關(guān)閉.");
});

五、使用方法和代碼地址

使用就比較簡(jiǎn)單了:

WaitCommand ??= new RelayCommand(o => true, async o =>{await ConfirmBoxHelper.ShowWait(DialogVm, "正在執(zhí)行業(yè)務(wù)操作...", async () =>{await Task.Delay(1000 * 10);
        Console.WriteLine("操作完成");
    });
});

代碼地址:https:///dlgcy/WPFTemplate

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多