Форум программистов, компьютерный форум, киберфорум
C#: WPF, UWP и Silverlight
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.77/13: Рейтинг темы: голосов - 13, средняя оценка - 4.77
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
WPF

Поиск библиотеки для отрисовки 2D графики в WPF, аналогичной QGraphicsView в Qt

22.02.2024, 16:38. Показов 4472. Ответов 42

Студворк — интернет-сервис помощи студентам
Здравствуйте, коллеги.

Я недавно начал работать с C# и WPF после долгого опыта работы с Python и PySide6. В Qt я часто использовал виджет QGraphicsView для отрисовки графики, который предоставлял удобные функции, такие как адаптация размеров под содержимое, масштабирование и панорамирование. Однако, я обнаружил, что стандартный canvas в WPF не так удобен в использовании. При построении графики мне приходится самостоятельно увеличивать размеры листа под содержимое, а также реализовывать масштабирование и панорамирование.

Я хотел бы узнать, существуют ли библиотеки для C# и WPF, которые предлагают функциональность, аналогичную QGraphicsView в Qt? Мне нужно добавить на форму виджет и отрисовывать в нем простую 2D графику.
Буду благодарен за любые рекомендации. Спасибо!
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
22.02.2024, 16:38
Ответы с готовыми решениями:

Поиск библиотеки для отрисовки графа
Народ, подскажите, существует ли бесплатная библиотека для отрисовки графов? Необходимо изобразить граф, указать направления, указать...

Уменьшение частоты отрисовки QGraphicsView
Только разбираюсь с Qt, не ругайтесь сильно. Данные часто меняются и QGraphicsView также часто перерисовывает (~200fps). Мне этого не...

Что использовать для отрисовки графики?
Мне нужно изобразить дерево (граф), в котором могут быть десятки тысяч вершин. Если использовать Shape, то возникает вопрос - зачем мне...

42
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 13:13
Студворк — интернет-сервис помощи студентам
Вот первое что попалось по поиску https://www.nuget.org/packages... f.SharpDX/

Добавлено через 4 минуты
Цитата Сообщение от Карян Посмотреть сообщение
внутри этого контейнера будет находится чертеж.
чертеж - это что за объект?
Откуда вы его получает? В первую очередь, интересует как вы его сохраняете и восстанавливаете.
В ООП (если точнее - в MV* паттернах) создания приложения, если у разработчика нет достаточного опыта, надо начинать с реализации хранилища. Иначе будет "грабля за граблей".
0
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
23.02.2024, 13:23  [ТС]
Элд Хасп, Чертеж рисуется програмно, есть 10 полей ввода, это значения параметров в мм, по ввденым данным мы осуществляем расчеты.
В результате мы получаем массив линий, каждая линия представлена массивом из координат начала и конца.
Соотвествено по полученным координатам необходимо отрисовать линии и получится чертеж. Так как чертеж большой, и в нем есть мелкие элементы он должен масштабироватся, и должны удобно прокручивать чертеж. вот все требования.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 13:29
Цитата Сообщение от Карян Посмотреть сообщение
начал работать с C# и WPF после долгого опыта работы с Python и PySide6.
Это "роги" на первых парах.
Python, PySide6 и даже Плюсы - там есть, вроде, ООП, но почти всеми разработчиками оно полностью игнорируется. Когда смотришь исходники - как-будто на Фортран и Бейсик программу полувековой давности видишь.

Шарп (уже не говоря про WPF) - требует хорошего знания ООП и умения реализации без нарушений ООП. Откройте учебник по Шарпу и в каждом будет огромный раздел посвящённый паттернам. И это "баловство" авторов. Умение реализации паттернов архиважно для Шарп-разработчика. Иначе любое приложение превращается в неустойчивую гору кривых костылей.
Без ПП (процедурного программирования) - конечно, никуда не деться. Любой метод это ПП. Но архитектура приложение, его построение, функциональное разделение по объектам и их членам и т.п. - всё это должно быть реализовано согласно принципам ООП.

Добавлено через 1 минуту
Цитата Сообщение от Карян Посмотреть сообщение
Соотвествено по полученным координатам необходимо отрисовать линии и получится чертеж. Так как чертеж большой, и в нем есть мелкие элементы он должен масштабироватся, и должны удобно прокручивать чертеж. вот все требования.
Это просто.
У вас одноцветный чертёж из одинаковых линий?
0
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
23.02.2024, 13:51  [ТС]
чертеж состоит из линий, есть два типа линий толщиной в 1 и 3 px, и два основных цвета соотвественно.

Добавлено через 18 минут
сам чертеж нарисовать на канвасе не сложно, все данные есть, вопрос в том что у нас канвас на форме имеет размеры 390 на 390, а размеры чертежа превышают его. Сотвествено нужен контейнерс данными размерами в котором будет поле для рисования (рисование происходит програмно по вычесленым координатам).
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 14:07
Лучший ответ Сообщение было отмечено Карян как решение

Решение

В самом простом виде, у вас должна быть некая коллекция:
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
109
110
111
112
113
114
115
116
117
118
119
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
 
namespace Core2024.CF.Карян
{
    public record Line(Point Start, Point End);
 
    public class LinesCollection : ObservableCollection<Line>
    {
        public double Width { get; private set; }
        public double Height { get; private set; }
        public double OffsetLeft { get; private set; }
        public double OffsetTop { get; private set; }
 
        public static readonly PropertyChangedEventArgs WidthChangedArgs = new(nameof(Width));
        public static readonly PropertyChangedEventArgs HeightChangedArgs = new(nameof(Height));
        public static readonly PropertyChangedEventArgs OffsetLeftChangedArgs = new(nameof(OffsetLeft));
        public static readonly PropertyChangedEventArgs OffsetTopChangedArgs = new(nameof(OffsetTop));
 
        protected override void InsertItem(int index, Line line)
        {
            base.InsertItem(index, line);
 
            double left = Min(line.Start.X, line.End.X);
            double top = Min(line.Start.Y, line.End.Y);
 
 
            double width = Math.Abs(line.Start.X - line.End.X);
            double height = Math.Abs(line.Start.Y - line.End.Y);
 
            Resize(left, top, width, height);
        }
 
        private void Resize(double left, double top, double width, double height)
        {
            if (OffsetLeft > left)
            {
                OffsetLeft = left;
                OnPropertyChanged(OffsetLeftChangedArgs);
            }
            if (OffsetTop > top)
            {
                OffsetTop = top;
                OnPropertyChanged(OffsetTopChangedArgs);
            }
            if (Width < width)
            {
                Width = width;
                OnPropertyChanged(WidthChangedArgs);
            }
            if (Height < height)
            {
                Height = height;
                OnPropertyChanged(HeightChangedArgs);
            }
        }
 
        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
            RefreshSize();
        }
        protected override void SetItem(int index, Line item)
        {
            base.SetItem(index, item);
            RefreshSize();
        }
        private double Min(params double[] nums)
        {
            double min = double.MaxValue;
            foreach (var x in nums)
            {
                if (min > x)
                    min = x;
            }
            return min;
        }
        private double Max(params double[] nums)
        {
            double max = double.MinValue;
            foreach (var x in nums)
            {
                if (max < x)
                    max = x;
            }
            return max;
        }
        private void RefreshSize()
        {
            double left = 0;
            double top = 0;
            double right = 0;
            double bottom = 0;
            foreach (var line in this)
            {
                left = Min(line.Start.X, line.End.X, left);
                top = Min(line.Start.Y, line.End.Y, top);
                right = Max(line.Start.X, line.End.X, right);
                bottom = Max(line.Start.Y, line.End.Y, bottom);
            }
 
            Resize(left, top, right - left, bottom - top);
        }
 
        protected override void ClearItems()
        {
            base.ClearItems();
            Width = 0;
            Height = 0;
            OffsetLeft = 0;
            OffsetTop = 0;
            OnPropertyChanged(OffsetLeftChangedArgs);
            OnPropertyChanged(OffsetTopChangedArgs);
            OnPropertyChanged(WidthChangedArgs);
            OnPropertyChanged(HeightChangedArgs);
        }
    }
}
И уже потом отталкиваяь от неё можно строить её представление.

Добавлено через 1 минуту
Цитата Сообщение от Карян Посмотреть сообщение
есть два типа линий толщиной в 1 и 3 px, и два основных цвета соотвественно.
Чуть изменить базовый тип линии:
C#
1
2
    public enum LineType { One, Two}
    public record Line(Point Start, Point End, LineType Type);
Добавлено через 1 минуту
Цитата Сообщение от Карян Посмотреть сообщение
вопрос в том что у нас канвас на форме имеет размеры 390 на 390
Canvas может иметь любые размеры. Как и любой другой WPF элемент.
Хоть со всю Вселенную.
1
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
23.02.2024, 14:07  [ТС]
Элд Хасп, Спасибо за помощь.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 14:24
Карян, ошибка в юсинге была. Исправил using System.Drawing на using System.Windows
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 15:27
Карян, отлаженный пример:
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
109
110
111
112
113
114
115
116
117
118
119
120
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
 
namespace Core2024.CF.Карян
{
    public enum LineType { One, Two }
    public record Line(Point Start, Point End, LineType Type);
 
    public class LinesCollection : ObservableCollection<Line>
    {
        public double Width { get; private set; }
        public double Height { get; private set; }
        public double OffsetLeft { get; private set; }
        public double OffsetTop { get; private set; }
 
        public static readonly PropertyChangedEventArgs WidthChangedArgs = new(nameof(Width));
        public static readonly PropertyChangedEventArgs HeightChangedArgs = new(nameof(Height));
        public static readonly PropertyChangedEventArgs OffsetLeftChangedArgs = new(nameof(OffsetLeft));
        public static readonly PropertyChangedEventArgs OffsetTopChangedArgs = new(nameof(OffsetTop));
 
        protected override void InsertItem(int index, Line line)
        {
            base.InsertItem(index, line);
 
            double left = Min(line.Start.X, line.End.X);
            double top = Min(line.Start.Y, line.End.Y);
 
 
            double width = Math.Abs(line.Start.X - line.End.X);
            double height = Math.Abs(line.Start.Y - line.End.Y);
 
            Resize(left, top, width, height);
        }
 
        private void Resize(double left, double top, double width, double height)
        {
            if (OffsetLeft > left)
            {
                OffsetLeft = left;
                OnPropertyChanged(OffsetLeftChangedArgs);
            }
            if (OffsetTop > top)
            {
                OffsetTop = top;
                OnPropertyChanged(OffsetTopChangedArgs);
            }
            if (Width < width)
            {
                Width = width;
                OnPropertyChanged(WidthChangedArgs);
            }
            if (Height < height)
            {
                Height = height;
                OnPropertyChanged(HeightChangedArgs);
            }
        }
 
        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
            RefreshSize();
        }
        protected override void SetItem(int index, Line item)
        {
            base.SetItem(index, item);
            RefreshSize();
        }
        private double Min(params double[] nums)
        {
            double min = double.MaxValue;
            foreach (var x in nums)
            {
                if (min > x)
                    min = x;
            }
            return min;
        }
        private double Max(params double[] nums)
        {
            double max = double.MinValue;
            foreach (var x in nums)
            {
                if (max < x)
                    max = x;
            }
            return max;
        }
        private void RefreshSize()
        {
            double left = 0;
            double top = 0;
            double right = 0;
            double bottom = 0;
            foreach (var line in this)
            {
                left = Min(line.Start.X, line.End.X, left);
                top = Min(line.Start.Y, line.End.Y, top);
                right = Max(line.Start.X, line.End.X, right);
                bottom = Max(line.Start.Y, line.End.Y, bottom);
            }
 
            Resize(left, top, right - left, bottom - top);
        }
 
        protected override void ClearItems()
        {
            base.ClearItems();
            Width = 0;
            Height = 0;
            OffsetLeft = 0;
            OffsetTop = 0;
            OnPropertyChanged(OffsetLeftChangedArgs);
            OnPropertyChanged(OffsetTopChangedArgs);
            OnPropertyChanged(WidthChangedArgs);
            OnPropertyChanged(HeightChangedArgs);
        }
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using System.Windows;
 
namespace Core2024.CF.Карян
{
 
    // Здесь собственно вся логика по работе с данными.
    // Если появится Хранилище, то работу с ним нужно будет вынести в отдельный класс - Репозиторий.
    public class LinesViewModel
    {
        public LinesCollection Lines { get; } = new()
        {
            new Line(new Point(-500, -100), new Point(+500, + 100), LineType.One),
            new Line(new Point(-500, +100), new Point(+500, -100), LineType.Two)
        };
    }
}
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
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
 
namespace Core2024.CF.Карян
{
    public class OffsetConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                double value = (double)values[0];
                double offset = (double)values[1];
                return value - offset;
 
            }
            catch (Exception)
            {
                return DependencyProperty.UnsetValue;
            }
        }
 
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        private OffsetConverter() { }
 
        public static readonly OffsetConverter Instance = new OffsetConverter();
    }
 
    public class OffsetConverterExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
            => OffsetConverter.Instance;
    }
 
}
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
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
<Window x:Class="Core2024.CF.Карян.LinesWindow"
        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:Core2024.CF.Карян"
        mc:Ignorable="d"
        Title="LinesWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:LinesViewModel x:Key="vm"/>
    </Window.Resources>
    <Grid>
        <ListBox ItemsSource="{Binding Lines}">
            
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas RenderTransformOrigin="0.5,0.5"
                            Width="{Binding Lines.Width, Source={StaticResource vm}}"
                            Height="{Binding Lines.Height, Source={StaticResource vm}}">
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="Canvas.Top">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Start.X"/>
                                <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Canvas.Left">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Start.Y"/>
                                <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <Line x:Name="line" Stroke="Aqua" StrokeThickness="5">
                                    <Line.X1>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="Start.X"/>
                                            <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                        </MultiBinding>
                                    </Line.X1>
                                    <Line.X2>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="End.X"/>
                                            <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                        </MultiBinding>
                                    </Line.X2>
                                    <Line.Y1>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="Start.Y"/>
                                            <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                        </MultiBinding>
                                    </Line.Y1>
                                    <Line.Y2>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="End.Y"/>
                                            <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                        </MultiBinding>
                                    </Line.Y2>
                                </Line>
                                <ControlTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Type}" Value="Two">
                                        <Setter TargetName="line" Property="Stroke" Value="LightGreen"/>
                                        <Setter TargetName="line" Property="StrokeThickness" Value="15"/>
                                    </DataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ListBox>
    </Grid>
</Window>
Добавлено через 1 минуту
Бонусом ещё получаете возможность выбора линии, чтобы потом ещё произвести с ней какие-то действия. Например, удалить.

Добавлено через 4 минуты
Реализация масштабирования задача неоднозначная.
Изменить размеры не проблема.
Но как должна меняться толщина линии?
У вас указано что линии толщиной 1 и 3 пикселя.
А раз так, то при масштабировании с уменьшением их толщина станет значительно меньше пикселя и они станут не видны.
Если же нужно масштабироваться только позиции и размеры элементов, то здесь не применишь трансформацию и ViewBox напрямую.
Придётся придумывать способы масштабирования не затрагивающие толщину линий.

Добавлено через 2 минуты
Так же можно расширить функционал классов Line и LinesCollection - переместив в них всю логику по смещению и масштабированию.

Добавлено через 12 минут
Карян, "код отлажен" в том смысле, что что-то там выводит. Насколько правильно - я не проверял. Могут быть арифметические ошибки.

Добавлено через 7 минут
Карян, немного не по теме.
Смотрю я на код примера что скинул выше.... И думаю, что для вас это такими дебрями выглядит, наверное.


Но для WPF-разработчика это простейшие коды, которые "накидываются на коленке" за 10-20-30 минут. Искать по Nuget готовый пакет дольше будет.
1
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 15:45
Лучший ответ Сообщение было отмечено Карян как решение

Решение

Цитата Сообщение от Элд Хасп Посмотреть сообщение
Если же нужно масштабироваться только позиции и размеры элементов, то здесь не применишь трансформацию и ViewBox напрямую.
Придётся придумывать способы масштабирования не затрагивающие толщину линий.
Пример такого масштабирования:
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
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
 
namespace Core2024.CF.Карян
{
    public class OffsetConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                double value = (double)values[0];
                double offset = (double)values[1];
                double scale = (double)values[2];
                return (value - offset) * scale;
 
            }
            catch (Exception)
            {
                return DependencyProperty.UnsetValue;
            }
        }
 
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        private OffsetConverter() { }
 
        public static readonly OffsetConverter Instance = new OffsetConverter();
    }
 
    public class OffsetConverterExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
            => OffsetConverter.Instance;
    }
}
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
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
109
<Window x:Class="Core2024.CF.Карян.LinesWindow"
        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:Core2024.CF.Карян" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
        mc:Ignorable="d"
        Title="LinesWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:LinesViewModel x:Key="vm"/>
        <sys:Double x:Key="zero">0</sys:Double>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding Lines}">
 
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas>
                        <Canvas.Width>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Lines.Width"  Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Canvas.Width>
                        <Canvas.Height>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Lines.Height"  Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Canvas.Height>
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="Canvas.Top">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Start.X"/>
                                <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Canvas.Left">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Start.Y"/>
                                <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <Line x:Name="line" Stroke="Aqua" StrokeThickness="5">
                                    <Line.X1>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="Start.X"/>
                                            <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.X1>
                                    <Line.X2>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="End.X"/>
                                            <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.X2>
                                    <Line.Y1>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="Start.Y"/>
                                            <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.Y1>
                                    <Line.Y2>
                                        <MultiBinding Converter="{local:OffsetConverter}">
                                            <Binding Path="End.Y"/>
                                            <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.Y2>
                                </Line>
                                <ControlTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Type}" Value="Two">
                                        <Setter TargetName="line" Property="Stroke" Value="LightGreen"/>
                                        <Setter TargetName="line" Property="StrokeThickness" Value="15"/>
                                    </DataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ListBox>
        <Slider x:Name="scale" Minimum="0.01" Maximum="1" SmallChange="0.01" Grid.Row="1"
                Value="0.1"/>
    </Grid>
</Window>
1
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
23.02.2024, 15:53  [ТС]
Элд Хасп, Огромное спасибо.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
23.02.2024, 18:53
Элд Хасп, класс, но выстрел в молоко.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
23.02.2024, 23:18
Цитата Сообщение от wizard41 Посмотреть сообщение
но выстрел в молоко.
???
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
24.02.2024, 09:14
wizard41, если что - я арифметику там не проверял.
Вполне могут быть ошибки.

Карян, сколько максимум линий у вас может быть на чертеже?
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3461 / 2782 / 575
Регистрация: 04.09.2018
Сообщений: 8,746
Записей в блоге: 3
24.02.2024, 09:46
Цитата Сообщение от Элд Хасп Посмотреть сообщение
???
Не, я к тому, что для ТС это все филькина грамота.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
24.02.2024, 09:49
Цитата Сообщение от wizard41 Посмотреть сообщение
что для ТС это все филькина грамота.
Ну, здесь ничего не поделаешь.
Просто так, без труда, без обучения, пересесть на новую платформу не выйдет.
0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
24.02.2024, 18:15
Лучший ответ Сообщение было отмечено Карян как решение

Решение

Карян, подебажил чуток. Выявил несколько ошибок.
Вариант реализации:
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using static System.Math;
 
namespace Core2024.CF.Карян
{
    public enum LineType { One, Two }
    public record Line
    {
        public Point Start { get; }
        public Point End { get; }
        public LineType Type { get; }
        public string Name { get; }
 
        public double Left { get; }
        public double Top { get; }
 
        public double Width { get; }
        public double Height { get; }
 
        public double Right { get; }
        public double Bottom { get; }
 
        public Line(Point start, Point end, LineType type, string name)
        {
            Start = start;
            End = end;
            Type = type;
            Name = name;
 
            Left = Min(Start.X, End.X);
            Top = Min(Start.Y, End.Y);
            Right = Max(Start.X, End.X);
            Bottom = Max(Start.Y, End.Y);
 
            Width = Right - Left;
            Height = Bottom - Top;
        }
    }
 
    public class LinesCollection : ObservableCollection<Line>
    {
        public double Width { get; private set; }
        public double Height { get; private set; }
        public double OffsetLeft { get; private set; }
        public double OffsetTop { get; private set; }
 
        public static readonly PropertyChangedEventArgs WidthChangedArgs = new(nameof(Width));
        public static readonly PropertyChangedEventArgs HeightChangedArgs = new(nameof(Height));
        public static readonly PropertyChangedEventArgs OffsetLeftChangedArgs = new(nameof(OffsetLeft));
        public static readonly PropertyChangedEventArgs OffsetTopChangedArgs = new(nameof(OffsetTop));
 
        protected override void InsertItem(int index, Line line)
        {
            base.InsertItem(index, line);
 
            Resize(line.Left, line.Top, line.Right, line.Bottom);
        }
 
        private void Resize(double left, double top, double right, double bottom)
        {
            if (OffsetLeft > left)
            {
                OffsetLeft = left;
                OnPropertyChanged(OffsetLeftChangedArgs);
            }
            if (OffsetTop > top)
            {
                OffsetTop = top;
                OnPropertyChanged(OffsetTopChangedArgs);
            }
            right -= OffsetLeft;
            bottom -= OffsetTop;
 
            if (Width < right)
            {
                Width = right;
                OnPropertyChanged(WidthChangedArgs);
            }
            if (Height < bottom)
            {
                Height = bottom;
                OnPropertyChanged(HeightChangedArgs);
            }
        }
 
        protected override void RemoveItem(int index)
        {
            Line line = this[index];
            base.RemoveItem(index);
            if (line is not null &&
                (OffsetLeft == line.Left ||
                OffsetTop == line.Top ||
                Width == line.Width - OffsetLeft ||
                Height == line.Height - OffsetTop))
            {
                RefreshSize();
            }
        }
        protected override void SetItem(int index, Line line)
        {
            Line old = this[index];
            base.SetItem(index, line);
 
            if (old is not null &&
                (OffsetLeft == old.Left ||
                OffsetTop == old.Top ||
                Width == old.Width - OffsetLeft ||
                Height == old.Height - OffsetTop))
            {
                RefreshSize();
            }
            else
            {
                Resize(line.Left, line.Top, line.Right, line.Bottom);
            }
 
        }
        private void RefreshSize()
        {
            double left = double.MaxValue;
            double top = double.MaxValue;
            double right = double.MinValue;
            double bottom = double.MinValue;
            foreach (var line in this)
            {
                if (left > line.Left)
                    left = line.Left;
                if (top > line.Top)
                    top = line.Top;
                if (right < line.Right)
                    right = line.Right;
                if (bottom < line.Bottom)
                    bottom = line.Bottom;
            }
 
            Resize(left, top, right, bottom);
        }
 
        protected override void ClearItems()
        {
            base.ClearItems();
            Width = 0;
            Height = 0;
            OffsetLeft = 0;
            OffsetTop = 0;
            OnPropertyChanged(OffsetLeftChangedArgs);
            OnPropertyChanged(OffsetTopChangedArgs);
            OnPropertyChanged(WidthChangedArgs);
            OnPropertyChanged(HeightChangedArgs);
        }
    }
}
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
using System.Windows;
 
namespace Core2024.CF.Карян
{
 
    // Здесь собственно вся логика по работе с данными.
    // Если появится Хранилище, то работу с ним нужно будет вынести в отдельный класс - Репозиторий.
    public class LinesViewModel
    {
        public LinesCollection Lines { get; } = new()
        {
            new Line(new Point(-500, -100), new Point(+500, +100), LineType.One, "Первый"),
            new Line(new Point(-500, +100), new Point(+500, -100), LineType.Two, "Второй"),
            new Line(new Point(-200, -100), new Point(-200, +100), LineType.One, "Третий"),
            new Line(new Point(200, 100), new Point(200, -100), LineType.Two, "Четвёртый"),
            new Line(new Point(0, -200), new Point(0, +200), LineType.One, "Пятый"),
            new Line(new Point(-300, 0), new Point(+300, 0), LineType.Two, "Шестой")
        };
    }
}
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
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
 
namespace Core2024.CF.Карян
{
    public class OffsetConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                double value = (double)values[0];
                double offsetPanel = (double)values[1];
                double offsetElement = (double)values[2];
                double scale = (double)values[3];
                double result = (value - offsetPanel) * scale - offsetElement;
                return result;
 
            }
            catch (Exception)
            {
                return DependencyProperty.UnsetValue;
            }
        }
 
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        private OffsetConverter() { }
 
        public static readonly OffsetConverter Instance = new OffsetConverter();
    }
 
    public class OffsetConverterExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
            => OffsetConverter.Instance;
    }
}
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
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
109
110
111
112
113
114
115
116
117
118
119
120
121
<Window x:Class="Core2024.CF.Карян.LinesWindow"
        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:Core2024.CF.Карян" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
        mc:Ignorable="d"
        Title="LinesWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:LinesViewModel x:Key="vm"/>
        <sys:Double x:Key="zero">0</sys:Double>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding Lines}">
 
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas>
                        <Canvas.Width>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Lines.Width"  Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Canvas.Width>
                        <Canvas.Height>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Lines.Height"  Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Canvas.Height>
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="Canvas.Left">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Left"/>
                                <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Canvas.Top">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Top"/>
                                <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <Line x:Name="line" Stroke="Aqua" StrokeThickness="5">
                                    <Line.X1>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                                                      ConverterParameter="True">
                                            <Binding Path="Start.X"/>
                                            <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                            <Binding Path="(Canvas.Left)" RelativeSource="{RelativeSource TemplatedParent}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.X1>
                                    <Line.X2>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                                                      ConverterParameter="True">
                                            <Binding Path="End.X"/>
                                            <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                            <Binding Path="(Canvas.Left)" RelativeSource="{RelativeSource TemplatedParent}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.X2>
                                    <Line.Y1>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                                                      ConverterParameter="True">
                                            <Binding Path="Start.Y"/>
                                            <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                            <Binding Path="(Canvas.Top)" RelativeSource="{RelativeSource TemplatedParent}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.Y1>
                                    <Line.Y2>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                                                      ConverterParameter="True">
                                            <Binding Path="End.Y"/>
                                            <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                            <Binding Path="(Canvas.Top)" RelativeSource="{RelativeSource TemplatedParent}"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.Y2>
                                </Line>
                                <ControlTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Type}" Value="Two">
                                        <Setter TargetName="line" Property="Stroke" Value="LightGreen"/>
                                        <Setter TargetName="line" Property="StrokeThickness" Value="15"/>
                                    </DataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ListBox>
        <Slider x:Name="scale" Minimum="0.01" Maximum="1" SmallChange="0.01" Grid.Row="1"
                Value="1"/>
    </Grid>
</Window>
0
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
24.02.2024, 18:55  [ТС]
Большое спасибо Элд Хасп за вашу помощь. Ваше решение задачи было очень ценным и полезным. Я очень ценю ваше время и усилия. Вы действительно помогли мне в моей задаче. Еще раз спасибо!
0
24.02.2024, 19:03

Не по теме:

Цитата Сообщение от Карян Посмотреть сообщение
Вы действительно помогли мне в моей задаче.
Были у меня сомнения, что это очередной бот... Теперь сомнений нет, точно бот ))

0
Модератор
Эксперт .NET
 Аватар для Элд Хасп
16165 / 11285 / 2891
Регистрация: 21.04.2018
Сообщений: 33,174
Записей в блоге: 2
24.02.2024, 19:40
wizard41, я это больше делаю для собственного "развлечения" - отвлекаюсь от своей работы.

Добавлено через 52 секунды
Карян, ещё чуть упрощения.
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
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
 
namespace Core2024.CF.Карян
{
    public class OffsetConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            try
            {
                double value = (double)values[0];
                double offset = (double)values[1];
                double scale = (double)values[2];
                double result = (value - offset) * scale;
                return result;
 
            }
            catch (Exception)
            {
                return DependencyProperty.UnsetValue;
            }
        }
 
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
        private OffsetConverter() { }
 
        public static readonly OffsetConverter Instance = new OffsetConverter();
    }
 
    public class OffsetConverterExtension : MarkupExtension
    {
        public override object ProvideValue(IServiceProvider serviceProvider)
            => OffsetConverter.Instance;
    }
}
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
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
109
110
111
112
113
<Window x:Class="Core2024.CF.Карян.LinesWindow"
        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:Core2024.CF.Карян" xmlns:sys="clr-namespace:System;assembly=System.Runtime"
        mc:Ignorable="d"
        Title="LinesWindow" Height="450" Width="800"
        DataContext="{DynamicResource vm}">
    <Window.Resources>
        <local:LinesViewModel x:Key="vm"/>
        <sys:Double x:Key="zero">0</sys:Double>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <ListBox ItemsSource="{Binding Lines}">
 
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas>
                        <Canvas.Width>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Lines.Width"  Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Canvas.Width>
                        <Canvas.Height>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Lines.Height"  Source="{StaticResource vm}"/>
                                <Binding Source="{StaticResource zero}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Canvas.Height>
                    </Canvas>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="Canvas.Left">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Left"/>
                                <Binding Path="Lines.OffsetLeft" Source="{StaticResource vm}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Canvas.Top">
                        <Setter.Value>
                            <MultiBinding Converter="{local:OffsetConverter}">
                                <Binding Path="Top"/>
                                <Binding Path="Lines.OffsetTop" Source="{StaticResource vm}"/>
                                <Binding Path="Value" ElementName="scale"/>
                            </MultiBinding>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <Line x:Name="line" Stroke="Aqua" StrokeThickness="5">
                                    <Line.X1>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                          ConverterParameter="True">
                                            <Binding Path="Start.X"/>
                                            <Binding Path="Left"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.X1>
                                    <Line.X2>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                          ConverterParameter="True">
                                            <Binding Path="End.X"/>
                                            <Binding Path="Left"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.X2>
                                    <Line.Y1>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                          ConverterParameter="True">
                                            <Binding Path="Start.Y"/>
                                            <Binding Path="Top"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.Y1>
                                    <Line.Y2>
                                        <MultiBinding Converter="{local:OffsetConverter}"
                          ConverterParameter="True">
                                            <Binding Path="End.Y"/>
                                            <Binding Path="Top"/>
                                            <Binding Path="Value" ElementName="scale"/>
                                        </MultiBinding>
                                    </Line.Y2>
                                </Line>
                                <ControlTemplate.Triggers>
                                    <DataTrigger Binding="{Binding Type}" Value="Two">
                                        <Setter TargetName="line" Property="Stroke" Value="LightGreen"/>
                                        <Setter TargetName="line" Property="StrokeThickness" Value="15"/>
                                    </DataTrigger>
                                </ControlTemplate.Triggers>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ListBox>
        <Slider x:Name="scale" Minimum="0.01" Maximum="1" SmallChange="0.01" Grid.Row="1"
                Value="1"/>
    </Grid>
</Window>
Добавлено через 2 минуты
Карян, предупреждаю.
Скорее всего этот подход будет плохо работать для большего количества линий.
Потестируйте на своих данных. Если будут лаги, то определите с какого количества линий они начинаются и насколько серьёзны.
Возможно придётся использовать геометрию или пиксельное "рисование".
0
92 / 58 / 21
Регистрация: 09.05.2019
Сообщений: 184
25.02.2024, 14:53  [ТС]
Элд Хасп, Подскажите пожалуйста столкнулся с такой проблемой, решил потестить программу дома, на рабочем компьютере зашел в сборку деспетчер конфигураций перевел с Debug на Release. После чего сборка собрать решение. Получившийся .exe запустил дома и тутже получил сообщение от антивируса, что программа распознана как троян. В чем может быть причина. Проверил на сайте вирус тотал вирусов нет, однако защитник виндоус блокирует программу.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
25.02.2024, 14:53

Компонент для точной отрисовки графики
Уважаемые форумчане, столкнулся с проблемкой Мог бы кто-нибудь подсказать компонент для отрисовки графиков С типом данных real, видел...

Выбрать платформу для отрисовки 3D графики
Разрабатываю windows приложение, которое принимает некоторые исходные данные, производит расчеты и результаты выводит в виде неких...

Библиотеки для создания (отрисовки) схем
Здравствуйте, я бы хотел спросить есть ли какие либо библиотеки для создания(отрисовки) схем.Давайте темам вменяемые названия

Что выбрать для отрисовки графика в wpf?
Будут поступать данные с com порта. И нужно будет эти данные показать в виде кривой, которая через каждые (допустим 10 сек) будет меняться,...

Что лучше всего использовать для отрисовки простой 2D графики
Здравствуйте. Недавно начал изучать C++ для одного проекта. Передо мной встала задача. Нужно отрисовывать простую 2D графику. Что лучше...


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

Или воспользуйтесь поиском по форуму:
40
Ответ Создать тему
Новые блоги и статьи
Лето уходит
kumehtar 17.08.2026
Мысли в слух
kumehtar 17.08.2026
Забавно, насколько сейчас стала доступна информация. Например о магии, духовном развитии, медитациях, и других подобных направлениях, ранее зачастую тайных, передаваемых от учителя к ученику. Хотя. . .
Перемещение строк из ТЧ в другой документ с учетом текущего пробега
Maks 17.08.2026
Реализация из решения ниже выполнена на примере нетипового документа "Автозапчасти", с ТЧ "Шины". За основу взят алгоритм отсюда: https:/ / www. cyberforum. ru/ blogs/ 359708/ 10838. html Задача: . . .
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru