【WPF學習】第五十章 故事板

来源:https://www.cnblogs.com/Peter-Luo/archive/2020/02/29/12380975.html
-Advertisement-
Play Games

正如上一章介紹,WPF動畫通過一組動畫類(Animation類)表示。使用少數幾個熟悉設置相關信息,如開始值、結束值以及持續時間。這顯然使得它們非常適合於XAML。不是很清晰的時:如何為特定的事件和屬性關聯動畫,以及如何在正確的時間觸發動畫。 在所有聲明式動畫中都會用到如下兩個要素: 故事板。故事板 ...


  正如上一章介紹,WPF動畫通過一組動畫類(Animation類)表示。使用少數幾個熟悉設置相關信息,如開始值、結束值以及持續時間。這顯然使得它們非常適合於XAML。不是很清晰的時:如何為特定的事件和屬性關聯動畫,以及如何在正確的時間觸發動畫。

  在所有聲明式動畫中都會用到如下兩個要素:

  •   故事板。故事板是BeginAnimation()方法的XAML等價物。通過故事板將動畫指定到合適的元素和屬性。
  •   事件觸發器。事件觸發器響應屬性變化或事件(如按鈕的Click事件),並控制故事板。例如,為了開始動畫,事件觸發器必須開始故事板。

一、故事板

  故事板是增強的事件線,可用來分組多個動畫,而且具有控制動畫播放的能力——暫停、停止以及改變播放位置。然而,Storyboard類提供的最基本功能是,能夠使用TargetProperty和TargetName屬性指向某個特定屬性和特定元素。換句話說,故事板在動畫和希望應用動畫的屬性之間架起了一座橋梁。

  下麵的標記演示瞭如何定義用於管理DoubleAnimation的故事板:

<Storyboard TargetName="cmdGrow" TargetProperty="Width">
     <DoubleAnimation From="160" To="300" Duration="0:0:5"></DoubleAnimation>
</Storyboard>

  TargetName和TargetProperty都是附加屬性。這意味著可以直接將他們應用於動畫,如下所示:

<Storyboard >
     <DoubleAnimation Storyboard.TargetName="cmdGrow" Storyboard.TargetProperty="Width" 
                         From="160" To="300" Duration="0:0:5">    
     </DoubleAnimation>
</Storyboard>

  上面的語法更常用,因為通過這種語法可在同一個故事板中放置幾個動畫,並且每個動畫可用於不同的元素和屬性。

  定義故事板是創建動畫的第一步。為讓故事板實際運行起來,還需要有事件觸發器。

二、事件觸發器

  在“【WPF學習】第三十七章 觸發器 ”時第一次提到事件觸發器。樣式提供了一種將事件觸發器關聯到元素的方法。然而,可在如下4個位置定義事件觸發器:

  •   在樣式中(Styles.Triggers集合)
  •   在數據目標中(DataTemplate.Triggers集合)
  •   在控制項模板中(ControlTemplate.Triggers集合)
  •   直接在元素中定義事件觸發器(FrameworkElement.Triggers集合)

  當創建事件觸發器時,需要制定開始出發其的路由事件和由觸發器執行的一個或多個動作。對於動畫,最常用的動作是BeginStoryboard,該動作相當於調用BeginAnimation()方法。

  下麵的示例使用按鈕的Triggers集合為Click事件關聯某個動畫。當單擊按鈕時,該動畫增長按鈕:

<Button Margin="10" Name="cmdGrow" Height="40" Width="160"
             HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <BeginStoryboard>
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="Width"
                                         To="300" Duration="0:0:5">
                        </DoubleAnimation>
                    </Storyboard>
                </BeginStoryboard>
            </EventTrigger>
        </Button.Triggers>
        <Button.Content>
            Click and Make Me Grow
        </Button.Content>
    </Button>

  Storyboard.TargetProperty屬性指定了希望改變的屬性(在這個示例中是Width屬性)。如果沒有提供類的名稱,故事板使用其父元素,在此使用的是希望擴展的按鈕。如果希望設置附加屬性(如Canvas.Left或Canvas.Top),需要在括弧中封裝整個屬性,如下所示:

<DoubleAnimation Storyboard.TargetName="(Canvas.Top)" .../>

  在這個示例中需不需要使用Storyboard.TargetName屬性。當忽略該屬性時,故事板使用父元素,在此是按鈕。

  在這個示例中使用的聲明式方法和前面演示的只使用代碼的方法存在如下區別:To值被硬編碼為300個單位,而不是相對於包含按鈕的視窗的尺寸設置。如果希望使用視窗寬度,需要使用數據綁定表達式,如下所示:

<DoubleAnimation Storyboard.TargetProperty="Width"
        To="{Binding ElementName=cmdGrow, Path=Width}" Duration="0:0:5">
</DoubleAnimation>

  這仍不能準確地得到所希望的結果。在此,按鈕從當前尺寸增大到視窗的完整寬度。只使用代碼的方法使用一種簡單的計算,將按鈕擴大到比整個視窗寬度小30個單位的值。但XAML不支持內聯計算。一種解決方法是構建能夠自動完成工作的IValueConverter介面。如下所示的示例:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Data;

namespace Animation
{
    public class ArithmeticConverter : IValueConverter
    {
        private const string ArithmeticParseExpression = "([+\\-*/]{1,1})\\s{0,}(\\-?[\\d\\.]+)";
        private Regex arithmeticRegex = new Regex(ArithmeticParseExpression);
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value is double && parameter != null)
            {
                string param = parameter.ToString();

                if (param.Length > 0)
                {
                    Match match = arithmeticRegex.Match(param);
                    if (match != null && match.Groups.Count == 3)
                    {
                        string operation = match.Groups[1].Value.Trim();
                        string numericValue = match.Groups[2].Value;

                        double number = 0;
                        if (double.TryParse(numericValue, out number)) // this should always succeed or our regex is broken
                        {
                            double valueAsDouble = (double)value;
                            double returnValue = 0;

                            switch (operation)
                            {
                                case "+":
                                    returnValue = valueAsDouble + number;
                                    break;

                                case "-":
                                    returnValue = valueAsDouble - number;
                                    break;

                                case "*":
                                    returnValue = valueAsDouble * number;
                                    break;

                                case "/":
                                    returnValue = valueAsDouble / number;
                                    break;
                            }

                            return returnValue;
                        }
                    }
                }
            }

            return null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
ArithmeticConverter
<Window x:Class="Animation.XamlAnimation"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Animation"
        Title="XamlAnimation" Height="300" Width="300">
    <Window.Resources>
        <local:ArithmeticConverter x:Key="converter"></local:ArithmeticConverter>
    </Window.Resources>
    <Button Padding="10" Name="cmdGrow" Height="40" Width="160"
          HorizontalAlignment="Center" VerticalAlignment="Center">
        <Button.Triggers>
            <EventTrigger RoutedEvent="Button.Click">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="Width"
                To="{Binding ElementName=window,Path=Width,Converter={StaticResource converter},ConverterParameter=-30}"
                               Duration="0:0:5"></DoubleAnimation>
                            <DoubleAnimation Storyboard.TargetProperty="Height"
                To="{Binding ElementName=window,Path=Height,Converter={StaticResource converter},ConverterParameter=-50}"
                               Duration="0:0:5"></DoubleAnimation>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </Button.Triggers>
        <Button.Content>
            Click and Make Me Grow
        </Button.Content>
    </Button>
</Window>
XamlAnimation

  使用樣式關聯觸發器

  FrameworkElement.Triggers集合有點奇怪,它僅支持事件觸發器。其他觸發器集合(Style.Triggers、DataTemplate.Triggers與ControlTemplate.Triggers)的功能更強大,他們支持三種基本類型的WPF觸發器:屬性觸發器、數據觸發器以及事件觸發器。

  使用事件觸發器是關聯動畫的最常用方式,但並不是唯一的選擇。如果使用位於樣式、數據模板或控制項模板中的Triggers集合,還可創建當屬性值發生變化時進行響應的屬性觸發器。例如,下麵的樣式複製了前面顯示的示例。當IsPressed屬性為true時,該樣式觸發一個故事板:

<Window.Resources>

        <Style x:Key="GrowButtonStyle">
            <Style.Triggers>
                <Trigger Property="Button.IsPressed" Value="True">
                    <Trigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard>
                                <DoubleAnimation Storyboard.TargetProperty="Width"
                  To="250" Duration="0:0:5"></DoubleAnimation>
                            </Storyboard>
                        </BeginStoryboard>
                    </Trigger.EnterActions>
                </Trigger>
            </Style.Triggers>
        </Style>

    </Window.Resources>

  可使用兩種方式為屬性觸發器關聯動作。可使用Trigger.EnterActions設置當屬性改變到指定的數值時希望執行的動作(在上面的示例中,當IsPressed屬性值變為true時),也可以使用Trigger.ExitActions設置當屬性改變回原來的數值時執行的動作(當IsPressed屬性的值變回false時)。這是一種封裝一堆互補動畫的簡便方法。

  下麵的按鈕使用上面顯示的樣式:

<Button Padding="10" Name="cmdGrow" Height="40" Width="160" Style="{StaticResource GrowButtonStyle}"
          HorizontalAlignment="Center" VerticalAlignment="Center">
        Click and Make Me Grow
</Button>

  請記住,不見得在樣式中使用屬性觸發器。也可使用事件觸發器,就像在前面介紹的那樣。最後,不見得以與使用樣式的按鈕相分離的方式定義樣式(也可使用內聯樣式設置Button.Style屬性)。但是這種兩部分相分離的方法更常用,並且提供了為多個元素應用相同的靈活性。

三、重疊動畫

  故事板提供了改變處理重疊動畫方式的能力——換句話說,決定第二個動畫何時被應用到已經具有一個正在運行的動畫的屬性上。可使用BeginStoryboard.HandoffBehavior屬性改變處理重疊動畫的方式。

  通常,當兩個動畫相互重疊時,第二個動畫會立即覆蓋第一個動畫。這種行為就是所謂的“快照並替換”(由HandoffBehavior枚舉中的SnapshotAndReplace值表示)。當第二個動畫開始時,第二個動畫獲取屬性當前值(基於第一個動畫)的快照,停止動畫,並用新動畫替換第一個動畫。

  另一個HandoffBehavior選項是Compose,這種方式將第二個動畫融合到第一個動畫的時間線中。例如,分析ListBox示例的修改版本,當縮小按鈕時使用HandoffBehavior.Compose:

<EventTrigger  RoutedEvent="ListBoxItem.MouseLeave">
     <EventTrigger.Actions>
          <BeginStoryboard HandoffBehavior="Compose">
               <Storyboard>
                   <DoubleAnimation  Storyboard.TargetProperty="FontSize"
                                         BeginTime="0:0:0.5" Duration="0:0:0.2"></DoubleAnimation>
                </Storyboard>
            </BeginStoryboard>
      </EventTrigger.Actions>
</EventTrigger>

  現在,如果將滑鼠移到ListBoxItem對象上,然後在移開,將看到不同的行為。當滑鼠移開項時,項會繼續擴張,這種行為非常明顯,知道第二個動畫到達其0.5秒得開始時間延遲,然後,第二個動畫會縮小按鈕。如果不使用Compose行為,在第二個動畫開始之前的0.5秒得時間間隔內,按鈕會處於等待狀態,並固定為當前尺寸。

  使用組合的HandoffBehavior行為需要更大開銷。這是因為當第二個動畫開始時,用於運行原來動畫的時鐘不能被釋放。相反,這個時鐘會繼續保持存活,知道ListBoxItem對象被垃圾回收或為相同的屬性應用新的動畫為止。

四、同步的動畫

  Storyboard類間接地繼承自TimelineGroup類,所以Storyboard類能包含多個動畫,最令人高興的是,這些動畫可以作為一組進行管理——這意味著他們在同一時間開始。

  為查看這個一個示例,分析下麵的故事板。它開始兩個動畫,一個動畫用於按鈕的Width屬性,而另一個動畫用於按鈕的Height屬性。因為動畫被分組到故事板中,它們共同增加按鈕的尺寸,所以可得到比在代碼中通過簡單地多次調用BeginAnimation()方法得到的效果更趨向同步的效果。

 <EventTrigger  RoutedEvent="Button.Click">
       <EventTrigger.Actions>
            <BeginStoryboard>
                 <Storyboard>
                     <DoubleAnimation Storyboard.TargetProperty="Width"
                                         To="300" Duration="0:0:5"></DoubleAnimation>
                     <DoubleAnimation Storyboard.TargetProperty="Height"
                                         To="300" Duration="0:0:5"></DoubleAnimation>
                  </Storyboard>
             </BeginStoryboard>
       </EventTrigger.Actions>
</EventTrigger>

  在這個示例中,兩個動畫具有相同的持續時間,但這並不是必須的,對於在不同時間結束的動畫,唯一需要考慮的是它們的FillBehavior行為。如果一個動畫的FillBehavior屬性被設置為HoldEnd,它會保持值直到故事板中所有的動畫都結束。如果故事板的FillBehavior屬性是HoldEnd,最後那個動畫的值將被永久保存(直到使用新的動畫替換這個動畫或手動刪除了這個動畫)。

  上一章列出的Timeline類的屬性開始變得特別有用。例如,可通過SpeedRatio屬性使故事板中的某個動畫比其他動畫更快,也可以使用BeginTime屬性相對於一個動畫來編譯另一個動畫的開始時間,使該動畫在特定的時間點開始。

五、控制播放

  到目前位置,已在事件觸發器中使用了一個動作——載入動畫的BeginStoryboard動作。然而,一旦創建故事板,就可以用在其他動作控制故事板。這些工作類都繼承自ControllableStoryboardAction類,下表列出了這些類。

表 控制故事板的動作類

   幫助文檔中沒有記載會妨礙使用這些動作的內容。為成功地執行這些動作,必須在同一個Triggers集合中定義所有觸發器。如果將BeginStoryboard動作的觸發器和PauseStoryboard動作的觸發器放置到不同集合中,PauseStoryboard動作就無法工作。為查看需要使用的設計,分析示例是有幫助的。

  例如,分析下圖中顯示的視窗。該視窗使用一個網格在完全相同的位置精確地重疊了兩個Image元素。最初,只有最頂部的圖像可見。但當動畫運行是,該圖像從1到0逐漸地增加透明度,最終使夜間的場景完全蓋過白天場景。效果就像是圖像從白天變換到黑夜,就像連續的隨時間流逝的照片。

   下麵的標記定義了包含兩個圖像的Grid控制項:

<Grid>
       <Image Source="night.jpg"></Image>
       <Image Source="day.jpg" Name="imgDay"></Image>
</Grid>

  下麵是從一幅圖像淡入到另一幅圖像的動畫:

<DoubleAnimation Storyboard.TargetName="imgDay" Storyboard.TargetProperty="Opacity"
                                     From="1" To="0" Duration="0:0:10"></DoubleAnimation>

  為增加這個示例的趣味性,還在底部提供了幾個用於控制動畫播放的按鈕。使用這些按鈕,可執行典型的媒體播放器動作,如暫停、恢復播放以及停止(可添加其他按鈕來改變速度繫數以及挑選特定的時間)。

  下麵的標記定義了這些按鈕:

<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
            <Button Name="cmdStart">Start</Button>
            <Button Name="cmdPause">Pause</Button>
            <Button Name="cmdResume">Resume</Button>
            <Button Name="cmdStop">Stop</Button>
            <Button Name="cmdMiddle">Move To Middle</Button>
</StackPanel>

  通常,可選擇在每個按鈕的Triggers集合中放置事件觸發器。然而,在前面已解釋過,對於動畫這種方法不能工作。最簡單的解決方法是在一個地方定義所有事件觸發器,例如,在包含元素的Triggers集合中,使用EventTrigger.SourceName屬性關聯這些事件觸發器。只要SourceName屬性和為按鈕設置的Name屬性相匹配,觸發器就會應用到恰當的按鈕上。

  這個示例中,可使用包含這些按鈕的StackPanel面板的Triggers集合。然而,使用頂級元素(在這個示例中是視窗)的Triggers集合通常最簡單。這樣,就可在用戶界面中將按鈕移到不同的位置,而不會禁用他們的功能。

 <Window.Triggers>
        <EventTrigger SourceName="cmdStart" RoutedEvent="Button.Click">
            <BeginStoryboard Name="fadeStoryboardBegin">
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetName="imgDay" Storyboard.TargetProperty="Opacity"
                                     From="1" To="0" Duration="0:0:10"></DoubleAnimation>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
        <EventTrigger SourceName="cmdPause" RoutedEvent="Button.Click">
            <PauseStoryboard BeginStoryboardName="fadeStoryboardBegin">
            </PauseStoryboard>
        </EventTrigger>
        <EventTrigger SourceName="cmdResume" RoutedEvent="Button.Click">
            <ResumeStoryboard BeginStoryboardName="fadeStoryboardBegin"></ResumeStoryboard>
        </EventTrigger>
        <EventTrigger SourceName="cmdStop" RoutedEvent="Button.Click">
            <StopStoryboard BeginStoryboardName="fadeStoryboardBegin"></StopStoryboard>
        </EventTrigger>
        <EventTrigger SourceName="cmdMiddle" RoutedEvent="Button.Click">
            <SeekStoryboard BeginStoryboardName="fadeStoryboardBegin"
                            Offset="0:0:5"></SeekStoryboard>
        </EventTrigger>
    </Window.Triggers>

  註意,必須為BeginStoryboard動作指定名稱(在這個示例中,名稱是fadeStoryboardBegin)。其他觸發器通過為BeginStoryboardName屬性指定這個名稱,連接到相同的故事板。

  當使用故事板動作時將遇到限制。他們提供的屬性(如SeekStoryboard.Offset和SetStoryboardSpeedRatio.SpeedRatio屬性)不是依賴性項屬性,這會限制使用數據綁定表達式。例如,不能自動讀取Slider.Value屬性值並將其應用到SetStoryboardSpeedRatio.SpeedRatio動作,因為SpeedRatio屬性不接受數據綁定表達式。可能認為通過使用Storyboard對象的SpeedRatio屬性來解決這個問題。但這是行不同的,當動畫開始時,讀取SpeedRatio值並創建一個動畫時鐘。此後,即使改變了SpeedRatio屬性的值,動畫也仍會保持正常的速度。

  如果希望動態調整速度或位置,唯一的解決方法是使用代碼。Storyboard類中的方法提供了與故事板觸發器相同的功能,包括Begin()、Pause()、Resume()、Seek()、Stop()、SkipToFill()、SetSpeedRatio()以及Remove()方法。

  要訪問Storyboard對象,必須在標記中設置其Name屬性:

<Storyboard Name="fadeStoryboard">

  現在只需要編寫恰當的事件處理程式,並使用Storyboard對象的方法(請記住,簡單地改變故事板的屬性(比如SpeedRatio)是沒有任何效果的,它們僅配置當動畫開始時將要使用的設置)。

  當拖動Slider控制項上的滑塊時,下麵的事件處理程式會進行響應。該事件處理程式獲取滑動條的值(範圍是0~3),並使用該數值應用新的速率:

private void sldSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            fadeStoryboard.SetSpeedRatio(this, sldSpeed.Value);
        }

  註意,SetSpeedRatio()方法需要兩個參數。第一個參數是頂級動畫容器(在這個示例中,是指當前視窗)。所有故事板方法都需要這個引用。第二個參數是新的速率。

六、監視動畫進度

  上一節顯示的動畫播放器仍缺少一個在大多數媒體播放器中都具有的功能——確定當前位置的能力。為使這個動畫播放器更加精緻,可添加一些文本來顯示時間的流逝,並添加進度條來指示動畫只需的速度。下圖顯示了使用這兩個細節的動畫播放器的修改版。

   添加這些細節相當簡單。首先需要使用TextBlock元素顯示時間,而後需要使用ProgressBar控制項顯示圖形進度條,可能認為,可使用數據綁定表達式設置TextBlock值和ProgressBar內容,但這是行不同的。因為從故事板中檢索當前動畫時鐘相關的唯一方式是使用方法,如GetCurrentTime()和GetCurrentProgress()。無法從屬性中獲取相同的信息。

  最簡單的解決方法是響應下表中列出的某個故事板事件。

表 故事板事件

  名    稱    說    明
Completed 動畫已經到達終點
CurrentGlobalSpeedInvalidated 速度發生了變化,或者動畫被暫停、重新開始、停止或移到某個新的位置。當動畫時鐘反轉時(在可反轉動畫的終點),以及當動畫加速和減速時,也會引發該事件
CurrentStateInvalidated 動畫已經開始或結束
CurrentTimeInvalidated 動畫時鐘已經向前移動了一個步長,正在更改動畫。當動畫開始、停止或結束時也會引發該事件
RemoveRequested 動畫正在被移除。使用動畫的屬性隨後會返回為原來的值

  這個示例需要使用CurrentTimeInvalidated事件,每次向前移動動畫時鐘都會引發該事件(通常,每秒移動60此,但如果執行的代碼需要更長時間,可能會丟失時鐘刻度)。

  當引發CurrentTimeInvalidated事件時,發送者是Clock對象(Clock類位於System.Windows.Media.Animation名稱空間)。可以通過Clock對象檢索當前時間,當前時間使用TimeSpan對象表示;並且可檢索當前進度,當前進度使用0~1之間的數值表示。

  下麵的代碼更新標簽和進度條:

 private void storyboard_CurrentTimeInvalidated(object sender, EventArgs e)
        {
            // Sender is the clock that was created for this storyboard.
            Clock storyboardClock = (Clock)sender;

            if (storyboardClock.CurrentProgress == null)
            {
                lblTime.Text = "[[ stopped ]]";
                progressBar.Value = 0;
            }
            else
            {
                lblTime.Text = storyboardClock.CurrentTime.ToString();
                progressBar.Value = (double)storyboardClock.CurrentProgress;
            }
        }

 


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

-Advertisement-
Play Games
更多相關文章
  • Title:ElasticSearch實戰系列四: ElasticSearch的聚合查詢基礎使用教程之度量(Metric)聚合 前言 在上上一篇中介紹了 "ElasticSearch實戰系列三: ElasticSearch的JAVA API使用教程" ,介紹了ElasticSearch Java A ...
  • [toc] 泛型: 首先,本人來介紹一下 什麼是泛型 : 泛型概述 : 是一種把類型明確的工作 推遲到創建對象 或者 調用方法的時候 才去 明確的特殊的類型 。 參數化類型 ,把類型當作參數一樣的傳遞。 通俗一點來講:泛型 是JAVA 中一種十分強大的機制,因為它能夠完成在 未知元素類型 的情況下對 ...
  • 在Java發展的里程碑上,有三個版本做出的改動,是革命性的 為什麼說是革命性的呢? 因為這三個版本所推出的有些新機制,在之後的Java框架開發、新類的產生等等中, 都被廣泛使用了。 那麼,這三個版本的JDK,都有哪些新特性呢? 現在,右轉哥就來帶你剖析這三個版本的JDK的新特性: [toc] 首先是 ...
  • xml配置 1.xml基本結構: 其中id是bean字元串,bean的唯一標識符,相當於對象名,class是bean類名的完全限定路徑 2.別名 起別名有兩種方式, 1.通過alias 2.通過bean中的name屬性 用bean中name更高級,可以起多個別名 IoC創建對象方式 1.通過無參構造 ...
  • 一、屬性(Property)作為類和結構的成員,是對欄位的一種封裝方式,實際上是一種特殊的方法,被稱為訪問器(Accessor),從而隱藏實現和驗證代碼,有助於提高欄位讀取和賦值的安全性和靈活性; 1.屬性訪問器包含兩種類型:用於讀取並返回值的get訪問器,用於賦值新值的set屬性訪問器;通常將欄位 ...
  • Lucene.Net Lucene.net是Lucene的.net移植版本,是一個開源的全文檢索引擎開發包,即它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎。 Lucene.net是Apache軟體基金會贊助的開源項目,基於Apache License協議。 ...
  • 前言 如今 C 雖然發展到了 8.0 版本,引入了諸多的函數式特性,但其實在 C 未來的規劃當中,還有很多足以大規模影響現有 C 代碼結構和組成的特性,本文中將會對就重要的特性進行介紹,並用代碼示例展示這些特性。 以下特性將會在 C 9.0、10.0 或者更高版本提供。 Records Record ...
  • Introduction: 在C#6及以上版本中,加入了一項特別好用的運算符:Null條件運算符?.和?[]可以用來方便的執行判空操作,當運算符左側操作數不為null時才會進行訪問操作,否則直接返回null。這極大的簡化的判空代碼的書寫,但在使用過程中仍然需要註意一些問題,以免其帶來我們意想不到的後 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...