Giter Site home page Giter Site logo

miroiu / nodify Goto Github PK

View Code? Open in Web Editor NEW
1.1K 26.0 168.0 4.34 MB

Highly performant and modular controls for node-based editors designed for data-binding and MVVM.

Home Page: https://miroiu.github.io/nodify

License: MIT License

C# 100.00%
wpf node-editor mvvm controls vpl editor visual-programming flow-based-programming calculator state-machine infinite-canvas graph node-network good-first-issue panning graph-control net6 zooming node-graph

nodify's Introduction

Nodify Nodify

NuGet NuGet License C#

A collection of highly performant controls for node based editors designed for MVVM.

Tip

There is now a fantastic Avalonia port available! You can check it out here. Huge thanks to BAndysc who made this possible!

πŸš€ Examples of node-based applications

🎨 A playground application where you can try all the available settings.

Examples/Nodify.Playground

Playground

πŸŒ“ A state machine where each state represents an executable action, and each transition represents a condition for the next action to execute.

Examples/Nodify.StateMachine

StateMachine

πŸ’» A simple "real-time" calculator where each node represents an operation that takes input and feeds its output into other nodes input.

Examples/Nodify.Calculator

Calculator

πŸ“₯ Installation

Use the nuget package manager to install Nodify.

Install-Package Nodify

⭐️ Features

  • Designed from the start to work with MVVM
  • No dependencies other than WPF
  • Optimized for interactions with hundreds of nodes at once
  • Built-in dark and light themes
  • Selecting, zooming, panning with auto panning when close to edge
  • Select, move and connect nodes
  • Lots of configurable dependency properties
  • Ready for undo/redo
  • Example applications: 🎨 Playground, πŸŒ“ State machine, πŸ’» Calculator

πŸ“ Documentation

Check out the wiki and the changelog in github.

❀️ Contributing

Helping with documentation, bug reports, pull requests or anything else is very welcome.

nodify's People

Contributors

bandysc avatar brandonxu360 avatar fantasydr avatar labiej avatar makesyt avatar miroiu avatar mosayyeb-ebrahimi avatar mshumayl avatar riju-bak avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nodify's Issues

[Bug] Connected pin updates even when disconnected

Describe the bug
In the calculator application, the connected pins update even after they are disconnected.

To Reproduce
Connect and disconnect two pins, then recompute the output pin's value and the input pin will get updated.

Expected behavior
Pins should not update if disconnected.

[Application] Can you show a non-MVVM approach to this?

I don't know how to use MVVM properly and was curious on how to do this, the examples aren't really simple to understand either for a beginner (when I looked into the examples I got crushed to be honest, I don't understand one bit of the implementation).

For example, I'd begin by adding children to the NodeEditor, say my first child is "Add", this "Add" is a class that has two properties called "x" and "y" with an output property called "z", then when I add this node to the NodeEditor (by using the right click context menu) it would display a simple Node with two entry points and one exit point, this exit point then being able to be fetched with a simple "MessageBox.Show(AddNode.z);".

Can it be that simple or is the entire framework based upon MVVM principles?

[Bug] Large amount of lag with 10+ nodes.

Hi,

Having some issues with lag when adding a large amount of nodes (10+)

2023-04-18.14-34-11.mp4

Only present with nodes that have UI elements in the port (textbox, combobox, etc.). With or without bindings. It appears to only effect the camera panning which you can see in the attached video. The rest of the UI is lag free.

Version 1.7

πŸ‘‹ Who is using Nodify?

It would be nice to showcase projects that are using Nodify whether they are public or private applications.

Please include:

  • Product/Name/Title (optional)
  • Description
  • Screenshot
  • Link to website (or github repo for open source projects) (optional)

[Feature] Configurable arrowhead for connections

Describe the solution you'd like

The BaseConnection can display an arrowhead at the end of the line if ArrowSize is valid. I would like to be able to specify at which end of the line the arrow should be displayed. (possible values for ArrowHeadEnds: None, Start, End, Both).

Don't forget to add the new setting to the Playground app.

Optional: Customize the shape of the arrowhead: Arrow (default), Ellipse, Rectangle

[Feature] Open context menu when connection is dropped

Is your feature request related to a problem? Please describe.
I would like to open a context menu from a pending connection.

Describe the solution you'd like
When dropping a pending connection onto the editor's surface, a context menu should open showing all available nodes. When a node is selected, a new connection to that node should be created.

Additional context
Something similar to the context menu from UE4's Blueprint editor.

Example context menu

NodifyEditor.SelectionChanged Event always triggered when left mouse button selection a node?

Code in Nodify.Calculator, add SelectionChanged="Editor_SelectionChanged"
xaml code:

 <nodify:NodifyEditor DataContext="{Binding Current}"
                             ItemsSource="{Binding Operations}"
                             Connections="{Binding Connections}"
                             SelectedItems="{Binding SelectedOperations}"
                             ConnectionCompletedCommand="{Binding CreateConnectionCommand}"
                             DisconnectConnectorCommand="{Binding DisconnectConnectorCommand}"
                             Background="{StaticResource SmallGridLinesDrawingBrush}"
                             GridCellSize="15"
                             SelectionChanged="Editor_SelectionChanged"
                             x:Name="Editor">

xaml.cs code:

       private void Editor_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
        {
            Debug.WriteLine("Editor_SelectionChanged");
        }

image

Is there a beter way to trigger selection changed event? Thank you.

[Question] Connection Behind Grouping Nodes

Hi!
First of all: Thanks for Sharing this Awesome Node Editor!!!

Now to the question:

Somehow i Cant get the Connection to be above a Group-node.
As soon as i add a Group i Cant hover over a connection to disconnect it with ALT+Click.

This behavior is in the Playground example by grouping some nodes together and connecting them.

I know, there is an option to display all Connections on Top, but then the Connections are above every Node,
which can look weird in some cases.

Is there a way to get the connection "between" the Grouping nodes and the other nodes?

[Question] Limit inifinte panning

Hello! First of all, thank you for your amazing job with Nodify! Wanted to ask what is the best possible way of limiting panning to actual contents of the editor? I am building an application that suggests placing nodes in ordered columns with user having an option to add more columns and further add nodes in these new columns. Therefore from the start user workspace will be limited to visible screen area and zooming is disabled by design for the whole app. It would be great to be able to have horizontal and vertical scrollbars so that user would interact with the working area in the canvas-style way (horizontal and vertical scroll with scrolling stopping when we reach the end of canvas). Canvas can expand when user adds sufficient number of nodes in a column (vertical expansion) or columns (horizontal expansion).

[Question] What is the best way to save all current node state?

What is the best way to save all current node state?

I'm a beginner in programming, and your project is really helpful to me.
I think you already have in mind,
What's the best way to save and reload all of the current node state?

If you already have a solution, please let me know.

Thank you very much.

[Question]Can I change the size of the node?

Hi miroiu.

The current node will be sized according to the number of Connectors and the length of the Content, can I customize its length and width?
And can I impose more styles for nodes? For example, modify the border, modify the background color of the node?
I plan to use this for a circuit schematic display, so it would be nice to be able to replace the node Content with a graphic.

If there is a good way please tell me, thank you.

[Bug] Connections do not render correctly in TabControl

Firstly, thank you immensely for the project, this library will be used as part of an internal tool that produces Lua scripts from a node graph. It has many of my colleagues quite excited!

Describe the bug
The connection lines fail to render / update their positions correctly when the NodifyEditor is parented by a TabControl.

To Reproduce
My scenario is relatively complicated as I'm using a fork of the Avalon Dock but I have managed to reproduce it in a simpler manner.

  1. Using your calculator example has a base, replace the NodifyEditor in the MainWindow with the following (reusing the same DataContext does not appear to have any affect as the bug is still present):
<TabControl>
  <TabItem Header="Tab 1">
    <nodify:NodifyEditor ... />
  </TabItem>
  <TabItem Header="Tab 2">
    <nodify:NodifyEditor ... />
  </TabItem>
</TabControl>
  1. Run the project.
  2. Add 2 nodes the the node graph (in Tab 1) and make a connection between them.
  3. Switch to Tab 2, we are sharing a DataContext so the nodes we added in Tab 1 will appear. You can correctly move a node in this graph and the connection will adjust correctly.
  4. Switch back to Tab 1. The connection now stays in place and does not update when either node is moved.

Expected behavior
The connections should continue to render / update when switching tabs.

Please let me know if there's any additional information I could provide to help reproduce the issue. Thanks again!

[Question] Sorting the graph

Hello I am using this project to generate a node graph of function calls in a C# project. Being that all the code is generated and not manually created by the user I was wondering if there is a simple way to sort / organise the nodes like in a Code Map where you can align everything from Left -> Right or Top -> Bottom etc.

Like this example from Visual Studio's Code Map.
image

[Feature] Different shapes and colors of connectors. Also: Only connect matching connectors

Is your feature request related to a problem? Please describe.

I plan to renew my feeder for vJoy and release this as open source. vJoy is a virtual gaming device which is controlled by a so called feeder. For Example there is Joystick Gremlin
I allready have my own, fully working feeder which i did not release because of the complex configuration i have done. Joystick Gremlin is till now easier :)
NodeRed like virtual programming seems a good solution for such a configuration task.
While seaching for a NodeRed like programming interface for c# i come to nodify, which is by the way a very cool project.

The only thing what is missing to me is to use different shaped and colored connectors.

Let me explain why:
I want that the user can only connect bool outputs to bool inputs, POV outputs to POV inputs and Axis outputs to Axis inputs. Converting between them should be done in different nodes.
So the user should see which connectors can be connected on the shape (or the color) of the connector.

Describe the solution you'd like
For me it would be enought to have the circle shape, a recangle shape and a triangle shape for an connector and only allow connections with the same type.

But i know this is only my need. So i would suggest to implement a system which can be used by other programmers.
For example an property in the connector which is defining its shape and another property whis is defining its color. But this will be override theming. I know :( Any solutions in mind for that?

Also i suggest another property in the connector which define to which other connector it can be connected. For example: the output connector has an int property with the value 2. The input connector in this example has this property set to 3. A bitwise AND is bigger then zero so this two connectors can be connected.
Second example: output connector property is 2 and input connector is 1. Bitwise AND is 0, so this two connectors can not match.

The color of a connection of two matching connectors should be the color the output connector.

Describe alternatives you've considered
I hope i made it clear in my solution. If not ask.

Additional context
I don't have any exaple pictures yet

[Bug] In Calculator example, connecting a node to the input of another node doesn''t replace the existing connection visually

Describe the bug
Say I have an add node, and in the top input I have pi connected. If I have another node, lets say a subtract node, and drag from the subtract output to the top input on add, then logically it replaces the pi node being input (all values now represent the subtract node entering at top input), however the pi node is visually also still connected to that same top input

To Reproduce
Drag 3 nodes out. Connect one node to another, and then with the third connect that output to the already used input on one of the other 2 nodes

Expected behavior
Visually, the old input gets disconnected

Screenshots

Additional context
Saw this project from Discord, Will be using it as soon as my base project template is sorted, have a few more things to do. Have an old project in dire need of a V2, and this is perfect for it.

[Docs] Generate API Reference on build action

Describe the issue
Find a way to generate API Reference from XML comments in the build pipeline (GitHub action) and upload it to the wiki.
Please make sure that documentation is generated only for types inside Nodify.dll and that links to WPF classes are pointing to the Microsoft documentation (e.g. references to FrameworkElement will point to this https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement?view=windowsdesktop-6.0)

Additional context
The API Reference was previously generated manually with a custom tool and uploaded to the website but that tool had multiple issues and it could not be used in a pipeline.

[Bug] Changing BorderThickness will cause an exception to be displayed when nodes are selected

Describe the bug
If BorderThickness is changed to a value not equal to 1, it will cause an abnormal display when the node is selected. If it is selected, the node will move slightly
Modifying BorderBrush will cause SelectedBrush to be modified together and the SelectedBrush modifications to be invalid
It is also not possible to change the Selected BorderThickness
To Reproduce
change ItemContainer style

Expected behavior
No displacement

Screenshots
image
image

Additional context
Add any other context about the problem here.

Hierarchical graph layout

=> just saw Group Node after suggesting this.

Grouping Node

Describe the solution you'd like
When the number of nodes getting too many, it would make sense to group them into groups each represented by a SINGLE Group Node BUT same size has single Non-group node. When click on it, it expands OR a side window appears, showing the nodes layout of the nodes within the clicked module.

image

Excellent work!!!

[Bug] Cannot bind a Context Menu to Input/Output Connector

Describe the bug
I created a ConnectorViewModel with ContextMenu, and try to bind to ContextMenu of nodify:NodeInput in EditorView, but it can't be open.

To Reproduce

EditorView.xaml

<!--...-->
<nodify:Node.InputConnectorTemplate>
    <DataTemplate DataType="{x:Type localConnectors:IConnectorViewModel`1}">
        <nodify:NodeInput
            ...
            ContextMenu="{Binding ConnectorContextMenu}"
            ...
            >
<!--...-->

ConnectorViewModel.cs

using System.Windows.Controls
using CommunityToolkit.Mvvm.ComponentModel;

public interface IConnectorViewModel : IObservableObject
{
  //...
  ContextMenu? ConnectorContextMenu { get; set; }
  //...
}

public class ConnectorViewModel : ObservableRecipient, IConnectorViewModel 
{
  //...
  protected ContextMenu? connectorContextMenu = new();
  public virtual ContextMenu? ConnectorContextMenu { get => this.connectorContextMenu; 
      set => SetProperty(ref this.connectorContextMenu, value); }

  protected ObservableCollection<object> connectorContextMenuItems
      = new ObservableCollection<object>();
  public virtual ObservableCollection<object> ConnectorContextMenuItems {
      get => this.connectorContextMenuItems;
      set => SetProperty(ref this.connectorContextMenuItems, value); }
  //...
  public ConnectorViewModel()
  {
    var menuItem = new MenuItem() { 
        Header = "TheMenuItem",
        Name = "TheMenuItem" };
    this.ConnectorContextMenuItems.Add(menuItem);
    this.ConnectorContextMenu = new() { ItemsSource = this.ConnectorContextMenuItems };
  }
  //...
}

public interface IConnectorViewModel<T> : IConnectorViewModel
{
  //...
}

public class ConnectorViewModel<T> : ConnectorViewModel
  , IConnectorViewModel<T>
{
  //...
}

Expected behavior
You can create a NodeViewModel and add a ContextMenu and a ContextMenuItems, and bind to the nodify:Node in EditorView.
NodeContextMenuTriggered

Examples don't work with nuget packages

Describe the bug
When I try to build the examples, using the nuget package -- rather than a local build of the nodify csproj -- I get many xaml namespace errors. Reading the change log this appears to be from the repository having been updated since the last nuget package release to provide the namespace support.

Requested Fix
I'd like to ask for a new nuget package release. Thank-you!

Collaboration and Tree View

It would be nice if i can host the app on a server so few of my friends edit the graphs collaboratively. And it would also be nice if the app has some sort of tree view showing packages and the graphs within the server.

[Feature] Configurable arrowhead shape

The BaseConnection can display an arrowhead in the specified position by ArrowEnds if ArrowSize is valid. I would like to be able to customize the shape of the arrowhead. (possible values for ArrowHeadShape: Arrow, Ellipse, Rectangle).

Don't forget to add the new setting to the Playground app and to the changelog.

[Application] Add more examples.

Can you make some examples of minimal use of nodify, the current three examples are a bit too hard to understand for beginners. Thanks.

[Docs] API docs

Describe the issue

It might be useful to consider using a tool to generate API docs from XML comments.
This way it is possible to generate an API reference like Microsoft.

By including the generated xml file in the NuGet the XML comment will also be available for intellisense.

The main 'problem' I see right now is how to integrate it with the current documentation.

Screenshots

n/a

Additional context

n/a

[Feature] Configurable editor key-bindings

Is your feature request related to a problem? Please describe.
It's possible but very hard to override the default keybindings for panning, selecting, etc.

Describe the solution you'd like
I would like to have the possibility to change the default keybindings.

Additional context
WolvenKit

[Bug] Connections can be created cross graphs

Describe the bug
It is possible to connect nodes to child graphs, which is not intended and will result in bad Anchor calculations.

To Reproduce
Add an operation graph in the calculator example and connect a node from the root graph to another node inside the operation graph.

Expected behavior
I should not be able to create connections across graphs.

Screenshots
In this example, the output of the Add node is connected to the input of the Output parameters nodes inside the Operations graph.

image

[Bug] "Input Parameters" node does not remove connections when pins are removed.

Describe the bug
"Input Parameters" node does not remove connections when pins are removed.

To Reproduce
Create a Calculator node, open it, press the "+" button in the generated "Input Parameters" node, connect the new pin to another node and press the "-" button to remove the pin.

Expected behavior
Connection is removed when the pin is removed.

Screenshots

screenshot

[Feature] Generate settings panel from view model

Is your feature request related to a problem? Please describe.
Adding new entries to the Playground's settings panel is error-prone and it's really hard to change their order.

Describe the solution you'd like
The settings panel could be generated based on a view model that has a list of available settings.

Additional context
The settings panel in Nodify.Playground
The settings panel

Text on Lines

Hi ,
Is it possible to insert text on the connecting lines between nodes?
I didn't see any preview of this feature in the demos that have been showcased.
Would it be too difficult to implement this, if its not implemented yet, could you let me know what part of the code needs to be modified to implement this feature?

[Bug] Unable to dynamically switch themes

Describe the bug
When I modify the theme by modifying the dictionary theme path, some controls cannot be changed
image
Light to Dark
image
Dark to Light
image

After reopen
image

To Reproduce


        Collection<ResourceDictionary> applicationDictionaries = Application
            .Current
            .Resources
            .MergedDictionaries;

        if (applicationDictionaries.Count == 0)
        {
            return;
        }

        for (var i = 0; i < applicationDictionaries.Count; i++)
        {
            string sourceUri;

            if (applicationDictionaries[i]?.Source != null)
            {
                sourceUri = applicationDictionaries[i].Source.ToString().ToLower().Trim();

                if (sourceUri.Contains("pack://application:,,,/nodify;component"))
                {
                    switch (theme)
                    {
                        case ApplicationTheme.Dark:
                            applicationDictionaries[i] = new()
                            {
                                Source = new Uri("pack://application:,,,/Nodify;component/Themes/Dark.xaml",
                                    UriKind.Absolute)
                            };
                            break;
                        case ApplicationTheme.Light:
                            applicationDictionaries[i] = new()
                            {
                                Source = new Uri("pack://application:,,,/Nodify;component/Themes/light.xaml",
                                    UriKind.Absolute)
                            };
                            break;
                    }


                    break;
                }
            }
        }

Expected behavior
A clear and concise description of what you expected to happen.
image
to
image
Screenshots
If applicable, add screenshots to help explain your problem.

Additional context
Add any other context about the problem here.

[Question] StartedCommand CanExecute is not working

Hi @miroiu. I am trying to restrict a connector to having only one outgoing connection by checking if it is already connected and returning a false from CanExecute.

StartConnectionCommand = new RelayCommand<ConnectorViewModel>(source => PendingConnection.Source = source!, model => !model.IsConnected);

But this is not working. Am I doing it the correct way?

NETFramework

NuGet package not installing for NETFramework

Connector as INOUT? [Question]

Is there a possibility that a connector is a destination for one input and the source for many connections at the same time?

[Question] Bring the selected node on top?

Hi. Is it possible to bring the selected node on top? Currently the last added node comes on top of all other nodes and if you select others they are behind the last added one.

[Feature] Add multiple tabs to the calculator app

Is your feature request related to a problem? Please describe.
It would be good to have an example of how to use multiple editors in an application.

Describe the solution you'd like
Use a tab control with a 'plus' button like in Chrome to create a new tab. The new tab will contain an empty editor instance.

Optional
Add a way to close the tabs. Suggestions:

  • a 'close' button present in each tab
  • a key-binding like CTRL+W
  • a context menu with a close command

[Question] Is it possible to snap selection rectangle

Hello. I was wondering if it is possible to make the selection rectangle snap to the/a grid? Doing an image editor currently, and I'm trying to get it to snap to the pixels but it seems like it is not possible currently? Not that experienced with WPF yet. :)

Also, is it possible to use the selection as a dependencyobject? I'm trying to do marching ants on the selection as well.

WolvenKit for modding CyberPunk 2077

Hi miroiu

The devs of WolvenKit are exploring implementing Nodify as a node based component for the open sourced application.
It’s a modding application for Cyberpunk 2077 and similar RED engine based games.
While the user base for this application is in the several 1000s, and very active, the devs are all volunteers. Keen to reach out and see if you could advise/support us?
See https://github.com/WolvenKit

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.