昨天逛论坛,看到一个哥们用 WPF 做了一个 9 宫的拼图游戏,发现初学 WPF 的人都很容易犯一个错误(当年我也犯过):把 WPF 当 WINFORM 用!所以想写一个比较符合 WPF 风格的版本(9 宫拼图一直也想做,但是懒得动,虽然也不要几行代码),于是就抽工作的空余时间(好吧,挪用了点工作时间)做了一个,其实就是因为很久没写博客了,写个出来凑数 o(╯□╰)o.效果如下:
(图片为:阿普利亚 GPR125)
代码简要解释:主窗口,只是生成核心类,和绑定主要事件(比较简单,就没有使用什么 Command 之类的了),主要逻辑都在 VM 类里面
public partial class MainWindow: Window {
PicManager manager;
public MainWindow() {
InitializeComponent();
manager = new PicManager("", 3, 3);
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
DataContext = manager;
}
private void Kong_KeyDown(object sender, KeyEventArgs e) {
switch (e.Key) {
case Key.Up:
manager.MovePic(Direction.D_Up);
break;
case Key.Down:
manager.MovePic(Direction.D_Down);
break;
case Key.Left:
manager.MovePic(Direction.D_Left);
break;
case Key.Right:
manager.MovePic(Direction.D_Right);
break;
case Key.R:
manager.Random(50);
break;
}
if (manager.Step != 0 && manager.CheckOVER()) {
MessageBox.Show("YOU WIN!");
manager.Step = 0;
}
}
}
PicViewMod:是每一个小拼图的类,主要是保存一个图片的ID号,当ID变化,会通过WPF的绑定功能,自动更新界面的图片,而不是用事件去刷新!这是WPF和WINFROM之间的一个最大区别!
class PicViewMod: INotifyPropertyChanged {
int _id = -1;
public event PropertyChangedEventHandler PropertyChanged;
public PicViewMod(int i) {
_id = i;
}
public int ID {
set {
if (_id != value) {
_id = value;
PropertyChanged(this, new PropertyChangedEventArgs("ID"));
}
}
get {
return _id;
}
}
}
PicManager:主要封装了9个小拼图块,和一些操作函数,包括移动块,生成随机拼图(只是循环模拟调用了移动块的函数)
//拼图面板VM
class PicManager: INotifyPropertyChanged {
public ObservableCollection Pics {
set;
get;
}
public event PropertyChangedEventHandler PropertyChanged;
int _step;
int _pos; //空白块当前位置
int _colum,
_row; //行列数
public int Step {
set {
if (_step != value) {
_step = value;
PropertyChanged(this, new PropertyChangedEventArgs("Step"));
}
}
get {
return _step;
}
}
public PicViewMod Ori {
set;
get;
} //原图
public PicManager(string path, int col, int row) {
Pics = new ObservableCollection();
_colum = col;
_row = row;
_pos = 0;
_step = 0;
Ori = new PicViewMod(col * row);
for (int x = 0; x < _colum; x++) {
for (int y = 0; y < _row; y++) {
Pics.Add(new PicViewMod(x * _row + y));
}
}
}
void ClipPic(string path) {}
public void MovePic(Direction dir) {
switch (dir) {
case Direction.D_Up:
//键盘的向上,实际就是空白块的向下
{
if (_pos / _colum < _row - 1) {
int id = Pics[_pos + _colum].ID;
Pics[_pos + _colum].ID = Pics[_pos].ID;
Pics[_pos].ID = id;
_pos += _colum;
Step++;
}
}
break;
case Direction.D_Down:
{
if (_pos / _colum > 0) {
int id = Pics[_pos - _colum].ID;
Pics[_pos - _colum].ID = Pics[_pos].ID;
Pics[_pos].ID = id;
_pos -= _colum;
Step++;
}
}
break;
case Direction.D_Left:
{
if (_pos % _colum < _colum - 1) {
int id = Pics[_pos + 1].ID;
Pics[_pos + 1].ID = Pics[_pos].ID;
Pics[_pos].ID = id;
_pos += 1;
Step++;
}
}
break;
case Direction.D_Right:
{
if (_pos % _colum > 0) {
int id = Pics[_pos - 1].ID;
Pics[_pos - 1].ID = Pics[_pos].ID;
Pics[_pos].ID = id;
_pos -= 1;
Step++;
}
}
break;
}
}
public bool CheckOVER() {
for (int i = 0; i < _row * _colum; i++) {
if (Pics[i].ID != i) return false;
}
return true;
}
public void Random(int count) {
System.Random rand = new System.Random((int) DateTime.Now.Ticks);
for (int i = 0; i < count; i++) {
int d = rand.Next(0, 4);
MovePic((Direction) d);
}
Step = 0;
}
}
}
VIEW 的 XAML 定义:使用 ItemsContrl 来展示 Image
<Window x:Class="NineKong.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:NineKong"
Title="拼图游戏" Height="494" Width="849" Loaded="Window_Loaded" KeyDown="Kong_KeyDown">
<Window.Resources>
<local:DataConverter x:Key="conver">
</local:DataConverter>
</Window.Resources>
<Canvas Name="Kong">
<ItemsControl Height="454" Width="607" Name="Nine" ItemsSource="{Binding Pics}"
Background="Cyan">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel ItemHeight="135" ItemWidth="200" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Image Margin="2" Source="{Binding ID,Converter={StaticResource conver}}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Image Height="145" Width="219" Canvas.Left="612" Source="{Binding Ori.ID ,Converter={StaticResource conver}}"
Canvas.Top="10" />
<TextBlock Canvas.Left="726" TextWrapping="Wrap" Text="{Binding Step}"
FontSize="35" Canvas.Top="160" Width="105" Height="42" />
</Canvas>
</Window>
其他具体实现请查看源代码,代码比较简单,所以并没有什么注释.总的来说个人觉得 WINFORM 和 WPF 的主要差别:一个是事件驱动,一个是数据驱动!由于时间比较仓促,然后对 WPF 其实也不是很熟练(很早以前自学过 2 个月,然后基本没用过了),很多东西概念知道,但是真的用起来发现比较吃力,边百度边实验,对图片的操作和 GDI + 模式差别比较大,所以没有实现通过指定的图片按指定大小切图来自定义游戏...不过这篇博客主要是想演示一下 WPF 和 WINFORM 差别,真的真的非常非常大!所以也就不要太在意某些细节...
九宫拼图源码
来源: