如何避免复制/粘贴许多事件处理程序

时间:2013-05-23 12:37:58

标签: c# events

我的应用程序无法访问特定的菜单项,除非某些条件为真(DataRepository.IsAllDataLoaded)。我想出了这个代码,效果很好。它首先检查条件。如果它没有准备好,它会调用一个计时器,它等待几毫秒并再次调用相同的方法。 Timer需要一个ElapsedEventHandler。

public void FirstMenuItem_Click(object sender, RoutedEventArgs e)
{
  if (!DataRepository.IsAllDataLoaded)
  {
    WaitForDataLoading(FirstTimedEvent);        
  }
  else
  {        
    Dispatcher.BeginInvoke(new Action(() =>
   {
     IndividualEntryWindow Window = new IndividualEntryWindow();
     Window.Show();
   }));
  }
}
private void FirstTimedEvent(object source, ElapsedEventArgs e)
{
  FirstMenuItem_Click(null, null);
}

private static void WaitForDataLoading(ElapsedEventHandler timerEvent)
{
  Timer t = new Timer();
  t.Interval = 0.2;
  t.AutoReset = false; 
  t.Elapsed += new ElapsedEventHandler(timerEvent);
  t.Start();
}

最初,FirstMenuItem_Click是唯一的方法。我必须为我的计时器添加FirstTimedEvent处理程序。有没有办法避免创建ElapsedEventHandler?我可以在FirstMenuItem_Click方法中内联创建吗?

我现在必须对许多其他Item_Click方法使用相同的模式。我希望我不必为每个Item_Click方法创建ElapsedEventHandler

3 个答案:

答案 0 :(得分:2)

使用匿名lambda表达式:

WaitForDataLoading((s,e) => FirstMenuItem_Click(null, null));

答案 1 :(得分:0)

在以下代码中,您可以在数据准备就绪时随时调用窗口CheckDataShowWindow()。如果你想把它添加到另一个cick处理程序,你可以像这样做另一个:

public void Another_Click(object sender, RoutedEventArgs e)
{
    CheckDataShowWindow();
}

主要代码

public void FirstMenuItem_Click(object sender, RoutedEventArgs e)
{
    CheckDataShowWindow();
}

private void CheckDataShowWindow()
{
    if (!DataRepository.IsAllDataLoaded)
    {
        Timer t = new Timer();
        t.Interval = 0.2;
        t.AutoReset = false; 
        t.Elapsed += (s,e) => CheckDataShowWindow();
        t.Start();
    }
    else
    {
        Dispatcher.BeginInvoke(new Action(() =>
        {
            IndividualEntryWindow Window = new IndividualEntryWindow();
            Window.Show();
        }));
    }
}

<强>更新

如果您可以编辑datarepository的代码,则应该在数据加载完成时添加一个事件。

public delegate void DoneLoadingHandler(object sender, EventArgs e);
public class DataRepository
{
    public event DoneLoadingHandler DoneLoading;
    //Your loading function
    private void LoadAllData()
    {
        //Load like you do now

        //Now fire the event that loading is done.
        if(DoneLoading != null)
            DoneLoading(this, new EventArgs());
    }
}

现在在你的另一堂课:

public void FirstMenuItem_Click(object sender, RoutedEventArgs e)
{
    CheckDataShowWindow();
}

private bool AllReadyWaiting = false;
private void CheckDataShowWindow()
{
    if (!DataRepository.IsAllDataLoaded)
    {
        if(!AllReadyWaiting)
        {
            DataRepository.DoneLoading += (s,e) => ShowWindow();
            AllReadyWaiting = true;
        }
    }
    else
    {
        ShowWindow();
    }
}

private void ShowWindow()
{
    Dispatcher.BeginInvoke(new Action(() =>
    {
        IndividualEntryWindow Window = new IndividualEntryWindow();
        Window.Show();
    }));
}

答案 2 :(得分:0)

您似乎使用WPF,基于您对Dispatcher类的使用。在这种情况下,您可以通过更好的方式控制对UI的访问。

其中两个是:

  1. 将菜单的Enabled属性绑定到ViewModel类,该类具有指示菜单是否可用的属性。当您的长期作业完成后,将该属性设置为true并启用菜单。

  2. 使用ICommand来推动菜单的行为。在长时间运行的作业处于活动状态时,命令CanExecute返回false,这将导致菜单在作业完成之前自动禁用。

  3. 值得注意的是,这会巧妙地改变菜单的行为 - 但我认为,这并不是很糟糕。您的当前代码将在显示对话框之前等待作业完成 - 但在此期间没有任何内容可以阻止用户再次单击该菜单。这些多次单击将等待作业完成,并且每个作业完成时都会显示自己的对话框。在一个微不足道的情况下,这可能意味着我看到多个对话框出现;在严重的情况下,您正在创建的多个计时器可能会严重影响应用程序的性能。

    上面提到的任何一种方法都会阻止在作业运行时点击菜单,这不是你当前的行为,但我认为从可用性的角度来看会更有意义。

相关问题