Why does a WPF canvas result in System.InvalidOperationException?

bhs67

Well-known member
Joined
Oct 11, 2021
Messages
52
Programming Experience
10+
With this code:

.xaml

C#:
    <Canvas x:Name="gCanvasPlotTop"
      HorizontalAlignment="Left"
      VerticalAlignment="Top"
      Margin="0,0,0,0"
      Width="500"
      Height="150" />

.cs

C#:
    for (int ii = 0; ii < iNumOfPoints; ii++) {
      var pointResult = new Point {
        X = (listPoints[ii].X - dXmin) * canvas.Width / dPlotWidth,
        Y = canvas.Height - (listPoints[ii].Y - dYmin) * canvas.Height / dPlotHeight
        };
      poXY.Points.Add(pointResult);
    }
    gCanvasPlotTop.Children.Add(poXY);

Why does executing "gCanvasPlotTop.Children.Add(poXY)" result in this message "System.InvalidOperationException: 'Specified element is already the logical child of another element. Disconnect it first.'"

The name "gCanvasPlotTop" is only in the .xaml.
 
Solution
Thanks for identifying the problem. In MainWindow I had
C#:
Polyline poXYTop = new Polyline { Stroke = Brushes.Blue};
Polyline poXYBottom = new Polyline { Stroke = Brushes.Blue};
OnePlot(listdSignalXValues, listdSignalYValues, poXYTop, "Signal");
TwoPlots(listdSignalXValues, listdSignalYValues, poXYTop, "Signal DupTop",
         listdSignalXValues, listdSignalYValues, poXYBottom, "Signal Bottom");


I had assumed that poXYTop would not return poXYTop with additional info as it was not a ref poXYTop. Now I know.

This works:

C#:
Polyline poXY = new Polyline { Stroke = Brushes.Blue };
Polyline poXYTop = new Polyline { Stroke = Brushes.Blue };
Polyline poXYBottom = new Polyline { Stroke = Brushes.Blue }...
The error is talking about your poXY, not your gCanvasPlotTop.
 
As a quick aside, if you are going to use Hungarian naming, make sure to use it properly. The g prefix means global. Your canvas is not global. It is a member of of the window, page, or control that contains it. Additional, C# has no globals.

iNumOfPoints should be cpoint which in Hungarian naming means "count of points".

dXmin and dYMin should be dxMin and dyMin. Part me thinks that you may actually mean cxMin and cyMin, but without seeing the context of how those variables are initialized, I can't really tell.

dPlotWidth and dPlotHeight should be cxPlot and cyPlot.

ii should just be i or iPoint.
 
Last edited:
I use a variation of Hungarian. The "g" means global within that .xaml / .cs. The iNumOfPoints means that is an int. An "adValues" means an array of doubles. A "listdParams" means List<double>.

Below is more of the code. Before I call dlgDisplayTwoXYPlots(), I define poXY:

C#:
Polyline poXYTop = new Polyline { Stroke = Brushes.Blue };
Polyline poXYBottom = new Polyline { Stroke = Brushes.Blue };

C#:
//------------------------------
public dlgDisplayTwoXYPlots(List<double> listdParamsTop,
                            List<Point> listPointsTop,
                            Polyline poXYTop,
                            List<double> listdParamsBottom,
                            List<Point> listPointsBottom,
                            Polyline poXYBottom)
{
  InitializeComponent();
  glistdParamsTop = listdParamsTop;
  glistPointsTop = listPointsTop;
  gpoXYTop = poXYTop;
  glistdParamsBottom = listdParamsBottom;
  glistPointsBottom = listPointsBottom;
  gpoXYBottom = poXYBottom;
}//DlgPlotXY()
//------------------------------
//------------------------------
private void Window_Loaded(object sender, RoutedEventArgs e)
{
  Plot(gCanvasPlotTop, gpoXYTop, glistdParamsTop, glistPointsTop);
  Plot(gCanvasPlotBottom, gpoXYBottom, glistdParamsBottom, glistPointsBottom);
}//Window_Loaded
//------------------------------
//------------------------------
private void Plot(Canvas canvas, Polyline poXY, List<double> listdParams, List<Point> listPoints)
{
  int iii = 0;
  int iNumOfPoints = (int)listdParams[iii++];
  double dXmin = listdParams[iii++];
  double dXmax = listdParams[iii++];
  double dYmin = listdParams[iii++];
  double dYmax = listdParams[iii++];

  double dPlotWidth = dXmax - dXmin;
  double dPlotHeight = dYmax - dYmin;

  for (int ii = 0; ii < iNumOfPoints; ii++) {
    var pointResult = new Point {
      X = (listPoints[ii].X - dXmin) * canvas.Width / dPlotWidth,
      Y = canvas.Height - (listPoints[ii].Y - dYmin) * canvas.Height / dPlotHeight
    };
    poXY.Points.Add(pointResult);
  }
  canvas.Children.Add(poXY);
//------------------------------
 
In Hungarian notation, arrays are denoted by rg, or arr. So it looks like you are trying do your own version of Systems Hungarian rather than Apps Hungarian as envisioned by Simonyi in his thesis. Most people using Hungarian will use the prefix m_ on _ to denote that something is a member of class, which is what happens in the case of the .xaml / .cs partial classes.

Anyway, it looks like your poXYTop and poXYBottom that are passed in on lines 4 and 7 are already children of another element, and so you are getting the exception thrown.
 
Thanks for identifying the problem. In MainWindow I had
C#:
Polyline poXYTop = new Polyline { Stroke = Brushes.Blue};
Polyline poXYBottom = new Polyline { Stroke = Brushes.Blue};
OnePlot(listdSignalXValues, listdSignalYValues, poXYTop, "Signal");
TwoPlots(listdSignalXValues, listdSignalYValues, poXYTop, "Signal DupTop",
         listdSignalXValues, listdSignalYValues, poXYBottom, "Signal Bottom");


I had assumed that poXYTop would not return poXYTop with additional info as it was not a ref poXYTop. Now I know.

This works:

C#:
Polyline poXY = new Polyline { Stroke = Brushes.Blue };
Polyline poXYTop = new Polyline { Stroke = Brushes.Blue };
Polyline poXYBottom = new Polyline { Stroke = Brushes.Blue };
OnePlot(listdSignalXValues, listdSignalYValues, poXY, "Signal");
TwoPlots(listdSignalXValues, listdSignalYValues, poXYTop, "Signal DupTop",
         listdSignalXValues, listdSignalYValues, poXYBottom, "Signal Bottom");


You are correct about standard Hungarian notation.

I find my Hungarian variation code is easier to understand, with fewer mistakes. There is no doubt with the variation I use. :) Thanks!
 
Last edited:
Solution
I'm not allowed to mark your post as a solution too. You should get credit as you helped me find the solution.
 
No problem. Just tap on the up pointing caret to provide some points.
 
I forgot to mention, many years ago I switched --- for(int i = 0; --- to --- for (int ii = 0; --- because it was easier to find ii.

When there was an inside --- for --- I used iii.
 
I agree for the bad old days when editors (or worse monitors and printers) had fixed fonts. With customizable fonts, syntax coloring find the i is much easier. One of the teams I worked with had the similar naming convention for i, j, and l used as indexes because we used to do code reviews on paper printouts.
 
Last edited:
Back
Top Bottom