Можно ли привязать встроенную диаграмму winform к наблюдаемой коллекции в WPF

Я хотел встроить элемент управления WinForm chart в окно WPF, которое будет привязано к коллекции observablecollection, заполненной путем ввода данных в WPF DataGrid.
Observablecollection необходим, потому что я заполняю его с помощью WPF-DataGrid, в котором я могу вставить или обновить данные.

Поэтому я добавил в свой WPF-проект следующие зависимости:
— Система.Windows.Формы
— Система.Windows.Формы.Датавизуализация

Для первого теста я жестко закодировал в конструкторе окна WPF некоторые данные в observablecollection и связал элемент управления chart.
В этом случае отображение на графике работает нормально.

Но в окончательной версии я хочу вставить и / или обновить данные в DataGrid, и диаграмма должна отображать эти данные все сразу.
Можно ли это сделать?

Вот код для окна и классов в качестве примера.

Главное окно окна WPF.код XAML:

<Window x_Class="StepFunctions.MainWindow"
        
        
        
        
        
        
        
        mc_Ignorable="d"
        Title="StepFunctions"
        Height="350"
        Width="525">
    <Grid x_Name="maingrid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <!--DataGrid for insert and update of step function data-->
        <DataGrid x_Name="grd_stepdata"
                  Grid.Row="0"
                  Grid.Column="0"
                  Margin="5"
                  AutoGenerateColumns="False"
                  CanUserAddRows="True"
                  RowEditEnding="grd_stepdata_RowEditEnding"
                  ItemsSource="{Binding StepDataSource, NotifyOnSourceUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
            <DataGrid.Columns>
                <DataGridTextColumn x_Name="col_LowerComparer" Binding="{Binding LowerComparer, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Lower comparer"/>
                <DataGridTextColumn x_Name="col_LowerBound" Binding="{Binding LowerBound, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Lower bound"/>
                <DataGridTextColumn x_Name="col_StepValue" Binding="{Binding StepValue, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Value"/>
            </DataGrid.Columns>
        </DataGrid>

        <!--Chart for displaying the step function data-->
        <WindowsFormsHost x_Name="host"
                          Grid.Row="0"
                          Grid.Column="1"
                          Margin="5">
            <winformchart:Chart x_Name="myWinformChart"
                                Dock="Fill">
                <winformchart:Chart.Series>
                    <winformchart:Series Name="series" ChartType="Line"/>
                </winformchart:Chart.Series>
                <winformchart:Chart.ChartAreas>
                    <winformchart:ChartArea/>
                </winformchart:Chart.ChartAreas>
            </winformchart:Chart>
        </WindowsFormsHost>

        <!--Button for test-->
        <Button x_Name="btn_do"
                Grid.Row="2"
                Grid.Column="0"
                Margin="5"
                Click="btn_do_Click">Do it</Button>
    </Grid>
</Window>

Код-за главным окном.код XAML:

using StepFunctions.ViewModels;
using System.Windows;
using System.Windows.Controls;

namespace StepFunctions
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private MainWindowViewModel vm = new MainWindowViewModel();

        public MainWindow()
        {
            InitializeComponent();
            DataContext = vm;

            // These lines are just for the first test.
            // Normally these lines would be out-commented.
            AddStepdata();
            ChartDataRefresh();
        }

        // When the user leaves a DataGrid-row after insert or update the chart shall be refreshed.
        private void grd_stepdata_RowEditEnding(object sender, DataGridRowEditEndingEventArgs e)
        {
            ChartDataRefresh();
        }

        private void AddStepdata()
        {
            vm.StepDataSource.Add(new StepData("<", 10, 1));
            vm.StepDataSource.Add(new StepData("<", 100, 2));
            vm.StepDataSource.Add(new StepData("<", 1000, 3));
        }

        private void ChartDataRefresh()
        {
            myWinformChart.DataSource = vm.StepDataSource;
            myWinformChart.Series["series"].XValueMember = "LowerBound";
            myWinformChart.Series["series"].YValueMembers = "StepValue";
        }

        /// <summary>
        /// For testing the refresh of the chart after the window was loaded.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_do_Click(object sender, RoutedEventArgs e)
        {
            AddStepdata();
            ChartDataRefresh();
        }

    }
}

Модель представления:

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace StepFunctions.ViewModels
{
    public class MainWindowViewModel : INotifyPropertyChanged
    {
        /// <summary>
        /// Eventhandler for signalising that a property has changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        private ObservableCollection<StepData> stepdataSource = new ObservableCollection<StepData>();

        public ObservableCollection<StepData> StepDataSource
        {
            get { return stepdataSource; }
            set
            {
                stepdataSource = value;
                RaisePropertyChanged("StepDataSource");
            }
        }

        /// <summary>
        /// Informs the target which is bound to a property, that it's source was changed and that it shall update.
        /// </summary>
        /// <param name="propertyName">The name of the property.</param>
        public void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

    }
}

И, наконец, класс StepData, который является основой для observablecollection:

namespace StepFunctions.ViewModels
{
    /// <summary>
    /// Class for data of stepfunctions.
    /// </summary>
    public class StepData
    {
        /// <summary>
        /// The constructor.
        /// </summary>
        public StepData()
        {
            // Do nothing
        }

        public StepData(string lowerComparer, double lowerBound, double stepValue)
        {
            LowerComparer = lowerComparer;
            LowerBound = lowerBound;
            StepValue = stepValue;
        }

        public string LowerComparer { get; set; }

        public double LowerBound { get; set; }

        public double StepValue { get; set; }

    }
}

1 ответ

  1. Я понял!
    Диаграмма должна создаваться в коде, а не в XAML.

    Поэтому метод ChartDataRefresh должен выглядеть так:

    private void ChartDataRefresh()
    {
        Chart myWinformChart = new Chart();
        myWinformChart.Dock = System.Windows.Forms.DockStyle.Fill;
        Series mySeries = new Series("series");
        mySeries.ChartType = SeriesChartType.Line;
        myWinformChart.Series.Add(mySeries);
        ChartArea myArea = new ChartArea();
        myWinformChart.ChartAreas.Add(myArea);
        myWinformChart.DataSource = vm.StepDataSource;
        myWinformChart.Series["series"].XValueMember = "LowerBound";
        myWinformChart.Series["series"].YValueMembers = "StepValue";
        host.Child = myWinformChart;
    }
    

    При вводе данных в WPF-DataGrid элемент управления Winform chart обновляется и данные отображаются в виде строки для проверки правильности данных.