主頁 > .NET開發 > WPF之事件

WPF之事件

2021-02-24 06:05:11 .NET開發

目錄
  • WPF的樹形結構
  • 事件
  • 路由事件
    • 使用WPF內置路由事件
    • 自定義路由事件
      • ButtonBase類的Click路由事件
      • 創建一個路由事件
      • RoutedEventArgs的Source與OriginalSource
    • 附加事件
      • 不使用CLR屬性作為包裝器
      • 使用CLR屬性作為包裝器

就像屬性系統在WPF中得到升級、進化為依賴屬性一樣,事件系統在WPF中也被升級一進化成為路由事件(Routed Event),并在其基礎上衍生出命令傳遞機制

WPF的樹形結構

WPF中有兩種“樹”:一種叫邏輯樹(Logical Tree);一種叫可視元素樹(Visual Tree)
前面見到的所有樹形結構都是Logical Tree,Logical Tree最顯著的特點就是它完全由布局組件和控制元件構成(包括串列類控制元件中的條目元素),它的每個結點不是布局組件就是控制元件,
每個WPF控制元件本身也是一棵由更細微級別的組件(它們不是控制元件,而是一些可視化組件,派生自Visual類)組成的樹,使用Blend可以解剖并觀察一個控制元件的模板(Template)是怎樣的,可以把Template理解為控制元件的骨架,把Logical Tree延伸至Template組件級別,得到的就是Visual Tree

注:如果你的程式需要借助Visual Tree來完成一些與業務邏輯(而不是純表現邏輯)相關的功能,多半是由程式設計不良而造成的,請重新考慮邏輯、功能和資料型別方面的設計,

如果想在Logical Tree 上導航或查找元素,可以借助LogicalTreeHelper類的static方法來實作:

  • BringIntoView:把選定元素帶進用戶可視區域,經常用于可滾動的視圖,
  • FindLogicalNode:按給定名稱(Name屬性值)查找元素,包括子級樹上的元素,
  • GetChildren:獲取所有直接子級元素,
  • GetParent:獲取直接父級元素,

如果想在Visual Tree 上導航或查找元素,則可借助VisualTreeHelper類的static方法來實作,

事件

事件的前身是訊息(Message),訊息本質就是一條資料,這條資料里記載著訊息的類別,必要的時候還記載一些訊息引數(如WM_LBUTTONDOWN訊息所攜帶的引數——滑鼠單擊處的X、Y坐標),也有些訊息是不用攜帶引數的(如按鈕被單擊的訊息——程式員并不關心滑鼠點在按鈕的哪個位置上了),
隨著微軟面向物件開發平臺日趨成熟,微軟把訊息機制封裝成了更容易讓人理解的事件模型,事件模型隱藏了訊息機制的很多細節,訊息驅動機制在事件模型中被簡化為3個關鍵點

  • 事件的擁有者:即訊息的發送者,事件的宿主可以在某些條件下激發它擁有的事件,即事件被觸發,事件被觸發則訊息被發送,
  • 事件的回應者:即訊息的接收者、處理者,事件接收者使用其事件處理器(Event Handler)對事件做出回應,
  • 事件的訂閱關系:事件的擁有者可以隨時激發事件,但事件發生后會不會得到回應要看有沒有事件的回應者(事件是否被關注),

事件的回應者通過訂閱關系直接關聯在事件擁有者的事件上,為了與WPF的路由事件模型區分開,把這種事件模型稱為直接事件模型或者CLR事件模型,在CLR直接事件模型中,事件的擁有者就是訊息的發送者(sender),

只要支持事件的委托與影響事件的方法在簽名上保持一致(即引數串列和回傳值一致),則一個事件可以由多個事件處理器來回應(多播事件)、一個事件處理器也可以用來回應多個事件,

直接事件模型并不完美——事件的回應者與事件擁有者之間必須建立事件訂閱這個“專線聯系”,至少有兩個弊端:

  • 每對訊息是“發送一回應”關系,必須建立顯式的點對點訂閱關系,
  • 事件的宿主必須能夠直接訪問事件的回應者,不然無法建立訂閱關系,

直接事件模型的弱點會在下面兩種情況中顯露出來:

  • 程式運行期在容器中動態生成一組相同控制元件,每個控制元件的同一個事件都使用同一個事件處理器來回應,在動態生成控制元件的同時就需要顯式書寫事件訂閱代碼
  • 用戶控制元件的內部事件不能被外界所訂閱,必須為用戶控制元件定義新的事件用以向外界暴露內部事件,如果想讓很外層的容器訂閱深層控制元件的某個事件就需要為每一層組件定義用于暴露內部事件的事件、形成事件鏈

路由事件

路由(Roule):起點與終點間有若干個中轉站,從起點出發后經過每個中轉站時要做出選擇,最終以正確(比如最短或者最快)的路徑到達終點,

從Windows AP1開發到傳統的.NE開發,訊息的傳遞(或者說事件的激發與回應)都是直接模式的,即訊息直接由發送者交給接收者(或者說事件宿主發生的事件直接由事件回應者的事件處理器來處理),
WPF把這種直接訊息模型升級為可傳遞的訊息模型——WPF的UI是由布局組件和控制元件構最的樹形結構,當這棵樹上的某個結點激發出某個事件時,程式員可以選擇以傳統的直接事件模式讓回應者來回應之,也可以讓這個事件在UI組件樹沿著一定的方向傳遞且路過多個中轉結點,并在這個路由程序中被恰當地處理,

路由事件與直接事件的區別在于:

  • 直接事件激發時,發送者直接將訊息通過事件訂閱交送給事件回應者,事件回應者使用其事件處理器方法對事件的發生做出回應、驅動程式邏輯按客戶需求運行;
  • 路由事件的事件擁有者和事件回應者之間則沒有直接顯式的訂閱關系,事件的擁有者只負責激發事件,事件將由誰回應它并不知道,事件的回應者則安裝有事件偵聽器,針對某類事件進行偵聽,當有此類事件傳遞至此時事件回應者就使用事件處理器來回應事件并決定事件是否可以繼續傳遞,

盡管WPF推出了路由事件機制,但它仍然支持傳統的直接事件模型,

注:WPF的UI可以表示為Logical Tree和Visual Tree,當一個路由事件被激發后是沿著Visual Tree傳遞的——只有這樣,“藏”在Template里的控制元件才能把訊息送出來

使用WPF內置路由事件

WPF系統中的大多數事件都是可路由事件,以Button的Click事件來說明路由事件的使用,XAML代碼如下:

<Grid x:Name="gridRoot" Background="Lime">
    <Grid x:Name="gridA" Margin="10" Background="Blue">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Canvas x:Name="canvasLeft" Grid.Column="0" Background="Red" Margin="10">
            <Button x:Name="buttonLeft" Content="Left" Width="40" Height="100" Margin="10"/>
        </Canvas>
        <Canvas x:Name="canvasRight" Grid.Column="1" Background="Yellow" Margin="10">
            <Button x:Name="butonRight" Content="Right" Width="40" Height="100" Margin="10"/>
        </Canvas>
    </Grid>
</Grid>

下面為gridRoot安裝針對Button.Click事件的偵聽器,C#代碼如下:

//AddHandler方法源自UIElement類,所有UI控制元件都具有這個方法
this.gridRoot.AddHandler(Button.ClickEvent, new RoutedEventHandler(this.ButtonClicked));

WPF的事件系統也使用了與屬性系統類似的“靜態欄位一包裝器”的策略,路由事件本身是一個RoutedEvent 型別的靜態成員變數(Button.ClickEvent),Button還有一個與之對應的Click事件(CLR包裝)專門用于對外界暴露這個事件,效仿依賴屬性,把路由事件的CLR包裝稱為“CLR事件”,就像每個依賴屬性擁有自己的CLR屬性包裝一樣,每個路由事件都擁有自己的CLR事件,

在XAML里也可以完成,代碼如下:

<Grid x:Name="gridRoot" Background="Lime" ButtonBase.Click="ButtonClicked">
     <!--原有內容-->   
</Grid>

建議使用ButtonBase.Click而不是Button.Click,因為ClickEvent這個路由事件是ButonBase類的靜態成員變數(Button類是通過繼承獲得它的),而XAML編輯器只認得包含ClickEvent欄位定義的類,

上面的代碼讓最外層的Grid(gridRoot)能夠捕捉到從內部“飄”出來的按鈕單擊事件,捕捉到后會用this.ButonClicked方法來進行回應處理,ButtonClicked方法代碼如下:

private void ButtonClicked(object sender,RoutedEventArgs e)
{
    MessageBox.Show((e.OriginalSource as FrameworkElement).Name);
}

傳入ButtonClicked方法的引數sender實際上是gridRoot而不是被單擊的Button,如果想查看事件的源頭(最初發起者)可使用e.OriginalSource,使用它的時候需要使用as/is運算子或者強制型別轉換把它識別/轉換為正確的型別,

運行程式并單擊右邊的按鈕,效果如下:

自定義路由事件

創建自定義路由事件大體可以分為三個步驟:

  • 宣告并注冊路由事件,
  • 為路由事件添加CLR事件包裝,
  • 創建可以激發路由事件的方法,

ButtonBase類的Click路由事件

下面以從ButtonBase類中抽取出的代碼為例來展示這3個步驟,此處對代碼做了些簡化:

public abstract class ButtonBase : ContentControl,ICommandSource
{
    //宣告并注冊路由事件
    public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent
        ("Click", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(ButtonBase));

    //為路由事件添加CLR事件包裝器
    public event RoutedEventHandler Click
    {
        add { this.AddHandler(ClickEvent, value); }
        remove { this.RemoveHandler(ClickEvent, value);}
    }

    //激發路由事件的方法,此方法在用戶單擊滑鼠時會被Windows系統呼叫
    protected virtual void OnClick() 
    {
        RoutedEventArgs newEvent = new RoutedEventArgs(ButtonBase.ClickEvent,this);
        this.RaiseEvent(newEvent);
        //..
    }

    //..
}

定義路由事件:為類宣告一個由public static readonly修飾的RoutedEvent 型別欄位,然后使用EventManager類的RegisterRoutedEvent方法進行注冊,

為路由事件添加CLR事件包裝:與使用CLR屬性包裝依賴屬性的代碼格式非常相近,只是關鍵字get和set被替換為add和remove:

  • 當使用運算子(+=)添加對路由事件的偵聽處理時,add分支的代碼會被呼叫,
  • 當使用運算子(-=)移除對此事件的偵聽處理時,remove分支的代碼會被呼叫,
    注:CLR事件只是“看上去像”一個直接事件,本質上不過是在當前元素(路由的第一站)上呼叫AddHandler和RemoveHandler而已,XAML編輯器也是靠這個CLR事件包裝器來產生自動提示,

激發路由事件:首先創建需要讓事件攜帶的訊息(RoutedEventArgs類的實體)并把它與路由事件關聯,然后呼叫元素的RaiseEvent方法(繼承自UIElement類)把事件發送出去,
注:傳統直接事件的激發是通過呼叫CLR事件的Invoke方法實作的,而路由事件的激發與作為其包裝器的CLR事件毫不相干

EventManager.RegisterRoutedEvent方法的四個引數:

  • 第一個引數:為string型別,被稱為路由事件的名稱,這個字串應該與RoutedEvent變數的前綴和CLR事件包裝器的名稱一致,字串不能為空(需要使用這個字串去生成用于注冊路由事件的Hash Code),
  • 第二個引數:稱為路由事件的策略,是一個RoutingStrategy列舉值,
  • 第三個引數:用于指定事件處理器的型別,事件處理器的回傳值型別和引數串列必須與此引數指定的委托保持一致,不然會導致在編譯時拋出例外,
  • 第四個引數:用于指明路由事件的宿主(擁有者)是哪個型別,這個型別和第一個引數共同參與一些底層演算法且產生這個路由事件的Hash Code并被注冊到程式的路由事件串列中,

WPF路由事件有3種路由策略,即RoutingStrategy列舉有三個值:

  • Bubble(冒泡式):路由事件由事件的激發者出發向它的上級容器一層一層路由,直至最外層容器(Window或者Page),
  • Tunnel(隧道式):事件的路由方向正好與Bubble策略相反,是由UI樹的樹根向事件激發控制元件移動,
  • Direct(直達式):模仿CLR直接事件,直接將事件訊息送達事件處理器,

創建一個路由事件

下面創建一個路由事件,用途是報告事件發生的時間,創建一個RoutedEventArgs類的派生類,并為其添加ClickTime屬性:

//用于承載時間訊息的事件引數
class ReportTimeEventArgs : RoutedEventArgs
{
    public ReportTimeEventArgs(RoutedEvent routedEvent, object source)
        : base(routedEvent, source) { }

    public DateTime ClickTime { get; set; }
}

再創建一個Button類的派生類并按前述步驟為其添加路由事件:

class TimeButton : Button 
{
    //宣告和注冊路由事件
    public static readonly RoutedEvent ReportTimeEvent = EventManager.RegisterRoutedEvent
        ("ReportTime",RoutingStrategy.Bubble,typeof(EventHandler<ReportTimeEventArgs>),typeof(TimeButton));
    //CLR事件包裝器
    public event RoutedEventHandler ReportTime
    {
        add { this.AddHandler(ReportTimeEvent, value); }
        remove { this.RemoveHandler(ReportTimeEvent, value); } 
    }
    //激發路由事件,借用Click事件的激發方法
    protected override void OnClick()
    {
        base.OnClick(); //保證Button原有功能正常使用、Click事件能被激發

        ReportTimeEventArgs args = new ReportTimeEventArgs(ReportTimeEvent, this);
        args.ClickTime = DateTime.Now;
        this.RaiseEvent(args);
    }
}

程式的界面XAML代碼如下:

<!--省略Window的部分代碼-->
<Window x:Name="windows_1" local:TimeButton.ReportTime="ReportTimeHandler">
    <Grid x:Name="grid_1" local:TimeButton.ReportTime="ReportTimeHandler">
        <Grid x:Name="grid_2" local:TimeButton.ReportTime="ReportTimeHandler">
            <Grid x:Name="grid_3" local:TimeButton.ReportTime="ReportTimeHandler">
                <StackPanel x:Name="stackPanel_1" local:TimeButton.ReportTime="ReportTimeHandler">
                    <ListBox x:Name="listBox"/>
                    <local:TimeButton x:Name="timeButton" Width="80" Height="80" Content="報時" local:TimeButton.ReportTime="ReportTimeHandler"/>
                </StackPanel>
            </Grid>
        </Grid>
    </Grid>
</Window>

ReportTimeHandler的代碼如下:

//ReportTimeEvent 路由事件處理器
private void ReportTimeHandler(object sender, ReportTimeEventArgs e) 
{

    FrameworkElement element = sender as FrameworkElement;
    string timeStr = e.ClickTime.ToLongTimeString();
    string content = string.Format("{0}到達{1}", timeStr, element.Name);
    this.listBox.Items.Add(content);
}

效果如下:

為TimeButton注冊ReportTimeEvent時使用的是Bubble策略,所以事件是沿這樣的路徑由內向外傳遞的:TimeButton→StackPanel→Grid→Grid→Grid→Window,
如果把TimeReportEvent的策略改為Tunnel,則正好與Bubble策略相反,Tunnel策略使事件沿著從外向內的路徑傳遞:Window→Grid→Grid→Grid→StackPanel→TimeButton,

路由事件攜帶的事件引數必須是RoutedEventArgs類或其派生類的實體,RoutedEventArgs類具有一個bool型別屬性Handled,一旦這個屬性被設定為true,就表示路由事件“已經被處理”了(Handle有“處理”、“搞定”的意思),那么路由事件也就不必再往下傳遞了,
如果把上面的ReportTimeEvent處理器修改為這樣:

//ReportTimeEvent 路由事件處理器
private void ReportTimeHandler(object sender, ReportTimeEventArgs e) 
{

    FrameworkElement element = sender as FrameworkElement;
    string timeStr = e.ClickTime.ToLongTimeString();
    string content = string.Format("{0}到達{1}", timeStr, element.Name);
    this.listBox.Items.Add(content);

    if (element == this.grid_2)
    {
        e.Handled = true;
    }
}

效果如下:

e.Handled被設定為true,無論是Bubble策略還是Tunnel策略,路由事件在經過grid_2后就被處理了、不再向下傳遞,

路由事件將程式中的組件進一步解耦(比用直接事件傳遞訊息還要松散),需要注意的是:

  • 很多類的事件都是路由事件,如TextBox類的TextChanged 事件、Binding類的SourceU/pdated事件等,不要墨守傳統NET編程帶來的習慣,活用路由事件
  • 路由事件雖好,但也不要濫用,如讓表單捕捉并處理所有Button的Click 事件,正確的辦法是,事件該由誰來描捉處理,待到這個地方時就應該處理掉

RoutedEventArgs的Source與OriginalSource

路由事件是沿著VisualTree傳遞的,VisualTree與LogicalTree的區別就在于:LogicalTree的葉子結點是構成用戶界面的控制元件,而VisualTree要連控制元件中的細微結構也算上

路由事件的訊息包含在RoutedEventArgs實體中,Source和OriginalSource都表示路由事件傳遞的起點(即事件訊息的源頭),區別在于:

  • Source表示的是LogicalTree上的訊息源頭
  • OriginalSource則表示VisualfTree上的源頭

創建了一個名為MyUserControl的UserControl,XAML代碼如下(沒有C#邏輯代碼):

<!--省略UserControl部分代碼-->
<Grid>
    <Border BorderBrush="Orange" BorderThickness="3" CornerRadius="5">
        <Button x:Name="innerButon" Width="80" Height="80" Content="OK"/>
    </Border>
</Grid>

把這個UserControl添加到主表單中:

<Grid>
    <local:MyUserControl x:Name="myUserControl" Margin="10"/>
</Grid>

在后臺代碼中為主表單添加對Button.Click路由事件的偵聽:

public MainWindow()
{
    InitializeComponent();

    //為主表單添加對Button.Click事件的偵聽
    this.AddHandler(Button.ClickEvent,new RoutedEventHandler(this.Button_Click));
}

//路由事件處理器
private void Button_Click(object sender, RoutedEventArgs e) 
{
    string strOriginalSource = string.Format("VisualTree start point:{0},type is {1}",
        (e.OriginalSource as FrameworkElement).Name,e.OriginalSource.GetType().Name);
    string strSource = string.Format("LogicalTree start point:{0},type is {1}",
        (e.Source as FrameworkElement).Name, e.Source.GetType().Name); 
    MessageBox.Show(strOriginalSource + "\r\n" + strSource);
}

效果如下:

Button.Click 路由事件是從MyUserControl的innerButton 發出來的,主表單中myUserControl是LogicalTree的末端結點,而表單的VisualTree則包含了myUserControl的內部結構,所以e.Source是myUserControl、e.OriginalSource是innerButton,

附加事件

在WPF事件系統中還有一種事件被稱為附加事件(Attached Event),它就是路由事件,擁有附加事件的類有:

  • Binding類:SourceUpdated事件、TargetUpdated事件,
  • Mouse類:MouseEnter 事件、MouseLeave 事件、MouseDown事件、MouseUp事件等,
  • Keyboard類:KeyDown事件、KeyUp事件等,

對比一下那些擁有路由事件的類,路由事件的宿主都是些擁有可視化物體的界面元素,而附加事件則不具備顯示在用戶界面上的能力

不使用CLR屬性作為包裝器

設計一個名為Student的類,如果Student實體的Name屬性值發生了變化就激發一個路由事件,使用界面元素來捕捉這個事件,這個類的代碼如下:

public class Student
{
    //宣告并定義路由事件
    public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRoutedEvent
        ("NameChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student));
   
    public int Id { get; set; }
    public string Name { get; set; }
}

設計一個簡單的界面:

<Grid x:Name="gridMain">
    <Button x:Name="button1" Content="OK" Width="80" Height="80" Click="Button_Click"/>
</Grid>

后臺代碼如下:

public MainWindow()
{
    InitializeComponent();
    // 為外層Grid添加路由事件偵聽器
    this.gridMain.AddHandler(Student.NameChangedEvent, new RoutedEventHandler(this.StudentNameChangedHandler));
}

//Click 事件處理器
private void Button_Click(object sender,RoutedEventArgs e)
{
    Student stu = new Student(){ Id = 101,Name = "Tim"}; 
    stu.Name = "Tom";
    //準備事件訊息并發送路由事件
    RoutedEventArgs arg = new RoutedEventArgs(Student.NameChangedEvent, stu); 
    this.button1.RaiseEvent(arg);
}

//Grid 捕捉到NameChangedEvent后的處理器
private void StudentNameChangedHandler(object sender, RoutedEventArgs e)
{
    MessageBox.Show((e.OriginalSource as Student).Id.ToString());
}    

注:因為Student不是UIElement的派生類,所以它不具有RaiseEvent這個方法,為了發送路由事件就不得不“借用”一下Button的RaiseEvent方法了

運行程式并單擊按鈕,效果如下:

Student類并非派生自UIElement,因此亦不具備AddHandler和RemoveHandler這兩個方法,所以不能使用CLR屬性作為包裝器(因為CLR屬性包裝器的add和remove分支分別呼叫當前物件的AddHandler和RemoveHandler),

使用CLR屬性作為包裝器

微軟的官方檔案約定要為附加事件添加一個CLR包裝以便XAML編輯器識別并進行智能提示:

  • 為目標UI元素添加附加事件偵聽器的包裝器是一個名為Add*Handler的public static方法,星號代表事件名稱(與注冊事件時的名稱一致),
  • 解除UI元素對附加事件偵聽的包裝器是名為RemoveHandler的public static方法,星號亦為事件名稱,引數與AddHandler一致,
  • AddHandler與RemoveHandler的引數一致,接收兩個引數:第一個引數是事件的偵聽者(型別為DependencyObject),第二個引數為事件的處理器(RoutedEventHandler委托型別),

按照規范,Student類被升級為這樣:

public class Student
{
    //宣告并定義路由事件
    public static readonly RoutedEvent NameChangedEvent = EventManager.RegisterRoutedEvent
        ("NameChanged", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Student));

    //為界面元素添加路由事件偵聽
    public static void AddNameChangedHandler(DependencyObject d,RoutedEventHandler h)
    {
        UIElement e = d as UIElement; 
        if (e!= null)
        {
            e.AddHandler(Student.NameChangedEvent, h);
        }
    }

    //移除偵聽
    public static void RemoveNameChangedHandler(DependencyObject d, RoutedEventHandler h)
    {
        UIElement e = d as UIElement;
        if (e != null)
        {
            e.RemoveHandler(Student.NameChangedEvent, h);
        }
    }

    public int Id { get; set; }
    public string Name { get; set; }
}

原來的代碼只有添加事件偵聽一處需要改動:

//為外層Grid添加路由事件偵聽器
Student.AddNameChangedHandler(this.gridMain, new RoutedEventHandler(this.StudentNameChangedHandler));

UIElement類是路由事件宿主與附加事件宿主的分水嶺,因為從UIElement類開始才具備了在界面上顯示的能力且RaiseEvent、AddHandler和RemoveHandler這些方法也定義在UIElement類中,

附加事件只能算是路由事件的一種用法而非一個新概念,如果在一個非UIElement派生類中注冊了路由事件,則這個類的實體既不能自己激發(Raise)此路由事件也無法自己偵聽此路由事件,只能把這個事件的激發“附著”在某個具有RaiseEvent方法的物件上,借助這個物件的RaiseEvent方法把事件發送出去;事件的偵聽任務也只能交給別的物件去做,

使用附加事件時需注意:

  • 路由事件路由時的第一站就是事件的激發者,附加事件路由的第一站是激發它的元素
  • 實際上很少會把附加事件定義在Student這種與業務邏輯相關的類中,一般都是定義在像Binding、Mouse、Keyboard這種全域的Helper類中,

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/262755.html

標籤:WPF

上一篇:WPF 如何修改button圓角(經典)

下一篇:Oracle決議XML字串

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • WebAPI簡介

    Web體系結構: 有三個核心:資源(resource),URL(統一資源識別符號)和表示 他們的關系是這樣的:一個資源由一個URL進行標識,HTTP客戶端使用URL定位資源,表示是從資源回傳資料,媒體型別是資源回傳的資料格式。 接下來我們說下HTTP. HTTP協議的系統是一種無狀態的方式,使用請求/ ......

    uj5u.com 2020-09-09 22:07:47 more
  • asp.net core 3.1 入口:Program.cs中的Main函式

    本文分析Program.cs 中Main()函式中代碼的運行順序分析asp.net core程式的啟動,重點不是剖析原始碼,而是理清程式開始時執行的順序。到呼叫了哪些實體,哪些法方。asp.net core 3.1 的程式入口在專案Program.cs檔案里,如下。ususing System; us ......

    uj5u.com 2020-09-09 22:07:49 more
  • asp.net網站作為websocket服務端的應用該如何寫

    最近被websocket的一個問題困擾了很久,有一個需求是在web網站中搭建websocket服務。客戶端通過網頁與服務器建立連接,然后服務器根據ip給客戶端網頁發送資訊。 其實,這個需求并不難,只是剛開始對websocket的內容不太了解。上網搜索了一下,有通過asp.net core 實作的、有 ......

    uj5u.com 2020-09-09 22:08:02 more
  • ASP.NET 開源匯入匯出庫Magicodes.IE Docker中使用

    Magicodes.IE在Docker中使用 更新歷史 2019.02.13 【Nuget】版本更新到2.0.2 【匯入】修復單列匯入的Bug,單元測驗“OneColumnImporter_Test”。問題見(https://github.com/dotnetcore/Magicodes.IE/is ......

    uj5u.com 2020-09-09 22:08:05 more
  • 在webform中使用ajax

    如果你用過Asp.net webform, 說明你也算是.NET 開發的老兵了。WEBform應該是2011 2013左右,當時還用visual studio 2005、 visual studio 2008。后來基本都用的是MVC。 如果是新開發的專案,估計沒人會用webform技術。但是有些舊版 ......

    uj5u.com 2020-09-09 22:08:50 more
  • iis添加asp.net網站,訪問提示:由于擴展配置問題而無法提供您請求的

    今天在iis服務器配置asp.net網站,遇到一個問題,記錄一下: 問題:由于擴展配置問題而無法提供您請求的頁面。如果該頁面是腳本,請添加處理程式。如果應下載檔案,請添加 MIME 映射。 WindowServer2012服務器,添加角色安裝完.netframework和iis之后,運行aspx頁面 ......

    uj5u.com 2020-09-09 22:10:00 more
  • WebAPI-處理架構

    帶著問題去思考,大家好! 問題1:HTTP請求和回傳相應的HTTP回應資訊之間發生了什么? 1:首先是最底層,托管層,位于WebAPI和底層HTTP堆疊之間 2:其次是 訊息處理程式管道層,這里比如日志和快取。OWIN的參考是將訊息處理程式管道的一些功能下移到堆疊下端的OWIN中間件了。 3:控制器處理 ......

    uj5u.com 2020-09-09 22:11:13 more
  • 微信門戶開發框架-使用指導說明書

    微信門戶應用管理系統,采用基于 MVC + Bootstrap + Ajax + Enterprise Library的技術路線,界面層采用Boostrap + Metronic組合的前端框架,資料訪問層支持Oracle、SQLServer、MySQL、PostgreSQL等資料庫。框架以MVC5,... ......

    uj5u.com 2020-09-09 22:15:18 more
  • WebAPI-HTTP編程模型

    帶著問題去思考,大家好!它是什么?它包含什么?它能干什么? 訊息 HTTP編程模型的核心就是訊息抽象,表示為:HttPRequestMessage,HttpResponseMessage.用于客戶端和服務端之間交換請求和回應訊息。 HttpMethod類包含了一組靜態屬性: private stat ......

    uj5u.com 2020-09-09 22:15:23 more
  • 部署WebApi隨筆

    一、跨域 NuGet參考Microsoft.AspNet.WebApi.Cors WebApiConfig.cs中配置: // Web API 配置和服務 config.EnableCors(new EnableCorsAttribute("*", "*", "*")); 二、清除默認回傳XML格式 ......

    uj5u.com 2020-09-09 22:15:48 more
最新发布
  • C#多執行緒學習(二) 如何操縱一個執行緒

    <a href="https://www.cnblogs.com/x-zhi/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2943582/20220801082530.png" alt="" /></...

    uj5u.com 2023-04-19 09:17:20 more
  • C#多執行緒學習(二) 如何操縱一個執行緒

    C#多執行緒學習(二) 如何操縱一個執行緒 執行緒學習第一篇:C#多執行緒學習(一) 多執行緒的相關概念 下面我們就動手來創建一個執行緒,使用Thread類創建執行緒時,只需提供執行緒入口即可。(執行緒入口使程式知道該讓這個執行緒干什么事) 在C#中,執行緒入口是通過ThreadStart代理(delegate)來提供的 ......

    uj5u.com 2023-04-19 09:16:49 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    <a href="https://www.cnblogs.com/huangxincheng/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/214741/20200614104537.png" alt="" /&g...

    uj5u.com 2023-04-18 08:39:04 more
  • 記一次 .NET某醫療器械清洗系統 卡死分析

    一:背景 1. 講故事 前段時間協助訓練營里的一位朋友分析了一個程式卡死的問題,回過頭來看這個案例比較經典,這篇稍微整理一下供后來者少踩坑吧。 二:WinDbg 分析 1. 為什么會卡死 因為是表單程式,理所當然就是看主執行緒此時正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k # ......

    uj5u.com 2023-04-18 08:33:10 more
  • SignalR, No Connection with that ID,IIS

    <a href="https://www.cnblogs.com/smartstar/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/u36196.jpg" alt="" /></a>...

    uj5u.com 2023-03-30 17:21:52 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:15:33 more
  • 一次對pool的誤用導致的.net頻繁gc的診斷分析

    <a href="https://www.cnblogs.com/dotnet-diagnostic/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/3115652/20230225090434.png" alt=""...

    uj5u.com 2023-03-28 10:13:31 more
  • C#遍歷指定檔案夾中所有檔案的3種方法

    <a href="https://www.cnblogs.com/xbhp/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/957602/20230310105611.png" alt="" /></a&...

    uj5u.com 2023-03-27 14:46:55 more
  • C#/VB.NET:如何將PDF轉為PDF/A

    <a href="https://www.cnblogs.com/Carina-baby/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/2859233/20220427162558.png" alt="" />...

    uj5u.com 2023-03-27 14:46:35 more
  • 武裝你的WEBAPI-OData聚合查詢

    <a href="https://www.cnblogs.com/podolski/" target="_blank"><img width="48" height="48" class="pfs" src="https://pic.cnblogs.com/face/616093/20140323000327.png" alt="" /><...

    uj5u.com 2023-03-27 14:46:16 more