使用 MVVM / MVVMLight 时简单绘图应用程序的代码结构

Code structure for simple drawing apps when using MVVM / MVVMLight

我目前在我的一个应用程序中使用 MVVM 模式,更具体地说,我正在使用 MVVMLight 框架。在其中一个页面中,我将有一个屏幕,用户可以在其中输入 widthlength 来绘制矩形,代码逻辑不多,所以我想将所有代码放在代码隐藏,因为此屏幕中发生的大部分内容都与 UI 相关。

在这种情况下使用代码隐藏是否有意义?如果不是,您将如何构建代码以使用 MVVM 模式,在这种情况下,您将在 ViewModel 中放入什么以及您将在代码后面放入什么?

这里是没有使用 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>

代码隐藏:

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

编辑: 第二张图(仅供参考):

视图中的 Button 应该绑定到视图模型的 ICommand 属性。单击 Button 时将执行该命令。有关如何在 MVVM 应用程序中处理事件的信息,请参阅 this blog post。在 MvvmLight 中,ICommand 实现被称为 RelayCommand.

您还应该将 TextBoxesText 属性绑定到视图模型的两个源属性,并且视图中的 Canvas 元素应该替换为 ItemsControl 绑定到视图模型中定义的对象集合。

请参考下面的示例代码

型号:

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

查看:

<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}"/>

查看模型:

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();
    }
}

在此示例中,所有应用程序逻辑都已移至它所属的视图模型。视图的代码隐藏 class 中没有任何逻辑。

另请注意,视图模型会创建 Model 个对象的实例,而不是创建 Rectangle 个元素。通常认为在视图模型 class 中引用 UI 元素是一种不好的做法。 Rectangle 元素由 ItemsControl 创建。查看视图中的 ItemTemplate