WPF版地图上位机 - (第二十三讲)
视频讲解如下:
工程源码下载:GPS定位系统系列教程源码下载
WPF版本的上位机使用了.net4.8的框架,同时也集成前面的DbEntity和Link两个类库,开发环境还是VS2022。参考文章:
《百度地图上显示GPS坐标》
《C#环境下使用EF操作MySql》
《如何实现TCP通信》
1、集成事项
集成后的代码如下:

界面布局图如下:

<Window x:Class="WpfApp.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:WpfApp"
mc:Ignorable="d"
Title="MainWindow" Height="480" Width="950">
<Grid ShowGridLines="False" Background="#FF0BC3F3">
<Grid.RowDefinitions>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition Height="40"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="250"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Margin="5,5,5,0" Content="本机主机地址:" FontSize="18" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<ComboBox Grid.Row="1" Grid.Column="0" Margin="5,0,5,5" x:Name="ComboBoxList" FontSize="18"/>
<Label Grid.Row="2" Grid.Column="0" Margin="5,5,5,0" Content="本机主机端口:" FontSize="18" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<TextBox Grid.Row="3" Grid.Column="0" Margin="5,5,5,5" TextWrapping="Wrap" FontSize="18" x:Name="MyPort" Text="2233" />
<Button Grid.Row="4" Grid.Column="0" Margin="5,5,5,5" FontSize="18" Content="打开网络" x:Name="OpenTcp" Click="OpenTcp_Click"/>
<Button Grid.Row="5" Grid.Column="0" Margin="5,5,5,5" FontSize="18" Content="清空日志" x:Name="ClearLog" Click="ClearLog_Click"/>
<Button Grid.Row="6" Grid.Column="0" Margin="5,5,5,5" FontSize="18" Content="主动请求" x:Name="ReadData" Click="ReadData_Click" />
<Button Grid.Row="7" Grid.Column="0" Margin="5,5,5,5" FontSize="18" Content="清除地图" x:Name="ClearMap" Click="ClearMap_Click"/>
<ListBox Grid.Row="0" Grid.Column="1" Grid.RowSpan="9" Margin="5,5,5,5" x:Name="ListBoxLog" FontSize="8" BorderThickness="10"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" Background="Black"></ListBox>
<WebBrowser Grid.Row="0" Grid.Column="2" Grid.RowSpan="9" Margin="5,5,5,5" x:Name="MyBrowser" VerticalAlignment="Stretch"
HorizontalAlignment="Center" />
</Grid>
</Window>
MainWindow.xaml.cs文件代码如下:
using DbEntity;
using DbEntity.Tables;
using Link;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfApp
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
// <summary>
/// 窗体日志信息
/// </summary>
public static ObservableCollection<TextBlock> ListBoxLogs { get; set; } = new ObservableCollection<TextBlock>();
public MainWindow()
{
InitializeComponent();
// 关联到数据
ListBoxLog.ItemsSource = ListBoxLogs;
// 绑定IP数据
ComboBoxList.Items.Add("127.0.0.1");
ComboBoxList.Items.Add(GetLocalIP());
ComboBoxList.SelectedIndex = 0;
// 启动数据库
DbInit();
MyBrowser.Navigate(new Uri(Directory.GetCurrentDirectory() + @"\html\index.html"));
}
#region 数据库
/// <summary>
/// 启动数据库
/// </summary>
private void DbInit()
{
try
{
wRes.MyDb = new MyDbContext() { config = wRes.SqlStr };
wRes.MyDb.Database.EnsureCreated();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
#endregion
#region TCP网络
/// <summary>
/// 启动TCP Server
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OpenTcp_Click(object sender, RoutedEventArgs e)
{
if (Link.Res.TSC == null || !Link.Res.TSC.IsOpen())
{
// 定义一个设备
Link.Res.Socs.Add(new Link.Equipment.GpsModel() { equi = 0x01, equino = 1.IntToByte() });
// 启动Socket Server,端口是2233
Link.Res.TSC = new Link.Sockets.TcpSocketServer(ComboBoxList.Text, int.Parse(MyPort.Text));
Link.Res.TSC.HandleRecMsg = new Link.Equipment.Server().Calculation;
Link.Res.TSC.HandleClientClose = new Link.Equipment.Server().ClientDisconnect;
Link.Res.TSC.StartServer();
// 初始化接收事件
EventInit();
OpenTcp.Content = "关闭网络";
OpenTcp.Background = Brushes.Red;
}
else
{
Link.Res.TSC.StopServer();
OpenTcp.Content = "打开网络";
OpenTcp.Background = Brushes.White;
}
}
/// <summary>
/// 事件初始化
/// </summary>
private void EventInit()
{
int color = 0;
// 保存GPS数据
Link.Res.SenseDataMsg = (dev, a, b, c) =>
{
System.Diagnostics.Debug.WriteLine($"SenseDataMsg 接收到数据:{a},{b},{c}");
// 如果用户不存在,属于非法数据,可以忽略
User user = wRes.MyDb.Tb_User.FirstOrDefault(x => x.ID == a);
if (user == null) return;
// 显示地图
this.Dispatcher.BeginInvoke((Action)delegate ()
{
List<Brush> colors = new List<Brush>() { Brushes.Yellow, Brushes.SpringGreen };
MyBrowser.InvokeScript("WriteLine", new Object[] { b, c });
TextBlock item = new TextBlock();
item.Text = $"{ListBoxLogs.Count}:{b},{c}";
item.FontSize = 14;
item.TextWrapping = TextWrapping.Wrap;
item.Foreground = colors[color];
ListBoxLogs.Insert(0, item);
color = color == 0 ? 1 : 0;
//控制只显示20条
if (ListBoxLogs.Count > 20)
{
for (int i = 10; i < ListBoxLogs.Count; i++)
{
ListBoxLogs.RemoveAt(i);
}
}
});
// 将接收到的数据保存到数据库
using (MyDbContext db = new MyDbContext() { config = wRes.SqlStr })
{
GpsData data = new GpsData()
{
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now,
UserId = user.UserId,
gps = $"{b},{c}"
};
db.Tb_GpsData.Add(data);
db.SaveChanges();
}
};
// 后面可以扩展
/* ... */
}
#endregion
/// <summary>
/// 清除地图
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClearMap_Click(object sender, RoutedEventArgs e)
{
MyBrowser.InvokeScript("clearAll");
}
/// <summary>
/// 清空日志
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClearLog_Click(object sender, RoutedEventArgs e)
{
ListBoxLogs.Clear();
}
/// <summary>
/// 主动请求数据
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ReadData_Click(object sender, RoutedEventArgs e)
{
// 由于初始化的时候,设置的设备类型是1,设备号也是1,所以这边查询的时候也要按这个参数查询
Link.Equipment.GpsModel sense = Link.Res.Socs.FirstOrDefault(v => v.equi == 1 && v.equino.ByteToInt() == 1) as Link.Equipment.GpsModel;
if (sense != null)
{
if (sense.socketstate == 1)
{
// 下发命令,可以带参数,这里用0
int ret = sense.SetState(0);
if (ret == 0) MessageBox.Show("命令发送成功");
if (ret == 1) MessageBox.Show("命令发送失败");
if (ret == -1) MessageBox.Show("网络错误");
}
else MessageBox.Show("设备链接已断开");
}
else MessageBox.Show("未查询到设备");
}
public string GetLocalIP()
{
try
{
string HostName = Dns.GetHostName(); //得到主机名
IPHostEntry IpEntry = Dns.GetHostEntry(HostName);
for (int i = 0; i < IpEntry.AddressList.Length; i++)
{
//从IP地址列表中筛选出IPv4类型的IP地址
//AddressFamily.InterNetwork表示此IP为IPv4,
//AddressFamily.InterNetworkV6表示此地址为IPv6类型
if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork)
{
string strip = IpEntry.AddressList[i].ToString();
string[] sArray = strip.Split('.');
if (sArray[3] != "1")
return IpEntry.AddressList[i].ToString();
}
}
return "";
}
catch (Exception ex)
{
return "";
}
}
}
}
2、最终演示效果

注意:如果是直接运行生成的exe文件,会提示下面这个信息,直接允许运行即可!
