What does InitializeComponent() do, and how does it work in WPF? - c#

What does InitializeComponent() do, and how does it work in WPF?
In general first, but I would especially be interested to know the gory details of order of construction, and what happens when there are Attached Properties.

The call to InitializeComponent() (which is usually called in the default constructor of at least Window and UserControl) is actually a method call to the partial class of the control (rather than a call up the object hierarchy as I first expected).
This method locates a URI to the XAML for the Window/UserControl that is loading, and passes it to the System.Windows.Application.LoadComponent() static method. LoadComponent() loads the XAML file that is located at the passed in URI, and converts it to an instance of the object that is specified by the root element of the XAML file.
In more detail, LoadComponent creates an instance of the XamlParser, and builds a tree of the XAML. Each node is parsed by the XamlParser.ProcessXamlNode(). This gets passed to the BamlRecordWriter class. Some time after this I get a bit lost in how the BAML is converted to objects, but this may be enough to help you on the path to enlightenment.
Note: Interestingly, the InitializeComponent is a method on the System.Windows.Markup.IComponentConnector interface, of which Window/UserControl implement in the partial generated class.

Looking at the code always helps too. That is, you can actually take a look at the generated partial class (that calls LoadComponent) by doing the following:
Go to the Solution Explorer pane in the Visual Studio solution that you are interested in.
There is a button in the tool bar of the Solution Explorer titled 'Show All Files'. Toggle that button.
Now, expand the obj folder and then the Debug or Release folder (or whatever configuration you are building) and you will see a file titled YourClass.g.cs.
The YourClass.g.cs ... is the code for generated partial class. Again, if you open that up you can see the InitializeComponent method and how it calls LoadComponent ... and much more.

Related

how to copy paste a WPF Window (clone) and not have errors

I am using .Net 4.5.2 with WPF and C# 5.0. I created a Window in a WPF project. I would like to copy paste this window in the solution explorer, making a second window, and rename it to a new name.
When I do this, the new (copied) window's InitializeComponent() method always gives me an error. How does one cleanly copy a window (and it's code, etc.) in the solution explorer?
This question was answered partially here: Copy pasting WPF window gives error however the answer did not solve my issue.
My approach (that does not work):
Create a window and title it WindowTest
In the solution explorer, select WindowTest and copy, then paste it into the same project
Rename the new copied Window to WindowTestCopy
In WindowTestCopy, change the x:class property in xaml to be WindowTestCopy instead of WindowTest
Open the code behind in WindowTestCopy, and change any references to WindowTest to WindowTestCopy
Compile
Expected: no errors, the copy (clone) operation is successful
Actual: compile error "Cannot access non-static method 'InitializeComponent' in static context".
I have only this one error. Obviously InitializeComponent() is becoming an ambiguous reference, but it isn't clear to me how to make manual edits to the code to fix this. I wish that VS or Resharper would automatically assist me with this.
UPDATE
WindowTest contains two userControls that I had not mentioned previously. After the copy/paste occurs, for some reason the following xaml elements became malformed within WindowTestCopy:
xmlns:userControls....(ellided)
xmlns:userControls....(ellided)
By deleting these, Resharper determined that the userControl objects were missing xmlns reference tags and asked me if I wanted to import them automatically. I selected yes. After Resharper added the missing xmlns reference tags I was able to compile (all errors disappeared).
I do not have an explanation for why this happened. In my steps to reproduce, I do not edit the xaml and it should therefore be identical to the originating xaml. This is curious behavior, but at least there is a workaround as stated.
When it happened to me, it went like this:
Copy the xaml in solution explorer.
Rename the xaml in solution explorer.
Rename the x:Class in the xaml.
Rename the class name in the xaml.cs.
Rename the constructor in the xaml.cs.
Why is everything still broken? <== (you are here probably)
Realize there is secret voodoo witchcraft underneath.
Copy the contents of the xaml and the xaml.cs to a couple of notepads.
Delete the xaml from solution explorer.
Create a new xaml in the solution explorer with the name you wanted.
Overwrite both the xaml and the xaml.cs with the exact contents of the two notepads.
Errors are gone.
Someone else actually posted and then deleted had the correct idea, but brief on why.
The issue you were running into is due to a duplication of a class within the same project... even though in the solution explorer you can easily copy/paste a WPF form or any other class, user control class, etc. When copied, it suffixes the file names to " - Copy", and if you compile right away will fail. Reason being? In the class itself the class name and window name is the same. So, in the project you have TWO instances of the class and it is choking on that hoping for you to resolve the duplicates. I ran into this exact same thing early on in my WPF development.
So, now that I've explained WHY it is failing, here is what you would need to do, to correct the issue. I created a window in my sample project called "FirstWindowSample", so two files are created FirstWindowSample.xaml and FirstWindowSample.xaml.cs. The .cs version is the code-behind of the window (or user control class, the principle is EXACTLY the same.
If you look in the "FirstWindowSample.xaml" code (the visual-design version), the code will look something like...
<Window x:Class="YourProject.FirstWindowSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="FirstWindowSample" Height="300" Width="300">
<Grid>
</Grid>
</Window>
Notice in the first line
x:Class="YourProject.FirstWindowSample"
You need to change the ".FirstWindowSample" to whatever you want for your new form such as
x:Class="YourProject.SecondWindowSample"
Next, change over to the CODE-BEHIND file "FirstWindowSample.xaml.cs".
namespace YourProject
{
/// <summary>
/// Interaction logic for FirstWindowSample.xaml
/// </summary>
public partial class FirstWindowSample : Window
{
public FirstWindowSample()
{
InitializeComponent();
}
}
}
This is the hard-reference of the class name and its corresponding constructor. Just change these to match the new window name you are expecting (such as sample SecondWindowSample)
namespace YourProject
{
/// <summary>
/// Interaction logic for SecondWindowSample.xaml
/// </summary>
public partial class SecondWindowSample : Window
{
public SecondWindowSample()
{
InitializeComponent();
}
}
}
At the end your project will now have a completely different class name that is not duplicated and cause compile errors. The FIRST time you run into this, yeah... a pain to find out. Once you get it, the next 2-3 times are the "oh-yeah" refresher... After that you sort of do it without thinking about it.
The issue was resolved by deleting all xlmns elements in the xaml and letting resharper automatically add them back in. I am not satisfied with this solution because it is not logical; however, it did resolve the compile error.

Change properties in a class using a form from a different class

I have a number of propertied in a class that creates a pdf document that need to be set before the pdf is created. This class is named generatePDF. I would like be able to change the properties using a form and persist the properties.
INotifyPropertyChanged was implemented in the generatePDF class. I used the Data Source Configuration Wizard (Open DataSources vertical tab on the far right side of the screen , click the upper left icon to add a Data source) to bind the GeneratePDF class. Now in the Datasources windows I can see all the properties and can easily drag them onto a configuration form.
I have a method in the generatePDF class that puts initial values into the properties. I ran this method before showing the configuration form.
When the configuration form is shown the textboxes that I would expect to be filled with the initial values are blank. When I enter a value that should change the property in my GeneratePDF instance, the property is not changed. In addition, there is control automatically added to the configuration form that apparently allows the user to step through different instances of the GeneratePDF class -- like rows in a database table. The configuration form doesn't appear to be bound to the single instance of the GeneratePDF class.
I tried to change the GeneratePDF and all the properties and methods of the to static, but when that was completed and error "... can't implement INotifyPropertyChanged on a static class..." was shown.
I'm guessing I'm not the first one to experience this problem but I was unable to state my question clearly enough so that a google search would find an answer or a tutorial.
I'd appreciate help binding a specific instance of a class to a configuration form.
OK, I've been working on this all day, and I think I've found a solution.
When I added the project data source GeneratePDF, a ??what does microsoft call this?? 'GeneratePDFBindingSource' was added to the project. I went ahead and drug all the properties from this class onto a configuration form.
I modified the constructor of the configuration form to the the instance of GeneratePDF as a paramter.
The constructor is also modified. After Initialize Component runs, the configuration form binding source is just a 'typeOf' GeneratePDF. The DataSource property of GeneratePDFBindingSource was updated to point to the instance which I just passed in.
However, when the form was displayed the values STILL were showing blanks. I looked at the system generated code for the configuration and noticed that there was an EndEdit() method that could be called when changes were completed. Adding this to the constructor and... it worked!
So below are the few simple lines of finished code. I'm posting this answer in case others happen upon this question, or if I forget how to do this at some time in the future and need a reference.
namespace com.myCompany.myApp
{
public partial class frm_Configuration : Form
{
public frm_Configuration(GeneratePDF generatePdf)
{
InitializeComponent();
GeneratePDFBindingSource.DataSource = generatePdf;
GeneratePDFBindingSource.EndEdit();
}
}
}

Prevent Designer from removing Attributes on Control members

I want to mark certain controls on my Windows Form with Attributes. So I added the Attribute in my TestAttributes.Designer.cs:
[AmbientValue(true)]
private System.Windows.Forms.Label label1;
But whenever I change the Modifiers-property of label1 using the properties-window of the designer, the Designer silently removes my Attribute from the declaration:
public System.Windows.Forms.Label label1;
I tried putting the declaration in the TestAttributes.cs to not mess with the .Designer.cs file. But when changing the Modifiers property the declaration is moved back to TestAttributes.Designer.cs and the Attribute is gone.
How can I prevent the Designer from removing my Attributes?
EDIT:
The question should better be: Can I permanently move the declaration of a control out of the *.Designer.cs file, so I can apply an Attribute there? As I wrote above, it gets moved back in some cases.
Thank you!
richn
If you want to keep designer support, I recommend using an external class library.
namespace ClassLibrary1
{
[System.ComponentModel.AmbientValue(true)]
public class TestClass : System.Windows.Forms.Label
{
}
}
You can compile something like this to a class library, and then import it to visual studio. If you don't know how to do that, follow the instructions below.
Right click the toolbox
Click "Choose Items"
Wait. Do not make the mistake of terminating visual studio now.
Select Browse.
Select your dll file.
I normally don't like people who give the "don't do it" answer, but don't do it.
Changing the designer class is dangerous, and can almost always lead to unexpected consequences. The designer code is reserved for the compiler, and it should always look like the compiler expects it to look.
If you really wanted attributes in what would otherwise be your designer code, you should instead make an empty application, and do the forms code yourself. You shouldn't be trying to modify designer code anyway, if you don't understand how to do it from scratch
If that seems like a lot of work, you can always try to use the designer as a model, and then copy that over to a new, non-windows forms project.
Untill recently i was lead to believe that designer was just part of the code and it was not to be touched. I was wrong. Turns out you can rip the entire thing out and then compiler will throw an error at you. Okay, that's fine, just define your own stuff, completely thus eliminating the necessity for a designer. here is how. Say you have a tag
<div id="div1" runat="server">
and you want to make it invisible on the back side without any designer.cs file present (we deleted it). First reference it along with other declarations (i.e. outside of page_load, somewhere on the outside)
protected System.Web.UI.HtmlControls.HtmlGenericControl div1;
Remember however, you MUST reference EVERYTHING that you use on the front side that has a runat="server" tag. So say you have a page with only a runat="server" label, on the back we reference to it as
namespace yournamespace.something
{
public class yourpagetitle: System.Web.UI.Page
{
protected System.Web.UI.WebControls.Image imgLogo;
protected System.Web.UI.WebControls.TextBox tbDate;
protected System.Web.UI.WebControls.Label yourLabelName;
Notice I added image and a textbox as additional examples. And huzza! You have thus completely eliminated the need for designer.cs file. Oh, and do notice, designer.cs file is nothing more than what we just did above, but it does it for you automatically. ...most of the time... When it fails, time to handle things your own way. I'm sure if you reformat your computer, reinstall everything, etc etc etc things will work again, no doubt. This is just a legitimate work around for those who do not have time to babysit and troubleshoot every little hick-up Microsoft does.

Static reference to instance in IE Toolbar

I am having an interesting issue with a COM component written to function as a toolbar in IE. Basically if you open up several tabs at once in IE the individual instances of the COM objects get all twisted around. Bear with me here.
Say I open up five browser tabs all at once by right clicking several different links and opening them in new tabs. Now a function of my toolbar involves selecting text in the web page and then clicking a button to copy that text into the Toolbar. So let's do that in tab 3. We select text and click the button and nothing is there. However, if we select text in tab 2, then go back to tab 3 and click the button we get the text selected in tab 2. So...the toolbar in tab 3 getting stuff from tab 2. Not good.
I have traced this problem back to static references inside our COM object, the toolbar.
[ComVisible(true), Guid("2CC75392-1182-470D-BECC-EFA33E629AB8")]
[CLSCompliant(false)]
public sealed class Toolbar : ADXIEToolbar
{
public static Toolbar Instance;
public Toolbar()
{
Instance = this;
InitializeComponent();
}
...other code...
}
Note only one toolbar instance exists per each IE tab.
This reference doesn't get assigned properly, almost like it isn't thread safe (it isn't) but instead not domain safe or something. It will sometimes reference another instance down the line. Same with other static fields and even thread-safe singletons. I don't get it.
Also note that if I pass a reference to this toolbar (inside InitializeComponent) to a control I have the same issue.
this.publicationDateCb.Toolbar = this;
This reference will sometimes point to a different tab.
If I use a purely subscription based model with absolutely zero static references with the toolbar as the referee then things seem to work fine. This basically means I would have to re-design the program to where no classes interacted with each other directly - they fire events that the toolbar subscribes to, calling methods in other classes. Ouch.
So should I go with that model (which may be ideal but I am pretty far along here) or is there a simple fix I am missing here?
Other notes:
All IE tabs are running in seperate processes.
The BHO/Toolbar is running in the same process as the IE tab.
I am using Add-In-Express for Internet Explorer to handle the IE integration.
The project is written for .NET 3.5; the loader uses .NET 2.0
If you want to share your selected text within all your toolbars you can look at: http://www.add-in-express.com/creating-addins-blog/2009/06/19/internet-explorer-plugin-settings-synchronize/
Problem solved but static references are gone. I did a few things:
First off, I changed the target .NET version to 4.0. Apparently BHOs written in 4.0 work better - I can't find a link to substantiate this claim but I have read it somewhere.
More importantly I did away with static references within the assembly altogether. I got rid of the singletons and instead created a property for each former singleton class in my Toolbar class, which will always be unique. I then passed a reference to the Toolbar whenever a class needed to reference a former singleton.
So...constructors look like this now:
internal class RegistryData
{
public RegistryData(Toolbar toolbar)
{
ToolbarRef = toolbar;
}
...
}
And let's say RegistryData needs to call Messaging.
private void RegistryUpdated(int keyId)
{
ToolbarRef.Messaging.SendMessage(keyId);
}
Huge pain, right? Hours of work. But problem solved. I would not be shocked if this issue were related exclusively to Add-In-Express.

Where is the rest of my windows form code

Looking at the source code for a windows form application, the class declaration states its a partial class. I understand that this means there are parts of the class in different physical files.
The code in MyForm.designer.cs doesnt appear to have a constructor or any means of generating the form. So my question is, where do i find the rest of the code for my windows form?
The constructor for MyForm is in the main MyForm.cs file. Because it is partial, the constructor can reside in MyForm.cs, and the generated code can stay in MyForm.Designer.cs, allowing for separation of generated and developer-created code.
Use the View Code option to see the actual source of MyForm.cs, which has a constructor as well as all of your code.
MyForm.cs has the constructor and MyForm.designer.cs has the function private void InitializeComponent() which will be called from the constructor (in MyForm.cs).
In private void InitializeComponent() your components will be created and initialized.
Using Partial keyword code can be reside in multiple classes.When you add a window form that creates three files 1. Code file(.CS) 2. Designer file(.Designer.cs) 3. Your Design part. All used Partial keyword.
If you want to see code then double click on design form you will direct to code file there you can find the code and you can handle all the code and events(Developer's Code) like constructor and all.
In Designer.cs you initialize the controls their control properties.It's system generated code but still you can modify.
Hope this helps.....
Double click on the form will take you there.

Categories

Resources