mangesh.raykar Ответов: 0

Как запустить событие 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
}

0 Ответов