ZeroTouch Node with WPF

Does anyone have any experience with WPF node creation?

So far I have my nodes coming in and working great. Problem is, the import of this whole windows library. As far as I can tell, there isn’t anywhere to make this class invisible in Dynamo library.

Hi @john_pierson have you tried the following:

  1. Firstly, without seeing your code, have you added the[IsVisibleInDynamoLibrary(false)] attribute above any public methods you are compiling but want to hide (that said the problem, since this is a library you’ve not created, is unlikely to apply to this)?

  2. Have you created a default constructor for your class?

  3. Are you creating multiple cs files for one class or creating seperate helper classes that are referenced in the cs that generates your node? If so, try combining them into one cs. I had the same problem recently and found this was the only solution

1 Like

I’ve added the false attribute everywhere I could.

Let me check it out. I have separate classes for the WPF items.

Thanks @Thomas_Mahon! I will report back

I put everything in one class and it didn’t change the import of the whole system category.

I did manage to get the select method to stop showing up by marking it abstract.

Here’s a peak at my code, (Be gentle I am a noob at this for sure. :wink:)

###My Selection Class
using Autodesk.DesignScript.Runtime;
using Dynamo.Graph.Nodes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace Beaker
{
    public abstract class Selection
    {
        /// <summary>
        /// This node will take an input list and allow you to choose items in it.
        /// Compatible with Dynamo Player.
        /// </summary>
        /// <param name="list">Input list</param>
        /// <returns name="selectedItems">The user selected items.</returns>
        /// <search>
        /// Pick, UI, Filter
        /// </search>
        public static List<Object> PickItemsFromList (List<Object> list)
        {
            List<Object> listReturn = new List<object>();
            PickFromList window = new PickFromList();

            //selectList
            window.listBox.ItemsSource = list;


            var res = window.ShowDialog();  
            if (!res.HasValue && res.Value)
                window.Close();

            var data = window.listBox.SelectedItems;
            foreach (object element in data)
            {
                listReturn.Add(element);
            }
            return listReturn;
        }
    }
}

###And the class for my XAML

using Autodesk.DesignScript.Runtime;
using System.Windows;
using System.Windows.Controls;

namespace Beaker
{
    [IsVisibleInDynamoLibrary(false)]
    /// <summary>
    /// Interaction logic for PickFromList.xaml
    /// </summary>
    public partial class PickFromList 
    {
        internal PickFromList()
        {
            InitializeComponent();
        }

        private void accept_button_Click(object sender, RoutedEventArgs e)
        {
            DialogResult = true;
        }
        private void listBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }

    }
}
2 Likes

Hi @john_pierson

I think you’re using the Revit API UI methods which I’m not familiar with! I’m not near a laptop at the moment and I need one to examine your code. Nevertheless, I can explain the approach I take which uses the WPF view model and model approach with bindings which might give you a steer:

Setup
1.Get the latest nuget packages installed. If you search for “Dynamo visual” and install DynamoVisualProgramming.WpfUILibrary it will install all the required nuget packages.

2.Add the RevitAPI, RevitAPIUI, Revit Services and Revit Nodes dll to your references

Creating the node
I create a new class - the view model - and create a default constructor. The [IsDesignScriptCompatible] attribute has to be added to every class and the IsValid() method has to be set to true otherwise it wont work (the below is just a snippet and wont work by itself…there’s some help classes which support it!):

using System.Collections.Generic;
using Bimorph.Nodes.Infrastructure;
using Dynamo.Graph.Nodes;
using Variable = Bimorph.Nodes.Infrastructure.Variable;

namespace DropDown
{
    [NodeName("Dropdown")]
    [OutPortNames("Selected Category")]
    [OutPortTypes("string")]
    [IsDesignScriptCompatible]
    public class DropDownViewModel : DynamoNodeViewModelBase<DropDownView>
    {
        //The list of the drop down items
        //It doesn't need to call the OnNodePropertyChange() method
        //if it doesn't change during program execution
        public List<string> DropDownItems { get; set; } = new List<string> { "Sheets", "Views", "Elements" };

        private string selectedDropDownItem = "Sheets";
        //The selected drop down item
        public string SelectedDropDownItem
        {
            get { return selectedDropDownItem; }
            set
            {
                selectedDropDownItem = value;
                //When the value is set this method notifies
                //the node about the change
                OnNodePropertyChange();
            }
        }

        //When this method returns true the BuildAssignments method will run
        protected override bool IsValid()
        {
            return true;
        }

        //Makes the assignements
        //variable = expression
        //in our case OUT[0] = SelectedDropDownItem
        protected override Dictionary<Variable, Expression> BuildAssignments(NodeInputs IN, NodeOutputs OUT)
        {
            return new Dictionary<Variable, Expression>
            {
                { Variable.FromOutput(OUT[0]), Expression.FromPrimitiveValue(SelectedDropDownItem) }
            };
        }
    }
}

Next, I create the view by adding a new XAML file to my project and create the bindings to the view model:

<UserControl x:Class="DropDown.DropDownView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" 
             Width="200" Height="100"
             >
    <StackPanel>
        <ComboBox Height="20" ItemsSource="{Binding DropDownItems}" SelectedItem="{Binding SelectedDropDownItem}"></ComboBox>
    </StackPanel>
</UserControl>

Finally, I compile and load the new node into the project:

4 Likes

Also, one other tip to add - install ReSharper, its awesome and will help you to format your syntax and it provides hints when it comes to calling the relevant methods to structure your code https://www.jetbrains.com/resharper/

1 Like

@Thomas_Mahon you are awesome man. Let me give this a try shortly. I think part of the hangup on my end, is I am forcing this as a popup for Dynamo Player.
Here is my node in action, :slight_smile:

4 Likes

I will check this out as well. Thanks again man!

__To quote @solamour "Learning is fun! :smile: "

1 Like

@john_pierson Awesome work, I’m really liking the icon/graphics too! UI is a pain tbh - give me a double curved roof structure to rationalise any day, its easier! @solamour has hit the nail on the head there! Ultimately, its that ability to keep going regardless of the challenge and to enjoy it no matter what that gets you to “superstar” status. Good luck with it all

1 Like

I think the custom node approach eliminates the problem because with that you have to introduce a secondary assembly and you can then keep all your references to WPF in that second assembly (away from the primary ZTN assembly which is loaded when Dynamo starts and builds the tree). My understanding from trying to delete dll files when Dynamo is running is that the primary ZTN assembly gets loaded when Dynamo starts, whilst the secondary one doesn’t.

I also noticed recently there is an option when you are publishing a dll file to mark it as a node library, not sure if that was on the 1.2 version (it was too subtle for me to notice).

Hi,

Is it possible to send the selected item real time to the output without pressing accept(while the script is still running), or by means of a button that does not close the UI window.