WPF UI在事件触发时挂起

时间:2010-09-19 06:02:56

标签: c# .net wpf

我正在尝试以编程方式滚动ScrollViewer。但是一旦事件发生,我的UI就会挂起。如果我取出while循环,这个问题就消失了,但是我没有得到我想要的行为。

我正在使用WPF 4.0

以下是代码:

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:System="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d"
    x:Class="MyApp.Home"
    x:Name="HomePage"
    WindowTitle="Home"
    FlowDirection="LeftToRight" Foreground="{x:Null}" MinWidth="1100" MinHeight="629" Loaded="HomePage_Loaded" SizeChanged="HomePage_SizeChanged">

    <Grid x:Name="LayoutRoot">
        <TextBlock HorizontalAlignment="Right" Height="48.806" Margin="0,15,15,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="138.908" FontSize="34.667" FontFamily="/MyApp;component/Fonts/#Calibri" Text="featured." Foreground="#FF333333" d:IsLocked="True"/>
        <ScrollViewer Height="215" Margin="-20.648,67.806,-20.648,0" VerticalAlignment="Top" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled" Name="FeaturedScroll" Width="1141.296">
            <ListView x:Name="FeaturedList" Background="{x:Null}" BorderBrush="{x:Null}" Foreground="{x:Null}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Disabled">
                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <StackPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
            </ListView>
        </ScrollViewer>
        <Rectangle x:Name="Left" Height="215" Width="77.129" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,68,0,0" MouseEnter="Left_MouseEnter" Cursor="Hand" Fill="#00000000" MouseLeave="Left_MouseLeave" />
        <Rectangle x:Name="Right" Height="215" Width="77.129" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,68,0,0" MouseEnter="Right_MouseEnter" Cursor="Hand" Fill="#00000000" MouseLeave="Right_MouseLeave" />
    </Grid>
</Page>

private void Left_MouseEnter(object sender, MouseEventArgs e) {
    ScrollLeft = true;

    while (ScrollLeft == true)
        FeaturedScroll.ScrollToHorizontalOffset(FeaturedScroll.HorizontalOffset - 1);
}

private void Right_MouseEnter(object sender, MouseEventArgs e) {
    ScrollRight = true;

    while (ScrollRight == true)
        FeaturedScroll.ScrollToHorizontalOffset(FeaturedScroll.HorizontalOffset + 1);
}

private void Left_MouseLeave(object sender, MouseEventArgs e) {
    ScrollLeft = false;
}

private void Right_MouseLeave(object sender, MouseEventArgs e) {
    ScrollRight = false;
}

4 个答案:

答案 0 :(得分:2)

你的循环将无限循环,因为没有任何东西可以使ScrollLeftScrollRight为假。由于循环没有退出,事件处理程序永远不会完成,并且不会触发新事件。

我建议可以使用故事板动画滚动,然后在动画结束时调查鼠标的状态。

答案 1 :(得分:2)

您的代码似乎期望事件(Left_MouseLeave)将在另一个事件(Left_MouseEnter)仍在执行时触发。

情况并非如此,事件由1个线程提供服务,不会重叠。

您必须更改为更加事件驱动的算法。我没有在WPF中看到MouseHover,所以你需要别的东西。

答案 2 :(得分:2)

您无法从其他线程滚动,但您不必滚动,因为您可以通过一些简单的代码更改来解决问题。

正如其他人所说,当鼠标输入事件工作时你无法获得鼠标离开事件,你必须更改你的代码,以便鼠标输入事件立即返回(顺便说一句,所有事件应该立即返回,否则你程序 - 在极端情况下,整个系统将变得无响应。)

您可以尝试以下两种方法:

  1. 向您的班级添加DispatcherTimer,在计时器的Tick事件中滚动固定金额,在鼠标上输入调用计时器Start方法,在鼠标离开调用Stop上。

  2. 使用动画,在鼠标输入时使用DoubleAnimation开始滚动,停止动画并在鼠标离开时检查当前值

  3. 使用RepeatButton,如果在RepeatButton Click事件上滚动固定数量,则在按下按钮时将重复调用事件(这会稍微改变行为,因为您必须按下按钮而不仅仅是将鼠标悬停在它)。

  4. 可能还有很多其他选项,其中没有涉及线程(因为多线程解决方案在这里不起作用)

答案 3 :(得分:0)

这是最终的代码。希望它可以帮助某人:)

public MySlider() {
    this.InitializeComponent();

    ScrollLeft = true;

    timer = new DispatcherTimer();
    timer.Interval = TimeSpan.FromMilliseconds(0.01);
    timer.Tick += new EventHandler(timer_Tick);
}

void timer_Tick(object sender, EventArgs e) {
    if (ScrollLeft) MyScrollViewer.ScrollToHorizontalOffset(MyScrollViewer.HorizontalOffset - 0.1);
    else MyScrollViewer.ScrollToHorizontalOffset(MyScrollViewer.HorizontalOffset + 0.1);
}

private void Left_MouseEnter(object sender, MouseEventArgs e) {
    ScrollLeft = true;

    timer.Start();
}

private void Right_MouseEnter(object sender, MouseEventArgs e) {
    ScrollLeft = false;

    timer.Start();
}

private void Left_MouseLeave(object sender, MouseEventArgs e) {
    timer.Stop();

    ScrollLeft = false;
}

private void Right_MouseLeave(object sender, MouseEventArgs e) {
    timer.Stop();
}