Almost all examples of MVVM are applications that have View that may consist of ListBoxes, TextBoxes, Labels, Buttons, etc. to present and process data. ListBoxes, TextBoxes, and Labels are bound to properties and Buttons are bound to Commands in a ViewModel. Those types of Views are mainly static in nature.
However, there are not any real examples of how to deal with a fluid View, such as a drawing program. When you think about it, a drawing program is just a Canvas. Because there are no Buttons to which to bind nor Listboxes, TextBoxes, or Labels to interact with, how should you interact with the Canvas?
So far, the only way that I have found to interact with the Canvas is to use the Canvas' code-behind to use the Mouse Events to allow you to pass data into the ViewModel through exposed methods such as Add(Point point) which will add a point to an ObservableCollection only to be bound to an ItemsControl that will process the point data based on the ItemTemplate.
CanvasView
<ItemsControl x:Name="itemsControl" ItemsSource="{Binding CanvasObjectModels}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="1000" Height="1000" Background="GhostWhite"
MouseDown="Canvas_MouseDown"/>-->
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Origin.X}" />
<Setter Property="Canvas.Top" Value="{Binding Origin.Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CanvasObject:Node Origin="{Binding Origin}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
IMyCanvas
public interface IMyCanvas
{
public void AddCanvasObject(Point origin);
}
CanvasView Code-Behind
public partial class CanvasView : UserControl
{
private IMyCanvas pViewModel;
public CanvasView()
{
InitializeComponent();
Loaded += InitializeViewModel;
}
private void InitializeViewModel(object sender, RoutedEventArgs e)
{
if (DataContext is IMyCanvas viewModel)
pViewModel = viewModel;
else
throw new Exception("DataContext does not implement IMyCanvas!");
Loaded -= InitializeViewModel;
}
private void Canvas_MouseDown(object sender, MouseButtonEventArgs e)
{
var canvas = sender as Canvas;
if (e.LeftButton == MouseButtonState.Pressed)
{
pViewModel.AddCanvasObject(e.GetPosition(canvas));
}
}
}
CanvasObjectModel
public class CanvasObjectModel
{
public Point Origin { get; set; }
}
CanvasViewModel
internal class CanvasViewModel : ViewModelBase, IMyCanvas
{
public ObservableCollection<CanvasObjectModel> CanvasObjectModels { get; set; }
public CanvasViewModel()
{
CanvasObjectModels = new ObservableCollection<CanvasObjectModel>();
}
public void AddCanvasObject(Point origin)
{
CanvasObjectModels.Add(new CanvasObjectModel() { Origin = origin});
}
}
Is there a better way to do this?