Создание прогресс бара

Автор Тема: Создание прогресс бара  (Прочитано 13805 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн Павел55Автор темы

  • ADN OPEN
  • ***
  • Сообщений: 106
  • Карма: 3
Создание прогресс бара
« : 18-04-2019, 13:26:41 »
пытаюсь создать прогресс бар, но выдает ошибки  в тех местах, где в качестве аргумента передается база данных
я так понимаю это что то связанное с потоками, потому что если вызывать только метод work (коммандой ss), то все работает


Код - C# [Выбрать]
  1.     public class Class1
  2.     {
  3.  
  4.         MainWindow mw = new MainWindow();
  5.         Document curenntdoc;
  6.         [CommandMethod("ss")]
  7.         public void CreateFromTin()
  8.         {
  9.             curenntdoc = Application.DocumentManager.MdiActiveDocument;
  10.             mw.bworker.DoWork += work;
  11.             mw.bworker.RunWorkerAsync();
  12.  
  13.         }
  14.         public void work(object sender, DoWorkEventArgs e)
  15.         {
  16.             Editor ed = curenntdoc.Editor;
  17.             using (Transaction ts = curenntdoc.Database.TransactionManager.StartTransaction())
  18.             {
  19.  
  20.                 Database db = curenntdoc.Database;
  21.                 CivilDocument civil_doc = CivilApplication.ActiveDocument;
  22.                 ObjectId surface_id1 = civil_doc.GetSurfaceIds()[1];
  23.                 Autodesk.Civil.DatabaseServices.TinSurface tin_surface1 = surface_id1.GetObject(OpenMode.ForRead) as Autodesk.Civil.DatabaseServices.TinSurface;
  24.  
  25.                 ObjectId newSurfid = TinSurface.Create(db, "Surf1");
  26.  
  27.                 TinSurface newtin = newSurfid.GetObject(OpenMode.ForWrite) as TinSurface;
  28.                 newtin.CopyFrom(tin_surface1);
  29.                 newtin.RaiseSurface(5);
  30.                 ObjectId borderId = newtin.ExtractBorder(Autodesk.Civil.SurfaceExtractionSettingsType.Plan)[0];
  31.                 Polyline3d border = borderId.GetObject(OpenMode.ForRead) as Polyline3d;
  32.                
  33.                 Point3dCollection vertexInPoly = new Point3dCollection();
  34.                 foreach (ObjectId acObjIdVert in border)
  35.                 {
  36.                     PolylineVertex3d acPolVer3d;
  37.                     acPolVer3d = ts.GetObject(acObjIdVert,
  38.                                                    OpenMode.ForRead) as PolylineVertex3d;
  39.                    
  40.                     vertexInPoly.Add(acPolVer3d.Position);
  41.                 }
  42.                 Point3dCollection offsetline = new Point3dCollection();
  43.                 List<string> angle = new List<string>();
  44.                 for (int i = 0; i < vertexInPoly.Count-1; i++)
  45.                 {
  46.                     int k = i - 1;
  47.                     if (k < 0)
  48.                     {
  49.                         k = vertexInPoly.Count - 2;
  50.                     }
  51.                     int j = i + 1;
  52.                     if (j > vertexInPoly.Count - 2)
  53.                     {
  54.                         j = 0;
  55.                     }
  56.                     Mpoint m = new Mpoint(vertexInPoly[k], vertexInPoly[i], vertexInPoly[j], 5);
  57.  
  58.                     //////////////////////////////////////////////
  59.                     Thread.Sleep(1000);
  60.                     mw.bworker.ReportProgress(1);
  61.  
  62.                     if (m.SharpAngle == false)//если угол больше 180 - строим дугу между перпендикулярами
  63.                     {
  64.                         offsetline.Add(m.OrthoPointFromEdge1);
  65.                         foreach (Point3d item in m.PointInAngle)
  66.                         {
  67.                             offsetline.Add(item);
  68.                         }
  69.                         offsetline.Add(m.OrthoPointFromEdge2);
  70.                     }
  71.                     else if (m.SharpAngle == true)// если угол меньше 180 - строим одну точку(угол)
  72.                     {
  73.                      
  74.                         if (m.GetDist(vertexInPoly[k], vertexInPoly[j]) > 10)// если расстояние между двумя(крайними) точками меньше расстояния подобия*2 то не строим угловую точку
  75.                         {
  76.                             offsetline.Add(m.Intersect);
  77.                         }
  78.                        
  79.                     }
  80.                    
  81.                     //////////////////////////////////////////////
  82.                    
  83.                     string s = "угол " + i.ToString() + " " + m.Angle.ToString() + " " + m.SharpAngle.ToString() + "  Кол-во точек для угла: " + m.CountPointInAngle.ToString();
  84.                     angle.Add(s + "\n");
  85.                 }
  86.  
  87.                 BlockTable acBlkTbl;
  88.                 acBlkTbl = ts.GetObject(db.BlockTableId,
  89.                                              OpenMode.ForRead) as BlockTable;
  90.  
  91.                 // Open the Block table record Model space for write
  92.                 BlockTableRecord acBlkTblRec;
  93.                 acBlkTblRec = ts.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
  94.                                                 OpenMode.ForWrite) as BlockTableRecord;
  95.                 Polyline3d newpoly = new Polyline3d();
  96.                 newpoly.SetDatabaseDefaults();
  97.                 newpoly.ColorIndex = 1;
  98.  
  99.                 // Add the new object to the block table record and the transaction
  100.                 acBlkTblRec.AppendEntity(newpoly);
  101.                 ts.AddNewlyCreatedDBObject(newpoly, true);
  102.  
  103.                 for (int i = 0; i < offsetline.Count; i++)
  104.                 {
  105.                     newpoly.AppendVertex(new PolylineVertex3d(offsetline[i]));
  106.                 }
  107.                 newpoly.Closed = true;
  108.                 foreach (string s in angle)
  109.                 {
  110.                     ed.WriteMessage(s);
  111.                 }
  112.                 foreach (Point3d p in vertexInPoly)
  113.                 {
  114.                     ed.WriteMessage(p.X.ToString() + " / " + p.Y.ToString());
  115.                 }
  116.                 ts.Commit();
  117.             }
  118.  
  119.         }
  120.  
  121.         }            
  122.  
« Последнее редактирование: 18-04-2019, 13:56:52 от Александр Ривилис »

Онлайн Алексей Терно

  • ADN Club
  • ****
  • Сообщений: 381
  • Карма: 33
    • C3D Extensions
  • Skype: alexeyterno
Re: Создание прогресс бара
« Ответ #1 : 18-04-2019, 13:37:33 »
Создание прогресс бара:
Код - C# [Выбрать]
  1. ProgressMeter pm = new ProgressMeter();
  2. pm.SetLimit(max_len);
  3. pm.Start("текст");
  4. System.Windows.Forms.Application.DoEvents();

В цикле для увеличения счетчика:
Код - C# [Выбрать]
  1. pm.MeterProgress();
  2. System.Windows.Forms.Application.DoEvents();

Убрать прогресс бар после выполнения:
Код - C# [Выбрать]
  1. pm.Stop();
  2. System.Windows.Forms.Application.DoEvents();

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #2 : 18-04-2019, 13:58:18 »
Павел55,
Внимательно прочитайте у меня в подписи как следует форматировать код для форума и соблюдайте это правило!
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #3 : 18-04-2019, 13:59:54 »
я так понимаю это что то связанное с потоками, потому что если вызывать только метод work (коммандой ss), то все работает
Правильно понимаешь. AutoCAD не потокобезопасный. Обращаться к его API безопасно можно только из основного потока.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Отмечено как Решение Павел55 27-07-2019, 08:47:51

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #4 : 18-04-2019, 15:50:59 »
Создание прогресс бара:
Код - C# [Выбрать]
  1. ProgressMeter pm = new ProgressMeter();
  2. pm.SetLimit(max_len);
  3. pm.Start("текст");
  4. System.Windows.Forms.Application.DoEvents();

В цикле для увеличения счетчика:
Код - C# [Выбрать]
  1. pm.MeterProgress();
  2. System.Windows.Forms.Application.DoEvents();

Убрать прогресс бар после выполнения:
Код - C# [Выбрать]
  1. pm.Stop();
  2. System.Windows.Forms.Application.DoEvents();
Иногда стандартный прогресс-бар лучше не использовать. Например, когда к нему параллельно внутренние механизмы обращаются. Тогда можно сделать свой на любой вкус и цвет с помощью WPF. В общем случае, даже писать ничего не надо - примеры реализации есть в сети.
Недавно, например, вот такой сделал:

Ещё примерчик:

Кстати, а что эта тема делает в Civil 3D?
« Последнее редактирование: 18-04-2019, 16:27:12 от Дмитрий Загорулькин »

Оффлайн Павел55Автор темы

  • ADN OPEN
  • ***
  • Сообщений: 106
  • Карма: 3
Re: Создание прогресс бара
« Ответ #5 : 19-04-2019, 22:24:56 »
И все равно не понимаю как заставить работать прогресс бар.
Получатся у меня 2 потока - один это в AutoCAD, второй - прогресс бар. после запуска происходит следующее:
открывается окно с прогресс баром, выполняются все вычисления в цикле, а потом (по завершению цикла )
в прогресс бар передается последнее значение цикла. То есть он срабатывает, когда все вычисления закончены. Как заставить его работать синхронно с циклом?
Код - C# [Выбрать]
  1. namespace PtogressBar
  2. {
  3.     /// <summary>
  4.     /// Логика взаимодействия для MainWindow.xaml
  5.     /// </summary>
  6.     public partial class MainWindow : Window
  7.     {
  8.         private object threadLock = new object();
  9.         public MainWindow()
  10.         {
  11.             InitializeComponent();
  12.             System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(updadingThread));
  13.             t.Start();
  14.         }
  15.         public delegate void Update(int value);
  16.         public void updadeBar(int value)
  17.         {
  18.            
  19.             this.PG.Value = value;
  20.         }
  21.  
  22.         public void updadingThread(object sender)
  23.         {
  24.             Update update = new Update(updadeBar);
  25.  
  26.         }
  27.     }
  28. }


               
Код - C# [Выбрать]
  1.  MainWindow mw = new MainWindow();
  2.                 mw.Show();
  3.                 for (int i = 0; i < vertexInPoly.Count-1; i++)
  4.                 {
  5.                         Thread.Sleep(500);
  6.                         mw.updadeBar(i * 5);
  7.                         int k = i - 1;
  8.                         if (k < 0)
  9.                         {
  10.                             k = vertexInPoly.Count - 2;
  11.                         }
  12.                         int j = i + 1;
  13.                         if (j > vertexInPoly.Count - 2)
  14.                         {
  15.                             j = 0;
  16.                         }
  17.                         Mpoint m = new Mpoint(vertexInPoly[k], vertexInPoly[i], vertexInPoly[j], 5);
  18.  
  19.                         //////////////////////////////////////////////
  20.  
  21.                         if (m.SharpAngle == false)//если угол больше 180 - строим дугу между перпендикулярами
  22.                         {
  23.                             offsetline.Add(m.OrthoPointFromEdge1);
  24.                             foreach (Point3d item in m.PointInAngle)
  25.                             {
  26.                                 offsetline.Add(item);
  27.                             }
  28.                             offsetline.Add(m.OrthoPointFromEdge2);
  29.                         }
  30.                         else if (m.SharpAngle == true)// если угол меньше 180 - строим одну точку(угол)
  31.                         {
  32.  
  33.                             if (m.GetDist(vertexInPoly[k], vertexInPoly[j]) > 10)// если расстояние между двумя(крайними) точками меньше расстояния подобия*2 то не строим угловую точку
  34.                             {
  35.                                 offsetline.Add(m.Intersect);
  36.                             }
  37.  
  38.                         }
  39.                 }

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #6 : 20-04-2019, 01:27:09 »
Есть куча готовых прогрессбаров. Зачем изобретать велосипед?
Ну например: http://drive-cad-with-code.blogspot.com/2015/04/showing-progress-for-long-code.html
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #7 : 22-04-2019, 07:49:38 »
Создание прогресс бара:
Код - C# [Выбрать]
ProgressMeter pm = new ProgressMeter();
pm.SetLimit(max_len);
pm.Start("текст");
System.Windows.Forms.Application.DoEvents();
Не совсем так ;-)

Как ни странно сначала
pm.Start("текст");
а потом
pm.SetLimit(max_len);

Иначе в AutoCAD progressbar идет скачками, а кое-где(что мы тут не обсуждаем)  pm.Start("") сбрасывает счетчик на 100.

Что поделаешь, такое API.

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #8 : 22-04-2019, 10:30:42 »
И все равно не понимаю как заставить работать прогресс бар.
Получатся у меня 2 потока - один это в AutoCAD, второй - прогресс бар. после запуска происходит следующее:
открывается окно с прогресс баром, выполняются все вычисления в цикле, а потом (по завершению цикла )
в прогресс бар передается последнее значение цикла. То есть он срабатывает, когда все вычисления закончены. Как заставить его работать синхронно с циклом?
Делал тестовый пример, код пока не нашел, но примерно так:
1. Всю работу с AutoCAD делаешь в основном потоке AutoCAD. Не пытайся вынести в другой поток.
2. Окно progressbar запускаешь в другом потоке.
3. Из основного потока вызываешь методы progressbar. Только методы progressbar должны менять состояние progressbar.
4. Периодически выполняешь System.Windows.Forms.Application.DoEvents(); чтобы интерфейс обновлялся.

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #9 : 22-04-2019, 13:03:35 »
Привалов Дмитрий,
Зачем второй поток? Пункта 4 должно быть достаточно. Или речь про нестандартный прогрессбар?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #10 : 22-04-2019, 13:09:38 »
Делал нестандартные, но второй поток не запускал. Так что, это необязательная процедура.
4. Периодически выполняешь System.Windows.Forms.Application.DoEvents(); чтобы интерфейс обновлялся.
А вот без этого, да, согласен - прогрессбар не обновляется, пока автокад не закончит работу с циклом.

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #11 : 22-04-2019, 13:27:49 »
Зачем второй поток?
Я сделал некий тестовый проект и пока ни разу не применил.

Задумка была такая, что пока в AutoCAD обрабатывается основной поток пользователь в WPF окне мог смотреть информацию по обработке, например переключать вкладки. Т.е. случай, если информации больше, чем может уместиться на экране, и процесс обработки длительный.

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

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #12 : 30-03-2021, 11:42:50 »
Сначала накатал пост, потом стер его и еще три дня экспериментировал и все таки напишу, вдруг кому сэкономит время.

В общем, не нужно использовать штатный прогресс бар при создании и изменении динамических блоков, так как
вызов метода System.Windows.Forms.Application.DoEvents(); чтобы интерфейс обновлялся, одновременно как бы отменяет
using (App.DocumentLock docLock = doc.LockDocument()) и появляется куча разных ошибок в произвольных местах программы.

« Последнее редактирование: 05-04-2021, 14:34:43 от Владимир Шу »

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #13 : 05-04-2021, 15:53:24 »
вызов метода System.Windows.Forms.Application.DoEvents(); чтобы интерфейс обновлялся, одновременно как бы отменяет
using (App.DocumentLock docLock = doc.LockDocument()) и появляется куча разных ошибок в произвольных местах программы.

Я использую стандартный прогресс бар с DoEvents() и подобных ошибок не возникало.
Возможно в другом месте ошибка, и DoEvents() ускоряет ее появление?

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #14 : 05-04-2021, 16:22:44 »
Привалов Дмитрий, схема программы примерно следующая:

1. Открыть в редакторе документ
2. Сделать его активным
3. Блокируем документ
4. Базу этого документа так же делаем активной ( HostApplicationServices.WorkingDatabase)
5. Создаем блоки и заносим их ID в списочек
6. Проходим по созданному списку и назначаем созданным блокам нужные параметры
7. Прописываем доп. данные в XData (нужно сгруппировать блоки и записываются номера групп)
8. Перемещаем блоки в нужные позиции по группам (по простому, через метод Transform)
9. Сохраняем базу
10. Тут заканчивается блокировка документа
11. Оставляем чертеж открытым

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

Иногда вылет с фаталом, иногда просто закрывается автокад, как будто и не запускался, иногда это ошибка записи в динамические свойства блока, иногда ошибка записи XData, иногда фатал с сообщением о попытке записи в запрещенную область памяти, иногда ошибка с тем, что открытое для записи вхождение блока выдает ошибку сообщая, что это блок открыт только для чтения, но я же вижу в коде, что двумя строчками выше открывал этот блок для записи. И это все может быть в любой момент и на любой выборке данных, хоть 10, хоть 3.
НО, если прогресс бар убрать, то ни одна из перечисленных ошибок не появляется, хоть вставляй десяток тысяч блоков и при непрерывной работе больше часа, я проверял, больше 15 т. блоков вставляются нормально и стабильно, много раз... долго только, но все работает.
И да, все эти же ошибки будут если не блокировать документ вовсе.

Так не бывает.



Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #15 : 05-04-2021, 17:07:52 »
Владимир Шу,
Как часто ты вызвал в коде Application.DoEvents()? Я, когда делал ProgressBar с помощью ObjectARX, использовал код аналогичный Application.DoEvents(). Если его вызов происходил в каждой итерации (а они происходили по несколько десятков в миллисекунду), то иногда я тоже получал Fatal Error. После того как я добавил проверку на время между вызовами аналога Application.DoEvents() и это время установил в диапазоне от 0.1 до 0.5 секунды - во-первых, код стал выполняться значительно быстрее, во-вторых, Fatal Error исчезли вообще. Application.DoEvents() может быть достаточно длительной операцией.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #16 : 05-04-2021, 17:31:33 »
Как часто ты вызвал в коде Application.DoEvents()?
После обработки каждого блока.
После того как я добавил проверку на время между вызовами аналога Application.DoEvents() и это время установил в от 0.1 до 0.5 секунды

Переписал метод вот так:
Код - C# [Выбрать]
  1.     private static Dictionary<Db.ObjectId, ExcelRow> ModifyBlockRefToDb(Dictionary<Db.ObjectId, ExcelRow> blockRefForSetParam)
  2.     {
  3.       var pm = new Rtm.ProgressMeter();
  4.       pm.SetLimit(blockRefForSetParam.Count);
  5.       pm.Start("Редактирование блоков");
  6.       System.Windows.Forms.Application.DoEvents();
  7.  
  8.       long start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  9.  
  10.       var blockRefForSetParamX = new Dictionary<Db.ObjectId, ExcelRow>();
  11.       foreach (var ent in blockRefForSetParam)
  12.       {
  13.         Db.ObjectId id = Db.ObjectId.Null;
  14.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  15.         {
  16.           foreach (var row in ent.Value.Pairs)
  17.           {
  18.             blockRef.SetDynamicProperty(row.Key, row.Value);
  19.           }
  20.           id = blockRef.ObjectId;
  21.         }
  22.         if (!id.IsNull)
  23.           blockRefForSetParamX.Add(id, ent.Value);
  24.  
  25.  
  26.  
  27.         long difference = (DateTime.Now.Ticks / TimeSpan.TicksPerSecond - start);
  28.         if (difference > 1)
  29.         {
  30.           System.Windows.Forms.Application.DoEvents();
  31.           start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  32.         }
  33.         pm.MeterProgress();
  34.       }
  35.  
  36.       pm.Stop();
  37.       System.Windows.Forms.Application.DoEvents();
  38.       return blockRefForSetParamX;
  39.     }
  40.  
не смотря на задержку в 1 сек, Автокад вываливается в фатал. Остальной код программы не менял.
Если закоментить все что относится к прогресс бару и Application.DoEvents(), то программа опять начинает работать нормально.

Может я чего то не правильно делаю....

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #17 : 05-04-2021, 20:19:39 »
не смотря на задержку в 1 сек, Автокад вываливается в фатал.
1. Проверь, закомментируй все DoEvents(); будет ошибка или нет?

2. Потом, попробуй слегка модифицированный код.

   
Код - C# [Выбрать]
  1.  private static Dictionary<Db.ObjectId, ExcelRow> ModifyBlockRefToDb(Dictionary<Db.ObjectId, ExcelRow> blockRefForSetParam)
  2.     {
  3.       var pm = new Rtm.ProgressMeter();
  4.       pm.Start("Редактирование блоков");
  5.       pm.SetLimit(blockRefForSetParam.Count);//Сперва старт, затем установка лимита. Для плавности отображения прогрессбара. На ошибку не должен влиять.
  6.  
  7.       long start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  8.  
  9.       var blockRefForSetParamX = new Dictionary<Db.ObjectId, ExcelRow>();
  10.       foreach (var ent in blockRefForSetParam)
  11.       {
  12.         Db.ObjectId id = Db.ObjectId.Null;
  13.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  14.         {
  15.           foreach (var row in ent.Value.Pairs)
  16.           {
  17.             blockRef.SetDynamicProperty(row.Key, row.Value);
  18.           }
  19.           id = blockRef.ObjectId;
  20.         }
  21.         if (!id.IsNull)
  22.           blockRefForSetParamX.Add(id, ent.Value);
  23.  
  24.         long difference = (DateTime.Now.Ticks / TimeSpan.TicksPerSecond - start);
  25.         if (difference > 1)
  26.         {
  27.           start = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
  28.           pm.MeterProgress();//Тоже не стоит вызывать часто. Отобразит не корректно, не дойдет до конца, проверь пока так.
  29.           System.Windows.Forms.Application.DoEvents();
  30.         }
  31.       }
  32.  
  33.       pm.Stop();
  34.       return blockRefForSetParamX;
  35.     }

3. Если упадет в 1 и 2 случае, попробуй закомментить pm.MeterProgress(); а DoEvents(); оставь.

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #18 : 05-04-2021, 20:36:34 »
1 вариант - упал с фаталом
2 вариант - сначала просто по тихому выключился акад как будто из диспетчера задач процесс убили, второй запуск с фаталом
3 вариант - System.AccessViolationException: "Попытка чтения или записи в защищенную память. Это часто свидетельствует о том, что другая память повреждена" и на второй раз просто фатал
4 вариант - удаляем прогресс бар и DoEvents(); и все работает.

Я бы понял если бы ошибки были бы одинаковые или почти одинаковые, значит я где то накосячил с кодом, но тут полный раздрай.
На всякий случай Win7x64 и ADT 2017.

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #19 : 05-04-2021, 21:02:22 »
Я бы понял если бы ошибки были бы одинаковые или почти одинаковые, значит я где то накосячил с кодом, но тут полный раздрай.
Похоже, что проблема где-то в другом месте.
В функции не видно блокировки документа. Посмотри что с ней.

Попробуй закомментить. Возможно проблема в коде
Код - C# [Выбрать]
  1. Db.ObjectId id = Db.ObjectId.Null;
  2.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  3.         {
  4.           foreach (var row in ent.Value.Pairs)
  5.           {
  6.             blockRef.SetDynamicProperty(row.Key, row.Value);
  7.           }
  8.           id = blockRef.ObjectId;
  9.         }
  10.         if (!id.IsNull)
  11.           blockRefForSetParamX.Add(id, ent.Value)

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #20 : 05-04-2021, 22:21:47 »
Что-то сложно как-то время вычисляется. Я так и не разобрался - есть там секунда или нет.
Код - C# [Выбрать]
  1. ...
  2. var start = DateTime.Now;
  3. ...
  4. var difference = DateTime.Now - start;
  5. if (difference.TotalSeconds > 1)
  6. ...
  7.  
Вроде как, правильнее делать DoEvents после того, как прогрессметер "пнули", а не до этого, как в коде из #16.
А, Привалов Дмитрий уже поправил это в коде из #17.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #21 : 05-04-2021, 22:34:01 »
На всякий случай Win7x64 и ADT 2017.
Идея, конечно, из области догадок. Но может ADT в данном случае как-то вмешивается в работу прогрессбара? Возможно, что штатный прогрессбар в этот момент сам ADT задействует и возникает какой-то конфликт. У меня было такое, что я обрабатывал чертежи в фоне и пытался использовать этот штатный прогрессбар. В итоге в какой-то момент его "перехватывал" сам автокад и управление прогрессбаром из моего приложения прекращалось. Фатала не было но и не работало как надо. Мне пришлось переписать с использованием самописного погрессбара на основе немодального WPF окошка.

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #22 : 06-04-2021, 07:40:53 »
значит я где то накосячил с кодом

...Подумал утром, что смущает в коде.
1. работаешь без транзакции, могут быть различные глюки.
Код - C# [Выбрать]
  1. using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  2. {
  3.    if (blockRef != null) //if (!id.IsNull)... 2. какая-то нереализованная проверка?
  4.    {
  5.       foreach (var row in ent.Value.Pairs)
  6.       {
  7.          blockRef.SetDynamicProperty(row.Key, row.Value);//3. Выставляешь динамическое свойство. А существует ли оно и можно ли менять?
  8.       }
  9.       blockRefForSetParamX.Add(ent.Key, ent.Value);
  10.    }
  11. }

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #23 : 06-04-2021, 08:16:05 »
И с транзакцие (в разных ее вариациях) и без нее работал, все едино.
Да и не сошелся свет клином именно на этом методе, я его только для примера привел, то же самое поведение будет если я вставлю прогресс бар внутрь любого из методов использующихся в DrawBlocks или в самом DrawBlocks

Вроде как, правильнее делать DoEvents после того, как прогрессметер "пнули", а не до этого, как в коде из #16.
А, Привалов Дмитрий уже поправил это в коде из #17.
Вот результат



Я ранее расписывал общую структуру программы
Тут создаем и блокируем документ.
Сейчас тут AppContextOpenDocument(), ранее было создание базы и ReadDwg(), но теперешний вариант стабильнее и нужно что бы документ после завершения работы оставался открытым
Код - C# [Выбрать]
  1.     internal static void CreateDWG(string outPutDWG, List<ExcelRow> excelRows)
  2.     {
  3.       App.DocumentCollection acDocMgr = App.Application.DocumentManager;
  4.       //Создается чертеж по шаблону
  5.       acDocMgr.AppContextOpenDocument(outPutDWG);
  6.       App.Document docA = App.Application.DocumentManager.MdiActiveDocument;
  7.  
  8.       using (App.DocumentLock docLock = docA.LockDocument())
  9.       {
  10.         using (WorkingDatabaseSwitcher switcher = new WorkingDatabaseSwitcher(docA.Database))
  11.         {
  12.           //Проверяем регистрацию приложения для группировки
  13.           XDataExtension.SetXDataApp(docA.Database);
  14.  
  15.           //Рисуем блоки
  16.           DrawBlocks(docA, excelRows);
  17.         }
  18.       }
  19.  
  20.       docA.Database.SaveAs(outPutDWG, true, Db.DwgVersion.Current, null);
  21.     }

Тут собственно создаются, изменяются и растаскиваются по местам блоки
Код - C# [Выбрать]
  1.     private static void DrawBlocks(App.Document doc, List<ExcelRow> excelRows)
  2.     {
  3.       Db.Database newDb = doc.Database;
  4.       List<Db.ObjectId> eraseIds = new List<Db.ObjectId>();
  5.       //Просто очищаем пространство модели
  6.       var msId = Db.SymbolUtilityServices.GetBlockModelSpaceId(newDb);
  7.       using (var ms = msId.Open(Db.OpenMode.ForRead) as Db.BlockTableRecord)
  8.         foreach (Db.ObjectId id in ms)
  9.           if (id.ObjectClass.IsDerivedFrom(Rtm.RXObject.GetClass(typeof(Db.Entity))))
  10.             eraseIds.Add(id);
  11.  
  12.       //Размещаем блоки
  13.       var blockIds_mod = new Dictionary<Db.ObjectId, ExcelRow>();
  14.       blockIds_mod = PostBlockRefToDb(newDb, excelRows);
  15.  
  16.       if (blockIds_mod.Count == 0) return;
  17.  
  18.  
  19.       //Модифицируем блоки
  20.       var blockIds = new Dictionary<Db.ObjectId, ExcelRow>();
  21.       blockIds = ModifyBlockRefToDb(blockIds_mod);
  22.  
  23.       if (blockIds.Count == 0) return;
  24.  
  25.       //Маркеруем блоки
  26.       MarkerGroupb(blockIds);
  27.  
  28.       //Давай сделаем словарик, с номером группы и наибольшей высотой блока в этой группе
  29.       Dictionary<int, double> maxBlockRowHeigth = new Dictionary<int, double>();
  30.       maxBlockRowHeigth = GetMaxBlockRowHeigth(blockIds.Keys.ToList());
  31.  
  32.       if (maxBlockRowHeigth.Count == 0) return;
  33.  
  34.       //Двигаем блоки, сейчас они все стоят в точке 0,0,0
  35.       TransformBlocks(blockIds.Keys.ToList(), maxBlockRowHeigth);
  36.  
  37.       //Удалить объекты из модели, которые были там до начала работы скрипта
  38.       foreach (Db.ObjectId id in eraseIds)
  39.         using (var ent = id.Open(Db.OpenMode.ForWrite) as Db.DBObject)
  40.           ent.Erase();
  41.  
  42.       eraseIds.Clear();
  43.     }

Вот это вот: "Удалить объекты из модели, которые были там до начала работы скрипта" , это потому, что я думал что глюки при создании дин. блоков и потому переписывал на копирование блоков которые есть в модели в шаблоне, но нет, не помогает и обратно исправил на создание...

Метод ModifyBlockRefToDb показывал ранее, но вот поправленный вариант с результатом на картинке выше
Код - C# [Выбрать]
  1.      private static Dictionary<Db.ObjectId, ExcelRow> ModifyBlockRefToDb(Dictionary<Db.ObjectId, ExcelRow> blockRefForSetParam)
  2.     {
  3.       var pm = new Rtm.ProgressMeter();
  4.       pm.Start("Редактирование блоков");
  5.       pm.SetLimit(blockRefForSetParam.Count);
  6.       //System.Windows.Forms.Application.DoEvents();
  7.  
  8.  
  9.      var start = DateTime.Now;
  10.  
  11.       var blockRefForSetParamX = new Dictionary<Db.ObjectId, ExcelRow>();
  12.       foreach (var ent in blockRefForSetParam)
  13.       {
  14.         Db.ObjectId id = Db.ObjectId.Null;
  15.         using (var blockRef = ent.Key.Open(Db.OpenMode.ForWrite) as Db.BlockReference)
  16.         {
  17.           foreach (var row in ent.Value.Pairs)
  18.           {
  19.             blockRef.SetDynamicProperty(row.Key, row.Value);
  20.           }
  21.           id = blockRef.ObjectId;
  22.         }
  23.  
  24.         if (!id.IsNull)
  25.           blockRefForSetParamX.Add(id, ent.Value);
  26.  
  27.  
  28.         pm.MeterProgress();
  29.  
  30.         var difference = DateTime.Now - start;
  31.         if (difference.TotalSeconds > 1)
  32.         {
  33.           System.Windows.Forms.Application.DoEvents();
  34.           start = DateTime.Now;
  35.         }
  36.  
  37.  
  38.       }
  39.  
  40.       pm.Stop();
  41.       //System.Windows.Forms.Application.DoEvents();
  42.       return blockRefForSetParamX;
  43.     }
  44.  
  45.  

 но это все не имеет значения, я даже убирал блокировку в методе CreateDWG и переносил ее DrawBlocks и оборачивал блокированием документа только вызовы методов PostBlockRefToDb, ModifyBlockRefToDb, MarkerGroupb, GetMaxBlockRowHeigth, TransformBlocks, что бы сделать прогресс бар хотя бы по этапам. "Всё фигня, Миша. Начинаем сначала". Я на эту фигню более 30 часов убил и теперь просто принимаю как данность, лучше не использовать прогресс бар и DoEvents при работе с динамическими блоками


if (blockRef != null) //if (!id.IsNull)... 2. какая-то нереализованная проверка?
В моем коде такого нет.
3. Выставляешь динамическое свойство. А существует ли оно и можно ли менять?
Да, существует, да можно менять, да соответствует нужному типу, да укладывается в перечень разрешенных значений.
Код - C# [Выбрать]
  1.     public static void SetDynamicProperty(this Db.BlockReference acBlockRef, string PropName, object PropValue)
  2.     {
  3.       if (acBlockRef.IsDynamicBlock)
  4.       {
  5.         Db.DynamicBlockReferencePropertyCollection acBlockDynProp =
  6.           acBlockRef.DynamicBlockReferencePropertyCollection;
  7.  
  8.         if (acBlockDynProp != null)
  9.         {
  10.           foreach (Db.DynamicBlockReferenceProperty obj in acBlockDynProp)
  11.           {
  12.  
  13.             if (!obj.ReadOnly && obj.PropertyName.ToUpper() == PropName.ToUpper())
  14.             {
  15.               //Если есть что менять...
  16.               if (obj.Value.ToString() != PropValue.ToString())
  17.               {
  18.                 object val = null;
  19.                 if (obj.PropertyTypeCode == (short)DwgDataType.kDwgReal)
  20.                 {
  21.                   double v = 0;
  22.                   if (double.TryParse(PropValue.ToString(), out v))
  23.                   {
  24.                     val = v;
  25.                   }
  26.                 }
  27.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgInt32){...}
  28.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgInt16){...}
  29.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgInt8){...}
  30.                 else if (obj.PropertyTypeCode == (short)DwgDataType.kDwgText)
  31.                 {
  32.                   val = (object)PropValue.ToString();
  33.                 }
  34.  
  35.                 //А остальное нам не нужно.
  36.                 if (val != null)
  37.                 {
  38.                   var fff = new List<object>(obj.GetAllowedValues());
  39.                   if (fff.Count == 0 || fff.Any(q => q.ToString() == PropValue.ToString()))
  40.                   {
  41.                     obj.Value = val;
  42.                     break;
  43.                   }
  44.                 }
  45.               }
  46.             }
  47.           }
  48.         }
  49.       }
  50.     }

Что-то сложно как-то время вычисляется. Я так и не разобрался - есть там секунда или нет.
Есть, на отладке специально перепроверял. Я сначала пытался через тики 0,1.. 0,5 сек использовать и только потом до 1 секунды дошел. Не помогло.
« Последнее редактирование: 06-04-2021, 09:30:27 от Владимир Шу »

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #24 : 06-04-2021, 10:42:55 »
Сейчас тут AppContextOpenDocument(), ранее было создание базы и ReadDwg(), но теперешний вариант стабильнее и нужно что бы документ после завершения работы оставался открытым

А почему просто не открыть документ и не заблокировать?
Cad.DocumentManager.Open(...);

Меня смущает:
1. что ты что-то открываешь AppContextOpenDocument(outPutDWG);
2. Блокируешь документ
3. Переключаешь рабочую базу данных WorkingDatabaseSwitcher switcher = new WorkingDatabaseSwitcher(docA.Database). Причем указываешь саму базу документа.

Насколько корректно работает переключение БД  после блокировки документа? Нужно ли вызывать WorkingDatabaseSwitcher ?

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #25 : 06-04-2021, 10:50:44 »
Я на эту фигню более 30 часов убил и теперь просто принимаю как данность, лучше не использовать прогресс бар и DoEvents при работе с динамическими блоками
Думаю, тут проблема не в работе с динамическими блоками, а в том, что идёт работа с неактивным чертежом (если я правильно понял код).

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #26 : 06-04-2021, 10:55:33 »
А почему просто не открыть документ и не заблокировать?
Cad.DocumentManager.Open(...);
DocumentManager это объект класса DocumentCollection у него нет метода Open() и  AppContextOpenDocument(путь_к_открываемому_файлу) это именно открытие файла в редакторе, по крайне мере я другого способа не знаю.
Я пробовал различные комбинации, сначала блокировать потом переключать базу или сначала базу переключить потом блокировать. Результат один.

Думаю, тут проблема не в работе с динамическими блоками, а в том, что идёт работа с неактивным чертежом (если я правильно понял код).
Я так же подумал и переписал с ReadDwg() и работу в фоне, на AppContextOpenDocument() т.е. сознательно делаю и чертеж и базу активными. Результат не меняется.

Я думаю что это все таки связанно с дин. блоками, т.к. при изменении дин.свойств, автокад пересоздает объекты, по крайней мере Id объектов меняются (потому в коде пересозданние коллекций) и значит автокад обрабатывает какие то там зависимости.... и если в этот момент разрешить автокаду самовольничать, то все идет лесом.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #27 : 06-04-2021, 13:07:48 »
Я так же подумал и переписал с ReadDwg() и работу в фоне, на AppContextOpenDocument() т.е. сознательно делаю и чертеж и базу активными. Результат не меняется.
Я не увидел, а где чертёж делается активным? И если он активный, то зачем рабочую базу переключать?

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #28 : 06-04-2021, 13:19:32 »
Я не увидел, а где чертёж делается активным?
AppContextOpenDocument() открывает и делает активным, вот тут есть пример кода и видяшка https://adn-cis.org/forum/index.php?topic=8979.0 . Загвоздка только в том, что AppContextOpenDocument не возвращает ничего и я был бы рад скормить MdiActiveDocument или CurrentDocument что то...  но на нет и суда нет  (это я про строчку 41 в коде Александра в указанной теме... и еще вот тут https://github.com/kevinzhwl/ObjectARXCore/blob/master/2015/inc/acdocman.h в строке 382 сказано, что открывает и переключает на нужный вид). Если просто закоментить  отрисовку блока, то программа откроет документ в редакторе и можно сразу начинать с ним работать, т.е. он активный

И если он активный, то зачем рабочую базу переключать?
Потому что идей почему оно падает не было и на всякий случай и базу переключаю, может и масло масленое.

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #29 : 06-04-2021, 19:10:47 »
А вот интересно, в чём смысл AppContextOpenDocument(), если можно так?
Вопрос без подвоха, я правда не понимаю...

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #30 : 06-04-2021, 19:59:12 »
Никакого умысла или специального использования не было
Методы Add() и Open() для DocumentCollection acDocMgr = Application.DocumentManager;
на самом деле это методы расширения описанные в классе DocumentCollectionExtension и у меня они на прямую почему то не работают =(

Я просматривал методы и свойства класса DocumentCollection и там единственный метод на открытие документа это AppContextOpenDocument(), соответственно, выкручивался как мог.

Но по твоей наводке сейчас углубился в этот момент и попробовал заменить
Код - C# [Выбрать]
  1.       acDocMgr.AppContextOpenDocument(outPutDWG);
  2.       App.Document docA = App.Application.DocumentManager.MdiActiveDocument;
на
Код - C# [Выбрать]
  1.       App.Document docA = App.DocumentCollectionExtension.Open(acDocMgr, outPutDWG, false);
  2.       App.Application.DocumentManager.MdiActiveDocument = docA;
Поведение программы визуально не изменилось и работать с прогресс баром так же отказывается.

Разница этих функций, как я понял, для моего случая не сильно велика:
Open() - открыть указанный чертеж и вернуть документ
AppContextOpenDocument() - функция разрешающая синхронный доступ к операции открытия чертежа. Работает только в контексте приложения.

ЗЫ.
Порадовало описание метода AppContextRecoverDocument():
Цитировать
This is AppContextRecoverDocument, a member of class DocumentCollection.
Для чего, почему

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #31 : 06-04-2021, 20:23:21 »
ЗЫ.
Порадовало описание метода AppContextRecoverDocument():
Цитировать

    This is AppContextRecoverDocument, a member of class DocumentCollection.

Для чего, почему

Как обычно смотрим откуда растут ноги:



Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Создание прогресс бара
« Ответ #32 : 06-04-2021, 20:26:17 »
На всякий случай Win7x64 и ADT 2017.
Вполне возможно, что это баг в этой версии или то, что это не чистый AutoCAD.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #33 : 06-04-2021, 23:06:37 »
Если есть желание, можешь попробовать такой прогрессбар. Как он работает я показывал во втором видео сообщения #4 этой темы. Только тут он адаптирован под использование в Revit, надо метод вызова заменить на автокадовский Application.ShowModelessWindow. Может ещё что-то по мелочи подправить.
Код - XML [Выбрать]
  1. <Window x:Class="Support.ProgressMeterWindow"
  2.        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.        SizeToContent="WidthAndHeight"      
  5.        WindowStartupLocation="CenterScreen" ResizeMode="NoResize"
  6.        ShowInTaskbar="False" Focusable="False" IsHitTestVisible="False"
  7.        IsTabStop="False" ShowActivated="False" WindowStyle="None"
  8.        AllowsTransparency="True" x:Name="ThisControl">
  9.     <Border BorderThickness="1" BorderBrush="DarkGray" CornerRadius="2">
  10.         <StackPanel Margin="10">
  11.             <StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
  12.                 <TextBlock Text="{Binding ElementName=ThisControl, Path=Header, Mode=OneWay}"/>                
  13.             </StackPanel>
  14.             <Grid Margin="0,5,0,0"><ProgressBar VerticalAlignment="Center"
  15.            Name="pBar" Width="300" Height="17"
  16.            Maximum="{Binding Maximum, Mode=OneWay}"
  17.            Value="{Binding Value, Mode=OneWay}"/>
  18.                 <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
  19.                     <TextBlock Text="{Binding ElementName=pBar, Path=Value}"/>
  20.                     <TextBlock Text=" / "></TextBlock>
  21.                     <TextBlock Text="{Binding ElementName=pBar, Path=Maximum}"/>
  22.                 </StackPanel>
  23.             </Grid>
  24.         </StackPanel>
  25.     </Border>
  26. </Window>
Код - C# [Выбрать]
  1. using System.ComponentModel;
  2. using System.Windows;
  3.  
  4. namespace Support
  5. {
  6.     /// <summary>
  7.     /// Interaction logic for ProgressMeter.xaml
  8.     /// </summary>
  9.     public partial class ProgressMeterWindow : Window, INotifyPropertyChanged
  10.     {
  11.         int _maximum, _value;
  12.         string _header;
  13.  
  14.         public string Header
  15.         {
  16.             get => _header;
  17.             set
  18.             {
  19.                 _header = value;
  20.                 OnPropertyChanged(nameof(Header));
  21.             }
  22.         }
  23.  
  24.         public int Maximum
  25.         {
  26.             get => _maximum;
  27.             set
  28.             {
  29.                 _maximum = value;
  30.                 OnPropertyChanged(nameof(Maximum));
  31.             }
  32.         }
  33.         public int Value
  34.         {
  35.             get => _value;
  36.             set
  37.             {
  38.                 this._value = value;
  39.                 OnPropertyChanged(nameof(Value));
  40.                 System.Windows.Forms.Application.DoEvents();
  41.             }
  42.         }
  43.  
  44.         public ProgressMeterWindow()
  45.         {            
  46.             InitializeComponent();
  47.             this.DataContext = this;
  48.         }
  49.  
  50.         public ProgressMeterWindow(string header, int maximum) : this()
  51.         {
  52.             Header = header;
  53.             Maximum = maximum;
  54.             Show();
  55.             System.Windows.Forms.Application.DoEvents();
  56.         }
  57.  
  58.         public void Increment()
  59.         {
  60.             Value += 1;
  61.             System.Windows.Forms.Application.DoEvents();
  62.         }
  63.  
  64.         public event PropertyChangedEventHandler PropertyChanged;
  65.  
  66.         void OnPropertyChanged(string prop)
  67.         {
  68.             PropertyChanged?.Invoke
  69.                 (this, new PropertyChangedEventArgs(prop));
  70.         }
  71.     }
  72. }

Оффлайн Привалов Дмитрий

  • ADN Club
  • *****
  • Сообщений: 534
  • Карма: 117
Re: Создание прогресс бара
« Ответ #34 : 07-04-2021, 08:05:01 »
Может ещё что-то по мелочи подправить.
Основная беда прогресс баров, это избыточный вызов методов Value += 1; и как следствие перерисовки прогресс баров, это дорогостоящая операция и DoEvents(); для отображения обновления.
Как следствие, при использовании прогресс бара может сильно проседать производительность цикла. Зависит от значения maximum.


Я использую стандартный ProgressMeter(У меня то он работает :-D )
В нем небольшой алгоритм уменьшения количества вызовов Value и DoEvents().
Полезен при больших циклах > 100000, на маленьких циклах я ProgressMeter не использую.

Код - C# [Выбрать]
  1. public class UiProgressBar : IDisposable
  2. {
  3.         private ProgressMeter progressMeter = new ProgressMeter();
  4.         private int currentValue = 0;
  5.         private readonly float step;
  6.         private int progressBarProcent = 0;
  7.  
  8.         public UiProgressBar(string message, int max)
  9.         {
  10.                 progressMeter.Start(message);
  11.                 progressMeter.SetLimit(100);
  12.                 step = max / 100f;
  13.         }
  14.  
  15.  
  16.  
  17.         public void Progress()
  18.         {
  19.                 currentValue++;
  20.                 int currentProcent = (int)(currentValue / step);
  21.                 if (currentProcent != progressBarProcent)
  22.                 {
  23.                         progressMeter.MeterProgress();
  24.                         progressBarProcent = currentProcent;
  25.                         System.Windows.Forms.Application.DoEvents();//Чтобы не зависал интерфейс
  26.                 }
  27.         }
  28.  
  29.  
  30.  
  31.         public void Stop()
  32.         {
  33.                 progressMeter.Stop();
  34.                 progressMeter.Dispose();
  35.                 progressMeter = null;
  36.         }
  37.  
  38.  
  39.  
  40.         public void Dispose()
  41.         {
  42.                 Stop();
  43.         }
  44. }


Использовать как-то так:
Код - C# [Выбрать]
  1. int blockCount = 1231354;
  2. using (UiProgressBar progressBar = new UiProgressBar("Считывание блоков", blockCount))
  3. {
  4.         for (int i = 0; i < blockCount; i++)
  5.         {
  6.                 progressBar.Progress();
  7.         }
  8. }

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #35 : 07-04-2021, 08:56:30 »
Коллеги, искренне спасибо за примеры кода.
Я попробовал оба варианта и честно говоря думал, что пример Димы Загорулькина точно заработает, но увы, у меня так и не получилось.

Хотя если отдельно и без редактирования блоков запускать  прогресс бар, то все работает
Код - C# [Выбрать]
  1.   [Rtm.CommandMethod("zzz", Rtm.CommandFlags.Session)]
  2.   static public void zzz()
  3.   {
  4.     var pm = new ProgressMeterWindow("Редактирование блоков", 30);
  5.     App.Application.ShowModelessWindow(pm);
  6.     for (int i = 0; i < 30; i++)
  7.     {
  8.       System.Threading.Thread.Sleep(1000);
  9.       pm.Increment();
  10.     }
  11.     pm.Close();
  12.   }

Видимо это какой то глюк именно моей версии или системы в целом.
Винда то установлена лет 5 назад и аптайм около месяца, потом после перезагрузки попробую еще....

Оффлайн Дмитрий Загорулькин

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Re: Создание прогресс бара
« Ответ #36 : 07-04-2021, 09:19:12 »
А вот эту тему смотрел: https://adn-cis.org/forum/index.php?topic=7829.0 ?

Оффлайн Владимир Шу

  • ADN Club
  • *****
  • Сообщений: 611
  • Карма: 155
    • ПГСу Бложик
Re: Создание прогресс бара
« Ответ #37 : 07-04-2021, 09:34:26 »
Сейчас посмотрел мельком... вечером еще посмотрю подробнее, но пока у меня нет блоков с атрибутами, пока у дин блоков нет ручек, просто набор свойств и я очень внимательно отношусь к открытию-закрытию объектов и стараюсь открывать и использовать объекты минимально возможное время и по одному за раз.