Problem with C# and WPF

Hi there!
I was kind of forced to re-write my python code with C# since I failed to run non-modal WPF window with xaml file. If it’s modal - everything works fine, but since I need user to make changes in Revit while script is running - I need non-modal window. I tried everything that I could find - EventHandler mostly, but just couldn’t make it work. Revit was crushing every time when I tried to look up some values, or even open PyRevit console by printing something.
So I decided to re-write everything with C# in order to achieve more control over EventHandlers. Pretty new to C#, but I know a thing or two. 80% of code was piece of cake, but then it was time for xaml and wpf handling… Yup. I can Load xaml file, and I can start wpf Window with it. But I just can’t provide data to my DataGrid since I can’t achieve access to this component.
With Python it was kinda straight forward - self.MyDataGrid… and so on.
With C# I just can’t access anything. Maybe (probably) it’s my lack of knowledge - what type of classes and functions to use in C#. Below I will share simplified version of my code, and just hope that someone can help me with this.

And yes - I know that proper way to do things is to create full-blown C# project in Visual Studio, with code-behind etc, but I really need this script to be part of my PyRevit extention. Any help will be highly appreciated!

using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Markup;
using System.Collections.Generic;
using System.Linq;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.UI.Events;
using Autodesk.Revit.DB.Events;
using System.Windows.Data;


namespace Checklist
{
    [Transaction(TransactionMode.Manual)]
    public class Class1 : IExternalCommand
    {
        public Result Execute(
            ExternalCommandData commandData,
            ref string message,
            ElementSet elements)
        {

            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Document doc = uidoc.Document;
			
			
			// Here I am getting some data from CSV file and putting it to list of Class objects - dataList

			// Here I will call Window.Show() from WpfInit Class, passing dataList to it
			return Result.Succeeded;
            
        }
    }
	
	public partial class WpfInit
	{
		public static void init(List<RowItem> dataList)
		{
			try
            {	
				// here I am putting data to ListCollectionView to be able to group it 
				ListCollectionView finalList = new ListCollectionView(dataList);
				PropertyGroupDescription groupList = new PropertyGroupDescription("type");
				finalList.GroupDescriptions.Add(groupList);

				FileStream xamlFile = new FileStream("ui.xaml", FileMode.Open, FileAccess.Read);
				Window win = XamlReader.Load(xamlFile) as Window;
				win.Show();
				
				//self.MyDataGrid.ItemsSource = finalList   <-- string from Python code, and I need to access DataGrid somehow
            }

            catch (Exception err)
            {
                TaskDialog.Show("Error", err.Message);
            }
		}
	}
}

Hi Boris,

I tried to run the wpf in the same way with c# and unfortunately it did not work. Can you tell me more about how you did it?

// -- coding: UTF-8 --

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Markup;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Data;
using System.Xaml;

using pyRevitLabs;
using pyRevitLabs.NLog;
using PyRevitLabs.PyRevit.Runtime;
using System.Reflection;

namespace ND_018_ParameterFuellen.pushbutton
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class Parameter_Fuellen_scr : IExternalCommand
    {
        public ExecParams execParams;
        private Logger logger = LogManager.GetCurrentClassLogger();
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Document doc = uidoc.Document;

            // Access current selection
            //string path = "C:/Users/Nikolai Davydov/Desktop/Meine Ornder/GitHuB/REHUB_pyRevit_Dynamo/R_D.extension/R_D.tab/Niko.panel/ND_018_ParameterFuellen.pushbutton/";
            //Assembly.LoadFrom(path);
            //logger.Info("Logger works...");
            //logger.Debug("Logger works...");
            // Retrieve elements from database
            var BuiltInCategories = new Dictionary<BuiltInCategory, string>()
            {
                { BuiltInCategory.OST_Walls, "Wände"},
                { BuiltInCategory.OST_Roofs, "Dächer"},
                { BuiltInCategory.OST_Ceilings, "Decken"},
                { BuiltInCategory.OST_Windows, "Fenster"},
                { BuiltInCategory.OST_Doors, "Türen"},
                { BuiltInCategory.OST_Columns, "Stützen"}
            };
            //BuiltInCategory test = BuiltInCategory.OST_Walls;
            
            List<string> list = new List<string>() { "KategoryAuswaehlen_ui.xaml", "test" };
            //wpfInit.Init(list);
            //KategoryAuswaehlen kategoryAuswaehlen = new KategoryAuswaehlen(BuiltInCategories);
            //ListBiC listBiC = 
            //kategoryAuswaehlen.ShowDialog();
            /*List<BuiltInCategory> ListBuiltInCategories = new List<BuiltInCategory>(BuiltInCategories);

            

            FilteredElementCollector newFindCategory = new FilteredElementCollector(doc)
                                                .WhereElementIsNotElementType()
                                                .OfCategory(ListBuiltInCategories[0]);
            ICollection<Element> collector = newFindCategory.ToElements();

            // Filtered element collector is iterable

            foreach (Element e in collector)
            {
                Debug.Print(e.Name);
            }
            */
            // Modify document within a transaction

            using (Transaction tx = new Transaction(doc))
            {
                tx.Start("RE_Parameter füllen");

                TaskDialog.Show("Test", execParams.ScriptPath);

                tx.Commit();
            }
            return Result.Succeeded;
        }
        public partial class WpfInit
        {
            public static void Init(List<string> dataList)
            {
                try
                {
                    dataList.Add("TTTTEEEEST");
                    Init(dataList);

                    FileStream xamlFile = new FileStream("KategoryAuswaehlen_ui.xaml", FileMode.Open, FileAccess.Read);
                    Window win = (Window)System.Windows.Markup.XamlReader.Load(xamlFile);
                    win.Show();

                    //self.MyDataGrid.ItemsSource = finalList   <-- string from Python code, and I need to access DataGrid somehow
                }
                catch (Exception err)
                {
                    TaskDialog.Show("Error", err.Message);
                }
            }
        }
    }
}

Hi! Can you please share more info? Maybe what kind of eror message did you get.
I noticed that in case with C# pyRevit won’t read XAML file right from the script’s directory, but rather from current working Revit directory (something like this, mine was looking for xaml in last saving directory). I guess it’s a must to put a full proper file path to xaml in the script

I get this error that the xalm file cannot be found. Unfortunately I’m new to C# and don’t know how to add a file path.

I think you can put full path right in FileStream, like so, but you need to test it since I’m not sure:

FileStream xamlFile = new FileStream("C:\..\..\KategoryAuswaehlen_ui.xaml", FileMode.Open, FileAccess.Read);
                    Window win = (Window)System.Windows.Markup.XamlReader.Load(xamlFile);
                    win.Show();

unfortunately it did not work. it still does not see the file path

        public partial class WpfInit
        {
            public static void Init(List<string> dataList)
            {
                try
                {
                    dataList.Add("TTTTEEEEST");
                    Init(dataList);

                    FileStream xamlFile = new FileStream("C:\\Users\\Nikolai Davydov\\Desktop\\Meine Ornder\\GitHuB\\REHUB_pyRevit_Dynamo\\R_D.extension\\R_D.tab\\Niko.panel\\ND_018_ParameterFuellen.pushbutton\\KategoryAuswaehlen_ui.xaml", FileMode.Open, FileAccess.Read);
                    Window win = (Window)System.Windows.Markup.XamlReader.Load(xamlFile);
                    win.Show();

                    //self.MyDataGrid.ItemsSource = finalList   <-- string from Python code, and I need to access DataGrid somehow
                }
                catch (Exception err)
                {
                    TaskDialog.Show("Error", err.Message);
                }
            }
        }

Hi alllglg,

I am also loading wpf in pyrevit my solution for your reference.

Compil all necessary method and WPF in an dll.
Prepare the pyrevit script by using the method from this dll.

hi Zepeng!
also i can create script.dll and it would be work as normal script?

Hi alllglg,

I mean u can using external dll reference for your script.cs. the actual method just put it to the reference dll

Hi Zepeng,

i tried to add external .dll for my script but it dont work. Unfortunately, I am still new to C#.
this is my code

// -- coding: UTF-8 --

using Autodesk.Revit.ApplicationServices;
using Autodesk.Revit.Attributes;
using Autodesk.Revit.DB;
using Autodesk.Revit.UI;
using Autodesk.Revit.UI.Selection;
using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Markup;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Data;
using System.Xaml;

using pyRevitLabs;
//using pyRevitLabs.NLog;
//using PyRevitLabs.PyRevit.Runtime;
using System.Reflection;


namespace ND_018_ParameterFuellen.pushbutton
{
    [Transaction(TransactionMode.Manual)]
    [Regeneration(RegenerationOption.Manual)]
    public class Parameter_Fuellen_script : IExternalCommand
    {
        //public ExecParams execParams;
        //private Logger logger = LogManager.GetCurrentClassLogger();
        public Result Execute(
          ExternalCommandData commandData,
          ref string message,
          ElementSet elements)
        {
            UIApplication uiapp = commandData.Application;
            UIDocument uidoc = uiapp.ActiveUIDocument;
            Autodesk.Revit.ApplicationServices.Application app = uiapp.Application;
            Document doc = uidoc.Document;

            AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;

            
            var BuiltInCategories = new Dictionary<string, BuiltInCategory>()
            {
                { "Wände", BuiltInCategory.OST_Walls},
                { "Dächer", BuiltInCategory.OST_Roofs},
                { "Decken", BuiltInCategory.OST_Ceilings},
                { "Fenster", BuiltInCategory.OST_Windows },
                { "Türen", BuiltInCategory.OST_Doors},
                { "Stützen", BuiltInCategory.OST_Columns}
            };

            using (Transaction tx = new Transaction(doc))
            {
                tx.Start("RE_Parameter füllen");
                //execParams.ExecutedFromUI;
                //KategoryAuswaehlen kategoryAuswaehlen = new KategoryAuswaehlen(BuiltInCategories, doc);
               // kategoryAuswaehlen.ShowDialog();
                //string test = kategoryAuswaehlen.DropBox.SelectedItem.ToString();
                //ParameterAuswaehlen parameterAuswaehlen = new ParameterAuswaehlen();


                tx.Commit();
            }
            return Result.Succeeded;
        }

        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            string dllLocation = "C:\\Users\\Nikolai Davydov\\Desktop\\Meine Ornder\\GitHuB\\REHUB_pyRevit_Dynamo\\R_D.extension\\R_D.tab\\Niko.panel\\ND_018_ParameterFuellen.pushbutton\\bin\\Debug";
            string dllPath = Path.Combine(dllLocation, args.Name.Split(',')[0] + ".dll");
            //Console.WriteLine(dllPath);
            if (File.Exists(dllPath))
            {
                return Assembly.LoadFrom(dllPath);
            }
            else return null;
        }
    }
}

But everything works fine if I run it through the add-in manager

I did it. I will also tell you for others, if you have the same problems.
First you need to compile your code into a .dll and place it in the bin folder.

Screenshot 2022-12-21 135318

In the panel folder create a .invokebutton folder (NOT a .pushbutton) and a bundle.yamle file and just point assembly there

title: Test Direct Invoke (Custom Title)
tooltip: Test Direct Invoke Tooltip
context: zero-doc
author: "{{author}}"
highlight: new
assembly: ND_018_ParameterFuellen.pushbutton.dll
command_class:
2 Likes

Ugh, looks interesting! I will try it as soon as I can get to programming
Thanks for your research

It works! Wow, I see vast possibilities here