2016-09-27 30 views
1

我有一个矩形,我想使其可见,然后我想等待500毫秒,然后我想再次使它不可见。如何使用BackgroundWorker来显示/隐藏WPF UI元素?

起初我试过这段代码:

MuzzleFlash.Visibility = Visibility.Visible; 
Thread.Sleep(500); 
this.UpdateLayout(); 
this.InvalidateVisual(); 
MuzzleFlash.Visibility = Visibility.Collapsed; 

我想这两个中间线作为他们理应被迫窗户更新,但是这一切确实是半秒钟的停顿不改变任何形式的矩形。

所以我听说了BackgroundWorkers以及他们应该如何使用。经过一番简短的阅读后,我想出了这个。需要注意的是拍摄被预订在画布MouseDown事件,这在以前的工作:

private void Shoot(object sender, System.Windows.Input.MouseButtonEventArgs e) 
    { 

     BackgroundWorker UIUpdater = new BackgroundWorker(); 
     UIUpdater.WorkerSupportsCancellation = true; 
     UIUpdater.WorkerReportsProgress = false; 

     UIUpdater.DoWork += new DoWorkEventHandler(UI_DoWork); 
     UIUpdater.RunWorkerCompleted += new RunWorkerCompletedEventHandler(cancelUIUpdate); 

    } 

    private void UI_DoWork(object sender, DoWorkEventArgs e) 
    { 
     MuzzleFlash.Visibility = Visibility.Visible; 
     Thread.Sleep(500); 
     this.UpdateLayout(); 
     this.InvalidateVisual(); 
     MuzzleFlash.Visibility = Visibility.Collapsed; 
    } 

    private void cancelUIUpdate(object sender, RunWorkerCompletedEventArgs e) 
    { 
     BackgroundWorker bw = sender as BackgroundWorker; 
     bw.CancelAsync(); 
    } 

现在它甚至不停顿半秒,这表明,我认为工人没有做任何事情。我该如何解决这个问题,并使矩形出现/消失?

+2

我会使用一个计时器。你并没有真的在做工作。 – Paparazzi

+0

它不起作用,因为您正在执行工作线程中的所有操作。主UI线程说*“启动一个后台工作,然后结束”*,而bg工人说*“设置可见标志为真,等待一段时间,设置可见标志为假,然后结束回UI线程,以便UI可以更新”*。请注意,bg线程永远不会将通知发送回主UI线程以更新显示。如果你想这样做,我会建议寻找WPF的Dispatcher。请参阅[本答案](http://stackoverflow.com/a/15930792/302677)以获得一个简短摘要,其中使用了与您在此处执行的操作类似的示例。 – Rachel

+1

一个简单的“解决方案”就是将你的'MuzzleFlash.Visibility = Visibility.Visible'行移动到你的主UI线程以在'Shoot(...)'方法内执行。然后代码表示*“设置可见标志为真,启动bg工作,然后结束并更新显示”*,而bg工人说*“等待一段时间,设置可见标志为假,然后结束回UI线程,以便它可以更新”* 。此外,您可能希望在RunWorkerCompleted中放置*“set visible flag false”*位,因为WPF通常不会更新它们未创建的线程上的对象,因此您可能会遇到尝试从bg更新的异常工人。 – Rachel

回答

4

这是坏主意,使用后台工作在WPF管理动画后设置的值。动画应该从Storyboard运行。它可以通过xaml或代码来指定。

你的动画矩形的问题是非常简单的使用XAML:

<Rectangle x:Name="MuzzleFlash" Height="100" Width="100" Fill="Red"> 
    <Rectangle.Triggers> 
     <EventTrigger RoutedEvent="Loaded"> 
      <BeginStoryboard> 
       <Storyboard> 
        <ObjectAnimationUsingKeyFrames BeginTime="0:0:0.5" Storyboard.TargetProperty="Visibility"> 
         <DiscreteObjectKeyFrame Value="{x:Static Visibility.Collapsed}" /> 
        </ObjectAnimationUsingKeyFrames> 
       </Storyboard> 
      </BeginStoryboard> 
     </EventTrigger> 
    </Rectangle.Triggers> 
</Rectangle> 

对于更复杂的动画,可以使用混合交互设计它们。

评论后更新的回复: 如果您想要显示并消失,您必须从父容器中操作它,因为一旦矩形隐藏,它将不响应鼠标事件。

您可以将矩形放入网格中并通过名称引用它以更改其在动画中的可见性。

<Grid Background="Gray"> 
    <Grid.Triggers> 
     <EventTrigger RoutedEvent="MouseDown"> 
      <BeginStoryboard> 
       <Storyboard> 
        <ObjectAnimationUsingKeyFrames BeginTime="0:0:0.0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="MuzzleFlash"> 
         <DiscreteObjectKeyFrame Value="{x:Static Visibility.Visible}" /> 
         <DiscreteObjectKeyFrame KeyTime="0:0:0.5" Value="{x:Static Visibility.Hidden}" /> 
        </ObjectAnimationUsingKeyFrames> 
       </Storyboard> 
      </BeginStoryboard> 
     </EventTrigger> 
    </Grid.Triggers> 
    <Rectangle x:Name="MuzzleFlash" Height="100" Width="100" Fill="Red"> 

    </Rectangle> 
</Grid> 
+0

那我该如何触发呢?从字面上加载只是意味着'加载'还是我可以从代码中调用它? – lordnoob

+0

你想如何触发它?这里的例子使用Loaded就是一个例子。您可以为任何事件触发它。为什么你认为你需要它从代码触发它? – loopedcode

+0

好吧,我基本上想要它,所以当我点击矩形显示半秒钟,然后再次消失。我将事件设置为MouseDown,默认情况下将可见性设置为隐藏(因此x:静态可见性在故事板中可见),但它不起作用(无响应)。我认为我之前得到了一些回应,当我把它作为MouseDown时,默认情况下是可见的,因此隐藏。 – lordnoob

0
private BackgroundWorker bw = new BackgroundWorker(); 

    private void Window_Loaded(object sender, RoutedEventArgs e) 
    { 
     //Background Worker code/// 
     bw.WorkerReportsProgress = true; 
     bw.DoWork += bw_DoWork; 
     bw.ProgressChanged += bw_ProgressChanged; 
     bw.RunWorkerCompleted += bw_RunWorkerCompleted; 
     bw.RunWorkerAsync(); 

     //Progress Bar Window 
     grdProgress.Visibility = Visibility.Visible; 
    } 
    private void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     //something to do 
    } 

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     //Progress Bar Window close 
     grdProgress.Visibility = Visibility.Hidden; 
    } 

    private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     prg.Value = e.ProgressPercentage; 
    } 


    bw.ReportProgress(value); 
+0

除了提供代码,请解释为什么你的答案有效。 – buczek