WPF MouseEnter / MouseLeave / MouseMove事件隧道

时间:2018-01-04 21:45:09

标签: c# wpf

我正在尝试按下鼠标进入按钮时内容发生变化的按钮。 目前,这是我正在使用的代码:

的Xaml

<StackPanel Orientation="Vertical">
    <StackPanel Orientation="Horizontal">
        <Button x:Name="One" 
                Content="ONE" 
                Height="50" 
                Width="Auto" 
                MouseEnter="One_OnMouseEnter" 
                MouseLeave="One_OnMouseLeave" />
        <Button x:Name="Two" 
                PreviewMouseMove="Two_OnMouseEnter">
            <Grid>
                <Ellipse Fill="Black" 
                         Height="40" 
                         Width="40" />
                <Label Content="TWO" 
                       Foreground="White" 
                       VerticalAlignment="Center" 
                       HorizontalAlignment="Center" />
            </Grid>
        </Button>
    </StackPanel>
</StackPanel>

C#Code-Behind File

private void One_OnMouseEnter(object sender, MouseEventArgs e)
{
    Button b = sender as Button;
    if (b != null)
    {
        b.Foreground = Brushes.Purple;
        b.FontSize = 24;
    }
}

private void One_OnMouseLeave(object sender, MouseEventArgs e)
{
    Button b = sender as Button;
    if (b != null)
    {
        b.Foreground = Brushes.Black;
        b.FontSize = 12;
    }
}

private void Two_OnMouseEnter(object sender, MouseEventArgs e)
{

    Ellipse el = sender as Ellipse;
    if (el != null)
    {
        el.Height = 60;
        el.Width = 60;
        el.Fill = Brushes.White;
    }

    Label l = sender as Label;
    if (l != null)
    {
        l.Foreground = Brushes.Black;
    }

    Grid g = sender as Grid;
    if (g != null)
    {
        g.Height = 200;
        g.Width = 200;
    }
}

第一个按钮按预期工作。 当鼠标移动到“一个”按钮时,文本内容会按预期更改。文本颜色变为紫色,字体大小增加。

Normal

Mouse Over

我正在尝试用第二个按钮做类似的事情。增加尺寸并更改椭圆的颜色,更改标签的颜色,并更改网格的大小。

问题是第二个按钮似乎没有按预期响应。我尝试使用PreviewMouseMove,我理解使用隧道路由策略,它应该触发Button的子元素。我使用了断点来检查,事件似乎只是在发送者是Button的情况下触发。

我的问题是:为什么不会对孩子们提出这个事件,因为我已经知道隧道路由策略应该有效,我该怎么做才能解决它?

此外,MouseEnter和MouseLeave事件似乎遵循冒泡路由策略,但行为更类似于我想要做的事情。我可以强制使用隧道路由策略吗?

编辑:

为了进一步解释这个项目的目标:

我打算做的是更全面地了解WPF中的事件隧道。

今天早上,我更多地检查了这本书,并找到了一种方法,当鼠标进入每个特定孩子的区域时,这是一项改进。

以下是C#Code-Behind文件中的新代码:

private void Two_OnMouseEnter(object sender, MouseEventArgs e)
{
    Ellipse el = e.OriginalSource as Ellipse;
    if (el != null)
    {
        el.Height = 60;
        el.Width = 60;
        el.Fill = Brushes.Orange;
    }
    TextBlock t = e.OriginalSource as TextBlock;
    if (t != null)
    {
        t.Foreground = Brushes.Blue;
    }
    Grid g = e.OriginalSource as Grid;
    if (g != null)
    {
        g.Height = 200;
        g.Width = 200;
    }
}

与此代码的不同之处在于它使用方法签名中的MouseEventArgs e对象,将e.OriginalSource作为对象类型而不是转换sender对象。

此外,使用XAML文件中的PreviewMouseMove事件调用调用此代码:

<Button x:Name="Two" PreviewMouseMove="Two_OnMouseEnter">

似乎只允许内容在鼠标进入区域时更改,但在鼠标离开区域时则不会。这导致我回到原始问题的一部分:我可以强制MouseEnterMouseLeave遵循隧道路由策略吗?

1 个答案:

答案 0 :(得分:0)

它没有做任何事情,因为事件来自Button(因此是sender)。

这样的事情就是你要做的事情:

private void Two_OnMouseEnter(object sender, MouseEventArgs e)
{
    Button b = sender as Button;
    Grid g = b.Content as Grid;
    Ellipse el = g.Children[0] as Ellipse;
    Label l = g.Children[1] as Label;

    g.Height = 200;
    g.Width = 200;

    el.Height = 60;
    el.Width = 60;
    el.Fill = Brushes.White;

    l.Foreground = Brushes.Black;
}

<强>更新

为了更多地解释隧道策略,请考虑以下示例(为方便起见,我删除了“One”的事件处理程序):

<StackPanel Orientation="Vertical">
    <StackPanel Orientation="Horizontal">
        <Button x:Name="One" Content="ONE" Height="50" Width="Auto" />
        <Button x:Name="Two">
            <Grid PreviewMouseMove="Grid_PreviewMouseMove">
                <Ellipse Fill="Black" Height="40" Width="40" PreviewMouseMove="Ellipse_PreviewMouseMove" />
                <Label Content="TWO" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center"
                       PreviewMouseMove="Label_PreviewMouseMove"/>
            </Grid>
        </Button>
    </StackPanel>
</StackPanel>

在代码隐藏中:

private void Grid_PreviewMouseMove(object sender, MouseEventArgs e)
{
    Grid g = sender as Grid;

    if (g != null)
    {
        g.Height = 200;
        g.Width = 200;
    }

    System.Diagnostics.Debug.WriteLine("Sender: " + sender.GetType() + "; Source: " + e.Source.GetType());
}

private void Ellipse_PreviewMouseMove(object sender, MouseEventArgs e)
{
    Ellipse el = sender as Ellipse;

    if (el != null)
    {
        el.Height = 60;
        el.Width = 60;
        el.Fill = Brushes.White;
    }

    System.Diagnostics.Debug.WriteLine("Sender: " + sender.GetType() + "; Source: " + e.Source.GetType());
}

private void Label_PreviewMouseMove(object sender, MouseEventArgs e)
{
    Label l = sender as Label;

    if (l != null)
    {
        l.Foreground = Brushes.Black;
    }

    System.Diagnostics.Debug.WriteLine("Sender: " + sender.GetType() + "; Source: " + e.Source.GetType());
}

这些方法理论上可以重构为一种方法,应用一些逻辑来检测Type的{​​{1}},但它仍然会在XAML中三次。

有一点需要注意的是sender除非你设置Grid属性(例如,Background),否则不会触发自己的事件,尽管它仍然会触发TransparentEllipse开火。

另一件需要注意的事情是,当Label触发事件时,即当您将鼠标移到Label上时,不会调用Label的事件处理程序。这是因为隧道策略在逻辑上沿树而不是在视觉上进行。如果您在“输出”窗口中查看消息中的“来源:”部分,您将看到我的意思。冒泡活动也是如此。

作为一般观察,具有这种策略的事情是,当事件从不同元素发射时,你的控制并不真正起作用单Ellipse,这看起来是意图。如果你慢慢移动鼠标,你会发现Button先发射,然后发现Ellipse

没有任何LabelPreviewMouseEnter事件,因此无论如何都无法使用隧道。