3

I've got a Bing Map element in UserControl1.xaml file:

<UserControl x:Class="MyMaps.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   xmlns:m="clr-namespace:Microsoft.Maps.MapControl.WPF;assembly=Microsoft.Maps.MapControl.WPF">
    <Grid>
        <m:Map CredentialsProvider="Gr8GooglyMoogly" x:Name="myMap" />
    </Grid>
</UserControl>

I can access it like so from Form1, on which it sits:

this.userControl11.myMap.Mode = new RoadMode();

...but when I try to access it from another form, none of these attempts work:

userControl11.myMap.Children.Add(pin); // does not exist in the current context
Form1.userControl11.myMap.Children.Add(pin); // inaccessible due to its protection level
UserControl1.myMap.Children.Add(pin); // object reference is required for the static field, ...

How can I get a handle on the UserControl from another form?

UPDATE

Using Reza's comment to change the Modifier property of the map from Private to Public, and utilizing the method shown at the link provided, the following works:

var frmMain = new Form1();
frmMain.userControl11.myMap.Children.Add(pin);

UPDATE 2

Reza's idea worked perfectly. This is how I tested it to verify:

In "Form 2" (mdlDlgFrm_AddNewLocation):

// to access map on main form (Form1)
private Form1 frmMain;

// second constructor so as to access map on main form (to add pushpins)
public mdlDlgFrm_AddNewLocation(Form1 f1)
{
    InitializeComponent();
    this.frmMain = f1;
    // test
    AddPushpin("blaJustATest");
}

private void AddPushpin(string fullAddress)
{
    Pushpin pin = new Pushpin();
    // "brute-forcing" the coordinates for this test
    pin.Location = new Location(37.1481402218342, -119.644248783588); // Interesting location: out in the "boondocks" between Oakhurst and Auberry
    this.frmMain.userControl11.myMap.Children.Add(pin);
}

...and "Form 2" being invoked from the main form (Form 1):

private void addLocationToolStripMenuItem_Click(object sender, EventArgs e)
{
    mdlDlgFrm_AddNewLocation frmAddNewLocation = new mdlDlgFrm_AddNewLocation(this);
    frmAddNewLocation.ShowDialog(this);
    frmAddNewLocation.Dispose()
}
B. Clay Shannon-B. Crow Raven
  • 8,547
  • 144
  • 472
  • 862
  • 1
    You may want to consider a few options: [Interaction between forms — How to change a control of a form from another form?](https://stackoverflow.com/a/38769212/3110834). *Manipulate first form from second form* section contains options for your case. The easiest is changing the access modifier of the control. To do so, choose the userControl11 in design mode, then in the properties window set the [Modifiers](https://docs.microsoft.com/en-us/dotnet/desktop/winforms/advanced/how-to-use-the-modifiers-and-generatemember-properties?view=netframeworkdesktop-4.8&WT.mc_id=DT-MVP-5003235) to public. – Reza Aghaei Jan 16 '21 at 18:19
  • 1
    To be more precise here, you should choose `elementHost1` on design mode and change its Modifiers property to Public. As a result the designer also makes the `userControl11` public. – Reza Aghaei Jan 17 '21 at 14:53
  • I did that (changed Modifiers to Public) and still get the same error messages for the code shown in the question. – B. Clay Shannon-B. Crow Raven Jan 17 '21 at 15:37
  • 1
    If you mean you tried the exact code that I see in the question, then I'd say it definitely should show error. You need to pass an instance of `Form1` to your `Form2`, store it in a member field like `form1`. Then if you have set the `elementHost1` and `userControl11` as public, f`orm1.userControl11.myMap.Children.Add(pin);` will work – Reza Aghaei Jan 17 '21 at 15:41
  • 1
    The section **Example 7** in the linked post explains the solution well. It shows you how to pass an instance of Form1 to Form2 and how to access a public control of Form1 in Form2. – Reza Aghaei Jan 17 '21 at 15:45
  • 1
    Thanks! If you make these comments an answer, I will accept it and bountify it ASAP (see my Update) – B. Clay Shannon-B. Crow Raven Jan 17 '21 at 15:46
  • 1
    Warning: `var frmMain = new Form1();` looks wrong. You are creating a new instance of Form1, it's not the open instance that you are seeing and working with. You have solved the compile error, but it won't work in action. Instead, when creating Form2, you need to pass `this` to Form2 and store it in a member field in Form2 and use it. – Reza Aghaei Jan 17 '21 at 15:47
  • Well, I added two more options here, in addition to what I've explained in the linked post. – Reza Aghaei Jan 20 '21 at 09:42

2 Answers2

1

When you create a new form, you must send the current form as a parameter to the creator of the new form, where you can make changes to that instance using the instance you submitted.

Meysam Asadi
  • 6,438
  • 3
  • 7
  • 17
1

In addition to all the options that I've already explained in the linked post and has been told in other posts, including the one that I mentioned in the comments, you can consider the following options here:

  1. If you use ShowDialog to show Form2, you don't need a reference to Map or to Form1. Just return Pushpin from Form2 and use it.
  2. OR Just pass the dependency, the Map to your second form. (No need to make the usercontrol public or pass the whole form1).

Example 1 - Return Pushpin

If you use ShowDialog to show Form2, you don't need a Map in Form2. Just create the Pushpin and return it back to Form1 and use it there.

  1. In Form2, define a Pin property:

    //using Microsoft.Maps.MapControl.WPF;
    //...
    public Pushpin Pin {get; set;}
    
  2. In Form2, when you want to create the Pushpin, assign it to Pin property:

    //...
    this.Pin = new Pushpin(){Location = location};
    this.DialogResult = DialogResult.OK;
    
  3. Then in Form1 when you want to show Form2:

    using(var f2 = new Form2())
    {
        if(f2.ShowDialog() == DialogResult.OK)
        {
            this.userControl11.myMap.Children.Add(f2.Pin);
        }
    }
    

Example 2 - Pass the Map

Just pass the dependency, the Map to your second form. You don't need to make the user control public or you don't need to pass the whole form1:

  1. Change Form2 constructor to accept a Map:

    //using Microsoft.Maps.MapControl.WPF;
    //...
    private Map map;
    public Form2(Map map)
    {
        InitializeComponent();
        this.map = map;
    }
    
  2. In Form1, when you want to show Form2:

    var map = this.userControl11.myMap;
    using(var f2 = new Form2(map))
        f2.ShowDialog();
    
  3. In Form2, when you want to use the map:

    //...
    var pin = new Pushpin(){Location = locatin};
    map.Children.Add(pin);
    
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
  • 1
    @B. Clay Shannon *Do you really need reference to Map?* If you just want to create a Pushpin and you don't need any other feature of the Map here, then example 1 is a better option for you. But if you need to use some other map features as well, then option 2 makes sense. – Reza Aghaei Jan 20 '21 at 10:13
  • Thanks, Reza; I changed one modal form to Example 1, but kept the other one the same as it was due to the fact that it adds multiple pushpins when the user selects an existing map. – B. Clay Shannon-B. Crow Raven Jan 20 '21 at 17:47
  • 1
    The choice is yours and you know your requirements better; however, you can easily use a property `public List Pins{ get; set;} = new List();` and then in `Form2` when you want to add pushpins, `this.Pins.Add(new Pushpin(){Location = locatin});` then later in `Form1`, `foreach(Pushpin pin in f2.Pins){this.userControl11.myMap.Children.Add(pin);}` – Reza Aghaei Jan 20 '21 at 17:54
  • 1
    Well, if you have a working solution, that's enough and you don't need to change your implementation. It's just matter of a better code and I just wanted to share another (some times considered better) option with you which you may find useful in future cases :) – Reza Aghaei Jan 20 '21 at 17:56