0

As many have observed with MVVM, you do not want the View and Model to be directly coupled. Therefore, I have my View bound to an ObservableCollection in my ViewModel which needs to be updated from a List in my Model.

What I have tried to do is create the CanvasObject in the CanvasModel, add it to the List, and send a reference, via events, to the ViewModel and update the ObservableCollection there.

The following code and explanation will hopefully illustrate my attempt:

In my View, I have an ItemsControl with the ItemSource binding to my ObservableCollection with an ItemsPanelTemplate pointing to a Canvas.

<ItemsControl ItemsSource="{Binding CanvasObjects}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Width="1000" Height="1000" Background="GhostWhite"
                    MouseDown="Canvas_MouseDown"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

To allow a mouse click, I added a MouseDown event

public partial class CanvasView : UserControl
{
    dynamic viewModel;

    public CanvasView()
    {
        InitializeComponent();
        Loaded += InitializeViewModel;
    }

    private void InitializeViewModel(object sender, RoutedEventArgs e)
    {
        viewModel = DataContext;
        Loaded -= InitializeViewModel;
    }

    private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
    {
        viewModel.MouseDownEventHandler(sender, e);
    }
}

In my ViewModel, I implement INotifyPropertyChanged and also implemented a custom Interface for handling the mouse events from the View as you can see from the above code. I also implement a few event dispatchers for a separate ToolPanelView and other interconnected events. The ObservableCollection is a basic Property and I am creating the Model here in the ViewModel

internal class CanvasViewModel : ViewModelBase, IMouseEventController
{
    private readonly ToolPanelDispatcher pToolPanelDispatcher;
    private readonly CanvasDispatcher pCanvasDispatcher;
    private readonly CanvasObjectDispatcher pCanvasObjectDispatcher;

    private CanvasModel pCanvasModel;
    private ToolMode pToolMode;
    private Point pCurrentMousePointerCoordinates;
    private Point pPreviousMousePointerCoordinates;

    public ObservableCollection<CanvasObject> CanvasObjects { get; set; }

    public CanvasViewModel(ToolPanelDispatcher toolPanelDispatcher, CanvasDispatcher canvasDispatcher, CanvasObjectDispatcher canvasObjectDispatcher)
    {
        pToolPanelDispatcher = toolPanelDispatcher;
        pCanvasDispatcher = canvasDispatcher;
        pCanvasObjectDispatcher = canvasObjectDispatcher;

        // Injects CanvasDispatcher and CanvasObjectDispatcher into CanvasModel
        pCanvasModel = new CanvasModel(pCanvasDispatcher, pCanvasObjectDispatcher);

        CanvasObjects = new ObservableCollection<CanvasObject>();

        // Subscribes/listens for when ToolMode changes on the ToolPanelView
        pToolPanelDispatcher.ToolModeChanged += ToolModeChangedEventHandler;
        pCanvasDispatcher.CanvasObjectAdded += CanvasObjectAddedEventHandler;
     }

    private void ToolModeChangedEventHandler(ToolMode toolMode)
    {
        pToolMode = toolMode;
        // Relays current tool mode to CanvasObjects
        pCanvasDispatcher.PublishToolModeChanged(pToolMode);
    }

    private void CanvasObjectAddedEventHandler(CanvasObject canvasObject)
    {
        CanvasObjects.Add(canvasObject);
    }

    public void MouseDownEventHandler(object sender, MouseButtonEventArgs e)
    {
        pCurrentMousePointerCoordinates = e.GetPosition(sender as Canvas);

        switch (pToolMode)
        {
            case ToolMode.Node:
                pCanvasModel.DrawCanvasObject(CanvasObjectType.Node, pCurrentMousePointerCoordinates);
                break;
        }

        pPreviousMousePointerCoordinates = pCurrentMousePointerCoordinates;
    }
}

In my Model, it is pretty basic: I have my List for CanvasObjects, the event dispatchers, and a draw/create method.

internal class CanvasModel
{
    private readonly List<CanvasObject> pCanvasObjects;

    private readonly CanvasDispatcher pCanvasDispatcher;
    private readonly CanvasObjectDispatcher pCanvasObjectDispatcher;

    public CanvasModel(CanvasDispatcher canvasDispatcher, CanvasObjectDispatcher canvasObjectDispatcher)
    {
        pCanvasObjects = new List<CanvasObject>();
        pCanvasDispatcher = canvasDispatcher;
        pCanvasObjectDispatcher = canvasObjectDispatcher;
    }

    public void DrawCanvasObject(CanvasObjectType canvasObjectType, Point origin)
    {
        var canvasObject = new Node(origin, pCanvasDispatcher, pCanvasObjectDispatcher);

        pCanvasObjects.Add(canvasObject);
        pCanvasDispatcher.OnCanvasObjectAdded(canvasObject);
    }
}

You may note that in the DrawCanvasObject method there is an unused CanvasObjectType. This is for expanded functionality that is not implemented yet. Also note that CanvasObject is a base for Node and other unnamed CanvasObjects that are Custom Controls inherited from Control to allow them to be children of a Canvas and to utilize Mouse Events. Further note that the events are basic events utilizing this pattern:

public delegate void CanvasObjectAddedEventHandler(CanvasObject canvasObject);
public event CanvasObjectAddedEventHandler? CanvasObjectAdded;

public void OnCanvasObjectAdded(CanvasObject canvasObject)
{
    CanvasObjectAdded?.Invoke(canvasObject);
}

However, to reitterate, this is only way that I was able to think on how to update the ObservableCollection by "dispatching" a reference of the CanvasObject and have the ViewModel listen for and then update the ObservableCollection.

Is this the only way it can be done or is there a more efficient/direct way to update an ObservableCollection from a List?

Any help would be greatly appreciated, especially if there are best practices that could be better followed.

Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • In order to invoke an action in a view model from an event in the view you would typically use an EventToCommand implementation. Besides that, nobody forces you to implement this kind of strict separation. Does it make any sense in your application to have two collections of CanvasObjects in components that are arbitrily called a "model" and "view model"? It would be a lot easier and straightforward to call CanvasObject a model class, throw away CanvasModel and only manage one ObservableCollection in CanvasViewModel . – Clemens Jul 15 '23 at 18:20
  • @Clemens, I agree that it does not make sense to have two Lists, one in the ViewModel and one in the Model. However, because the ViewModel is only supposed to be a bridge between the View and the Model, I cannot do away with the Model outright, because that is where the application logic is being performed and would therefore need a List to populate the created CanvasObject to be somehow bound to the View. Because binding a Model and View together would violate the MVVM pattern, I need to "send" or "bind" the Model's List to the ObservableCollection Property the View needs from the ViewModel. – Experiment-626 Jul 15 '23 at 19:55
  • Replace the List in the model with an ObservableCollection which you pass through the view model by a wrapper property. Avoid two collections. – Clemens Jul 15 '23 at 20:26
  • @Clemens, thank you for the suggestion. Changing the List to an ObservableCollection in the Model is simple enough but I do not understand how to make a wrapper property. I searched for an example on how to make a wrapper property and could not find anything that I think will help, at least in the context of my issue. Would you be able to show a simple example or point me to a link that has an example? – Experiment-626 Jul 15 '23 at 21:25
  • What is the data flow? What are you doing with this code? Maybe you should separate out drawing and displaying and they should be in different view layers. Eg I use a inkcanvas for drawing in my map editor. The terrain is items in a itemscontrol which has a canvas as itemspresenter. Both are in parent grid. – Andy Jul 16 '23 at 12:49
  • I would say the model is usually between viewmodel and persistence and in terms of data classes is a dto. Viewmodel is where logic is used. Or view for some very view orientated things like drawing and drag drop. – Andy Jul 16 '23 at 12:51
  • Persistence can be some sort of feed or a device. Eg an app might be visualising data from a F1 race car. – Andy Jul 16 '23 at 13:01
  • @Andy, the purpose for this code is to create a flowchart-style application that will have nodes and bezier-style connections between these nodes, very much in the visual style of Unreal Engine Blueprints. That's the reason why I was extending Control and making my CanvasObjects custom controls. I figured it would have been easier to create the connectors as a custom control because I thought I could do it more dynamically and that is why the ViewModel and Model was creating the "nodes" because I do not know how to create these objects from the View layer without doing it the way I am. – Experiment-626 Jul 16 '23 at 15:03
  • I'd make the connecting lines paths. The boxes, user controls and template them to different shapes. I'd have an observable collection of connection node in the viewmodel for a box. That'd have properties including one is a point relative to top left corner of the box. So you can calc where it is. – Andy Jul 16 '23 at 16:03
  • Adorners to resize a box, rotate or whatever. – Andy Jul 16 '23 at 16:05
  • BTW you've considered online options like https://app.diagrams.net/ ? – Andy Jul 16 '23 at 16:11
  • @Andy, my app has a different purpose than to be a generic flowchart program. I need flowchart-like functionality though. However, that app.diagrams.net could be more useful than Visio for some of my flowchart needs. Thank you for that suggestion. – Experiment-626 Jul 16 '23 at 16:39
  • @Andy, for the most part, your suggestions for how the connecting lines and boxes should be was how I was structuring them in the custom control class. My most difficult issue was trying to get the Canvas in the View to be able to dynamically add these objects. I don't really know how to transfer the clicks in the canvas to create the objects, whether they be User Controls or Custom Controls, without using code-behind. BionicCode's answer seems to show that I should have a function in my ViewModel that is called from Canvas' code-behind to create the objects and store them in the model. – Experiment-626 Jul 16 '23 at 16:47
  • Roughly speaking. My map editor has a list of the different things you want to draw in a side panel. Select one and draw somewhere to add one. – Andy Jul 17 '23 at 15:39
  • They are added by adding an item to a bound observable collection. That is templated into UI. Manipulate that thing and you change properties in it's VM. – Andy Jul 17 '23 at 15:41
  • @Clemens, where did your answer go? I was going to select that as the correct answer to the actual question that I did ask. The help BionicCode gave is very helpful to me in way I approached coding my task, but, despite my approach being all wrong, your answer directly shown how to do exactly as my OP asked. – Experiment-626 Jul 18 '23 at 15:21

2 Answers2

0

You could have an ObservableCollection instead of a List in the model class

internal class CanvasModel
{
    public ObservableCollection<CanvasObject> CanvasObjects { get; }
        = new ObservableCollection<CanvasObject>();

    public void DrawCanvasObject(...)
    {
        ...
        CanvasObjects.Add(...);
        ...
    }

    ...
}

and a wrapper property in the view model that exposes the ObservableCollection of the underlying model:

internal class CanvasViewModel : ...
{
    private readonly CanvasModel canvasModel;

    public ObservableCollection<CanvasObject> CanvasObjects
    {
        get { return canvasModel.CanvasObjects; }
    }

    ...
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
0

Your design has several flaws.

  1. Don't pass RoutedEventArgs to your View Model. This event args object is part of the view. You see where you end up when passing an instance to the View Model: you start to handle controls in your View Model, for example:
// Bad smelling *View* *Model* code that breaks the MVVM pattern
pCurrentMousePointerCoordinates = e.GetPosition(sender as Canvas);

You obtained the reference to the control via the sender parameter. Additionally, your View Model class should not mimic the event handler:

// Bad View Model code
public void MouseDownEventHandler(object sender, MouseButtonEventArgs e) {}

Instead it should expose a normal method that takes the arguments it needs:

// Fixed version
public void CrateNewItem(Point location) {}

"CanvasObject is a base for Node and other unnamed CanvasObjects that are Custom Controls to allow them to be children of a Canvas"

  1. Your model object must not extend DispatcherObject (UIElement, Control etc.). You implement MVVM to avoid exactly this mixup. You want a clean separation between UI and business logic. Because the Canvas is the panel of the ItemsControl the item model are wrapped into a ContentPresenter. ContemntPresenter extends UIElement and therefore can be a child of Canvas. Just keep your models simple and design them like there is no View. There is only data that your business logic need s in order to do the application's business.

  2. Because the Model must be View agnostic it will never have a DrawCanvasObject method. Drawing requires knowledge about UI details. For example a graphics application may have a CreateDrawingModels.

  3. You are throwing around a lot of dispatchers. In case CanvasDispatcher and CanvasObjectDispatcher are wrappers of Dispatcher them those refrences shouldn't exist in the Model. We don't know what those types are, but it looks suspicious.

  4. The View Model is not a delegate or broker to establish any communication between the View and the Model. Your code does just that. In MVVM the boundaries are crucial. This wrong understanding results in a bad design where responsibilities are wrong. The View Model gets data from the Model to present it to the View and updates the Model with data that was modified by the view. For example, when the user adds an item to a DataGrid then the View communicates with the View Model to trigger the associated operation. The View Model then creates the new item model and adds it to the source collection. Now at some point, for example when the user saves his changes by clicking a corresponding button, you commit the changes by updating the Model with the modified and added/removed items.

Depending on the size/complexity of the application you may opt to use the same model classes for View Model and Model.
However, the following example assumes that the Model has its usual business entities and related data models defined (just to highlight the boundaries in the following example).

The following example shows the data flow between the MVVM components:
View Model gets data from Model. View Model presents data to the View. View manipulates data or creates new data. View Model creates new item for the View. View Model updates Model with modified and new data. Model persists new data and executes business logic on the data.

It also shows how to correctly position the item containers on a Canvas to eliminate the idea that your item models have to extend UIElement in order to be a child of Canvas:

ShapeModel.cs
Data model of the Model component.

class ShapeModel
{
  public Point Location { get; private set;}
}

ShapeController.cs
A random class of the Model component to act like a business entity that handles shape data models.

class ShapeController
{
  private IList<ShapeModel> Shapes { get; }
  
  public void AddNewShapes(IEnumerable<ShapeModel> newShapes)
  {
    // Do something with the new shape e.g. persist it or add it to a collection etc.
   newShapes.ToList().ForEach(this.Shapes.Add);
  }
  
  // Pretend this takes a while because much data must be fetched from a database etc
  public async Task<IEnumerable<ShapeModel>> GetShapesAsync(ShapeModel newShape)
    => this.Shapes;
}

ShapeItem.cs
Data model that serves as a binding source for the View.

class ShapeItem : INotifyPropertyChanged
{
  public Point Location { get; private set;}

  public double HorizontalOffset
  { 
    get => this.Location.X;
    set => this.location = new Point(value, this.VerticalOffset);
  }

  public double VerticalalOffset 
  { 
    get => this.Location.Y;
    set => this.location = new Point(this.HorizontalOffset, value);
  }
}

MainViewModel.cs

class MainViewModel : INotifyPropertyChanged
{
  public ObservableCollection<ShapeItem> ShapeItems { get; }
  public IList<ShapeItem> UnsavedShapeItems { get; }

  // Reference to a Model type
  private ShapeController ShapeController { get; }

  public MainViewModel()
  {
    this.ShapeController = new ShapeController();
  }

  public async Task InitializeAsync()
  {
    IEnumerable<ShaeModel> shapeModels = await this.ShapeController.GetShapesAsync();
    IEnumerable<ShapeItem> shapeItems = ConvertShapeModelToShapeItem(shapeModels);
    this.ShapeItems = new ObservableCollection<ShapeItem>(shapeItems);
  }

  // Example ICommand handler
  private void ExecuteAddNewShapesCommand(object commandParameter)
  {
    var newShapeItem = new ShapeItem();

    // "Directly" interact with the view via data binding. Direct means not using the Model for this.
    this.ShapeItems.Add(newShapeItem);
    this.UnsavedShapeItems.Add(newShapeItem);
  }

  // Example ICommand handler
  private void ExecuteSaveNewShapesCommand(object commandParameter)
  {
    IEnumerable<ShapeModel> shapeModels = ConvertShapeItemToShapeModel(this.UnsavedShapeItems); 

    // Send modified or new data back to the Model
    this.ShapeController.AddNewShapes(shapeModels);
  }

  private IEnumerable<Shapeitem> ConvertShapeModelToShapeItem(IEnumerable<ShapeModel> shapeModels)
    => shapeModels.Select(model => new ShapeItem() { Location = model.Location });

  private IEnumerable<ShapeModel> ConvertShapeItemToShapeModel(IEnumerable<ShapeItem> shapeItems)
    => shapeItems.Select(item => new ShapeModel() { Location = item.Location });
}

MainWindow.xaml.cs

partial class MainWindow : Window
{
  private MainViewModel ViewModel { get; }

  public MainWindow()
  {
    InitializeComponent();
  
    this.ViewModel = new MainViewModel();  
    this.DataContext = this.ViewModel();
    this.Loaded += OnLoaded;
  }

  private async void OnLoaded(object sender, RoutedEventArgs e)
  {
    await this.ViewModel.InitializeAsync();
  }
}

MainWindow.xaml

<Window>
  <ItemsControl ItemsSource="{Binding ShapeItems}"> <ItemsControl.ItemsPanel>
    <ItemsControl.ItemsPanel>
      <ItemsPanelTemplate>
        <Canvas Width="1000" 
                Height="1000" 
                Background="GhostWhite" />
      </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

  <!-- Wire the item container's Canvas position to the item model's Location property -->
    <ItemsControl.ItemsContainerStyle>
      <Style TarghetType="ContenPresenter">
        <Setter Property="Canvas.Left"
                Value="{Binding HorizontalOffset, Mode=TwoWay}" />
        <Setter Property="Canvas.Top"
                Value="{Binding VerticalOffset, Mode=TwoWay}" />
      </Style>
    </ItemsControl.ItemsContainerStyle>

    <!-- Define how the shapes should be rendered e.g. as custom control etc. -->    
    <ItemsControl.ItemTemplate>
      <DataTemplate DataType={x:Type local:ShapeItem}>
        <Rectangle Width="50"
                   Height="50"
                   Fill="Orange" />
      </DataTemplate>   
    </ItemsControl.ItemTemplate>  
  </ItemsControl>
</Window>
  • I admit that my design has flaws. I had my ViewModel mimic the Mouse Event because most people feel with MVVM you should never use code-behind to perform anything. If I am understanding your comment that `public void CrateNewItem(Point location) {}` should be called from code-behind, I would do that to cause a new CanvasObject to be created. However, I am not sure how to create dynamically created objects with your method that was described using a DataTemplate. The method that you show looks to be static. I will need my objects to be drawn and editable dynamically. – Experiment-626 Jul 16 '23 at 15:22
  • Plus, you use a lot of Asynchronous programming, as I think you are assuming that I am accessing a DB. While I will not be accessing a DB or the Internet for any of my Model data, I was not aware of this C# style and will look into it. It looks like it could ultimately be useful for my application. – Experiment-626 Jul 16 '23 at 15:29
  • Code-behind and MVVM are not related. MVVM is an architectural design pattern. It's concerned with application structure and not class level design. Partial class (aka code-behind) files are a compiler feature. It's absolutely fine and even necessary to have logic in code-behind. Avoiding this at all cost only leads to weird code like in your case. Not every code belongs to the View Model. There is code in the View and in the View Model. C# is your primary language. –  Jul 16 '23 at 17:38
  • How do get the idea that in the View C# or code-behind is "not allowed"? You then are forced to write code that really breaks MVVM because now you moved code from the View to the View Model. *"I am not sure how to create dynamically created objects with your method"* - The View Model creates the new items, adds them to the source collection and the DataTemplate will render the item. That's 100% dynamic. The posted example highlights this case. You can even define multiple DataTemplates to allow for different appearances. –  Jul 16 '23 at 17:38
  • *"you use a lot of Asynchronous programming, as I think you are assuming that I am accessing a DB."* - No, I was just creating a random example to show how the data flow or communication between View-->View Model-->Model works. The async initialization just adds some extra complexity to show that yo usually don't have to and better don't load all the data from the Model in the constructor. –  Jul 16 '23 at 17:38
  • This was just meant to give you a general example because I had the feeling that you are confusing the responsibilities of the MVVM components. Also your data flow is flawed like it is reversed: you want to update the View Model triggered by the Model - that's a MMVM violation. My example shows how the View Model uses the Model. The View Model decides when to read data from the Model and when to update the Model. –  Jul 16 '23 at 17:38
  • thank you for the feedback. I am rethinking my approach. I am going to try to follow this proper flow that you have pointed out, and I will also use the code-behind to connect my Canvas from View to the ViewModel. I have seen elsewhere that this is a proper way but then there are many others out there that say "NO! Don't use code-behind ever, not even to set ViewModel context or "bind" to ViewModel using functions, because that is tightly coupling the View to the ViewModel and the View should never know anything about the ViewModel." That has always confused me. – Experiment-626 Jul 16 '23 at 19:59
  • Also, can I make interactive DataTemplates? What I mean is if I make a Bezier connctor between two nodes, I would like the connector to grow in length between the nodes. Is this possible with this method? I have been thinking of ways on how it could be done but I am not sure if my way would somehow violate the flow again as I apparently have been doing. – Experiment-626 Jul 16 '23 at 20:03
  • *"Don't use code-behind ever, not even to set ViewModel context or "bind" to ViewModel using functions, because that is tightly coupling the View to the ViewModel and the View should never know anything about the ViewModel."* - Sorry, but this is utterly nonsense. The "avoid code-behind" matra is from the days when WinForms developers switched over to WPF. WPF has XAML markup that makes UI design so much more efficient in several ways. As a WinForms developer you are used to create controls and UI layout in C#. So they needed this matra in order to make them learn new habits. –  Jul 16 '23 at 22:06
  • You can check the source code of e.g. Button class. It's pure C# code. XAML doesn't allow to implement complex logic. And to move code to the View Model because you don't want to use C# in the View is insane. Ihat's exactly what breaks MVVM in the end. For example handling controls or dialogs in the View Model. The criteria whether code belongs to the View or View Model is not the programming language. It's the responsibility of the class. Design patterns are by definition language and compiler agnostic. Then how can a compiler detail (code-behind) or a language (C#) break a design pattern? –  Jul 16 '23 at 22:06
  • Additionally, MVVM is not about tight coupling or lose coupling. it's about component level dependencies. And the simple dependency graph is as follows: View --> View Model --> Model. As you can see the View *knows* about the View Model. It must know the View Model because it literally uses it. But the View Model is not allowed to know/use the View. The View Model knows/uses the Model, but the Model is not allowed to know/use the View Model: the dependencies in MVVM are unidirectional. Again, MVVM organizes dependencies on application level as MVVM is an architectural design pattern. –  Jul 16 '23 at 22:07
  • *"can I make interactive DataTemplates?"* - as DataTemplates contain controls they are always intercative (depending on the used controls). You can create a custom control or UserControl and add it to the template. DataTemplate is View, so you have to write UI code. For example encapsulate logic in a dedicated custom control. Yes what you want is absolutely possible following the above pattern (MVVM). –  Jul 16 '23 at 22:07
  • For example, to draw a line between two nodes, you can create a NodeModel and a LineModel. Let the NodeModel have a collection of LineModel and a location. Let LineModel have to points: start and end. Define a DataTemplate that defines how NodeModel and LineModel should look like. You should create a Node control and a Line control and wrap each into a DataTemplate (for the NodeModel and LineModel). Then render the line model using the start and end position. It's quite simple. All the logic of drawing and e.g. click handling is implemented inside the control. –  Jul 16 '23 at 22:12
  • Maybe I will post a short example tomorrow. –  Jul 16 '23 at 22:23
  • I think I was taking this approach regarding the canvas objects, however, I just don't think I was implementing them right. As you may remember, I made custom controls, one being the node and another I didn't mention, was a connector. In each, I set the themes/ResourceDictionary with Path tags and appropriate .cs code, like event handling and some model data I thought pertinent, like origin and other stuff. I think I wasn't creating them in the correct place. – Experiment-626 Jul 17 '23 at 03:29
  • It's not only about creation. It's about class responsibilities. Certain logic must reside in the correct component. For example handling mouse position must be implemented in the View. When the View Model is not aware of the View how can it handle mouse position? Another helper is to ask: "is my View Model class testable without a UI or UI related input?" In your case the answer is "no!". –  Jul 17 '23 at 08:26
  • Maybe [Patterns - WPF Apps With The Model-View-ViewModel Design Pattern](https://learn.microsoft.com/en-us/archive/msdn-magazine/2009/february/patterns-wpf-apps-with-the-model-view-viewmodel-design-pattern) helps to improve your understanding of the MVVM design pattern. –  Jul 17 '23 at 08:26
  • If I'm understanding correctly, I wasn't treating the Custom Controls as "Views" themselves. The XAML code of the Custom Control is just like the XAML of the View and the .cs file is basically the code-behind and not the ViewModel, which I was assuming. I should use my CanvasView's code-behind to access the CanvasViewModel to add a Node to an ObservableCollection, which in turn, if I am understanding correctly, essentially adds a new View to the CanvasView. From there, each Node View will interact with a NodeViewModel, the same way I used the CanvasView, to manipulate the individual nodes. – Experiment-626 Jul 18 '23 at 17:05
  • *"The XAML code of the Custom Control is just like the XAML of the View and the .cs file is basically the code-behind and not the ViewMode"* - No, not exactly. The term "[code-behind](https://learn.microsoft.com/en-us/dotnet/desktop/wpf/advanced/code-behind-and-xaml-in-wpf?view=netframeworkdesktop-4.8)" refers to a C# file (or class to be more precisely) that backs up a XAML file (or more precisely XAML object definition). This relationship is created by defining the C# class as partial class and the XAML object using the x:Class directive. –  Jul 18 '23 at 21:34
  • For example you have partial XAML/C# classes when defining the XAML root (e.g. Window or UserControl).. A custom control is different. A custom control extends Control or ContentControl or a more specialized type like e.g. Button. The class is not defined as partial and is not backing up a XAML file. instead, the custom control defines a default Style (that defines the default ControlTemplate). This Style must reside in the Generic.xaml file, either directly or as merged resource. –  Jul 18 '23 at 21:35
  • WPF default controls are all custom controls (they don't have a code-behind file). This is because a control that is defined as partial class (for example a UserControl) is less customizable than a control that has it's layout defined by a simple Style/ControlTemplate that can be overridden at any time to completely change or modify the layout. In other words, a custom control does not have code-behind, because it is not the partial class of a XAML object. A custom control defines it's layout using a Style and a CopntrolTemplate defined in Generic.xaml. –  Jul 18 '23 at 21:36
  • Code is considered View or View Model based on the responsibility and not based on the file extension (.xaml or .cs). If a .cs file contains UI logic then it belongs to the View. A good guide is that when a class (i.e. XAML or C# object) extends DispatcherObject or a subclass (e.g. UIElement etc.) it is a class of the View. If a class is responsible to get and set data on the Model and to expose data to the View then it belongs to the application's View Model. –  Jul 18 '23 at 21:36
  • *"I should use my CanvasView's code-behind to access the CanvasViewModel to add a Node to an ObservableCollection"* - Yes. Alternatively, you could also define a DependencyProperty in your CanvasView and bind it to the ViewModel. If you have to pass data back to the View Model then a DependencyProperty and data binding is the more elegant solution. In your case (you want to initiate the creation of a new item), your CanvasView should invoke a method or ICommand (if applicable). –  Jul 18 '23 at 21:36
  • *"[...] which in turn, if I am understanding correctly, essentially adds a new View to the CanvasView."* - Yes, because us would define a DataTemplate for the added item type. Updating the ItemsControl will cause it to load the ItemTemplate or a matching implicit DataTemplate for this item. –  Jul 18 '23 at 21:36
  • *"From there, each Node View will interact with a NodeViewModel, the same way I used the CanvasView, to manipulate the individual nodes."* - Not exactly (if I understood you correctly). You usually don't care about the item containers (the view of the item models). All item level interaction takes place between the item model (in the ItemsSource9) and the view model that defines the source collection with those item models. If you need to send data from the container to the item model you would e.g. bind the ListBoxItem.IsSelected to the item model by defining an ItemContainerStyle. –  Jul 18 '23 at 21:37
  • When you need user interaction with your item model then you would define the related bindings in the item template e.g. bind TextBox.Text or Button.Command to the item model. Item model interaction is always straight forward because they can directly accessed by the view model class. Control interaction is somewhat not that straight forward in the sense that the view model class can not directly interact with the control. The control must interact with the view model class via data binding, ICommand or method invocation. –  Jul 18 '23 at 21:37
  • BionicCode, the comment on Jul 16 at 22:12, you mentioned to make models for the nodes and lines, then use the ItemsControl to template them. If I am understanding you correctly, because I want to have the lines and nodes to be interactively drawn, I can create a CustomControl of each. The CustomControls will serve as only the manipulators of the Models but since the CustomControls have a template (Generic.xaml), what would I store in the ObservableCollection? If it is bound to the ItemsControl, would I not need to define a template on that because the template is the Generic.xaml? – Experiment-626 Jul 31 '23 at 20:30
  • If you have some patience I will crate a small example for you. Tomorrow or the day after. –  Aug 02 '23 at 15:31
  • Thank you BionicCode. I do have some patience. :) I actually succeeded in making a NodeModel that has the Origin only, added to an ObservableCollection and bound it to an ItemsControl. In the ItemsControl, I am using the ItemsControl.ItemContainerStyle to bind a property called Origin in the ViewModel to the Canvas.Left and Canvas.Top and I am using ItemsControl.ItemTemplate with my Custom Control Node to present my NodeModel. Within my Custom Control Node, I have a dependency property called Origin to bind the Origin to the CanvasViewModel Origin. I hope this was is the better way. – Experiment-626 Aug 02 '23 at 16:16