Kenshi Hemura
Kenshi Hemura

Reputation: 135

Specified Visual is already a child of another Visual or the root of the component target

Hi guys, i've been working and searching for a report using WPF C# and found out one of the a nice report and easy also, found this link and use it. So i tried to use it, pls check my code,

in my EmployeeProfileWindow in print button,

private void btnprintviolation_Click(object sender, RoutedEventArgs e)
{
    ReportViolationWindow NewReportViolationWindow = new ReportViolationWindow();
    //Windows.Add(NewReportViolationWindow);
    GlobalVar.ViolationEmpNum = txtdispid.Text;
    GlobalVar.ViolationRefNumToPrint.Clear();
    for (int i = 0; i < lvviolations.Items.Count; i++)
    {
        GlobalVar.ViolationRefNumToPrint.Add(((EmpViolationObject)lvviolations.Items[i]).VioRefNum);
    }
    NewReportViolationWindow.Show();
}

So if ever i click the button it will appear a new Window name NewReportViolationWindow. I'll just copy or edited as what in the open source sample is, in Template folder. I created my report named ReportViolation,

Now here's the code behind in NewReportViolationWindow.

ReportDocument reportDocument = new ReportDocument();
string ats = new DirectoryInfo(Environment.CurrentDirectory).Parent.Parent.FullName;
StreamReader reader = new StreamReader(new FileStream(ats.ToString() + @"\Template\ReportViolation.xaml", FileMode.Open, FileAccess.Read));
reportDocument.XamlData = reader.ReadToEnd();
reportDocument.XamlImagePath = Path.Combine(ats.ToString(), @"Template\");
reader.Close();


DateTime dateTimeStart = DateTime.Now; // start time measure here
List<ReportData> listData = new List<ReportData>();
//foreach (string item in GlobalVar.ViolationRefNumToPrint)
for (int i = 0; i < 5 ; i++)
{
    ReportData data = new ReportData();

    data.ReportDocumentValues.Add("PrintDate", DateTime.Now);
    data.ReportDocumentValues.Add("EmpIDNum", NewIDNumber.ToString());
    data.ReportDocumentValues.Add("EmpName", NewEmpName.ToString());
    data.ReportDocumentValues.Add("EmpPosition", NewPosition.ToString());

    //data.ReportDocumentValues.Add("VioRefCode", item.ToString());
    listData.Add(data);
}

XpsDocument xps = reportDocument.CreateXpsDocument(listData);
documentViewer.Document = xps.GetFixedDocumentSequence();

// show the elapsed time in window title
Title += " - generated in " + (DateTime.Now - dateTimeStart).TotalMilliseconds + "ms";

}
catch (Exception ex)
{
    // show exception
    MessageBox.Show(ex.Message + "\r\n\r\n" + ex.GetType() + "\r\n" + ex.StackTrace, ex.GetType().ToString(), MessageBoxButton.OK, MessageBoxImage.Stop);
}

Now when i run my application and click the print button. Sometimes at first it will open the NewReportViolationWindow with no error but when i try to close the report or click the button again, it will give a message,

Specified Visual is already a child of another Visual or the root of the component target

Here's an image of the error,

enter image description here

I think the problem is when i call the print report which the code behind the print button, hmmm can anyone? pls... :)

2ND EDIT

About your question:

Yes correct..

Sorry I don't have any idea coz what i did is i just followed the sample in the Open Source.

So far i still don't have a code for proper closing of my ReportViolationWindow. When i click the close button and that's it, sorry for that. :(

No. as far as i know.

Upvotes: 2

Views: 6105

Answers (2)

Farhad Karbasian
Farhad Karbasian

Reputation: 9

Ok, I might have a solution. If I change that second code snippet above to

Code Block

ContainerVisual smallerPage = new ContainerVisual( );

DrawingVisual pageVisual = page.Visual as DrawingVisual;

if ( pageVisual != null && pageVisual.Parent != null )

{

ContainerVisual parent = pageVisual.Parent as ContainerVisual;

parent.Children.Remove( pageVisual );

}

smallerPage.Children.Add( page.Visual );

It seems to work. Please comment. I would LOVE to know if there is a better way. This seems like a hack.

Upvotes: 1

JerKimball
JerKimball

Reputation: 16894

You have to "detach" a Visual from it's current Parent before you can add it to a new Parent - the reason for this is primarily due to the way the rendering and composition engine works; if the original parent element did not know it wasn't in charge of rendering that child anymore, and WPF allowed you to attach it to another parent, in the best case scenario, you'd have duplicate Visuals rendered, and in a worst case scenario, you could end up in an infinite loop!

Since the responsibility is on the Parent element to add/remove children, you would need to handle this at the parent level, generally with either a call to RemoveLogicalChild or RemoveVisualChild (or ideally, removing the item itself from the original ItemsSource and adding it to the new one)

EDIT: technically, the first paragraph is true, but I don't think the second applies to you...after looking through the source code for the ReportPaginator class at WpfReports on CodePlex, I noticed the following:

  • The latest version is different from yours, I believe: the line #'s don't quite match up. Possibly this is a bug that has been fixed?
  • (Nitpicky) I am not a fan of the structure of this code (the report engine that is)...ArrayLists? Single line if/else/return statements? ugh

Now, to your actual problem:

  • You say that the report window usually opens up without an error the first time, but not after that?

  • Are there any shared resources that are being used in the ReportViolationWindow?

  • How are you disposing of/handling the close of the ReportViolationWindow?

  • Are you keeping any other references to this ReportViolationWindow instance?

One thing I would try, just to see if the error continues, is to declare a single member variable of type NewReportViolationWindow in the window that is creating it (EmployeeProfileWindow), and instead of this:

private void btnprintviolation_Click(object sender, RoutedEventArgs e)
{
    ReportViolationWindow NewReportViolationWindow = new ReportViolationWindow();

Try something like:

ReportViolationWindow _reportViolationWindow;
private void btnprintviolation_Click(object sender, RoutedEventArgs e)
{
    if(_reportViolationWindow != null)
    {
        _reportViolationWindow.Close();
        _reportViolationWindow = null;
    }
    _reportViolationWindow = new ReportViolationWindow();

Upvotes: 1

Related Questions