Отдельные DataGrids, привязанные к одной и той же коллекции Observable

У меня есть приложение WPF с TabControl, и на двух разных элементах TabItem я хочу отображать один и тот же контент в DataGrid на каждом из них. Порядок действий следующий:

  1. Пользователь выбирает сигнал или диапазон сигналов из ListBox и добавляет его в первый DataGrid, который находится на той же вкладке. Этот же сигнал добавляется во вторую DataGrid на другой вкладке, которая затем используется для выбора сигналов в этой DataGrid для добавления в LiveGraph.

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

Первоначально я допустил ошибку, напрямую привязав оба DataGrid ItemsSource к одной и той же ObservableCollection, что привело к возникновению следующего исключения: «InvalidOperationException: операция недействительна, пока используется ItemsSource. Вместо этого обращайтесь к элементам и изменяйте их с помощью ItemsControl.ItemsSource».

Итак, следуя приведенной выше ссылке, я добавил два ListCollectionViews и привязал их к каждому DataGrid, но это же исключение все еще возникает при первом запуске приложения. Я также безуспешно пытался установить различные режимы для привязок ItemsSource.

В сообщении выше, за которым я следил, есть одна особенность: DataGrids в этом сообщении не объявляют ItemsSource в Xaml, но это делается в коде позади. Я предполагал, что моя привязка к Observables тоже сработает.

Если бы кто-нибудь мог помочь мне выяснить, какую деталь мне здесь не хватает, это было бы здорово, и я был бы очень признателен.

Вот первое объявление DataGrid:

<DataGrid
    x:Name = "PeekActionsDataGrid"
    Width = "auto"
    HorizontalAlignment = "Stretch"
    VerticalAlignment = "Center"
    HorizontalContentAlignment = "Stretch"
    VerticalContentAlignment = "Stretch"
    AutoGenerateColumns = "False"
    BorderBrush = "Black"
    BorderThickness = "1"
    DockPanel.Dock = "Top"
    GridLinesVisibility = "None"
    HorizontalScrollBarVisibility = "Auto"
    IsEnabled = "True"
    ItemsSource = "{Binding PeekSignalCollectionView}"
    ScrollViewer.CanContentScroll = "true"
    ScrollViewer.HorizontalScrollBarVisibility = "Visible"
    ScrollViewer.VerticalScrollBarVisibility = "Visible"
    VerticalScrollBarVisibility = "Auto"/>

Вот второе объявление DataGrid:

<DataGrid
    x:Name = "GraphSignalDataGrid"
    Grid.Row = "1"
    Grid.RowSpan = "2"
    Grid.Column = "2"
    Width = "auto"
    MinWidth = "50"
    MinHeight = "50"
    HorizontalAlignment = "Stretch"
    VerticalAlignment = "Center"
    HorizontalContentAlignment = "Stretch"
    VerticalContentAlignment = "Stretch"
    AutoGenerateColumns = "False"
    BorderBrush = "Black"
    BorderThickness = "1"
    DockPanel.Dock = "Top"
    GridLinesVisibility = "None"
    HorizontalScrollBarVisibility = "Auto"
    IsEnabled = "True"
    ItemsSource = "{Binding PeekGraphCollectionView}"
    ScrollViewer.CanContentScroll = "true"
    ScrollViewer.HorizontalScrollBarVisibility = "Visible"
    ScrollViewer.VerticalScrollBarVisibility = "Visible"
    VerticalScrollBarVisibility = "Auto"/>

Вот элементы MVVM ToolKit, которые я использую в коде, чтобы сделать их видимыми для представления из ViewModel:

    // holds the dictionary signals selected by the user 
    [ObservableProperty] public static RangeObservableCollection<EcwsPeekSignalModel>? _peekSignalsList = new();
    // trying to use the list above in two different datagrids on two separate tab items
    [ObservableProperty] private ListCollectionView _peekSignalCollectionView = new(_peekSignalsList);
    [ObservableProperty] private ListCollectionView _peekGraphCollectionView = new(_peekSignalsList);

**Обновление — воспроизводимый код **

Используя пример, предоставленный @Sir Rufo, я удалил части своего приложения, которые воспроизводят эту проблему.

MainWindow.xaml

<Window
x:Class = "DataGridBindingProblem.MainWindow"
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels = "clr-namespace:DataGridBindingProblem.ViewModels"
Title = "MainWindow"
Width = "800"
Height = "450"
mc:Ignorable = "d">
<Window.DataContext>
    <viewModels:MainViewModel />
</Window.DataContext>
<DockPanel>
    <TabControl>
        <TabItem Header = "Var. 1">
            <DataGrid
                Grid.Column = "0"
                AutoGenerateColumns = "False"
                ItemsSource = "{Binding PeekSignalList}">
                <DataGridTextColumn
                    Width = "auto"
                    Binding = "{Binding SignalName}"
                    Header = "Signal Name"
                    IsReadOnly = "True">
                    <DataGridColumn.HeaderStyle>
                        <Style TargetType = "DataGridColumnHeader">
                            <!--<Setter Property = "BorderThickness" Value = "0" />-->
                            <Setter Property = "Background" Value = "LightCyan" />
                            <Setter Property = "HorizontalAlignment" Value = "Center" />
                            <Setter Property = "Padding" Value = "5" />
                            <Setter Property = "Margin" Value = "2" />
                        </Style>
                    </DataGridColumn.HeaderStyle>
                    <DataGridTextColumn.CellStyle>
                        <Style TargetType = "DataGridCell">
                            <!--<Setter Property = "BorderThickness" Value = "0" />-->
                            <Setter Property = "IsTabStop" Value = "False" />
                            <Setter Property = "HorizontalAlignment" Value = "Left" />
                            <Setter Property = "Padding" Value = "5" />
                            <Setter Property = "Margin" Value = "2" />
                        </Style>
                    </DataGridTextColumn.CellStyle>
                </DataGridTextColumn>
            </DataGrid>
        </TabItem>
        <TabItem Header = "Var 2">
            <DataGrid AutoGenerateColumns = "False" ItemsSource = "{Binding PeekSignalList}">
                <!--
                    Here is where the problem occurs.  In my application the xaml parser isn't
                    providing me with too much info but in this new project it says that the
                    itemsource is in use and to use ItemsControl instead but I am not sure how
                    to do that with a datagrid in this situation.
                -->
                <DataGridTextColumn
                    Width = "auto"
                    Binding = "{Binding SignalName}"
                    Header = "Signal Name"
                    IsReadOnly = "True">
                    <DataGridColumn.HeaderStyle>
                        <Style TargetType = "DataGridColumnHeader">
                            <!--<Setter Property = "BorderThickness" Value = "0" />-->
                            <Setter Property = "Background" Value = "LightCyan" />
                            <Setter Property = "HorizontalAlignment" Value = "Center" />
                            <Setter Property = "Padding" Value = "5" />
                            <Setter Property = "Margin" Value = "2" />
                        </Style>
                    </DataGridColumn.HeaderStyle>
                    <DataGridTextColumn.CellStyle>
                        <Style TargetType = "DataGridCell">
                            <!--<Setter Property = "BorderThickness" Value = "0" />-->
                            <Setter Property = "IsTabStop" Value = "False" />
                            <Setter Property = "HorizontalAlignment" Value = "Left" />
                            <Setter Property = "Padding" Value = "5" />
                            <Setter Property = "Margin" Value = "2" />
                        </Style>
                    </DataGridTextColumn.CellStyle>
                </DataGridTextColumn>
            </DataGrid>
        </TabItem>
    </TabControl>
</DockPanel>

MainViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using DataGridBindingProblem.Models;

using CommunityToolkit.Mvvm.ComponentModel;

using DataGridBindingProblem.Models;

namespace DataGridBindingProblem.ViewModels
{
    public partial class MainViewModel : ObservableObject
    {
        [ObservableProperty] private RangeObservableCollection<EcwsPeekSignalModel> _peekSignalList;

        public MainViewModel()
        {
            PeekSignalList = new();

            PeekSignalList.Add(new("Signal1"));

            PeekSignalList.Add(new("Signal2"));
        }
    }
}

EnumTypes.cs

namespace DataGridBindingProblem.CustomTypes
{
    public enum FccIdEnumType
    {
        LA = 0,   
        LB,    
        RA,    
        RB,     
    }

    public enum SignalDataTypeEnumType
    {
        FLOAT = 0,
        SIGNED,
        UNSIGNED,
    }
}

ЭквсПикСигналМодел.cs

using CommunityToolkit.Mvvm.ComponentModel;
using DataGridBindingProblem.CustomTypes;
using Newtonsoft.Json;

namespace DataGridBindingProblem.Models
{
    public partial class EcwsPeekSignalModel : ObservableObject
    {

        #region constructors

        /* no defeault constructor a signal must at least have a name */
        public EcwsPeekSignalModel(string? signalName)
        {
            // current values
            _signalName = signalName;
            _laCurrentValue = "--";
            _lbCurrentValue = "--";
            _raCurrentValue = "--";
            _rbCurrentValue = "--";

            // last values
            LaLastValue = "--";
            LbLastValue = "--";
            RaLastValue = "--";
            RbLastValue = "--";

            LaLogList = new();
            LbLogList = new();
            RaLogList = new();
            RbLogList = new();
        }

        #endregion constructors

        // Used to create records for logging
        public void AddLogEntry(int? logTime,
                                        FccIdEnumType? sLane,
                                        string? sVal,
                                        int? rNum, bool? recordFlag = false)
        {
            switch(sLane)
            {
                case FccIdEnumType.LA:
                {
                    LaLastValue = LaCurrentValue;
                    LaCurrentValue = sVal;

                    // only create a log entry if recording is true
                    if (recordFlag == true)
                    {
                        // store the current value as history
                        LaLogList?.Add(new SignalLogEntry(logTime, sLane, sVal, rNum));
                    }

                    break;
                }
                case FccIdEnumType.LB:
                {
                    LbLastValue = LbCurrentValue;
                    LbCurrentValue = sVal;

                    // only create a log entry if recording is true
                    if (recordFlag == true)
                    {
                        // store the current value as history
                        LbLogList?.Add(new SignalLogEntry(logTime, sLane, sVal, rNum));
                    }

                    break;
                }
                case FccIdEnumType.RA:
                {
                    RaLastValue = RaCurrentValue;
                    RaCurrentValue = sVal;

                    // only create a log entry if recording is true
                    if (recordFlag == true)
                    {
                        // store the current value as history
                        RaLogList?.Add(new SignalLogEntry(logTime, sLane, sVal, rNum));

                    }
                    break;
                }
                case FccIdEnumType.RB:
                {
                    RbLastValue = RbCurrentValue;
                    RbCurrentValue = sVal;

                    // only create a log entry if recording is true
                    if (recordFlag == true)
                    {
                        // store the current value as history
                        RbLogList?.Add(new SignalLogEntry(logTime, sLane, sVal, rNum));

                    }
                    break;
                }
                case null:
                {
                    break;
                }
            }
        }

        // clears the log list and resets values
        public void ClearLogLists()
        {
            RemoveSignal = false;

            LaCurrentValue = "--";
            LbCurrentValue = "--";
            RaCurrentValue = "--";
            RbCurrentValue = "--";

            LaLastValue = LaCurrentValue;
            LbLastValue = LbCurrentValue;
            RbLastValue = RaCurrentValue;
            RaLastValue = RaCurrentValue;

            LaLogList?.Clear();
            LbLogList?.Clear();
            RaLogList?.Clear();
            RbLogList?.Clear();
        }

        #region LogLists

        [Newtonsoft.Json.JsonIgnore]
        private List<SignalLogEntry>? _laLogList;

        [Newtonsoft.Json.JsonIgnore]
        internal List<SignalLogEntry>? LaLogList { get => _laLogList; set => _laLogList = value; }

        [Newtonsoft.Json.JsonIgnore]
        private List<SignalLogEntry>? _lbLogList;

        [Newtonsoft.Json.JsonIgnore]
        internal List<SignalLogEntry>? LbLogList { get => _lbLogList; set => _lbLogList = value; }

        [Newtonsoft.Json.JsonIgnore]
        private List<SignalLogEntry>? _raLogList;

        [Newtonsoft.Json.JsonIgnore]
        internal List<SignalLogEntry>? RaLogList { get => _raLogList; set => _raLogList = value; }

        [Newtonsoft.Json.JsonIgnore]
        private List<SignalLogEntry>? _rbLogList;

        [Newtonsoft.Json.JsonIgnore]
        internal List<SignalLogEntry>? RbLogList { get => _rbLogList; set => _rbLogList = value; }

        #endregion LogLists

        #region jsonProperties


        //public string? SignalName { get; set; }
        [JsonProperty(Order = 1, PropertyName = "")]
        [ObservableProperty] private string? _signalName;

        #endregion jsonProperties

        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private SignalDataTypeEnumType? _signalDataType;

        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private bool? _removeSignal;

        // variables for historical data used when expBorting the log data
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _laCurrentValue;
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _lbCurrentValue;
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _raCurrentValue;
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _rbCurrentValue;

        // variables for historical data used when exporting the log data
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _laLastValue;
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _lbLastValue;
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _raLastValue;
        [Newtonsoft.Json.JsonIgnore]
        [ObservableProperty] private string? _rbLastValue;
    }
}

Сигналлогэнтри.cs

using System.Diagnostics.CodeAnalysis;

namespace DataGridBindingProblem.CustomTypes
{
    internal class SignalLogEntry
    {
        /* since this is a log entry I do not want to allow a default constructor
         * a logentry must have these items
         */
        private int? _rtcTime;
        private FccIdEnumType? _signalLane;
        private string? _signalValue;
        private int? _runNUm;

        [SetsRequiredMembers]
        public SignalLogEntry(int? rtc, FccIdEnumType? sLane, string? sVal, int?rNum)
        {
            _rtcTime = rtc;
            _signalLane = sLane;
            _signalValue = sVal;
            _runNUm = rNum;
        }

        // returns an array of strings for logging to excel
        public string[] GetLogArray()
        {
            return new string[] { $"{_rtcTime}", 
                $"{_signalValue}"};
        }

        public int? GetRtcTime()
        {
            return _rtcTime;
        }

        public FccIdEnumType? GetSignalLane()
        {
            return _signalLane;
        }

        public string? GetValue()
        {
            return _signalValue;
        }

        // returns a string representation of this log entry objects values
        public string GetLog()
        {
            return $"'{_rtcTime},{_signalValue},";
        }

        public int? GetLogEntryTime()
        {
            return _rtcTime;
        }

        public int? GetRunNum()
        {
            return (int?) _runNUm;
        }
    }
}

RangeObservableCollection.cs

using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace DataGridBindingProblem.Models
{
    public class RangeObservableCollection<T> : ObservableCollection<T>
    {
        private bool _suppressNotification = false;

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (_suppressNotification == false)
            {
                base.OnCollectionChanged(e);
            }
        }

        public void AddRange(IEnumerable<T> list)
        {
            if (list == null)
            {
                throw new ArgumentNullException("list");           
            }
                

            _suppressNotification = true;

            foreach (T item in list)
            {
                Add(item);
            }
            _suppressNotification = false;

            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
        }
    }
}

Итак, я нашел, где это происходит, но не знаю, как это исправить. Когда AutoGenerateColumns имеет значение true и DataGrid TextColumns удаляется, приложение запускается, но мне не нужны все автоматически сгенерированные столбцы ни в одной таблице.

Я не могу это воспроизвести gist.github.com/SirRufo/39b5903c8adc116e962572405b5c8af2

Sir Rufo 18.06.2024 08:22

Вы можете привязать к коллекции столько элементов управления ItemControl, сколько захотите. Исключение выдается, поскольку вы где-то изменяете (добавляете/удаляете/очищаете/перемещаете) ItemsControl через свойство ItemsControl.Items, что является незаконным, если возвращаемое представление коллекции было создано из значения ItemsSource.

BionicCode 18.06.2024 10:25

@SirRufo спасибо за ссылку. В моем XAML я устанавливал DataContext MainView в MainViewModel. В вашем случае вы делаете это в коде, а также создаете объект MainViewModel. Когда я попытался имитировать остальную часть вашего примера, я не увидел, чтобы значения в первом DataGrid отображались во втором, но я думаю, что поработаю над рефакторингом с этим новым DataContext, а затем посмотрю, что произойдет. Я обновлю пост, когда закончу и получу больше результатов. Спасибо!

Michael Riley 18.06.2024 22:17

@MichaelRiley Это вообще не имеет значения. я это тоже проверил

Sir Rufo 18.06.2024 22:31

@SirRufo да, ты прав. Я также загрузил и изменил ваше решение, и оно работает, но мое все еще не работает.

Michael Riley 19.06.2024 19:01

@BionicCode Да, я понимаю, о чем ты говоришь. В моем коде, когда существовал только первый DataGrid, я выполнял очистку PeekSignalList, но при загрузке формы единственное, что я делал, — это создавал экземпляр PeekSignalList с помощью new(). В исключении говорится: «Вместо этого получайте доступ и изменяйте элементы с помощью ItemsControl.ItemsSource», поэтому я не понимаю, относится ли это к коду позади или к XAML. Второму DataGrid нужны только данные, содержащиеся в списке PeekSignal, и он не будет изменяться. Я попытался изменить второй, чтобы он имел <ItemsControl ItemsSource = "{Binding PeekSignalsList}" />, но и там ничего не понравилось.

Michael Riley 19.06.2024 19:08

Если вы вызываетеclear непосредственно в исходной коллекции, значит, вы делаете это правильно. Вы не можете вызвать ItemsControl.Items.Clear. Также не создавайте дополнительных затрат на регистрацию этих ListCollectionViews, если только вам не нужно сортировать/фильтровать каждое представление коллекции независимо. Не могли бы вы опубликовать минимальный воспроизводимый пример, пожалуйста? Таким образом, мы сможем просмотреть ваш код. Трудно или невозможно догадаться, где вы ошиблись. XAML действительно недостаточно. XAML здесь даже не очень уместен. Гораздо интереснее, как вы обрабатываете коллекции и представления коллекций.

BionicCode 19.06.2024 19:17

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

BionicCode 19.06.2024 19:19

Вопрос @BionicCode обновлен по запросу. простите за опоздание

Michael Riley 19.06.2024 20:46

Спасибо за пример. Теперь я могу правильно просмотреть ваш код.

BionicCode 19.06.2024 21:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
10
59
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Просмотрев ваш код, я обнаружил ошибку в вашей разметке.

У вас есть два варианта заполнить ItemsControl:

  • используя свойство ItemsSource
  • или используя свойство Items

ItemsControl не может обработать оба варианта, поэтому вам придется выбрать один из способов, иначе парсер XAML выдаст опубликованное вами исключение:

«InvalidOperationException: операция недопустима, пока используется ItemsSource. Вместо этого обращайтесь к элементам и изменяйте их с помощью ItemsControl.ItemsSource». важно знать, что ты не можешь

Вы также можете добавлять элементы в свойство ItemsControl.Items в XAML, используя синтаксис коллекции и свойство содержимого (у ItemsControl свойство Items определено как свойство содержимого):

<ListBox>
  <ListBoxItem />
</ListBox>

Поскольку свойство содержимого — ItemsControl.Items, ListBoxItem из предыдущего примера неявно добавляется к свойству ListBox.Items.

Вы сделали именно это (вероятно случайно):

<DataGrid AutoGenerateColumns = "False"
          ItemsSource = "{Binding PeekSignalList}"> <!-- ItemsSource is used... -->
  <DataGridTextColumn Width = "auto"
                      Binding = "{Binding SignalName}"
                      Header = "Signal Name"
                      IsReadOnly = "True"> <!-- ...At the same time, this object is added to the Items property -->
  </DataGridTextColumn>
</DataGrid>

В то же время вы привязываете DataGrid.ItemsSource, что приводит к выдаче синтаксического анализатора XAML.

Однако контейнером элемента DataGrid является DataGridRow. Следовательно, предыдущий XAML семантически неверен, поскольку вы добавляли объекты DataGridColumn к свойству Items (вместо объектов DataGridRow).

Если бы вы не использовали свойство ItemsSource, правильный XAML был бы следующим:

<DataGrid AutoGenerateColumns = "False">
  <DataGridRow />
</DataGrid>

Однако дело в том, что объекты DataGridColumn необходимо добавить в коллекцию DataGrid.Columns. Добавлять их в коллекцию Items нет смысла, поскольку они не являются контейнерами предметов (я предполагаю, что это произошло каким-то образом случайно). Это всего лишь элементы макета, которые сообщают DataGrid, как структурирована таблица.

Фиксированный XAML выглядит следующим образом:

<DataGrid AutoGenerateColumns = "False"
          ItemsSource = "{Binding PeekSignalList}">
  <DataGrid.Columns> <!-- Add DatGridColumns to the DataGrid.Columns collection -->
    <DataGridTextColumn Width = "auto"
                        Binding = "{Binding SignalName}"
                        Header = "Signal Name"
                        IsReadOnly = "True">
    </DataGridTextColumn>
  </DataGrid.Columns>
</DataGrid>

@BionicCode...Друг мой, я очень ценю время и усилия, потраченные на то, чтобы прояснить это для меня. Когда я впервые использовал первый DataGrid, это было около 8 месяцев назад и раньше я его не использовал. Итак, вы были очень правы в том, что произошло вырезание и вставка или какая-то другая случайность, но это сработало, поэтому я оставил это позади. Глядя на ваше решение моей проблемы, я абсолютно вижу и согласен с ним. Еще раз спасибо!

Michael Riley 21.06.2024 21:03

Привет, Майкл, я рад, что смог помочь. И Вам огромное спасибо за высокую оценку и добрые слова! Спасибо. Хорошего дня!

BionicCode 21.06.2024 22:33

Другие вопросы по теме