fs_tigre
fs_tigre

Reputation: 10738

Code structure for simple drawing apps when using MVVM / MVVMLight

I'm currently using the MVVM pattern in one of my apps, to be more specific I'm using the MVVMLight framework. In one of the pages, I will have a screen where the user can input the width and length to draw rectangles, there is not much code logic so, I was thinking to put all of my code in the code-behind since most of what will be happening in this screen is UI related.

Does that make sense to use the code-behind in this case? If not, how would you structure the code to use the MVVM pattern, what would you put in the ViewModel in this case and what would you put in your code behind?

Here is the code without using MVVM.

XAML:

<Window x:Class="DrawingRectangles.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DrawingRectangles"
        mc:Ignorable="d"
        Title="MainWindow" Height="531.798" Width="782.115">
    <Grid Name="MyGrid" Width="480" Height="240" Margin="27,23,267,174">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="59*"/>
            <ColumnDefinition Width="421*"/>
        </Grid.ColumnDefinitions>
        <Canvas Name="MyCanvas" Background="#FFF1F0F0" Margin="10" Grid.ColumnSpan="2"/>

        <Grid Margin="10,235,10,-92" Background="WhiteSmoke" Grid.ColumnSpan="2">
            <Button x:Name="drawButton" Content="Draw"  Click="drawButton_Click"/>
            <Button x:Name="resetButton" Content="Reset" Click="resetButton_Click"/>
            <TextBox x:Name="textBoxPartWidth"/>
            <TextBox x:Name="textBoxPartLength"/>

        </Grid>
    </Grid>
</Window>

CODE-BEHIND:

namespace DrawingRectangles
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

        public MainWindow()
        {
            InitializeComponent();

        }

        private void drawButton_Click(object sender, RoutedEventArgs e)
        {
            clearScreen();

            int xParts = 10;
            int yParts = 10;

            for (int i = 0; i < xParts; i++) {

                for (int j = 0; j < yParts; j++) {
                    // Create a rectangle.
                    Rectangle myRectangle = new Rectangle();
                    myRectangle.Width = Convert.ToDouble(textBoxPartLength.Text);
                    myRectangle.Height = Convert.ToDouble(textBoxPartWidth.Text);
                    myRectangle.Margin = new Thickness((Convert.ToInt32(myRectangle.Width) + 1) * i, (Convert.ToInt32(myRectangle.Height) + 1) * j, 0, 0);
                    myRectangle.Fill = new SolidColorBrush(Color.FromArgb(170, 51, 51, 255));
                    MyCanvas.Children.Add(myRectangle);
                }
            }
        }

        private void resetButton_Click(object sender, RoutedEventArgs e)
        {
            MyCanvas.Children.Clear();
        }

        private void clearScreen()
        {
            MyCanvas.Children.Clear();
        }
    }
}

UI

enter image description here

EDIT: Second Image (reference only): enter image description here

Upvotes: 0

Views: 465

Answers (1)

mm8
mm8

Reputation: 169150

The Button in the view should be bound to an ICommand property of the view model. The command will be executed when you click on the Button. Please refer to this blog post for information about how to handle events in an MVVM application. In MvvmLight, the ICommand implementation is called RelayCommand.

You should also bind the Text properties of the TextBoxes to two source properties of the view model and the Canvas element in your view should be replaced with an ItemsControl that you bind to a collection of objects that are defined in the view model.

Please refer to the following sample code.

Model:

public class Model
{
    public int Width { get; set; }
    public int Height { get; set; }
    public Thickness Margin { get; set; }
    public Brush Fill { get; set; }
}

View:

<ItemsControl ItemsSource="{Binding Items}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas Background="#FFF1F0F0" Margin="10" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Width="{Binding Width}" 
                       Height="{Binding Height}" 
                       Margin="{Binding Margin}"
                       Fill="{Binding Fill}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

<Button Content="Draw" Command="{Binding DrawCommand}" />
<Button Content="Reset" Command="{Binding ResetCommand}" />
<TextBox Text="{Binding Width}"/>
<TextBox Text="{Binding Height}"/>

View Model:

public class ViewModel
{
    public ViewModel()
    {
        DrawCommand = new RelayCommand(Draw);
        ResetCommand = new RelayCommand(Clear);
    }

    public ObservableCollection<Model> Items { get; } = new ObservableCollection<Model>();

    public RelayCommand DrawCommand { get; }
    public RelayCommand ResetCommand { get; }

    public int Width { get; set; }
    public int Height { get; set; }

    private void Draw()
    {
        Clear();

        int xParts = 10;
        int yParts = 10;
        for (int i = 0; i < xParts; i++)
        {
            for (int j = 0; j < yParts; j++)
            {
                Model model = new Model();
                model.Width = Width;
                model.Height = Height;
                model.Margin = new Thickness((model.Width + 1) * i, (model.Height + 1) * j, 0, 0);
                model.Fill = new SolidColorBrush(Color.FromArgb(170, 51, 51, 255));
                Items.Add(model);
            }
        }
    }

    private void Clear()
    {
        Items.Clear();
    }
}

In this example all application logic has been moved to the view model where it belongs. There is no logic left in the code-behind class of the view.

Also note that the view models creates instances of Model objects rather than creating Rectangle elements. It's generally considered be a bad practice to reference UI elements in a view model class. The Rectangle elements are created by the ItemsControl. See the ItemTemplate in the view.

Upvotes: 1

Related Questions