【问题标题】:Why does this code only work the second time it is run?为什么此代码仅在第二次运行时才起作用?
【发布时间】:2021-06-09 01:42:42
【问题描述】:

在我的地图应用程序中,当我使用除坐标(纬度和经度)以外的所有位置数据填充表格时,我不希望用户知道/提供),这些值稍后会以编程方式确定数据所代表的地图首次加载到应用中时。

当我从主窗体加载现有地图时,代码是这样的:

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}", location, Environment.NewLine, fullAddress, latitude, 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},{4}{1}{5}", location, Environment.NewLine, fullAddress, _latitude, _longitude, mapDetailNotes),
                Content = iContentCounter,
                Background = new SolidColorBrush(GetColorForDesc(pushpinColor))
            });
            UpdateLocationWithCoordinates(location, _latitude, _longitude);
        }
    }
}

当到达上面代码中的“else”块时(当坐标在数据库中尚不存在时),第一次不会添加图钉。但是,如果我立即再次运行相同的代码,它就可以工作 - 即使不停止并重新启动应用程序。

我知道问题与异步代码有关,但为什么它从来没有(到目前为止)第一次工作,但总是(到目前为止)第二次工作?

更重要的是,有没有一种方法可以让我第一次使用它?

更新

对于 Reza Aghaei:

private void UpdateLocationWithCoordinates(string location, double latitude, 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和关闭按钮代码为:

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, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
    }
}

【问题讨论】:

  • 另外,您不应该公开您的 API 密钥。
  • LOL - Heavens2MurgatroidAndSufferinSuccotash :) 希望这是更新的假钥匙。如果没有,请编辑您的问题。
  • 这就是 git-go 的内容;是的,它是假的。
  • 您能否发布UpdateLocationWithCoordinates 方法并说明如何/何时关闭mdlDlgFrm_LoadExistingMap 对话框?
  • @Reza:完成,发布了包含该代码(以及更多)的更新。谢谢。

标签: c# wpf winforms async-await bing-maps


【解决方案1】:

据我所知,代码中的主要问题是调用async 方法而不使用await 运算符。使用 async/await 时请注意以下几点:

  1. 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 fullAddress, string mapDetailNotes, double latitude, double longitude, string pushpinColor)
    
  2. 当你想调用一个返回TaskTask&lt;T&gt;的方法时,你需要使用await操作符。

    变化:

    AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
    

    到:

    await AddPushpinToListOfPushpins(_md.LocationName, fullAddress, _md.MapDetailNotes, _md.Latitude, _md.Longitude, _md.PushpinColor);
    
  3. 当您需要使用await操作符时,包含该行代码的方法应声明为async,并且方法的返回类型应从void更改为Task(或从@ 987654335@ 至Task&lt;T&gt;)。 (唯一的例外是事件处理程序,将它们更改为async void。)

    变化:

    private void AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
    

    到:

    private async Task AddPushpinsToListOfPushpins(List<MapDetails> lstMapDetails)
    
  4. 关于第二点中提到的内容,更改:

    AddPushpinsToListOfPushpins(lstMapDetails);
    

    到:

    await AddPushpinsToListOfPushpins(lstMapDetails);
    
  5. 关于第三点(也是第一点)中提到的内容,更改:

    private void btnLoadSelectedMap_Click(object sender, EventArgs e)
    

    private async void btnLoadSelectedMap_Click(object sender, EventArgs e)
    

其他几点(与问题没有任何关系,但很好解决):

  • 当你想关闭一个对话框时,只需设置对话框结果,所以在btnLoadSelectedMap_Click中,将this.Close()替换为this.DialogResult = DialogResult.OK;,则不需要mdlDlgFrm_LoadExistingMap_FormClosed

  • 当您打开连接时,您有责任将其关闭,因此当您不再需要该连接时,任何con.Open(); 都应与con.Close(); 耦合。

【讨论】:

    【解决方案2】:

    您需要在添加图钉之前设置/更新位置坐标吗? 如果无法对此进行调试,则很难说。但感觉就像你运行 UpdateLocationWithCoordinates(location, _latitude, _longitude);在添加引脚之前调用方法,它将第一次为您工作。 我的想法是,该方法调用在第一次运行时在 Add Pins 部分之后运行。这就是为什么它会在第二次运行时成功运行并添加引脚,每次“到目前为止”。

    【讨论】:

    • 有时,至少需要坐标,因为并非每个位置都有“人类可读”的地址(例如,沉船的位置)。
    • UpdateLocationWithCoordinates 用坐标更新数据库。一旦用户使用地址创建了地图,并加载了地图,当发现坐标不在数据库中时,将进行 REST 调用以获取它们并更新表。但是,可以从调用该代码的主窗体访问 Pins 集合,并将 Pins 应用于地图 - 由于某种原因它们第一次不显示...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-18
    • 1970-01-01
    • 2023-02-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-06
    相关资源
    最近更新 更多