Создание C# WPF ModelVisual3D занимает слишком много времени и не может быть выполнено в отдельном потоке

У меня есть проект WPF (VS2010, .NET4.0), в котором я создаю довольно большой объект ModelVisual3D (чтение из файла пользовательского формата STL, обработка информации, создание сетки и т. д.). Это занимает около 3-4 секунд. создаться и еще 2-3 сек. сделать mainViewport.Children.Add(ModelVisual3D). Я делаю все это в пользовательском классе и вызываю этот метод:

 class My3DModel
{
...
        public MyModelVisual3D createModelVisual3D(MyTypes tType, int tNumber)
            {
                this.myModelVisual3D = new MyModelVisual3D(tType, tNumber);
                for (int i = 0, j = 0; i < this.Triangles.Length; i++)
                {
                    this.mesh.Positions.Add(this.Triangles[i].Vertex1);
                    this.mesh.Positions.Add(this.Triangles[i].Vertex2);
                    this.mesh.Positions.Add(this.Triangles[i].Vertex3);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.Normals.Add(this.Triangles[i].Normal);
                    this.mesh.TriangleIndices.Add(j++);
                    this.mesh.TriangleIndices.Add(j++);
                    this.mesh.TriangleIndices.Add(j++);
                }
                this.model3DGroup.Children.Add(new GeometryModel3D(this.mesh, material));
                this.myModelVisual3D.Content = this.model3DGroup;
                return this.myModelVisual3D;
            }
}

Возвращаемое значение также является созданным мной пользовательским классом:

class ToothModelVisual3D : ModelVisual3D
{
    //VARIABLES
    private MyTypes myType;
    private int number;

    //OPERATORS
    public MyTypes MyType
    {get { return myType; } set { myType = value; }}

    public int Number
    {get { return number; } set { number = value;}}

    public ToothModelVisual3D() { }

    public ToothModelVisual3D(MyTypes tType, int tNumber) { MyType = tType; Number = tNumber; }
}

Все, что я хочу сделать, это следующее один раз в начале программы:

{
        My3DModel myModel;
        myModel = new My3DModel();
        myModel.readFileBytes("C:\\registered\\" + 1 + ".stl");
        myModel.loadTriangles();
        mainViewport.Children.Add(myModel.createModelVisual3D(MyTypes.Sometype, 1);
}

Если я делаю это в основном потоке, пользовательский интерфейс зависает. Если я делаю это в рабочем потоке и вызываю mainViewport.Children.Add(...), он говорит, что не может получить доступ к ресурсам, созданным в этом рабочем потоке. Помощь?!

Насколько я понимаю, я достиг точки, когда у меня есть два потока и ресурсы, принадлежащие каждому из них (mainViewport => UIThread & myModel => WorkerThread). Ни один из потоков не может получить прямой доступ к другому ресурсу, но создание и использование myModel в UIThread приводит к его зависанию... Все, что я хочу сделать, это иметь достаточную отзывчивость пользовательского интерфейса, поэтому пользователь может свернуть программу, ожидая загрузки моделей. , ничего больше. Как я могу это сделать? Есть ли способ выполнить всю тяжелую работу ЦП в UIThread, чтобы не возникало конфликтов ресурсов и чтобы рабочий поток обрабатывал только пользовательский интерфейс в это время?

PS: я пробовал с классами Thread, BackgroundWorker и Task<TResult>. Результаты были схожими, если не сказать одинаковыми.

PPS: Полная версия будет загружать массивные модели, которые будут загружаться более 30-40 секунд...


person mandarin    schedule 26.09.2013    source источник
comment
Кстати, я не могу найти, как реализовать async/await. Если я пишу это в проекте, VS говорит, что такого нет.   -  person mandarin    schedule 26.09.2013


Ответы (3)


Недавно я столкнулся с той же проблемой при переносе приложения XNA на WPF. В моем случае я частично решил эту проблему, используя фоновый поток для загрузки позиций, нормалей и индексов из файла. Затем в том же потоке создайте поток памяти, содержащий XAML для Model3DGroup с GeometryModel3D и MeshGeometry3D.

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

Model3DGroup model = System.Windows.Markup.XamlReader.Load(memoryStream) as Model3DGroup;

Задержка все еще есть, но поскольку доступ к файлам осуществляется в фоновом потоке, она не такая серьезная.

person David    schedule 05.02.2015

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

    delegate void myDelegate();

    private void fileOpenButton_Click(object sender, RoutedEventArgs e)
    {
        try
        {
            Thread ViewportLoaderThread = new Thread(loadViewportItemsAsync);
            ViewportLoaderThread.IsBackground = true;
            ViewportLoaderThread.Start();
        }
        catch (Exception err) { UtilsProgram.writeErrorLog(err.ToString()); }
    }

    private void loadViewportItemsAsync()
    {
        try
        {
            //TRY to browse for a file
            if (!browseForFile()) return;

            Dispatcher.Invoke(new Action(() => { myStatusBar.Visibility = System.Windows.Visibility.Visible; menuItemHelpDemo.IsEnabled = false; }), null);

            //Load file, unpack, decrypt, load STLs and create ModelGroup3D objects
            UtilsDen.DenModel = new DenLoader(UtilsDen.Filename, UtilsDen.Certificate, UtilsDen.PrivateKey, this);

            //Load the models to viewport async
            myDelegate asyncDel = new myDelegate(sendModelsToViewportAsync);
            this.Dispatcher.BeginInvoke(asyncDel, null);
        }
        catch (Exception err) { MessageBox.Show(UtilsProgram.langDict["msg18"]); UtilsProgram.writeErrorLog(err.ToString()); }
    }

    private void sendModelsToViewportAsync()
    {
        for (int i = 0; i < UtilsDen.DenModel.StlFilesCount; i++)
        {
            //Add the models to MAIN VIEWPORT
            ModelVisual3D modelVisual = new ModelVisual3D();
            GeometryModel3D geometryModel = new GeometryModel3D();
            Model3DGroup modelGroup = new Model3DGroup();

            geometryModel = new GeometryModel3D(UtilsDen.DenModel.StlModels[i].MeshGeometry, UtilsDen.Material);

            modelGroup.Children.Add(geometryModel);
            modelVisual.Content = modelGroup;
            mainViewport.Children.Add(toothModelVisual);
        }
    }

Ключ был в том, чтобы использовать this.Dispatcher.BeginInvoke(asyncDel, null);, так как он работает в основном потоке, но не отстает от него, потому что выполняется асинхронно.

person mandarin    schedule 05.02.2015

Использование делегата по-прежнему приводит к задержке в пользовательском интерфейсе, лучшим решением является создание модели в рабочем потоке, а затем ее замораживание. Затем модель может быть клонирована потоком пользовательского интерфейса без раздражающего исключения. Это работает для меня с моделями, которые загружаются 25 секунд или более. Единственная проблема, которую я обнаружил, заключается в том, что это не работает, если модель содержит текстуру.

person user4531448    schedule 13.05.2015