-1

I create a usercontrol and I add it to a grid.

BuildingBlock m_Bblock = new BuildingBlocks.BuildingBlock(2, 2);

m_grid.Children.Add(m_Bblock);

The constructor of my BuildingBlock has as input, the number of buttons In and Out, which both are usercontrols. Inside the constructor I have:

 for (int kk = 0; kk < NumIn; ++kk)

{
   InButton inbtn = new InButton();

   inbtn.m_Button_Click += Button_In_Click;

   InStackPanel.Children.Add(inbtn);

   AllInBtn.Add(inbtn);
} 

And similar code for OutButton.

I need to have information about the position of the first OutButton on the grid, so I used:

OutButton btn = m_Bblock.AllOutBtn[0];
Point pt = btn.TransformToAncestor(m_grid).Transform(new Point(0, 0));

This leads to an error ('The specified Visual is not a predecessor of this Visual'). I think this is due to the fact that OutButton is not yet "created" by the system, since:

Point pt = m_Bblock.TransformToAncestor(GridDropAccept).Transform(new Point(0, 0));

works and:

OutButton btn = m_Bblock.AllOutBtn[0];
Point pt = btn.TransformToAncestor(m_grid).Transform(new Point(0, 0));

works if I call this code later (for example by a button click, after the creation event).

Why this happens? How can I refresh the "creation" of my OutButton so that I can use it for my purposes?

ASh
  • 34,632
  • 9
  • 60
  • 82
  • 1
    Layout is a multi-stage process in WPF. You cannot access the visual tree in your constructor, as you control will not have been added to the visual tree yet. You need to react to your control's [Loaded or Initialized events](https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/object-lifetime-events?redirectedfrom=MSDN) – GazTheDestroyer Nov 20 '19 at 11:44

2 Answers2

0

Why this happens?

Because the control hasn't yet been rendered on the screen when your constructor executes. This happens at a later stage.

You could your code in a Loaded event handler that will get triggered when the control is ready for interaction:

this.Loaded += (s, e) => 
{
    OutButton btn = m_Bblock.AllOutBtn[0];
    Point pt = btn.TransformToAncestor(m_grid).Transform(new Point(0, 0));
};
mm8
  • 163,881
  • 10
  • 57
  • 88
0

Thanks to mm8 and GazTheDestroyer for their suggestions on understanding the difference between initialized and loaded.

In my case a dispatcher (Is there a "All Children loaded" event in WPF) was a better solution:

Dispatcher.BeginInvoke(DispatcherPriority.Input, new Action(() => {
               prova(); })); 

with:

void prova ()
        {
            OutButton btn = m_Bblock.AllOutBtn[0];
            Point pt = btn.TransformToAncestor(GridDropAccept).Transform(new Point(0, 0));
        }

That works like a charm for me.