教程4:NoesisGUI中的事件

2020-03-13  本文已影响0人  YottaYuan

NoesisGUI中的事件

github-教程数据

NoesisGUI是一个事件驱动的框架,其中所有控件都公开了您可以订阅的一系列事件。您可以订阅这些事件,这意味着它们发生时将通知您的应用程序,您可以对此做出反应。

事件的类型很多,但是其中一些最常用的事件是用来响应用户的交互的。在大多数控件上,您会发现诸如KeyDownKeyUpMouseDownMouseUpTouchDownTouchUp之类的事件。例如,在UIElement文档的事件部分中,您可以找到该类公开的所有事件的列表。其余课程相同。

注意

在“ 事件概述”文档中找到有关事件的更多信息

订阅

可以使用两种不同的方式来订阅NoesisGUI中的事件:有和没有code-behind。使用命令的第三种选择是将视图与模型完全分离。

直接订阅

预订事件的最简单方法是使用委托直接向事件添加回调。使用FindName可以访问该对象,因此您需要使用x:Name关键字设置所需实例的名称。例如:

<Grid
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

  <Button x:Name="button" Width="100" VerticalAlignment="Center" Content="Click me"/>

</Grid>

C ++

Ptr<Grid> root = Noesis::GUI::LoadXaml<Grid>("Grid.xaml");
Button* button = root->FindName<Button>("button");
button->Click() += [](BaseComponent* sender, const RoutedEventArgs& args)
{
    printf("Button was clicked");
};

C#

Grid root = (Grid)Noesis.GUI.LoadXaml("Grid.xaml");
Button button = (Button)root.FindName("button");
button.Click += (object sender, RoutedEventArgs args) =>
{
    System.Console.WriteLine("Button was clicked");
};

后台代码订阅

直接预订的替代方法是使用代码隐藏类,并使用方法名称在XAML中连接事件。这些方法名称需要使用正确的事件签名在代码隐藏类中实现。例如:

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      VerticalAlignment="Center"
      x:Class="MyGrid">
  <Button Width="100" Content="Click me" Click="OnButton1Click"/>
  <Button Width="100" Content="Click me" Click="OnButton2Click"/>
</StackPanel>

注意

阅读扩展Noesis教程以了解有关实现代码隐藏类的更多信息

当加载XAML时,FrameworkElement公开虚拟函数ConnectEvent,该虚拟函数为每个挂钩事件调用。您需要相应地覆盖该功能。在C ++中,提供了宏NS_CONNECT_EVENT作为帮助程序,以轻松实现ConnectEvent。您必须为每个要连接的事件使用if。

C ++

class MyGrid: public Grid
{
public:
    MyGrid()
    {
        InitializeComponent();
    }

private:
    void InitializeComponent()
    {
        Noesis::GUI::LoadComponent(this, "Grid.xaml");
    }

    bool ConnectEvent(BaseComponent* source, const char* event, const char* handler) override
    {
        NS_CONNECT_EVENT(Button, Click, OnButton1Click);
        NS_CONNECT_EVENT(Button, Click, OnButton2Click);
        return false;
    }

    void OnButton1Click(BaseComponent* sender, const RoutedEventArgs& args)
    {
        printf("Button1 was clicked");
    }

    void OnButton2Click(BaseComponent* sender, const RoutedEventArgs& args)
    {
        printf("Button2 was clicked");
    }

    NS_IMPLEMENT_INLINE_REFLECTION(MyGrid, Grid)
    {
        NsMeta<TypeId>("MyGrid");
    }
};

C#

public class MyGrid: Grid
{
    public MyGrid()
    {
        InitializeComponent();
    }

    private void InitializeComponent()
    {
        Noesis.GUI.LoadComponent(this, "Grid.xaml");
    }

    protected override bool ConnectEvent(object source, string eventName, string handlerName)
    {
        if (eventName == "Click" && handlerName == "OnButton1Click")
        {
            ((Button)source).Click += this.OnButton1Click;
            return true;
        }

       if (eventName == "Click" && handlerName == "OnButton2Click")
        {
            ((Button)source).Click += this.OnButton2Click;
            return true;
        }

        return false;
    }

    private void OnButton1Click(object sender, RoutedEventArgs args)
    {
        System.Console.WriteLine("Button1 was clicked");
    }

    private void OnButton2Click(object sender, RoutedEventArgs args)
    {
        System.Console.WriteLine("Button2 was clicked");
    }
}

请注意,有时使用FindName不是有效的选项,而使用代码隐藏成员函数是连接事件的唯一方法。例如,当使用DataTemplate时,无法使用FindName访问可视树中的命名元素,因为为每个项目复制了数据模板。

<Grid x:Class="MyGrid"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Grid.Resources>
    <DataTemplate x:Key="BookItemTemplate">
      <StackPanel Orientation="Horizontal">
        <Button Content="+" Click="OnButtonClick" />
        <TextBlock Text="{Binding Title}"/>
      </StackPanel>
    </DataTemplate>
  </Grid.Resources>
  <ListBox ItemsSource="{Binding Books}" ItemTemplate="{StaticResource BookItemTemplate}"/>
</Grid>

加载和初始化事件

在Noesis中,控件同时具有Initialized事件和Loaded事件。了解这些事件何时发生对于正确初始化任何控件类非常重要。这是这些事件如何工作的一些背景。

初始化事件

初始化时说只是一个元素被创建,其性质已全部设置,并因此这通常他们的父母之前触发对孩子。因此,当在某个元素上引发Initialized时,可能会初始化其整个子树,但不会初始化其父树。在初始化加载的子树的XAML当事件通常被解雇。此事件对应于IsInitialized属性。

不应在类的构造函数中包含代码,而应始终使用Initialized事件。

C ++

class MainWindow final: public Noesis::UserControl
{
public:
    MainWindow()
    {
        Initialized() += MakeDelegate(this, &MainWindow::OnInitialized);
        InitializeComponent();
    }
private:
    void MainWindow::InitializeComponent()
    {
        GUI::LoadComponent(this, "MainWindow.xaml");
    }

    void MainWindow::OnInitialized(BaseComponent*, const EventArgs&)
    {
        SetDataContext(MakePtr<ViewModel>());
    }
}

C#

public partial class MainWindow : UserControl
{
    public MainWindow()
    {
        this.Initialized += OnInitialized;
        this.InitializeComponent();
    }

    private void InitializeComponent()
    {
        Noesis.GUI.LoadComponent(this, "MainWindow.xaml");
    }

    private void OnInitialized(object sender, EventArgs args)
    {
        this.DataContext = new ViewModel();
    }
}

载入事件

有时,初始化事件还不够。例如,您可能想知道一个元素的ActualWidth,但是当激发Initialized时,尚未计算ActualWidth值。或者,您可能想查看数据绑定属性的值,但是该值也尚未计算。

为了解决这个问题,Loaded事件表明不仅构建和初始化了元素,而且还在其上运行布局,绑定了数据,将其连接到View并处于呈现的边缘。到达该点时,将从树的根开始广播Loaded事件。此事件对应于IsLoaded属性。

Loaded事件对称,当从已加载元素的元素树中删除元素时,发生Unloaded事件。

注意

如果不确定使用哪个事件,请使用Loaded事件。通常是正确的选择。

上一篇 下一篇

猜你喜欢

热点阅读