ItemsControl - 可绑定集合 - Ui 由计时器事件更新

ItemsControl - Bindable Collection - Ui update by Timer Event

我正在为现场的几个传感器设备创建仪表板类型 UI。基于计时器的事件从数据库中获取最后一条记录并更新仪表板 UI。我想动态创建位于 BindableCollection 上的 "sensors",然后与包含嵌套 ItemsControl 的 XAML 上的 ItemsControl 相关联。 使用 MVVM 架构,并使用 Caliburn Micro 进行绑定。

已完全使用 属性 并确保在更新我的 BindableColleciton 后调用 NotifyOfPropertyChange

编辑:在其他线程之后,我更改了 DispatcherTimer() 的 System.Timer,因此更新在 UI 线程上运行。 DispatcherTimer vs a regular Timer in WPF app for a task scheduler

此外,因为我的 BindableCollection 成员总是相同的,所以我确保每次都重新创建集合,正如我的想法 "An ObservableCollection will notify the UI when a record is added or removed but not when a record is edited. It's up to the object that has been changed to notify that it has changed."

属性:

  private int _offlineSensors;
    public int OfflineSensors
    {
        get { return _offlineSensors; }
        set
        {
            _offlineSensors = value;
            NotifyOfPropertyChange(() => OfflineSensors);

        }
    }

    private int _latchedAlarms;
    public int LatchedAlarms
    {
        get { return _latchedAlarms; }
        set
        {
            _latchedAlarms = value;
            NotifyOfPropertyChange(() => LatchedAlarms);
        }
    }

    private int _activeAlarms;
    public int ActiveAlarms
    {
        get { return _activeAlarms; }
        set
        {
            _activeAlarms = value; 
            NotifyOfPropertyChange(() => ActiveAlarms);

        }
    }

    private Caliburn.Micro.BindableCollection<SensorStatusTable> _sensorStatusFilteredDash;
    public Caliburn.Micro.BindableCollection<SensorStatusTable> SensorStatusFilteredDash
    {
        get { return _sensorStatusFilteredDash; }
        set
        {
            _sensorStatusFilteredDash = value;
            NotifyOfPropertyChange(() => SensorStatusFilteredDash);
        }
    }

XAML:

<ItemsControl Foreground="Black" Background="Black" ItemsSource="{Binding Path=SensorStatusFilteredDash, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <WrapPanel Orientation="Horizontal"></WrapPanel>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Border BorderBrush="RoyalBlue" BorderThickness="2" Margin="2" Padding="5">
                        <StackPanel Orientation="Vertical">
                            <TextBlock Text="{Binding Name}" Foreground="White" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="Bold" FontSize="20" />
                            <Image MaxWidth="60" MaxHeight="60" Source="{Binding CardStatusImage, Converter={StaticResource ResourceKey = ImageConverter}, UpdateSourceTrigger=PropertyChanged}" Stretch="Uniform"></Image>
                                <ItemsControl Foreground="Black" Background="Black" ItemsSource="{Binding Path=SensorTypes, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <WrapPanel Orientation="Horizontal"></WrapPanel>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding }" TextWrapping="WrapWithOverflow" FontWeight="Bold" FontSize="12" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Margin="25.5,1,18,1"></TextBlock>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                                <ItemsControl Foreground="Black" Background="Black" ItemsSource="{Binding Path=InternalSensorStatusImages, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <WrapPanel Orientation="Horizontal"></WrapPanel>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <Image Margin="11.5,1" MaxWidth="40" HorizontalAlignment="Center" VerticalAlignment="Center" MaxHeight="40" Source="{Binding  Converter={StaticResource ResourceKey = ImageConverter}}" Stretch="Uniform"></Image>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>
                                <ItemsControl Foreground="Black" Background="Black" ItemsSource="{Binding Path=InternalSensorStatusDescription, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                                    <ItemsControl.ItemsPanel>
                                        <ItemsPanelTemplate>
                                            <WrapPanel Orientation="Horizontal"></WrapPanel>
                                        </ItemsPanelTemplate>
                                    </ItemsControl.ItemsPanel>
                                    <ItemsControl.ItemTemplate>
                                        <DataTemplate>
                                            <TextBlock Text="{Binding}" TextWrapping="WrapWithOverflow" FontWeight="Bold" FontSize="12" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Margin="10,1,10,1"></TextBlock>
                                        </DataTemplate>
                                    </ItemsControl.ItemTemplate>
                                </ItemsControl>

                            </StackPanel>
                    </Border>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

事件(已编辑):

 var conn = GlobalConfig.Connections.FirstOrDefault(c => c.GetType() == typeof(SQLConnector));
        if (conn == null) return;
        try
        {
            ActiveAlarms = conn.GetNumberActiveAlarms(0);
            LatchedAlarms = conn.GetNumberActiveAlarms(2);
            GasSensors = new Caliburn.Micro.BindableCollection<GasSensor>(conn.GetGasSensors());
            SensorStatusDash = new Caliburn.Micro.BindableCollection<SensorStatusTable>(conn.SensorStatusDashboard());
            _sensorStatusFilteredDash.Clear();
            _sensorStatusFilteredDash = new Caliburn.Micro.BindableCollection<SensorStatusTable>(StatusImageConverter());
            NotifyOfPropertyChange(() => SensorStatusFilteredDash);
            NotifyOfPropertyChange(() => OfflineSensors);
            NotifyOfPropertyChange(() => ActiveAlarms);
            NotifyOfPropertyChange(() => LatchedAlarms);
        }
        catch (Exception ex)
        {
            _logger.Error(ex, "Error - Refreshing Dashboard Status");
        }

这是我的应用程序的第一个视图/视图模型,上面的 UI 刷新事件始终有效,直到我将焦点放在不同的视图上并 return 到它。计时器仍然被触发并将正确的状态置于 BindableCollection 上。此外,即使是简单 Int 的属性也不会更新。在 属性 setter 上放置一个断点,我可以看到它永远不会触发它,就像 NotifyOfPropertyChange 事件没有被触发一样。有什么想法吗?

感谢大家的提示和帮助。

我成功了,有几件事导致 UI 无法刷新。

  1. 如上所述,我使用的是 System.Timer 的定时器。我已将其替换为 DispatcherTimer 以确保事件将在与 UI.
  2. 相同的线程上引发
  3. 每个周期重新创建可绑定集合以确保触发 PropertyChanged 事件,因为成员是常量并且只有属性被修改。
  4. 确保在swicth视图时取消订阅dispatcher timer事件,返回时重新订阅。

以上三点的综合。