Форум программистов, компьютерный форум, киберфорум
Наши страницы
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
 
Semyon001
1 / 1 / 0
Регистрация: 12.08.2015
Сообщений: 204
Завершенные тесты: 1
1

LiveChart Binding

29.10.2019, 13:40. Просмотров 306. Ответов 6

Использую Live Chart в своем проекте для построение графиков по точкам. Проект построен согласно паттерну MVVM.
Задача следующая: по нажатию на кнопку должно происходить построение графика. Не так важно откуда берутся данные, важно, что есть два набора чисел Х и Y соответственно. По ним должен строиться график, однако этого не происходит. Хочу сделать через привязку к свойству Series, но видимо здесь какая-то проблема, раз график не строится.
Привожу пример моего кода:
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<Window x:Class="WpfApp8.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:WpfApp8"
        xmlns:wpf="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel>
        <Button Content="click" Command="{Binding Execute}"/>
        <wpf:CartesianChart Series="{Binding Series, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" Height="300"/>
    </StackPanel>
</Window>
В конструкторе Code Behind:
C#
1
DataContext = new VM1();
ViewModel:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using LiveCharts;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
 
namespace WpfApp8
{
    class VM1 : VM
    {
        public VM1()
        {
            model = new Model1();
            Execute = new RelayCommand(ExecuteCommand, () => true);
            model.PropertyChanged += Model_PropertyChanged;
 
 
        }
 
        Model1 model;
 
        public SeriesCollection Series
        {
            get => model.SeriesCollection;
        }
 
        List<string> modelProperties = new List<string>() { nameof(Model1.SeriesCollection)};
        private void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            string propertyName = e.PropertyName;
            if (string.IsNullOrEmpty(propertyName) || modelProperties.IndexOf(propertyName) >= 0)
                OnPropertyChanged(propertyName);
        }
 
        public void ExecuteCommand()
        {
            ObservableCollection<double> x = new ObservableCollection<double>() { 1, 2, 3, 4, 5, 6, 10 };
            ObservableCollection<double> y = new ObservableCollection<double>() { 10, 20, 30, 40, 50, 60, 100 };
 
            model.setData(x, y);
            model.Plot();
        }
 
        public RelayCommand Execute { get; }
 
    } 
}



Model:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
using LiveCharts;
using LiveCharts.Defaults;
using LiveCharts.Wpf;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
 
namespace WpfApp8
{
    class Model1 : INotifyPropertyChanged
    {
        ObservableCollection<double> x = new ObservableCollection<double>();
        ObservableCollection<double> y = new ObservableCollection<double>();
        ChartValues<ObservablePoint> points = new ChartValues<ObservablePoint>();
        private SeriesCollection seriesCollection;
        public SeriesCollection SeriesCollection
        {
            get => seriesCollection;
            set { seriesCollection = value; OnPropertyChanged(); }
        }
 
        public Model1()
        {  }
 
        public void setData(ObservableCollection<double> _x, ObservableCollection<double> _y)
        {
            x = _x;
            y = _y;
        }
 
        public void Plot()
        {
 
            if (x.Count == 0 || y.Count == 0)
            {
                return;
            }
 
            points.Clear();
            points=new ChartValues<ObservablePoint>();
            SeriesCollection = new SeriesCollection();
 
            for (int i = 0; i < Math.Min(x.Count, y.Count); ++i)
            {
                points.Add(new ObservablePoint(x[i], y[i]));
            }
 
            SeriesCollection.Add(new LineSeries
            {
                Values = points,
                Fill = Brushes.Transparent
            }
            );
        }
 
        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged([CallerMemberName]string prop = "")
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }
}
0
QA
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
29.10.2019, 13:40
Ответы с готовыми решениями:

Передать внутри Binding в ConverterParameter ссылку на окно содержащее объект, который использует Binding
В главном окне в ресурсах есть Контекстные меню. У программы есть &quot;Ручной режим&quot;, если режим тру....

Binding
public class ProjectInfo { public List&lt;AHU&gt; ListAHU { get; set; } public...

Binding x:Name
Доброго времени суток. Подскажите пожалуйста как в Style получить наименование котнрола: ...

Binding
Добрый день! Подскажите, пожалуйста, можно ли в данном случае свойство Text элемента TextBlock...

Binding к ViewModel
Класс ViewModel задаётся через конструктор MainWindow(class Window) в его DataContext. Кнопка...

6
Элд Хасп
Модератор
4308 / 2999 / 928
Регистрация: 21.04.2018
Сообщений: 9,479
Записей в блоге: 2
29.10.2019, 18:33 2
Semyon001, а где в VM вы ловите INPC из Модели?
Само по себе INPC из Модели сквозняком через VM не попадёт в View.

Прочитайте тему "INPC и получение данных из Модели" в Готовые решения, примеры и рекомендации начинающим на WPF

Добавлено через 13 минут
Semyon001, какой смысл передавать коллекцию в Модель в типе ObservableCollection?
Для Модели это абсолютно излишне.
0
Semyon001
1 / 1 / 0
Регистрация: 12.08.2015
Сообщений: 204
Завершенные тесты: 1
30.10.2019, 10:45  [ТС] 3
Цитата Сообщение от Элд Хасп Посмотреть сообщение
какой смысл передавать коллекцию в Модель в типе ObservableCollection?
Это я на всякий случай перестраховался, от безысходности

Цитата Сообщение от Элд Хасп Посмотреть сообщение
а где в VM вы ловите INPC из Модели?
Понял в чем ошибка. Их оказалось даже две.
Первая. Имя свойства в Модели должно совпадать со свойством в VM. Типа того:
C#
1
2
3
4
5
        
public SeriesCollection SeriesCollection
        {
            get => model.SeriesCollection;
        }
Соответственно и в View:
XML
1
2
        
<wpf:CartesianChart Series="{Binding SeriesCollection, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
Вторая ошибка. В Модели в методе plot() нужно создать локальную переменную типа SeriesCollection. И потом уже ее присвоить свойству, т.к. метод Add() не вызывает метод OnPropertyChanged(), нужно именно присваивание (через равно):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void Plot()
        {
 
            if (x.Count == 0 || y.Count == 0)
            {
                MessageBox.Show("Один из буферов пустой");
                return;
            }
 
            points.Clear();
            points=new ChartValues<ObservablePoint>();
            SeriesCollection Series = new SeriesCollection();
 
            for (int i = 0; i < Math.Min(x.Count, y.Count); ++i)
            {
                points.Add(new ObservablePoint(x[i], y[i]));
            }
 
            Series.Add(new LineSeries
            {
                Values = points,
                Fill = Brushes.Transparent
            }
            );
            SeriesCollection = Series;
        }
0
Элд Хасп
Модератор
4308 / 2999 / 928
Регистрация: 21.04.2018
Сообщений: 9,479
Записей в блоге: 2
30.10.2019, 11:15 4
Semyon001, дело не в одноимёности свойств.
В VM нужно подписываться на события Модели.
И в прослушке надо изменять свойства VM которые поддерживают INPC или для свойства привязанных в маркет Модели просто создать событие.
При одноимёности это просто всё упрощается.
Но прослушка Модели нужна в любом случае обязательно.
0
Элд Хасп
Модератор
4308 / 2999 / 928
Регистрация: 21.04.2018
Сообщений: 9,479
Записей в блоге: 2
30.10.2019, 11:51 5
Semyon001, по второму пункту, в Модели создаются данные. В VM они при необходимости конвертируются в типы нужные в View.
SeriesCollection - это тип для View. И в Модели ему делать нечего.
Из Модели данные приходят в дефолтных типах или DTO типах.

В данном случае, насколько понял, из Модели должен прийти список координат точек на плоскости.
Для точки можно использовать тип Point.
Для списка любой тип коллекции: просто массив и List проще всего.

В VM такой список должен преобразовываться в SeriesCollection.
0
Semyon001
1 / 1 / 0
Регистрация: 12.08.2015
Сообщений: 204
Завершенные тесты: 1
30.10.2019, 13:19  [ТС] 6
Цитата Сообщение от Элд Хасп Посмотреть сообщение
SeriesCollection - это тип для View. И в Модели ему делать нечего.
Элд Хасп, тогда получается, Модель совсем не нужна? Т.к. ее задача принимать две коллекции чисел x и y (это было задумано изначально) и в методе plot() преобразовывать эти две коллекции в одну типа SeriesCollection, которая нужна для View.
Если все преобразование делать в VM, то потребность в Модели отпадает... Или я что-то не понял?

И еще, если в будущем возникнет потребность преобразовывать x и y в SeriesCollection определенным образом, например, разбивать коллекции x и y на несколько частей, чтобы получилось несколько линий на одном графике, тогда этот алгоритм будет будет реализован в VM, что вроде как неправильно.

И где можно почитать по-подробнее про DTO?
0
Элд Хасп
Модератор
4308 / 2999 / 928
Регистрация: 21.04.2018
Сообщений: 9,479
Записей в блоге: 2
30.10.2019, 13:48 7
Semyon001, необходимость Модели определяется наличием данных и метода работы с ними.
В данном случае у вас путаница из-за неправильного разделения функционала.

Данными в решении является коллекция точек.
C#
36
37
            ObservableCollection<double> x = new ObservableCollection<double>() { 1, 2, 3, 4, 5, 6, 10 };
            ObservableCollection<double> y = new ObservableCollection<double>() { 10, 20, 30, 40, 50, 60, 100 };
Она у вас создаётся в VM. А должна создаваться в Модели.

А вот метод преобразования из такой коллекции в тип для View Plot() должен находиться в VM.

Добавлено через 3 минуты
В данном случае, из-за простоты задачи, можно явно не разделять VM и Модель. Только это не означает, что Модели нет.
У вас просто будет один тип реализующий функции и VM, и Модели.
Но для себя вы должны чётко понимать где что, какой метод относится к VM, а какой к Модели.
Если опыта не достаточно, то не советую так делать.
Только запутаете самого себя.
0
30.10.2019, 13:48
Answers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
30.10.2019, 13:48

Binding к Combobox
Всем привет. Сижу не понимаю как привязывать к Combobox-у. У меня есть таблица(Entity Framework) с...

Radiobutton binding
Начал изучать WPF и столкнулся со следующей проблемой: как двум радиобаттонам присвоить одно...

Data Binding
Всем привет! Я разбирался с Data Binding. На сайте microsoft написано, что данные должны...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.