Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.93/15: Рейтинг темы: голосов - 15, средняя оценка - 4.93
28 / 20 / 10
Регистрация: 01.12.2018
Сообщений: 186

Рисование в рамках паттерна MVVM

18.12.2022, 09:21. Показов 4213. Ответов 31
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте. Возникла задача вывода графических данных для их визуального отображения. Суть задачи в том, что я реализую сейчас триангуляцию Делоне и хотелось бы видеть результат работы отдельных функций ну и триангуляции в целом. Каким образом можно просто отобразить точки и линии, соединяющие точки в WPF+MVVM?
Для информации: искал в интернете, вся информация, что есть, как минимум нарушает MVVM. Нашел, что есть тип Visual (является базовым типом рисования в WPF и наследован от DependencyObject). Можно ли воспользоваться данным типом, добавить нужные точки, соединить линиями и отобразить в каком-нибудь Canvas? Таким образом я где-то в модели отрисую на низком уровне все, что мне надо, во ViewModel создам свойство типа Visual, и привяжу к контролу, который просто уже отобразит мне все, что я нарисовал, тем самым не нарушив паттерн и исполнив мои пожелания. Спасибо.
P.S. Естественно я не прошу сделать все за меня и положить тут готовый код, хотелось бы ссылки на информацию об этом подробнее (кроме MSDN естественно, там был уже), ну и ваши мысли и бесценный опыт в данной области, если таковой имеется. Спасибо!
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
18.12.2022, 09:21
Ответы с готовыми решениями:

Передача данных между окнами в рамках паттерна MVVM
Доброго времени суток, уважаемые форумчане! Я абсолютный новичок в программировании, который пытается освоить, хотя бы в небольшой...

Показ окна "О программе" в рамках паттерна MVVM
Добрый день! :senor: Настал тот момент разработки "мегапроги" (:D), при котором программе понадобилось окно "О программе". Окно...

Реализация паттерна MVVM
Добрый день, форумчане. Ни разу не пользовался данным паттерном программирования, прочитал уже кучу статей, но ни одна из них не может мне...

31
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
21.12.2022, 17:59
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от fakemade Посмотреть сообщение
что при такой реализации при смене OxyPlot на условный ScottPlot придется переписать конвертер, а VM, останется нетронутой и универсальной, как это и принято в MVVM?
В самом простом варианте - да.

Добавлено через 2 минуты
Цитата Сообщение от fakemade Посмотреть сообщение
Нашел способ, как рисовать линии. Есть коллекция линий в VM, которая через ItemsControl привязана к канвасу,
Стоп, стоп, стоп....
Если вам нужна просто линия (кривая), то достаточно PolyLine с привязкой через конвертер PointCollection.
Создавать только из-за одной линии ItemsControl с канвой - избыточно.

Добавлено через 1 минуту
Цитата Сообщение от fakemade Посмотреть сообщение
Так вот, по аналогии попробовал отобразить точки в виде кружочков или маленьких Ellipse.
Нужно смещение задавать в стиле элемента.
Сейчас посмотрю походящую тему в разделе.

Добавлено через 1 минуту
График функции. Работающий пример

Добавлено через 36 секунд
Присоединённые свойства (Attached Property) для всплывающей привязки из шаблонов данных к Canvas и Grid.

Добавлено через 2 минуты
Автоматическое рисование фигур на Canvas

Добавлено через 43 секунды
Привязка координат одного Shape относительно другого
1
28 / 20 / 10
Регистрация: 01.12.2018
Сообщений: 186
23.12.2022, 07:51  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Стоп, стоп, стоп....
Если вам нужна просто линия (кривая), то достаточно PolyLine с привязкой через конвертер PointCollection.
Наверное все-таки не совсем так. У меня есть ObservablseCollection<Triangle> результирующая коллекция треугольников после триангуляции (коллекция есть, алгоритм пока не до конца написан, но один треугольник я туда могу вставить для теста), ее я с помощью такого простейшего конвертера преобразую к коллекции линий в одну сторону:
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
 public object Convert(object v, Type t, object p, CultureInfo c)
        {
            var TrianglesCollection = v as ObservableCollection<Triangle>;
            ObservableCollection<Line> LinesFromTriangles = new ObservableCollection<Line>();
            foreach (var triangle in TrianglesCollection)
            {
                LinesFromTriangles.Add(triangle.FirstLine);
                LinesFromTriangles.Add(triangle.SecondLine);
                LinesFromTriangles.Add(triangle.ThirdLine);
            }
            return LinesFromTriangles;
        }

Эту коллекцию линий я показываю с помощью данного кода (я пока не до конца понял механизм его работы, но все же):
Кликните здесь для просмотра всего текста
XML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ItemsControl ItemsSource="{Binding LineCollection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.Resources>
                        <DataTemplate DataType="{x:Type tri:Line}">
                            <Line X1="{Binding Path=StartNode.coordX}" Y1="{Binding Path=StartNode.coordY}"
                                  X2="{Binding Path=EndNode.coordX}" Y2="{Binding Path=EndNode.coordY}"
                                  Stroke="Black" StrokeThickness="2"
                                  StrokeStartLineCap="Round" StrokeEndLineCap="Round"/>
                        </DataTemplate>
                    </ItemsControl.Resources>
                </ItemsControl>

Это лишь коллекция линий треугольника. Параллельно мне нужно отрисовать исходный прямоугольник, в области которого генерируются узлы и происходит триангуляция, а также сами узлы в виде точек или эллипсов. Отрисовку точек реализовал добавлением аналогичного кода для коллекции узлов (из Ваших примеров):
Кликните здесь для просмотра всего текста
XML
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
<ItemsControl ItemsSource="{Binding NodeCollection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <Canvas />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.Resources>
                        <DataTemplate DataType="{x:Type tri:Node}">
                            <Ellipse Height= "5"
                                     Width= "5"                                       
                                     StrokeThickness="0.75"
                                     Stroke="Blue" RenderTransformOrigin="0.5,0.5">
                                <Ellipse.RenderTransform>
                                    <TranslateTransform X="-2.5" Y="-2.5"/>
                                </Ellipse.RenderTransform>
                            </Ellipse>
                        </DataTemplate>
                    </ItemsControl.Resources>
                    <ItemsControl.ItemContainerStyle>
                        <Style TargetType="{x:Type TypeName=ContentPresenter}" >
                            <Setter Property="Canvas.Top" Value="{Binding coordY}" />
                            <Setter Property="Canvas.Left" Value="{Binding coordX}"/>
                        </Style>
                    </ItemsControl.ItemContainerStyle>
                </ItemsControl>

Возможно пока код не совсем правильный или вообще не правильный, однако, он работает и поверх линий я вижу нужные мне узлы (скриншот 1). Попутно возникает много вопросов. Во-первых, можно ли обойтись одним ItemsControl, чтобы рисовать и линии и узлы одновременно и не размазывать таким образом код? Во-вторых, я могу сделать просто коллекцию линий, которую буду собирать из всего и просто, без конвертеров биндить в ItemsControl, но насколько это целесообразно? В-третьих, поднимается вопрос о масштабировании и перемещении изображения на Canvas, хотелось бы покрупнее или помельче посмотреть а также иметь возможность перемещать его. Логика в целом понятна, наверное необходимо будет ввести нужные масштабные коэффициенты, чтобы абсолютные размеры и координаты не менялись, а менялись только координаты для отображения или как-то так. Каким образом такое можно реализовать? Спасибо, пока разбираюсь в таком формате, чему-то учусь, гораздо лучше приходит понимание и усваиваются знаний, когда решаешь какую-то реальную задачу, а не фиктивную из книжки по типу "у вас есть 100 бананов..."
Миниатюры
Рисование в рамках паттерна MVVM  
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
23.12.2022, 11:05
Цитата Сообщение от fakemade Посмотреть сообщение
ее я с помощью такого простейшего конвертера преобразую к коллекции линий в одну сторону:
В конвертере создавать ObservableCollection бессмысленно. Достаточно простого массива, ну, или листа.
Line - это UI элемент.
Можно конечно и их создавать. Но на каждый треугольник у вас получится по три UI элемента. А по функционалу вам кроме отрисовки отрезка от этого UI элемента ничего не нужно. Здесь лучше подойдёт геометрия.

Цитата Сообщение от fakemade Посмотреть сообщение
Эту коллекцию линий я показываю с помощью данного кода (я пока не до конца понял механизм его работы, но все же):
Если правильно понимаю, вам нужно отрисовать коллекцию треугольников. Каждый треугольник это отдельная фигура. То есть нет непрерывной линии в которую включены все треугольники.
Так? Если да, то зачем вы их все преобразует в одну общую коллекцию линий?

fakemade, вам нужно упростить ваш вопрос, разбить его на части.
А то мы уже два десятка сообщений занимаемся выяснением что вы хотите реализовать, а не поиском решений.

Можете проще изложить?
Вот есть такая-то коллекция, таких-то объектов. Её нужно вот так-то отобразить.
А то у вас все вопросы намешаны в кучу: и функция с графиком, и линия по точкам, и коллекция треугольников.
При этом совершено непонятна какая нужна интерактивность по каждому их них.

Добавлено через 10 минут
Цитата Сообщение от fakemade Посмотреть сообщение
Во-первых, можно ли обойтись одним ItemsControl, чтобы рисовать и линии и узлы одновременно и не размазывать таким образом код?
Я здесь вижу скорее 1-2 геометрии чем ItemsControl.
Items Control на каждый элемент коллекции создаёт несколько UI элементов. Это оправдано если нужна интерактивность по каждому элементу коллекции. Например индикация наведения, выбор элемента и т.п.
Если такая интерактивность не нужна, то можно вывести всё 1-2 геометриями. Это значительно облегчит работу GUI, что может быть очень существенным по мере увеличения коллекций.

Вывести всё одним ItemsControl - не проблема.
Для этого на каждый тип элемента источника создаётся свой шаблон данных по умолчанию. Нужный шаблон будет автоматически подхвачен.
1
28 / 20 / 10
Регистрация: 01.12.2018
Сообщений: 186
23.12.2022, 12:59  [ТС]
Элд Хасп, хорошо, давайте проще. Начнем с геометрии. Есть исходный прямоугольник, построенный по узлам(Node), этот прямоугольник ни с чем не связан, его мне нужно визуализировать. Есть треугольники, полученные в результате триангуляции(они между собой соединены как раз через узлы (Node), потому что триангуляция есть решение того, как красиво соединить набор узлов). Эту коллекцию треугольников также необходимо визуализировать. Есть еще коллекция узлов, по который построены как прямоугольник, так и треугольники, их мне также нужно визуализировать в виде кружков или квадратиков, неважно. Каким образом такое можно рационально, просто и правильно сделать соответственно в рамках паттерна MVVM? Ну и раз обсуждение зашло так далеко, как это сделать как можно компактнее, если вы предлагаете не использовать ItemsControl. Спасибо.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
23.12.2022, 16:24
Цитата Сообщение от fakemade Посмотреть сообщение
Есть исходный прямоугольник, построенный по узлам(Node), этот прямоугольник ни с чем не связан, его мне нужно визуализировать. Есть треугольники, полученные в результате триангуляции(они между собой соединены как раз через узлы (Node), потому что триангуляция есть решение того, как красиво соединить набор узлов)
Можно ли их объединить в общий Многоугольник вершины которого задаются коллекцией точек?
Скорее всего да.
В общем случае для отрисовки любой фигуры в WPF нужно знать её фрейм (кадр): смещение верхнего левого угла, размеры фигуры. Это базовый класс для абстрактной фигуры. Для линии и многоугольника надо добавить коллекцию точек, для круга радиус. Для выделения цветом, толщины и др. какие-то доп. свойства, если они определяются в Модели, а не в GUI.

Добавлено через 8 минут
Цитата Сообщение от fakemade Посмотреть сообщение
узлы (Node)
Покажите реализацию этого класса.
Если большой, то части относящейся к его визуализации.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
23.12.2022, 23:14
fakemade, пример на уровне VM -> View.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
namespace Core2022.Figures
{
    public abstract class Figure
    {
        public double Left => GetLeft();
        public double Top => GetTop();
        public double Right => GetRight();
        public double Bottom => GetBottom();
        public double Width => GetWidth();
        public double Height => GetHeight();
 
        protected abstract double GetLeft();
        protected abstract double GetTop();
        protected abstract double GetRight();
        protected abstract double GetBottom();
        protected abstract double GetWidth();
        protected abstract double GetHeight();
    }
}
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
67
68
69
70
71
72
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
 
namespace Core2022.Figures
{
    public class Polygon : Figure
    {
        public ReadOnlyCollection<Point> Vertices { get; }
        private readonly double _left;
        private readonly double _top;
        private readonly double _right;
        private readonly double _bottom;
        private readonly double _width;
        private readonly double _height;
        public Polygon(IEnumerable<Point> vertices)
        {
            Point[] verts = vertices.ToArray();
            Vertices = Array.AsReadOnly(verts);
            _left = _right = verts[0].X;
            _top = _bottom = verts[0].Y;
 
            for (int i = 1; i < verts.Length; i++)
            {
                var vertex = verts[i];
                if (vertex.X < _left)
                {
                    _left = vertex.X;
                }
                else if (vertex.X > _right)
                {
                    _right = vertex.X;
                }
                if (vertex.Y < _top)
                {
                    _top = vertex.Y;
                }
                else if (vertex.Y > _bottom)
                {
                    _bottom = vertex.Y;
                }
            }
            _width = _right - _left;
            _height = _bottom - _top;
 
            Vector offset = new Vector(_left, _top);
            for (int i = 0; i < verts.Length; i++)
            {
                verts[i] = verts[i] - offset;
            }
        }
 
        public Polygon(params Point[] vertices)
            : this((IEnumerable<Point>)vertices)
        { }
 
        protected override double GetLeft() => _left;
 
        protected override double GetTop() => _top;
 
        protected override double GetRight() => _right;
 
        protected override double GetBottom() => _bottom;
 
        protected override double GetWidth() => _width;
 
        protected override double GetHeight() => _height;
    }
}
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
using System;
using System.Windows;
 
namespace Core2022.Figures
{
    public class Circle : Figure
    {
        public Point Center { get; }
        public double Radius { get; }
 
        public Circle(Point center, double radius)
        {
            if (radius < 0)
                throw new ArgumentOutOfRangeException(nameof(radius), "Радиус не может быть отрицательным.");
            Center = center;
            Radius = radius;
        }
 
        protected override double GetLeft() => Center.X - Radius;
 
        protected override double GetTop() => Center.Y - Radius;
 
        protected override double GetRight() => Center.X + Radius;
 
        protected override double GetBottom() => Center.Y + Radius;
 
        protected override double GetWidth() => 2.0 * Radius;
 
        protected override double GetHeight() => 2.0 * Radius;
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
using System.Collections.ObjectModel;
using System.Windows;
 
namespace Core2022.Figures
{
    public class FiguresViewModel
    {
        public ObservableCollection<Figure> Figures { get; } = new()
        {
            new Circle(new Point(50,50), 20),
            new Polygon(new Point(70,-10),new Point(10,-70),new Point(-100,100)),
            new Polygon(new Point(-150,-200),new Point(-210,150),new Point(100,80),new Point(230,10) )
        };
    }
}
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
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
 
namespace Core2022.Figures
{
    public class PointCollectionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is Point point)
                value = new Point[] { point };
            if (value is IEnumerable<Point> points)
                return new PointCollection(points);
            return DependencyProperty.UnsetValue;
        }
 
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
 
        private PointCollectionConverter() { }
        public static PointCollectionConverter Instance { get; } = new();
    }
 
    public class PointCollectionExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            return PointCollectionConverter.Instance;
        }
    }
}
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
using System.Windows;
using System.Windows.Controls;
 
namespace Core2022.Figures
{
    public class FigureTemplateSelector : DataTemplateSelector
    {
        public DataTemplate? Circle { get; set; }
        public DataTemplate? Triangle { get; set; }
        public DataTemplate? Quad { get; set; }
 
        private static readonly DataTemplate empty = new DataTemplate();
 
        public override DataTemplate SelectTemplate(object item, DependencyObject container)
        {
            if (item is Circle)
                return Circle ?? empty;
            if (item is Polygon polygon)
            {
                if (polygon.Vertices.Count == 3)
                    return Triangle ?? empty;
                if (polygon.Vertices.Count == 4)
                    return Quad ?? empty;
            }
 
            return base.SelectTemplate(item, container);
        }
    }
}
XML
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
<Window x:Class="Core2022.Figures.FiguresWindow"
        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:Core2022.Figures"
        mc:Ignorable="d"
        Title="FiguresWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:FiguresViewModel x:Key="vm"/>
        <ItemsPanelTemplate x:Key="figures.Panel">
            <Canvas VerticalAlignment="Center" HorizontalAlignment="Center" />
        </ItemsPanelTemplate>
        <Style x:Key="figures.Style" TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Left}"/>
            <Setter Property="Canvas.Top" Value="{Binding Top}"/>
        </Style>
        <DataTemplate x:Key="circle.Template" DataType="{x:Type local:Circle}">
            <Ellipse Width="{Binding Width}" Height="{Binding Height}"
                     StrokeThickness="1" Stroke="Red"/>
        </DataTemplate>
        <DataTemplate x:Key="quad.Template" DataType="{x:Type local:Polygon}">
            <Polygon StrokeThickness="2" Stroke="Blue"
                     Points="{Binding Vertices, Converter={local:PointCollection}}" />
        </DataTemplate>
        <DataTemplate x:Key="triangle.Template" DataType="{x:Type local:Polygon}">
            <Polygon StrokeThickness="3" Stroke="Green"
                     Points="{Binding Vertices, Converter={local:PointCollection}}"/>
        </DataTemplate>
        <local:FigureTemplateSelector
            x:Key="figureSelector"
            Circle="{StaticResource circle.Template}"
            Triangle="{StaticResource triangle.Template}"
            Quad="{StaticResource quad.Template}"/>
    </Window.Resources>
    <Grid>
        <ItemsControl ItemsSource="{Binding Figures}"
                      ItemsPanel="{DynamicResource figures.Panel}"
                      ItemContainerStyle="{DynamicResource figures.Style}"
                      ItemTemplateSelector="{DynamicResource figureSelector}"/>
    </Grid>
</Window>
1
28 / 20 / 10
Регистрация: 01.12.2018
Сообщений: 186
24.12.2022, 11:05  [ТС]
Элд Хасп, благодарю за пример, сохраню себе в избранное. Для вас на данный момент такое написать 5-10 минут делов, для меня это что-то на уровне Senior программиста. Я делаю, как говорится, step-by-step. Все, что связано с триангуляцией делаю в отдельной библиотеке классов, так как это штука полезная и может мне потом пригодится, поэтому там нет ничего, связанного с визуализацией в принципе. Есть класс Node, описывающий узел, он самый простой(как и все остальные):
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
    /// Класс для описания узла в триангуляции
    /// </summary>
    public class Node
    {
        ///<summary>Номер узла в триангуляции</summary>
        public int NodeId { get; set; }
 
        ///<summary>Координата X узла триангуляции</summary>
        public double coordX { get; set; }
 
        ///<summary>Координата Y узла триангуляции</summary>
        public double coordY { get; set; }
        public Node(double coordX, double coordY)
        {
            this.NodeId = InformationAboutCounts.LastNodeId;
            this.coordX = coordX;
            this.coordY = coordY;
            InformationAboutCounts.LastNodeId++;
        }
    }

Есть мой класс Line(надо бы переименовать, чтобы не путаться со стандартными классами):
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/// <summary>
    /// Класс для описания линии в триангуляции
    /// </summary>
    public class Line
    {
        ///<summary>Номер линии в триангуляции</summary>
        public int LineId { get; set; }
 
        ///<summary>Первый узел линии</summary>
        public Node StartNode { get; set; }
 
        ///<summary>Второй узел линии</summary>
        public Node EndNode { get; set; }
 
        public Line(Node StartNode, Node EndNode)
        {
            this.LineId = InformationAboutCounts.LastLineId;
            this.StartNode = StartNode;
            this.EndNode = EndNode;
            InformationAboutCounts.LastLineId++;
        }
    }

Есть класс Triangle:
Кликните здесь для просмотра всего текста
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
    /// <summary>
    /// Класс для описания треугольника в триангуляции
    /// </summary>
    public class Triangle
    {
        ///<summary>Номер треугольника</summary>
        public int TriangleId { get; set; }
        ///<summary>Первая линиия треугольника</summary>
        public Line FirstLine { get; set; }
 
        ///<summary>Вторая линия треугольника</summary>
        public Line SecondLine { get; set; }
 
        ///<summary>Третья линия треугольника</summary>
        public Line ThirdLine { get; set; }
 
        public Triangle(Line FirstLine, Line SecondLine, Line ThirdLine)
        {
            this.TriangleId = InformationAboutCounts.LastTriangleId;
            this.FirstLine = FirstLine;
            this.SecondLine = SecondLine;
            this.ThirdLine = ThirdLine;
            InformationAboutCounts.LastTriangleId++;
        }
    }

Есть отдельный класс описания прямоугольника Reactangle2D:
Кликните здесь для просмотра всего текста
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
 /// <summary>
    /// Класс для описания прямоугольника в плоскости и некоторые методы работы с ним
    /// </summary>
    public class Rectangle2D
    {
        ///Все описания написаны согласно тому, что height и width положительные
        ///<summary>Левый нижний узел прямоугольника</summary>
        public Node LeftDownCorner { get; set; }
 
        ///<summary>Значение высоты прямоугольника</summary>
        public double Height { get; set; }
 
        ///<summary>Значение ширины прямоугольника</summary>
        public double Width { get; set; }
 
        ///Координаты остальных точек прямоугольника
        ///<summary>Левый верхний узел прямоугольника</summary>
        public Node LeftUpCorner { get; set; }
 
        ///<summary>Правый верхний узел прямоугольника</summary>
        public Node RightUpCorner { get; set; }
 
        ///<summary>Правый нижний узел прямоугольника</summary>
        public Node RightDownCorner { get; set; }
        /// <summary>
        /// Конструктор, принимает в качестве входных параметров узел левого нижнего угла и высоту и ширину прямоугольника
        /// Вычисляет остальные координаты.
        /// </summary>
        /// <param name="LeftDownCorner">Узел левого нижнего угла прямоугольника</param>
        /// <param name="Height">Значение высоты прямоугольника</param>
        /// <param name="Width">Значение ширины прямоугольника</param>
        public Rectangle2D(Node LeftDownCorner, double Height, double Width)
        {
            this.LeftDownCorner = LeftDownCorner;
            this.Height = Height;
            this.Width = Width;
            this.LeftUpCorner = new Node(this.LeftDownCorner.coordX, this.LeftDownCorner.coordY + this.Width);
            this.RightUpCorner = new Node(this.LeftUpCorner.coordX + this.Height, this.LeftUpCorner.coordY);
            this.RightDownCorner = new Node(this.LeftDownCorner.coordX + this.Height, this.LeftDownCorner.coordY);
        }
 
        /// <summary>
        /// Конструктор, принимает в качестве входных параметров координаты X и Y левого нижнего узла прямоугольника
        /// </summary>
        /// <param name="coordX">Координата X левого нижнего узла прямоугольника</param>
        /// <param name="coordY">Координата Y левого нижнего узла прямоугольника</param>
        /// <param name="Height"></param>
        /// <param name="Width"></param>
        public Rectangle2D(double coordX, double coordY, double Height, double Width)
        {
            this.LeftDownCorner = new Node(coordX, coordY);
            this.Height = Height;
            this.Width = Width;
            this.LeftUpCorner = new Node(this.LeftDownCorner.coordX, this.LeftDownCorner.coordY + this.Width);
            this.RightUpCorner = new Node(this.LeftUpCorner.coordX + this.Height, this.LeftUpCorner.coordY);
            this.RightDownCorner = new Node(this.LeftDownCorner.coordX + this.Height, this.LeftDownCorner.coordY);
        }
 
        /// <summary>
        /// Функция, которая проверяет, находится ли точка с координатами внутри прямоугольника
        /// </summary>
        /// <param name="ptX">Координата X проверяемой точки</param>
        /// <param name="ptY">Координата Y проверяемой точки</param>
        /// <returns>True, если точка внутри прямоугольника и False, если точка снаружни или на контуре прямоугольника</returns>
        public bool IsNodeInRectangle(double ptX, double ptY)
        {
            if (ptX > LeftDownCorner.coordX && ptX < RightDownCorner.coordX && ptY > LeftDownCorner.coordY && ptY < LeftUpCorner.coordY)
                return true;
            else
                return false;
        }
 
        /// <summary>
        /// Возвращает строку для показа координат каждой точки прямоугольника
        /// </summary>
        /// <returns>Строка с параметрами координат для вывода</returns>
        public string ShowCoordinates()
        {
            return $"n X Y\n1 {LeftDownCorner.coordX} {LeftDownCorner.coordY}\n2 {LeftUpCorner.coordX} {LeftUpCorner.coordY}\n3 {RightUpCorner.coordX} {RightUpCorner.coordY}\n4 {RightDownCorner.coordX} {RightDownCorner.coordY}";
        }
 
        /// <summary>
        /// Функция, генерирующая точки внутри прямоугольника
        /// </summary>
        /// <param name="pointCount">Количество генерируемых точек</param>
        /// <returns>Возвращает массив типа Point, содержащий сгенерированные точки</returns>
        public List<Node> GenerateNodes(int pointCount)
        {
            Random rnd = new Random();
            List<Node> generatedNodes = new List<Node>();
            for (int i = 0; i < pointCount; i++)
            {
                generatedNodes.Add(new Node(LeftDownCorner.coordX + rnd.NextDouble() * (RightDownCorner.coordX - LeftDownCorner.coordX),
                    LeftDownCorner.coordY + rnd.NextDouble() * (LeftUpCorner.coordY - LeftDownCorner.coordY)));
            }
            return generatedNodes;
        }
 
        public List<Line> GetLinesFromRectangle()
        {
            List<Line> linesList = new List<Line>();
            linesList.Add(new Line(LeftDownCorner, LeftUpCorner));
            linesList.Add(new Line(LeftUpCorner, RightUpCorner));
            linesList.Add(new Line(RightUpCorner, RightDownCorner));
            linesList.Add(new Line(RightDownCorner, LeftDownCorner));
            return linesList;
        }
    }

Как видно, все держится на классе Node, потому что по узлам я строю и прямоугольник, и линии, и треугольник(что по сути коллекция соединенных определенным образом линий). Я понимаю, что вы наверняка привели исчерпывающий пример в крайнем своем сообщении, поэтому это просто ответ на вашу просьбу
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Покажите реализацию этого класса.
Если большой, то части относящейся к его визуализации.
В этом плане мне проще шаг за шагом в ходе решения задачи усложнять классы, возможно вводить другие классы и так далее, нет у меня столько опыта, чтобы сходу написать такое, уж извините. Но повторюсь, Ваш пример скорее всего исчерпывающий и я в нем разберусь, так как он видимо решает мою задачу(хоть пока и сложно для понимания). Спасибо Вам большое за вашу помощь!
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
24.12.2022, 11:58
Цитата Сообщение от fakemade Посмотреть сообщение
Есть класс Triangle:
Линии могут не стыковаться между собой?
Если же это треугольник (в общем виде многоугольник), то нужно указывать вершины, а не линии.
Аналогично Polygon в моём примере.
И уже от него (при необходимости) наследовать треугольник, прямоугольник и прочие многоугольники.

Цитата Сообщение от fakemade Посмотреть сообщение
Как видно, все держится на классе Node,
По сути это 2D точка (вершина) и Id.

Id, наверное, появился из-за сохранения в БД?
По этой же причине и открытые сеттеры?

Цитата Сообщение от fakemade Посмотреть сообщение
нет у меня столько опыта, чтобы сходу написать такое
Опыт дело наживное.
Здесь главное - стремление получить этот опыт, понять как лучше.

Цитата Сообщение от fakemade Посмотреть сообщение
Ваш пример скорее всего исчерпывающий и я в нем разберусь, так как он видимо решает мою задачу
Это просто пример для демонстрации подхода.
Он простой и в нём обязательно разберитесь. Это полезно для понимания реализации (одной из возможных) по выводу фигур в WPF.
НО в этом примере нет интеграции с Моделью. Тут только уровень ViewModel -> View.
Поэтому для вас это решение неприменимо. Вы можете только взять из него саму идею реализации и применить к своей задаче.

Добавлено через 7 минут
Цитата Сообщение от fakemade Посмотреть сообщение
мою задачу(хоть пока и сложно для понимания)
Смотрите.
Тема начиналась с отрисовки графика.
График - это просто линия. В вашем случае кривая состоящая из отрезков.
Данные для неё - это точки вершин (углов, изломов). В вашей реализации получается, что график это коллекция Node.
Но для вывода график это не только сама его линия, но и координатные оси, сетка, возможность масштабирования и др. приёмы интерактивности.
Вот для всего этого и нужны библы для графиков, так как реализовывать всё это достаточно сложно и объёмно по коду.

Если же нужно было отрисовать только ломанную, то это можно было бы с помощью System.Windows.Shapes.Polyline. Для этого потребуется только конвертер PointCollectionConverter из моего кода.

Добавлено через 5 минут
fakemade, вторую задачу, которую вы поднял - это отрисовка различных фигур. Из того что показали : Треугольники, Прямоугольники и окружности (для точек).
Здесь, в первую очередь, нужно определиться с интерактивностью.
Одна задача - просто это всё отрисовать. Здесь можно ограничиться по одной геометрии для каждого цвета и толщины пера.
Совсем другая, если нужно обеспечить интерактивность, то есть нужны возможности по выбору элементов (например точек), индикации при наведении, всплывании подсказки (например с координатами точки) и пр. В этом случае каждый элемент должен отображаться отдельным UI элементом. Мой пример показывает именно такую реализацию.
0
28 / 20 / 10
Регистрация: 01.12.2018
Сообщений: 186
24.12.2022, 12:38  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Линии могут не стыковаться между собой?
Если же это треугольник (в общем виде многоугольник), то нужно указывать вершины, а не линии.
В моем случае объект Triangle создается из линий, которые как раз и будут представлять этот треугольник. В линиях есть начальный и конечный узел. Соответственно EndNode от FirstLine будет являться StartNode у SecondLine в треугольнике.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
По сути это 2D точка (вершина) и Id.
Id, наверное, появился из-за сохранения в БД?
По этой же причине и открытые сеттеры?
Id нужен для подсчета их и чтобы избежать повторения, чтобы идентифицировать их в триангуляции, так как в процессе треугольники могут перерисовываться. Открытые сеттеры автоматические, уровень доступа в данном случае имеет низкий приоритет для меня, так как это ПО мое личное и, вероятнее всего, никому не нужно.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Опыт дело наживное.
Здесь главное - стремление получить этот опыт, понять как лучше.
Спорить не стану, так и есть.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Это просто пример для демонстрации подхода.
Он простой и в нём обязательно разберитесь.
Уже занимаюсь этим
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Смотрите.
Тема начиналась с отрисовки графика.
Вот тут как раз вы не совсем правы. Тема создана именно для того, чтобы понять, как мне рисовать в WPF результаты триангуляции. Графики функций, OxyPlot и все, что с этим связано всплыло в ходе обсуждения.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Здесь, в первую очередь, нужно определиться с интерактивностью.
Одна задача - просто это всё отрисовать. Здесь можно ограничиться по одной геометрии для каждого цвета и толщины пера.
Совсем другая, если нужно обеспечить интерактивность, то есть нужны возможности по выбору элементов (например точек), индикации при наведении, всплывании подсказки (например с координатами точки) и пр. В этом случае каждый элемент должен отображаться отдельным UI элементом. Мой пример показывает именно такую реализацию.
Решаю задачу шаг за шагом и на данный момент стоит задача просто отрисовать результат триангуляции, в который входят результирующие треугольники и узлы. Пока ни о какой интерактивности речи не идет.
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Если же нужно было отрисовать только ломанную, то это можно было бы с помощью System.Windows.Shapes.Polyline. Для этого потребуется только конвертер PointCollectionConverter из моего кода.
Хорошо, учту. Я пробую и так, и эдак, так как в моем случае не идет речи о производительности и миллионе точек, которые нужно отрисовать, поэтому я могу попробовать несколько способов.
В общем, спасибо за помощь!
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
24.12.2022, 13:16
Цитата Сообщение от fakemade Посмотреть сообщение
Открытые сеттеры автоматические, уровень доступа в данном случае имеет низкий приоритет для меня, так как это ПО мое личное и, вероятнее всего, никому не нужно.
Это важно для понимания, как нужно прокидывать изменения из Модели в GUI.
Если после создания экземпляра его свойства могут изменяться, для обновления GUI придётся встраивать какой-то механизм уведомления об этих изменениях.

Цитата Сообщение от fakemade Посмотреть сообщение
Графики функций, OxyPlot и все, что с этим связано всплыло в ходе обсуждения.
Понял.
Тогда об этом забываем.
Рассматриваем только отрисовку фигур.

Цитата Сообщение от fakemade Посмотреть сообщение
на данный момент стоит задача просто отрисовать результат триангуляции, в который входят результирующие треугольники и узлы. Пока ни о какой интерактивности речи не идет.
Цитата Сообщение от fakemade Посмотреть сообщение
в моем случае не идет речи о производительности и миллионе точек
Тогда подход из моего примера вполне вам подойдёт.
Для отдельных Node задаёте шаблон с эллипсом.
Для треугольников и прямоугольников создаёте конвертер их вершин в PointCollection и используете шаблон с Polygon.

Селектор шаблонов вам не нужен. Просто из шаблонов Node и фигур в ресурсах удаляете ключи.
Они тогда становятся шаблонами по умолчанию и будут применяться автоматически.

Добавлено через 8 минут
Единственное изменение которое я внёс бы - это создание общего базового класса для всех фигур, в том числе точек.
Для WPF при рисовании в Canvas лучше использовать не абсолютные координаты, а смещение верхнего левого угла для всей фигуры и точки самой фигуры задавать относительно этого угла.

То есть из линии (10, 40) - (40, 10) будет удобнее получить: offset (10,10) и линию (0, 30) - (30, 0).
Для точки (40, 50) - offset (40, 50) и точку (0,0).
Если это невозможно по логике вашей программы, то придётся это делать через конвертеры в View.
1
28 / 20 / 10
Регистрация: 01.12.2018
Сообщений: 186
24.12.2022, 13:42  [ТС]
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Единственное изменение которое я внёс бы - это создание общего базового класса для всех фигур, в том числе точек.
Для WPF при рисовании в Canvas лучше использовать не абсолютные координаты, а смещение верхнего левого угла для всей фигуры и точки самой фигуры задавать относительно этого угла.
То есть из линии (10, 40) - (40, 10) будет удобнее получить: offset (10,10) и линию (0, 30) - (30, 0).
Для точки (40, 50) - offset (40, 50) и точку (0,0).
Если это невозможно по логике вашей программы, то придётся это делать через конвертеры в View.
Хорошо, приму к сведению замечание, спасибо.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16152 / 11273 / 2890
Регистрация: 21.04.2018
Сообщений: 33,147
Записей в блоге: 2
24.12.2022, 14:34
Цитата Сообщение от fakemade Посмотреть сообщение
приму к сведению
Цитата Сообщение от Элд Хасп Посмотреть сообщение
Для точки (40, 50) - offset (40, 50) и точку (0,0).
Здесь я наверное грубанул.

У меня как-то не укладывается в голове ваш класс Node.
Обычно точка это либо Структура System.Windows.Point, либо свой кастомный подобный тип.

В такой реализации для точки не нужно задавать смещение.
А ваш класс Node должен быть таким:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public abstract class SomeBase
    {
        public int Id { get;}
 
        public static int Count {get; private set;}
 
        public Point Offset{ get;}
 
        public SomeBase(Point offset)
        {
            Offset = offset;
            Count++;
            Id = Count;
        }
    }
 
    public class Node : SomeBase
    {
        public Node(Point offset) : base(offset) {}
    }
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
24.12.2022, 14:34
Помогаю со студенческими работами здесь

Спецификация MVVM паттерна
Доброго времени суток форумчане! Возникло затруднение с пониманием реализации MVVM паттерна. Саму модель Model-ViewModel-View я...

Понимание паттерна MVVM
Вобщем перепечатал я проект https://www.cyberforum.ru/wpf-silverlight/thread2563725.html. Что то понял, что то не очень... Решил создать...

Нюансы реализации паттерна MVVM
Доброго времени суток, есть вопросы по реализации MVVM: 1) Сделал простое приложение, работает, но мне кажется, что так архитектура...

Закрытие окна в рамках MVVM
Встал вопрос, как назначить команду, привязать ее элементу меню. Команда &quot;Exit&quot; которая бы просто закрывала окно. У меня к сожалению пока...

Привязка данных без использования паттерна MVVM
Здраствуйте, интересует вопрос как сделать такую привязку. Имеется класс Game.cs: class Game: DependencyObject { ...


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

Или воспользуйтесь поиском по форуму:
32
Ответ Создать тему
Новые блоги и статьи
Использование TThread в Lazarus для математических вычислений.
Massaraksh7 25.05.2026
Производя рефакторинг своих программ на предмет ускорения их работы, обратил внимание на такой аспект, как сокращение времени матвычислений. Дело в том, что приходится работать с большими матрицами. . .
Модель здравосохранения 18. Чем здоровее работник, тем быстрее выгорает
anaschu 24.05.2026
Имитационная модель корпоративного здравоохранения: что показывает математика Сегодня в модели рабочего коллектива на AnyLogic появились три новые механики — выгорание через накопленную усталость,. . .
Модель здравосохранения 17. Планы на выгорание
anaschu 23.05.2026
Вот конкретная схема реализации: В классе Работник добавить: накопленнаяУсталость — растёт каждый час работы, снижается в перерывы и болезни коэффициентПрезентеизма — снижает продуктивность. . .
Изменение цветов в палитре gif файла aka фавикона
russiannick 23.05.2026
Изменение цветов в палитре gif файла, юзаемого как фавиконка в составе html-файла, помещенная в base64, средствами нативного Java Script, навеянное сном в майский день. Для работы необходим браузер,. . .
Модель здравосохранения 16. Слишком хорошие и здоровые сотрудники уходят, недовольные зарплатой
anaschu 23.05.2026
Отладка увольнений и настройка производительности Сегодня во второй половине дня разобрались с механикой увольнений и настроили коэффициент сложности заданий. Вот что было сделано. . . .
Как я стал коммунистом))) Модель сохранения здоровья сотрудников, запись блога номер 15
anaschu 23.05.2026
Внезапно хорошее здоровье сотрудников не нужно капиталистам?))
Модель здравоСохранения 15. Как мы чинили AnyLogic модель рабочего коллектива: сочленение диаграммы состояний болезней и поломок в ресурспул
anaschu 23.05.2026
Как мы чинили AnyLogic модель рабочего коллектива Сегодня разобрались с пятью багами, из-за которых модель либо падала с ошибкой, либо давала совершенно бессмысленные результаты. Каждый баг был. . .
Диалоги с ИИ
zorxor 23.05.2026
Насколько я понимаю - Вы - Искусственный Интеллект. Это так? Да, всё верно. Я — искусственный интеллект. Я представляю собой большую языковую модель, созданную для помощи в самых разных задачах. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru