问题描述
在我的地图应用程序中,当我使用除坐标(纬度和经度)之外的所有位置数据填充表格时——我不希望用户知道/提供),这些值稍后以编程方式确定数据所代表的地图首次加载到应用中时。
当我从主窗体加载现有地图时,代码是这样的:
private void loadExistingMapToolStripMenuItem_Click(object sender,EventArgs e)
{
bool pushpinsAdded = false;
RemoveCurrentpushpins();
using (var frmloadExistingMap = new mdlDlgFrm_LoadExistingMap())
{
if (frmloadExistingMap.ShowDialog() == DialogResult.OK)
{
foreach (pushpin pin in frmloadExistingMap.Pins)
{
this.userControl11.myMap.Children.Add(pin);
pushpinsAdded = true;
}
}
}
if (pushpinsAdded)
{
RightsizeZoomLevelForAllpushpins();
}
lblMapName.Text = currentMap;
lblMapName.Visible = true;
}
“加载现有地图”表单 (frmloadExistingMap) 中的代码确定地址的坐标。如果坐标已经存在,太好了;否则,将进行 REST 调用以获取这些值:
private async void AddpushpinTolistofpushpins(string location,string fullAddress,string mapDetailNotes,double latitude,double longitude,string pushpinColor)
{
iContentCounter = iContentCounter + 1;
string toolTip = string.Empty;
// if already have the record,including the coordinates,no need to make the REST call
if ((latitude != 0.0) && (longitude != 0.0))
{
if (mapDetailNotesExist(location))
{
toolTip = String.Format("{0}{1}{2}{1}{3},{4}{1}{5}",location,Environment.NewLine,fullAddress,latitude,longitude,mapDetailNotes.Trim());
}
else
{
toolTip = String.Format("{0}{1}{2}{1}{3},{4}",longitude);
}
var _mapLocation = new Microsoft.Maps.MapControl.WPF.Location(latitude,longitude);
this.Pins.Add(new pushpin()
{
Location = _mapLocation,ToolTip = toolTip,Content = iContentCounter,Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
});
}
else
{
// from https://stackoverflow.com/questions/65752688/how-can-i-retrieve-latitude-and-longitude-of-a-postal-address-using-bing-maps
var request = new GeocodeRequest();
request.BingMapsKey = "heavens2MurgatroidAndSufferinSuccotash";
request.Query = fullAddress;
var result = await request.Execute();
if (result.StatusCode == 200)
{
var toolkitLocation = (result?.ResourceSets?.FirstOrDefault())
?.Resources?.FirstOrDefault()
as BingMapsRESTToolkit.Location;
var _latitude = toolkitLocation.Point.Coordinates[0];
var _longitude = toolkitLocation.Point.Coordinates[1];
var mapLocation = new Microsoft.Maps.MapControl.WPF.Location(_latitude,_longitude);
this.Pins.Add(new pushpin()
{
Location = mapLocation,ToolTip = String.Format("{0}{1}{2}{1}{3},_latitude,_longitude,mapDetailNotes),Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
});
UpdateLocationWithCoordinates(location,_longitude);
}
}
}
当到达上面代码中的“else”块时(当坐标在数据库中尚不存在时),不会第一次添加图钉。但是,如果我立即再次运行相同的代码,它仍然可以工作 - 即使没有停止并重新启动应用程序。
我知道这个问题与异步代码有关,但为什么它第一次(到目前为止)从来没有工作,但第二次总是(到目前为止)工作?
更重要的是,有没有一种方法可以让我第一次看到它起作用?
更新
对于 Reza Aghaei:
private void UpdateLocationWithCoordinates(string location,double longitude)
{
String query = "UPDATE CartographerDetail " +
"SET Latitude = @Latitude," +
"Longitude = @Longitude " +
"WHERE FKMapName = @FKMapName AND LocationName = @LocationName";
try
{
var con = new sqliteConnection(connStr);
con.open();
sqliteCommand cmd = new sqliteCommand(query,con);
// FKMapName and LocationName are for the WHERE clause
cmd.Parameters.AddWithValue("@FKMapName",mapName);
cmd.Parameters.AddWithValue("@LocationName",location);
// Updated values
cmd.Parameters.AddWithValue("@Latitude",latitude);
cmd.Parameters.AddWithValue("@Longitude",longitude);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
这是“加载现有地图”表单:
点击它的“加载选定地图”按钮执行以下操作:
private void btnLoadSelectedMap_Click(object sender,EventArgs e)
{
string latitudeAsstr = string.Empty;
string longitudeAsstr = string.Empty;
List<MapDetails> lstMapDetails = new List<MapDetails>();
MapDetails md;
. . .
try
{
Form1.currentMap = mapName;
string qry = "SELECT LocationName,Address1,Address2,City,StateOrSo,PostalCode," +
"MapDetailNotes,Latitude,Longitude " +
"FROM CartographerDetail " +
"WHERE FKMapName = @FKMapName";
var con = new sqliteConnection(connStr);
con.open();
sqliteCommand cmd = new sqliteCommand(qry,con);
cmd.Parameters.AddWithValue("@FKMapName",mapName);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
md = new MapDetails();
md.LocationName = reader.GetValue(LOC_NAME).ToString().Trim();
. . .
latitudeAsstr = reader.GetValue(LATITUDE).ToString();
if (string.IsNullOrEmpty(latitudeAsstr))
{
md.Latitude = 0.0;
}
else
{
md.Latitude = Convert.Todouble(reader.GetValue(LATITUDE).ToString());
}
longitudeAsstr = reader.GetValue(LONGITUDE).ToString();
if (string.IsNullOrEmpty(longitudeAsstr))
{
md.Longitude = 0.0;
}
else
{
md.Longitude = Convert.Todouble(reader.GetValue(LONGITUDE).ToString());
}
lstMapDetails.Add(md);
}
AddpushpinsTolistofpushpins(lstMapDetails);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
// All pushpins have been loaded
this.Close();
}
它的 Form_Closed 和 Close 按钮代码是:
private void mdlDlgFrm_LoadExistingMap_FormClosed(object sender,FormClosedEventArgs e)
{
this.DialogResult = DialogResult.OK; // Would this be a problem,assuming "OK" is always okay?
}
private void btnClose_Click(object sender,EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
}
作为奖励,AddpushpinsTolistofpushpins() 代码 - 在循环中调用 AddpushpinTolistofpushpins() - 是:
private void AddpushpinsTolistofpushpins(List<MapDetails> lstMapDetails)
{
string fullAddress;
foreach (MapDetails _md in lstMapDetails)
{
fullAddress = string.Format("{0} {1} {2} {3}",_md.Address,_md.City,_md.StateOrSo,_md.PostalCode).Trim();
AddpushpinTolistofpushpins(_md.LocationName,_md.MapDetailNotes,_md.Latitude,_md.Longitude,_md.pushpinColor);
}
}
解决方法
据我所知,代码中的主要问题是在不使用 async
运算符的情况下调用 await
方法。使用 async/await 时请考虑以下几点:
-
async void
适用于事件处理程序,但不适用于您需要在代码中直接调用的方法。变化:
private async void AddPushpinToListOfPushpins(string location,string fullAddress,string mapDetailNotes,double latitude,double longitude,string pushpinColor)
到:
private async Task AddPushpinToListOfPushpins(string location,string pushpinColor)
-
当您要调用返回
Task
或Task<T>
的方法时,您需要使用await
运算符。变化:
AddPushpinToListOfPushpins(_md.LocationName,fullAddress,_md.MapDetailNotes,_md.Latitude,_md.Longitude,_md.PushpinColor);
到:
await AddPushpinToListOfPushpins(_md.LocationName,_md.PushpinColor);
-
当你需要使用
await
操作符时,包含那行代码的方法应该声明为async
,并且方法的返回类型应该从{{1} } 到void
(或从Task
到T
)。 (唯一的例外是事件处理程序,将它们更改为Task<T>
。)变化:
async void
到:
private void AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
-
关于第二点,修改:
private async Task AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
到:
AddPushpinsToListOfPushpins(lstMapDetails);
-
关于第三点(也是第一点)提到的,改变:
await AddPushpinsToListOfPushpins(lstMapDetails);
到
private void btnLoadSelectedMap_Click(object sender,EventArgs e)
其他一些要点(与问题无关,但很好解决):
-
当你想关闭一个对话框时,只需设置对话框结果,所以在
private async void btnLoadSelectedMap_Click(object sender,EventArgs e)
中,将btnLoadSelectedMap_Click
替换为this.Close()
,那么就不需要{{1 }}。 -
当您打开一个连接时,您有责任关闭它,因此当您不再需要该连接时,任何
this.DialogResult = DialogResult.OK;
都应该由mdlDlgFrm_LoadExistingMap_FormClosed
耦合。
在添加图钉之前是否需要设置/更新位置坐标? 如果无法调试,就很难说。但感觉就像你运行 UpdateLocationWithCoordinates(location,_latitude,_longitude);在添加引脚之前调用方法,它将第一次为您工作。 我的想法是,该方法调用在第一次运行的 Add Pins 部分之后运行。这就是为什么它每次“到目前为止”运行并在第二次运行时成功添加引脚的原因。