在WPF应用程序中应用MVVM模式

时间:2011-03-09 00:35:53

标签: c# wpf xaml mvvm

我正在制作一个简单的WPF应用程序,其中包含在画布上绘制的形状。该视图由地图组成,该地图在地图上的不同静态位置上具有多个正方形的复杂序列。

MapView是一个包含视图框和画布的UserControl。正方形由UserControl表示,带有简单的画布和形状(代码中的椭圆):

<Canvas>
    <Canvas.Resources>
        <BooleanToVisibilityConverter x:Key="boolToVisibility" />
    </Canvas.Resources>

    <Ellipse Stroke="Black" Fill="{Binding Color}" Width="{Binding Dimension}" Height="{Binding Dimension}" />
    <Ellipse Stroke="Black" Fill="Black" Canvas.Top="15" Canvas.Left="15" Width="20" Height="20" Visibility="{Binding IsOccupied, Converter={StaticResource boolToVisibility}}" />

</Canvas>

这些视图显然都有一个ViewModel(通过视图的DataContext属性绑定),由模型备份。

我的问题:

  • 我地图上的SquareViews都有一个mousedown事件,每个视图代表一个模型,我对如何在我的应用程序中以优雅的方式实现它感到困惑(关于MVVM模式)。我应该在XAML中预定义SquareViews,然后生成模型,还是事先生成模型,并根据在运行时对模型所做的更改动态创建视图。

  • 如何区分SquareViews?基于(查看)模型参考?位置坐标?我想避免给每个单独的广场分别给出一个单独的名字......

  • 将视图的DataContext设置为相应的viewmodel(无需使用框架)的其他方式,而不是将其添加到视图的代码隐藏中。

  • 有没有更好的方法在我的地图上定位方块? (我知道画布在缩放,不同分辨率,dpi等方面不是很灵活,但据说视图框应该改进这个,但我还没有完全测试过)

PS如果我的描述/问题是含糊不清的,请告诉我。

3 个答案:

答案 0 :(得分:3)

如果我理解你的问题......

我认为你可以采用的方法是使用DataTemplates,ItemsControl和ContentPresentor。

实际上,您要做的是告诉WPF“显示”您的视图模型。因为您的视图模型只是普通类,所以WPF不知道如何“渲染”它们。这就是DataTemplates的用武之地。这种方法的好处是DataTemplate内容的DataContext将自动设置为视图模型。 DataTemplates在Window或UserControl资源中定义:

<Window.Resources>
    <DataTemplate DataType="{x:Type ViewModels:SquareViewModel}">
        <Views:SquareView />
    </DataTemplate>
</Window.Resources>

当WPF遇到SquareViewModel时(以及将SquareView上的DataContext设置为SquareViewModel),上面的代码将呈现SquareView用户控件。

要将视图模型放置在视图中,您可以使用ContentPresenter(对于单个ViewModel):

<ContentPresenter Content="{Binding SingleSquare}" />

在您的情况下,您将希望显示一组SquareViewModel项目,以便您想要使用ItemsControl:

<ItemsControl ItemsSource="{Binding Squares}" />

但是这不会给你想要的结果,因为默认情况下它会像ListBox一样。您需要将模板应用于ItemsControl以使用底层Canvas。有关可能的实施,请参阅this blog by Pete Brown

祝你好运!

编辑:附加代码示例


查看模型:

public class MainViewModel
{
    public IEnumerable<SquareViewModel> Squares { get; set; }

    public MainViewModel()
    {
        var squares = new List<SquareViewModel>();
        squares.Add(new SquareViewModel(15, 15,100,100, Brushes.CadetBlue, "Square One"));
        squares.Add(new SquareViewModel(75,125, 80, 80, Brushes.Indigo, "Square Two"));
        Squares = squares;
    }
}

public class SquareViewModel
{
    public int X { get; set; }
    public int Y { get; set; }
    public int Width { get; set; }
    public int Height { get; set; }
    public Brush Color { get; set; }
    public string Name { get; set; }

    public SquareViewModel(int x, int y, int width, int height, Brush color, string name)
    {
        X = x;
        Y = y;
        Width = width;
        Height = height;
        Color = color;
        Name = name;
    }
}

<强>视图

<UserControl x:Class="MapView.Views.SquareView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    >
    <Grid Background="{Binding Color, FallbackValue=Azure}">
        <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding Name, FallbackValue=None}" />     
    </Grid>
</UserControl>

<Window x:Class="MapView.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:ViewModels="clr-namespace:MapView.ViewModels" 
    xmlns:Views="clr-namespace:MapView.Views" Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate DataType="{x:Type ViewModels:SquareViewModel}">
            <Views:SquareView />
        </DataTemplate>
    </Window.Resources>
    <ItemsControl ItemsSource="{Binding Squares}" >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Beige" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left"
                    Value="{Binding X}" />
                <Setter Property="Canvas.Top"
                    Value="{Binding Y}" />
                <Setter Property="Width"
                    Value="{Binding Width}" />
                <Setter Property="Height"
                    Value="{Binding Height}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Window>

在Window1构造函数中:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        DataContext = new MainViewModel();
    }
}

答案 1 :(得分:1)

由Ryan Cromwell看看这个blog。基本上,您希望在画布上显示正方形的“列表”。他解释了如何完全想要我想你的要求。

答案 2 :(得分:1)

你必须想出某种网格结构(WPF Datagrid将为你做)。网格的优点是可以像行一样使用它可以被视为x坐标和列作为y坐标。在开始实现之前,想象一下你想要在UI上显示什么。 WPF只是一种将您的想象力变为现实的技术。如果您的应用程序是UI驱动的,那么首先收集可能来自UI的所有操作,为视图模型中的那些操作创建命令。

相关问题