如何使用WPF可视化一个简单的2D世界(地图和元素) [英] How to use WPF to visualize a simple 2D world (map and elements)

查看:1026
本文介绍了如何使用WPF可视化一个简单的2D世界(地图和元素)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是相当新的WPF和寻找一个简单的解决方案,以下面所述的问题。我试图使这一尽可能短。

I'm fairly new to WPF and looking for a simple solution to the problem described below. I've tried to make this as short as possible.

我试图想象一个世界是由建模:

I'm trying to visualize a "world" that is modeled by:


  • 的地图图像,并在米已知原点(例如左上角是14,27),并以cm /像素的分辨率。该地图保持每隔几秒钟增长。地图,因此无需分页/平铺小。

  • 真实世界的元素和兴趣点。每个元件具有米地图区域内的二维位置。此外,每个元件可能会移动。

关于模型的一面,我有一个WorldState类,保持了地图和元素:

Regarding the model side, I have a WorldState class that keeps the map and elements:

interface IWorldState
{
    IEnumerable<IWorldElement> Elements { get; }
    IMapData CurrentMap { get; }
}

interface IWorldElement
{
    WorldLocation { get; }
    event EventHandler LocationChanged;
}

interface IMapData
{
    string FilePath { get; }
    WorldLocation TopLeft { get; }
    Size MapSize { get; }
}

现在对于可视化,我选择了Canvas类绘制地图和元素。每种类型的元素(从IWorldElement继承)应不同绘制。可能有一个以上的地图帆布,与元件的子集

Now regarding the visualization, I've chosen the Canvas class to draw the map and elements. Each type of element (inherits from IWorldElement) should be drawn differently. There might be more than one map canvas, with a subset of the elements.

<Canvas x:Name="mapCanvas">
    <Image x:Name="mapImage" />
</Canvas>

在code,我需要设置地图图像文件时,它改变:

In code, I need to set the map image file when it changes:

void MapChanged(IWorldState worldState)
{
    mapImage.Source = worldState.CurrentMap.FilePath;
}

要绘制的元素,我必须WorldLocation转换为(Canvas.Left,Canvas.Top)的方法:

To draw the elements, I have a method to convert WorldLocation to (Canvas.Left, Canvas.Top) :

Point WorldToScreen(WorldLocation worldLocation, IWorldState worldState)
{
    var topLeft = worldState.CurrentMap.TopLeft;
    var size = worldState.CurrentMap.Size;
    var left = ((worldLocation.X - topLeft.X) / size.X) * mapImage.ActualWidth;
    var top = ((worldLocation.Y - topLeft.Y) / size.Y) * mapImage.ActualHeight;
    return new Point(left, top);
}

现在的问题,我应该怎么胶水世界模型和帆布在一起吗?这可以通过概括

Now for the question, how should I glue the world model and the canvas together? This can be summarized by:


  1. 哪里摆放的 MapChanged 的和的 WorldToScreen 的功能。

  2. 当一个元件移动世界位置需要被转换到屏幕坐标。

  3. 每种类型的元素应该以不同的绘制,对于文本或填充矩形例如椭圆形。

  1. Where to put the MapChanged and WorldToScreen functions.
  2. When an element moves the world location needs to be converted to screen coordinates.
  3. Each type of element should be drawn differently, for example ellipse with text or filled rectangle.

什么是使用WPF何时实现胶水层推荐的方式?

What is the recommended way to implement the "glue" layer when using WPF?

推荐答案

数据绑定是要走的路。
读到这里,<一个href=\"http://msdn.microsoft.com/en-us/magazine/dd419663.aspx\">http://msdn.microsoft.com/en-us/magazine/dd419663.aspx.

DataBinding is the way to go. Read here, http://msdn.microsoft.com/en-us/magazine/dd419663.aspx.

一旦你建立了一个视图模型并设置视图的DataContext的。

Once you've set up a viewmodel and set the datacontext of the view.

我建议把在一个ObservableCollection的元素并将其绑定到一个ItemsControl在画布上。

I suggest putting your elements in an observablecollection and bind it to an itemscontrol in the canvas.

有关被定位的元素,你必须为它使用的画布,而不是默认的StackPanel集装箱ItemsControl的一个定制的ItemTemplate。

For the elements to be positioned, you must create a custom ItemTemplate for the ItemsControl which uses a canvas, rather than the default stackpanel as container.

然后就可以继续前进,为不同类型的元素创造的DataTemplate你必须得到一个具体的外观和感觉的公关元素的类型。

Then you can go ahead and create datatemplate for the various types of elements you have to get a specific look and feel pr element type.

这是一个解决方案的一个粗线条的,希望这有助于。

This is a rough outline of a solution, hope this helps.

例如:

<Window x:Class="WorldCanvas.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WorldCanvas"
Title="WorldCanvas" Height="500" Width="500"
>
<Window.Resources>
    <DataTemplate DataType="{x:Type local:HouseVM}" >
        <Canvas>
            <Rectangle Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}" Width="13" Height="23" Fill="Brown" />
        </Canvas>
    </DataTemplate>

    <DataTemplate DataType="{x:Type local:BallVM}">
        <Canvas>
            <Ellipse Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}" Width="13" Height="13" Fill="Blue" />
        </Canvas>
    </DataTemplate>
</Window.Resources>
<Grid>
    <Canvas x:Name="TheWorld" Background="DarkGreen">
    <Button Content="MoveFirst" Click="Button_Click" />
    <ItemsControl ItemsSource="{Binding Entities}">
        <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding X}" />
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Canvas>

</Grid>

    public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        var worldViewModel = new WorldViewModel();
        DataContext = worldViewModel;
    }

    void Button_Click(object sender, RoutedEventArgs e)
    {
        var viewModel = DataContext as WorldViewModel;
        if(viewModel != null)
        {
            var entity = viewModel.Entities.First();
            entity.X +=10;
        }
    }
}

的ViewModels

Viewmodels

  public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propertyName)
    {
        if(PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

public class WorldViewModel : ViewModelBase
{
    ObservableCollection<EntityVM> entities;

    public ObservableCollection<EntityVM> Entities {
        get { return entities; }
        set 
        { 
            entities = value;
            NotifyPropertyChanged("Entities"); 
        }
    }

    public WorldViewModel()
    {
        Entities = new ObservableCollection<EntityVM>();
        int y=0;
        for(int i=0; i<30; i++)
        {
            if(i %2 == 0)
            {
                Entities.Add(new BallVM(i*10, y+=20));
            }
            else
            {
                Entities.Add(new HouseVM(i*20, y+=20));
            }
        }
    }       
}   

public class EntityVM : ViewModelBase
{
    public EntityVM(double x, double y)
    {
        X = x;
        Y = y;
    }

    private double _x;
    public double X
    {
        get
        {
            return _x;
        }
        set
        {
            _x = value;
            NotifyPropertyChanged("X");
        }
    }

    private double _y;
    public double Y
    {
        get
        {
            return _y;
        }
        set
        {
            _y = value;
            NotifyPropertyChanged("Y");
        }
    }
}

public class BallVM : EntityVM
{
    public BallVM(double x, double y) : base(x, y)
    {
    }
}

public class HouseVM : EntityVM
{
    public HouseVM(double x, double y)  : base(x, y)
    {
    }
}

这篇关于如何使用WPF可视化一个简单的2D世界(地图和元素)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆