WPF——如何将事件从 Collection 类冒泡到主窗口

发布于 2024-07-29 21:46:22 字数 520 浏览 8 评论 0原文

我正在使用 ObjectDataProvider 调用 IObservableCollection 的类:

<ObjectDataProvider x:Key="WaitingPatientDS" 
      ObjectType="{x:Type local:clsPatients}">
      <ObjectDataProvider.ConstructorParameters>
          <sys:Boolean>True</sys:Boolean>
      </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>

clsPatient 获取数据并填充集合。 它还使用计时器按时间间隔轮询重新查询数据库。

问题:如何在 clsPatient 中为 StartPoll 和 EndPoll 创建事件,更重要的是,如何将这些事件冒泡到 WPF 窗口的代码隐藏?

I'm using an ObjectDataProvider to call a class of with IObservableCollection:

<ObjectDataProvider x:Key="WaitingPatientDS" 
      ObjectType="{x:Type local:clsPatients}">
      <ObjectDataProvider.ConstructorParameters>
          <sys:Boolean>True</sys:Boolean>
      </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>

clsPatient grabs the data and fills the collection. It also uses a timer to poll the requery the database on interval.

Question: how do I create an event for StartPoll and EndPoll in the clsPatient, and more importantly, how do I bubble those events up to the codebehind of my WPF Window?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

意犹 2024-08-05 21:46:22

我不清楚什么与谁连接以及如何连接,所以让我告诉你我会如何做。

ODP 构造了一个 clsPatients 实例,其中包含一个填充“数据”的“集合”。

public class clsPatients, INotifyPropertyChanged
{
  public IBindingList Data {get;private set;}
  private DispatcherTimer _timer;

  public ClsPatients()
  {
    _timer = new DispatcherTimer();
    _timer.Interval = TimeSpan.FromMilliseconds(someInterval);
    _timer.Tick += DispatcherTimerTick;
    _timer.Start();
  }
  /* etc etc */
}

clsPatients 还有一个 DispatcherTimer,它会定期更新 Data 属性并触发 PropertyChanged

public void DispatcherTimerTick(object sender, EventArgs e)
{
  Data = new BindingList(Repository.GetMyDataLol());
  // standard event firing method here, move along:
  OnPropertyChanged("Data");
}

在 UI 中,我将因此绑定此集合(这可能没有错误,也可能没有):

<ItemsControl 
  ItemsSource="{Binding Data Source={StaticResource WaitingPatientDS}}">
  <ItemsControl.Resources>  
    <DataTemplate>
      <!-- yadda -->

这是如何更新 UI 的更新数据时:

  1. clsPatient 由 ObjectDataProvider 提供给 ItemsControl
  2. ItemsControl 使用 WPF 绑定基础结构来绑定 ODP 提供的实例的 Data 属性。
  3. clsPatient 的 DispatcherTimer(在 UI 线程上操作)更新数据并触发 PropertyChanged,它通知订阅此事件的所有绑定,该属性已发生足够讽刺的更改。
  4. 绑定接管并刷新 ItemsControl

以显示指示正在加载的动画进度,向 clsPatient 添加另一个名为 Loading 的属性:

 public Visibility Loading{get;private set}

并更新计时器刻度事件:

public void DispatcherTimerTick(object sender, EventArgs e)
{
  Loading = Visibility.Visible;
  OnPropertyChanged("Loading");
  Data = new BindingList(Repository.GetMyDataLol());
  OnPropertyChanged("Data");
  Loading = Visibility.Hidden;
  OnPropertyChanged("Loading");
}

然后,在 ui 中,将指示器的 Visibility 属性绑定到 Loading:

<Grid DataContext="{Binding Data Source={StaticResource WaitingPatientDS}}">
  <ItemsControl 
    ItemsSource="{Binding Data}">
    <ItemsControl.Resources>  
      <DataTemplate>
        <!-- yadda -->
  </ItemsControl>
  <Image Source="hurf.jpg" Visibility="{Binding Loading}"
    HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

当 Loading 设置为 Visible 时,会出现图像(或您要使用的任何其他控件)当它设置为隐藏时就会消失。 这样您就可以在加载数据时显示图像。


如果 UI 未更新,则该进程可能会在 UI 线程中执行时阻塞 UI。

要解决此问题,请运行 System.Threading.Timer 而不是 DispatcherTimer。 STT 在 UI 之外的后台线程上运行。 在计时器的回调方法中,使用调度程序来更新 ui(Invoke 方法可能有错误;请检查 Dispatcher.Invoke 上的文档):

public void UpdateData(Object stateInfo)
{
  var disp = Dispatcher.CurrentDispatcher();
  Loading = Visibility.Visible;
  disp.Invoke(() => { OnPropertyChanged("Loading");});
  // optional sleep here
  Data = new BindingList(Repository.GetMyDataLol());
  disp.Invoke(() => { OnPropertyChanged("Data");});
  Loading = Visibility.Hidden;
  disp.Invoke(() => { OnPropertyChanged("Loading");});
}

I'm not clear on what is connecting to whom and how, so let me tell you how I'd do it.

ODP constructs an instance of clsPatients, which contains a "collection" filled with "data".

public class clsPatients, INotifyPropertyChanged
{
  public IBindingList Data {get;private set;}
  private DispatcherTimer _timer;

  public ClsPatients()
  {
    _timer = new DispatcherTimer();
    _timer.Interval = TimeSpan.FromMilliseconds(someInterval);
    _timer.Tick += DispatcherTimerTick;
    _timer.Start();
  }
  /* etc etc */
}

clsPatients also has a DispatcherTimer which, on a regular interval updates the Data property and fires PropertyChanged

public void DispatcherTimerTick(object sender, EventArgs e)
{
  Data = new BindingList(Repository.GetMyDataLol());
  // standard event firing method here, move along:
  OnPropertyChanged("Data");
}

In the UI, I'd bind against this collection thusly (this may be bug free, or maybe its not):

<ItemsControl 
  ItemsSource="{Binding Data Source={StaticResource WaitingPatientDS}}">
  <ItemsControl.Resources>  
    <DataTemplate>
      <!-- yadda -->

How this works to update the UI when Data is updated:

  1. clsPatient is provided to the ItemsControl by the ObjectDataProvider
  2. ItemsControl uses the WPF binding infrastructure to bind against the Data property of the instance provided by the ODP.
  3. The DispatcherTimer (operating on the UI thread) of the clsPatient updates Data and fires PropertyChanged, which notifies all bindings subscribing to this event that the property has ironically enough changed
  4. The binding takes over and refreshes the ItemsControl

To show an animation that indicates loading is in progress, add another property to clsPatient called Loading:

 public Visibility Loading{get;private set}

and update the timer tick event:

public void DispatcherTimerTick(object sender, EventArgs e)
{
  Loading = Visibility.Visible;
  OnPropertyChanged("Loading");
  Data = new BindingList(Repository.GetMyDataLol());
  OnPropertyChanged("Data");
  Loading = Visibility.Hidden;
  OnPropertyChanged("Loading");
}

then, in the ui, bind your indicator's Visibility property to Loading:

<Grid DataContext="{Binding Data Source={StaticResource WaitingPatientDS}}">
  <ItemsControl 
    ItemsSource="{Binding Data}">
    <ItemsControl.Resources>  
      <DataTemplate>
        <!-- yadda -->
  </ItemsControl>
  <Image Source="hurf.jpg" Visibility="{Binding Loading}"
    HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>

the Image (or whatever other control you want to use) appears when Loading is set to Visible and goes away when its set to Hidden. So you can show the image when you're loading data.


If the UI isn't updating, the process is probably blocking the UI as its executing in the UI thread.

To fix this, run a System.Threading.Timer instead of a DispatcherTimer. STT runs on a background thread other than the UI. In the timer's callback method, use the dispatcher to update the ui (the Invoke methods may be buggy; check the docs on Dispatcher.Invoke):

public void UpdateData(Object stateInfo)
{
  var disp = Dispatcher.CurrentDispatcher();
  Loading = Visibility.Visible;
  disp.Invoke(() => { OnPropertyChanged("Loading");});
  // optional sleep here
  Data = new BindingList(Repository.GetMyDataLol());
  disp.Invoke(() => { OnPropertyChanged("Data");});
  Loading = Visibility.Hidden;
  disp.Invoke(() => { OnPropertyChanged("Loading");});
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文