博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
WPF可视化一个简单的2D世界
阅读量:4626 次
发布时间:2019-06-09

本文共 5291 字,大约阅读时间需要 17 分钟。

问 题

    

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:

  • A map image, with a known origin in meters (e.g. top-left corner is 14,27) and resolution in cm/pixel. The map keeps growing every few seconds. Maps are small so no paging/tiling is required.
  • Real-world elements and points of interest. Each element has a 2D position in meters within the map area. Also, each element might move.

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

interface IWorldState{    IEnumerable
Elements { get; } IMapData CurrentMap { get; }}interface IWorldElement{ WorldLocation { get; } event EventHandler LocationChanged;}interface IMapData{ string FilePath { get; } WorldLocation TopLeft { get; } Size MapSize { get; }}

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.

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

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

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. 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.

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

解决方案

DataBinding is the way to go. Read here, .

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

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

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.

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.

Example:

 

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

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
entities; public ObservableCollection
Entities { get { return entities; } set { entities = value; NotifyPropertyChanged("Entities"); } } public WorldViewModel() { Entities = new ObservableCollection
(); 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) { }}

转载于:https://www.cnblogs.com/PowerOfHeart/p/7237931.html

你可能感兴趣的文章
OpenCV---环境安装和初次使用
查看>>
回调函数的经典代码使用
查看>>
【学术篇】bzoj3262 陌上花开. cdq分治入门
查看>>
daily scrum 12.8
查看>>
Nginx初识
查看>>
EOJ 2847 路由结点
查看>>
题解 化学反应
查看>>
题解 楼房重建
查看>>
Python汉字转换成拼音
查看>>
高德地图:定位、覆盖物
查看>>
抽象类不能实例化对象
查看>>
树状数组(hdu-4325,hdu-1166,pat-1057)
查看>>
C#引用类型参数,ref按引用传值
查看>>
Flume简介与使用(二)——Thrift Source采集数据
查看>>
原生对象-Array
查看>>
词法解析的基本原理
查看>>
IDEA安装
查看>>
MySQL分库分表
查看>>
PyQt5--TextDrag
查看>>
Netty轻量级对象池实现分析
查看>>