Как запустить событие collectionchanged для привязки observablecollection к itemscontrol.
Я заполняю набор точек данных и показываю их на линейном графике. Пользовательский элемент управления, имеющий свойство зависимостей IEnumerable, которое привязывается к ObservableCollection. Я использую LiveCharts для построения точек данных.
Вопрос: CollectionChanged не срабатывает, когда я добавляю данные в коллекцию в VM_CaptureData.
Пожалуйста, помогите мне с этим вопросом. Заранее спасибо. Пожалуйста, найдите образец проекта здесь.
Вот мой код,
Что я уже пробовал:
Пользовательских элементов управления.язык XAML
<UserControl x:Class="ChartLibrary.LineChart" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Grid> <lvc:CartesianChart x:Name="chart" Series="{Binding Series_Collection}" LegendLocation="Bottom" AnimationsSpeed="0:0:0.5" Hoverable="False" DataTooltip="{x:Null}" > <lvc:CartesianChart.AxisY> <lvc:Axis Name="Axis" Title="Readings" LabelFormatter="{Binding AxisYFormatter}" /> </lvc:CartesianChart.AxisY> <lvc:CartesianChart.AxisX> <lvc:Axis Title="Data Points" Labels="{Binding XAxisLabel}" LabelsRotation="-87"> <lvc:Axis.Separator> <lvc:Separator StrokeThickness="1" StrokeDashArray="2" IsEnabled="True" Step="1"> <!--Here, see the IsEnabled => false--> <lvc:Separator.Stroke> <SolidColorBrush Color="#404F56" /> </lvc:Separator.Stroke> </lvc:Separator> </lvc:Axis.Separator> </lvc:Axis> </lvc:CartesianChart.AxisX> </lvc:CartesianChart> </Grid>
Пользовательских элементов управления.в CS
public partial class LineChart : UserControl { public IEnumerable<double> Values { get { return (IEnumerable<double>)GetValue(ValuesProperty); } set { SetValue(ValuesProperty, value); } } // Using a DependencyProperty as the backing store for Values. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValuesProperty = DependencyProperty.Register("Values", typeof(IEnumerable<double>), typeof(LineChart), new PropertyMetadata(null, new PropertyChangedCallback(OnValueChanged))); private static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d != null && d is LineChart) { LineChart lineChart = d as LineChart; lineChart.ValueChanged(e); } } public LineChartData ChartData { get; set; } public LineChart() { InitializeComponent(); Values = new ObservableCollection<double>(); ChartData = new LineChartData(); chart.DataContext = ChartData; Unloaded += LineChart_Unloaded; } private void LineChart_Unloaded(object sender, RoutedEventArgs e) { ChartData = null; chart.DataContext = null; } private void Values_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e.NewItems != null && e.NewItems.Count > 0) { if (e.NewItems.Count == 1) ChartData.AddDataPoint(Convert.ToDouble(e.NewItems[0])); else ChartData.AddDataPointrange(e.NewItems.Cast<double>().GetEnumerator() as IEnumerable<double>); } } public void ValueChanged(DependencyPropertyChangedEventArgs e) { if (ChartData == null) return; if (e.NewValue == null) return; try { switch (e.Property.Name) { case "Values": if (e.OldValue != null) { var oldCollection = e.OldValue as INotifyCollectionChanged; if (oldCollection != null) { oldCollection.CollectionChanged -= Values_CollectionChanged; } } var newCollection = e.NewValue as INotifyCollectionChanged; if (newCollection != null) { newCollection.CollectionChanged += Values_CollectionChanged; if (e.NewValue is ObservableCollection<double>) { ChartData.Clear(); ChartData.AddDataPointrange(e.NewValue as ObservableCollection<double>); } } break; default: break; } } catch (Exception ex) { throw ex; } } }
Файл MainWindow.язык XAML
<Window x:Class="WpfTestDP_Coll_Changed.MainWindow" 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:local="clr-namespace:WpfTestDP_Coll_Changed" xmlns:layouts="clr-namespace:WpfTestDP_Coll_Changed.Layouts" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <BooleanToVisibilityConverter x:Key="VisibleIfTrueConverter"/> </Window.Resources> <DockPanel> <StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="0,0,0,10"> <Button Name="btnStart" Content="Start" Command="{Binding CaptureDataCommand}" CommandParameter="DemoTimerStart" Margin="0,0,10,0"/> <Button Name="btnStop" Content="Stop" Command="{Binding CaptureDataCommand}" CommandParameter="DemoTimerStop" /> </StackPanel> <Grid> <layouts:ChartLayout2 Visibility="{Binding Path=IsChecked,ElementName=radLayout2, Converter={StaticResource VisibleIfTrueConverter}}"/> </Grid> </DockPanel>
Файл MainWindow.в CS
public partial class MainWindow : Window { VM_CaptureData VM_CaptureData; public MainWindow() { InitializeComponent(); Unloaded += CaptureDataControl_Unloaded; VM_CaptureData = new VM_CaptureData(); DataContext = VM_CaptureData; } private void CaptureDataControl_Unloaded(object sender, RoutedEventArgs e) { DataContext = null; VM_CaptureData.Deinitialize(); VM_CaptureData = null; } }
ViewModel для MainWindow
class VM_CaptureData : INotifyPropertyChanged { private ObservableCollection<ChartLibrary.ChartDataPopulation> parametersData = new ObservableCollection<ChartLibrary.ChartDataPopulation>(); public ObservableCollection<ChartLibrary.ChartDataPopulation> ParametersData { get { return parametersData; } set { parametersData = value; OnPropertyChanged(); } } private ChartLibrary.ChartDataPopulation selectedParameterData; public ChartLibrary.ChartDataPopulation SelectedParameterData { get { return selectedParameterData; } set { selectedParameterData = value; OnPropertyChanged(); } } DispatcherTimer dispatcherTimer; public RelayCommand<object> CaptureDataCommand { get; set; } public VM_CaptureData() { CaptureDataCommand = new RelayCommand<object>(Execute, CanExecute); ParametersData.Add(new ChartLibrary.ChartDataPopulation()); ParametersData.Add(new ChartLibrary.ChartDataPopulation()); ParametersData.Add(new ChartLibrary.ChartDataPopulation()); selectedParameterData = ParametersData.First(); dispatcherTimer = new DispatcherTimer(); dispatcherTimer.Interval = TimeSpan.FromMilliseconds(1000); dispatcherTimer.Tick += DispatcherTimer_Tick; } Random random = new Random(); private void DispatcherTimer_Tick(object sender, EventArgs e) { foreach (var item in ParametersData) { item.AddDataAndCalculate(random.NextDouble()); } } private bool CanExecute(object arg) { return SelectedParameterData != null; } private void Execute(object obj) { try { if (obj != null) { switch (obj.ToString()) { case "DemoTimerStart": dispatcherTimer.Start(); break; case "DemoTimerStop": dispatcherTimer.Stop(); break; default: break; } } } catch (Exception ex) { throw ex; } } internal void Deinitialize() { try { dispatcherTimer.Stop(); ParametersData.Clear(); SelectedParameterData = null; } catch (Exception ex) { throw ex; } } #region INotifyPropertyChangedImplementation public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { var handler = PropertyChanged; if (PropertyChanged != null) PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } #endregion }