2017-06-18 47 views
3

簡而言之:我想聲明一個自定義路由事件,同時從聲明它的相同用戶控件中聽它。WPF - 聲明一個自定義路由事件並收聽它

我想實現的是有一個服務於特定任務要求的用戶控件,所以我想象這樣的情景:

  • 用戶控件類聲明的自定義路由事件
  • 用戶控制類聽經的AddHandler自己的定製路由事件(...)

然後:

  • 視覺樹中的一些隨機項使用RaiseEvent(...)來......好,提高事件。
  • 樹中用戶控件的一個實例服務於請求。

它似乎沒有工作。我知道,這與用戶控件聲明和提升事件的通常場景有點不同,所以我做了一些測試。


如何創建自定義路由事件似乎很清楚,這不是我第一次這樣做。我創建了一個示例用戶控件,並且它的代碼如下:

public partial class FuffaControl : UserControl 
    { 
     public static readonly RoutedEvent FuffaEvent = EventManager.RegisterRoutedEvent("Fuffa", RoutingStrategy.Bubble, typeof(FuffaEventHandler), typeof(FuffaControl)); 
     // Provide CLR accessors for the event 
     public event FuffaEventHandler Fuffa 
     { 
      add { AddHandler(FuffaEvent, value); } 
      remove { RemoveHandler(FuffaEvent, value); } 
     } 

     public FuffaControl() 
     { 
      InitializeComponent(); 
     } 
    } 

到目前爲止,太棒了。然後,爲了測試目的,我已經聲明瞭一個窗口,裏面有自定義控件和一個按鈕。這是該窗口的內容:

<Grid> 
      <local:FuffaControl> 
        <Grid> 
          <Button Content="Fuffa" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click"/> 
        </Grid> 
      </local:FuffaControl> 
    </Grid> 

在後面的代碼,我使用的AddHandler聽的事件,我引發事件的按鈕的點擊:

public MainWindow() 
    { 
     InitializeComponent(); 

     this.AddHandler(FuffaControl.FuffaEvent, new FuffaEventHandler(OnFuffaEvent)); 
    } 

    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     RoutedEventArgs newEventArgs = new RoutedEventArgs(FuffaControl.FuffaEvent); 
     RaiseEvent(newEventArgs); 
    } 

    private void OnFuffaEvent(object sender, RoutedEventArgs e) 
    { 
    } 

它的工作原理。它沒有什麼意義(我的意思是沒有用),但我只是用它來測試事件本身是否正常工作。好吧,除非C#正在做一些奇怪的事情並切割幾個角落,但乍看之下,在我看來,按鈕正在引發一個自定義事件,事件在樹中傳播(畢竟這是一個冒泡事件),並且窗口收到它。

因此,接下來我將AddHandler(...)調用和處理函數移動到用戶控件中;現在不是聽Raisevent(...)的窗口,而是用戶自己控制。如果一切正常,我有孩子元素引發一個事件,並且父的用戶控制管理它:

public partial class FuffaControl : UserControl 
{ 
    public static readonly RoutedEvent FuffaEvent = EventManager.RegisterRoutedEvent("Fuffa", RoutingStrategy.Bubble, typeof(FuffaEventHandler), typeof(FuffaControl)); 
    // Provide CLR accessors for the event 
    public event FuffaEventHandler Fuffa 
    { 
     add { AddHandler(FuffaEvent, value); } 
     remove { RemoveHandler(FuffaEvent, value); } 
    } 

    public FuffaControl() 
    { 
     InitializeComponent(); 

     this.AddHandler(FuffaControl.FuffaEvent, new FuffaEventHandler(OnFuffaEvent)); 
    } 

    private void OnFuffaEvent(object sender, RoutedEventArgs e) 
    { 
    } 
} 

Nnnnnope。它不起作用。爲什麼?這有什麼問題?爲什麼用戶控制不能聽自己的事件?

回答

4

的原因是在於你如何提高該事件:

private void Button_Click(object sender, RoutedEventArgs e) 
{ 
    RoutedEventArgs newEventArgs = new RoutedEventArgs(FuffaControl.FuffaEvent); 
    RaiseEvent(newEventArgs); 
} 

路由事件(像普通的.NET事件)的源(發送者)和參數。您只指定參數,發件人是您調用RaiseEvent的控件。你可以從MainWindow這個類中做到這一點,所以事件源將是MainWindow,而不是按鈕(按照你可能注意到,按鈕根本不參與你的事件提升代碼)。 WPF將搜索處理程序從發件人開始的路由事件,然後根據事件類型升級或降級。在你的情況下,事件冒泡,所以它會從MainWindow開始搜索樹。你的控制是窗口的子節點,所以它的處理程序不會被找到。

相反,你應該打電話RaiseEvent按鈕。按鈕,然後將發送者,也將努力爲您期望:

private void Button_Click(object sender, RoutedEventArgs e) { 
    ((FrameworkElement) sender).RaiseEvent(new RoutedEventArgs(FuffaControl.FuffaEvent)); 
} 
+0

有人殺了我,請。 – motoDrizzt

+1

從這個評論是不清楚,如果我的回答是否有幫助:) – Evk

+0

是的,幫助:現在我相信是時候掛斷鍵盤和改變工作。 – motoDrizzt

1

乍一看,您似乎將UserControl改爲ContentControl。不過,您可以通過將所有邏輯移至UC並從窗口的Button_Click處理程序中調用公共RaiseFuffaEvent來實現此目的。

FuffaControl.xaml。CS

public partial class FuffaControl : UserControl 
{ 
    public FuffaControl() 
    { 
     InitializeComponent(); 
     AddHandler(FuffaEvent, new RoutedEventHandler(OnFuffaEvent)); 
    } 

    public static readonly RoutedEvent FuffaEvent = EventManager.RegisterRoutedEvent(
     "Fuffa", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(FuffaControl)); 

    public event RoutedEventHandler Fuffa 
    { 
     add { AddHandler(FuffaEvent, value); } 
     remove { RemoveHandler(FuffaEvent, value); } 
    } 

    public void RaiseFuffaEvent() 
    { 
     RoutedEventArgs newEventArgs = new RoutedEventArgs(FuffaEvent); 
     RaiseEvent(newEventArgs); 
    } 

    private void OnFuffaEvent(object sender, RoutedEventArgs e) 
    { 

    } 
} 

使用這種實現的RaiseFuffaEvent,其他控件仍然可以監聽的事件,即使它從窗口的代碼背後升起。

Window.xaml

<Window 
    ...> 
    <Grid> 
     <local:FuffaControl x:Name="fuffa"> 
      <Button Content="Fuffa" HorizontalAlignment="Center" VerticalAlignment="Center" Click="Button_Click"/> 
     </local:FuffaControl> 
    </Grid> 
</Window> 

Window.xaml.cs

public partial class MainWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    private void Button_Click(object sender, RoutedEventArgs e) 
    { 
     fuffa.RaiseFuffaEvent(); 
    } 
} 

爲了完整起見,在一個 「正常」 的UserControl您會將按鈕FuffaControl.xaml並建立Button_Click處理程序FuffaControl.xaml.cs

+1

但在你'RaiseFuffaEvent'你只需要調用'OnFuffaEvent'直接,繞過整個事件系統。 – Evk

+0

@Evk採取點,更新我的代碼。不過,你的解決方案更好。 – Funk

+0

@Funk你的解決方案沒問題,但它以某種方式擊敗了我想要實現的目標。我基本上試圖建立一個駐留在可視化樹(甚至是多個實例)中的「服務提供者」,並且對每個人都是透明的:簡單地說,一個用戶控件轉發請求,最近的「服務提供者」將滿足它,但沒有人知道每個其他職位甚至存在的事情。但thx的幫助無論如何:-) – motoDrizzt