Subclassed form not behaving properly in Designer view (VS 2008) - c#

I have subclassed Form to include some extra functionality, which boils down to a List<Image> which displays in a set of predefined spots on the form. I have the following:
public class ButtonForm : Form
{
public class TitleButton
{
public TitleButton() { /* does stuff here */ }
// there's other stuff too, just thought I should point out there's
// a default constructor.
}
private List<TitleButton> _buttons = new List<TitleButton>();
public List<TitleButton> TitleButtons
{
get { return _buttons; }
set { _buttons = value; }
}
// Other stuff here
}
Then my actual form that I want to use is a subclass of ButtonForm instead of Form. This all works great, Designer even picks up the new property and shows it up on the property list. I thought this would be great! It showed the collection, I could add the buttons into there and away I would go. So I opened the collection editor, added in all the objects, and lo and behold, there sitting in the designer was a picture perfect view of what I wanted.
This is where it starts to get ugly. For some reason or another, Designer refuses to actually generate code to create the objects and attach them to the collection, so while it looks great in Design mode, as soon as I compile and run it, it all disappears again and I'm back to square one. I'm at a total loss as to why this would happen; if the Designer can generate it well enough to get a picture perfect view of my form with the extra behaviour, why can't/won't it generate the code into the actual code file?

First of all you need to inherit your TitleButton class from Component so that the designer knows it is a component that can be created via designer generated code. Then you need to instruct the designer code generator to work on the contents of the collection and not the collection instance itself. So try the following...
public class TitleButton : Component
{
// ...
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<TitleButton> TitleButtons
{
// ...
}

Related

How to serialize & deserialize user inputs gathered from inside a panel?

I'm creating an winforms application, that has the user make inputs in different panels. I already wrote a method to traverse through the panel and get the inputs from the different Controls. Now I need to find a way to serialize these inputs and deserialize them later on, so that all inputs are again in the right Controls (e.g. "Jack" is again in the TextBox "tbName").
I thought of multiple solutions, e.g. creating a list for each panel, which serializes to a .txt with a structure similiar to "tbName=Jack" and so on. But I don't really know how I would deserialize that, without traversing both my panel controls and the list again. Or can I possibly serialize the whole Panel object together with the Child-Controls?
//This is the method I use to gather the inputs from the panels.
public IEnumerable<Control> GetControls(Control parentControl)
{
foreach (Control child in parentControl.Controls)
{
yield return child;
foreach (Control controlChild in GetControls(child))
{
yield return controlChild;
}
}
}
It's not advised to serialize the whole form, as it has a lot of information you don't need (and t hat may affect performance). Instead, create a separate class, make it [Serializable()], make all the variables you need to store your information, and serialize that class.
EDIT:
Say you have the following form:
namespace Test
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// here, you create the serializing and deserializing methods
public void SerializingInfo()
{
// done however you see fit
}
public StorageClass DeserializingInfo()
{
// also done however you see fit
}
}
}
Then, add another class to your project, which in my example, is named StorageClass.
This will look like:
namespace Test
{
[Serializable()]
public class StorageClass
{
// has all your properties
}
}
Then, whatever you need to storage, you can do so by setting/getting the properties in the Form1. When you serialize it, all the properties are serialized together, and you can retrieve it by accessing their getter method in DeserializeInfo().
For a LIMITED number of controls, you could simply create Settings in your Project --> Properties for each one:
Then, in the ApplicationSettings property for your control, click the three dots to the right of PropertyBinding...
...and select the setting for the Text entry:
You'll now have this:
Finally, in the FormClosing() event of the form, save the settings:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Properties.Settings.Default.Save();
}
Thanks for the answers, both are correct and working, but in the end I figured out my own solution to the problem:
I made a seperate class with all the attributes I needed, just like #krobelusmeetsyndra suggested, and made a generic List of the class object i just made. Then I traversed through the controls (with the method from my question), put the data in the List and serialized that data with the XmlSerializer.
Same with deserializing: I made a List of my own object type, then loaded the data from the XML in that list, and then assigned it to the right controls.
Hope that helps everyone with the same question!

Loading Selenium Page Objects Based on Browser Window Size

We are building a web application that has expandable workspaces on the sides of the page that expand and contract based on page size. when the workplace is contracted the page objects of the expanded list are not visible to be initialized when the class is called as the site is being developed in Angular 2.0
We are also building an automated test framework with Selenium with C# bindings and using the Page Object Model to run our automation. Is it possible load objects based on the window into a single page class like this
public class PageObjectClass
{
public PageObjectClass(IwebDriver driver)
{
PageFactory.InitElements(driver, this)
}
private IWebElement anObjectVisibleWhenContracted
//load an object that is not visible based on window size
}
Or do I need to get the size of the window when I call the class and have separate classes based on window width in the test scripts like this?
if (driver.Manage().Window.Size.Width < 1280)
{
someVar = new PageObjectClass(driver):
}
else
{
someOtherVar = new exp[andedPageObjects
}
//do stuff here
If you don't want to face issues when an element is displayed but Selenium doesn't interact with it a good practice is to maximize your browser's window on setUp.
Still if you don't want to do this, Selenium scrolls to an element when you interact with it.
So, answering your question - you don't need to change the window size. When you use PageObject in C# like you shown then each element will be initialized when you address to it (click, sendKeys, etc.). Each time you address to an element it will be initialized again and Selenium must scroll to this element.
But there are some bugs that appears in some cases when an element is displayed at the edge of the page and Selenium can not scroll to it correctly. Why this happens I don't know but luckily it happens very rarely.
I figured this out on my own. In the classes where I needed to deal with expandable workspaces, I declared all my variables in the class and didn't assign them any values before running PageFactory.InitElements
In the class constructor I passed in a Size variable along with the WebDriver that has the current size of the window. Then the objects that were appropriate for the window size were all loaded based on that
Just took a little restructuring and now it's working like a charm. Class structure looks like this now
public class ClassName: InheritedClass
{
#region Page Objects
private IWebElement object1;
private IWebElement object2;
#endregion
public ClassName(IWebDriver driver, Size winSize)
{
PageFactory.InitElements(driver, this);
if (winSize.Width > 1440)
{
object1= driver.FindElement(expanded By phrase locator);
object1 = driver.FindElement(expanded By phrase locator);
}
else
{
object1= driver.FindElement(contracted By phrase locator);
object1 = driver.FindElement(contracted By phrase locator);
}
}
#region Page Methods that use these objects
#endregion
}

How to add properties to a Form on a group, and show in Properties Windows and be editable

I have this class:
public class MyProps
{
public MyProps()
{
}
protected string myVar;
public string MyProperty
{
get { return myVar; }
set { myVar = value; }
}
protected int myOtherVar;
public int MyOtherProperty
{
get { return myOtherVar; }
set { myOtherVar = value; }
}
}
That I want to add to my Form, so when I inherit from it I will be able to fill the properties in the MyPropsX property.
I have this code in my form:
protected MyProps propsX = new MyProps();
[TypeConverter(typeof(ExpandableObjectConverter))]
public MyProps MyPropsX
{
get
{
return propsX;
}
set
{
propsX = value;
}
}
Now, the properties MyProperty and MyOtherProperty are nicely shown in the Properties Window, and I can set their values directly there.
But when I close my form and I open it again, all my changes are lost, the properties being reset to show zero and an empty string.
What am I missing?
Should I inherit my MyProps class from certain special class or interfase?
Or some special attribute?
This is a little bit much for a comment and maybe your solution, so i'm answering to your comment with an answer instead with another comment:
With does not happen when I put properties directly on a form you mean, you are using the designer to set some property of the form. These will be written into the MyForm.designer.cs file. When you go into the code of your class you'll find within the constructor a method InitializeComponent(). Set the cursor on it an press F12. Here you can see what the designer has written into all the properties. You should respect the comment above the mentioned method and not start to modify the code with the code editor unless you really have understand how and when the designer will read and write code here (which is another chapter i can explain if needed). Otherwise it will happen that trying to opening your form with the designer after the code change will lead to an error message or code loss.
If you like to set some default value also, you should go back into the constructor and add the needed initialization code below the InitializeComponent() function and everything should work as expected.
Update
As you wrote in your comment you already know how the Designer interacts with the *.designer.cs file. So i really can't understand your concrete problem but maybe one of these articles can give you a more insight about how Microsoft wrote their components:
Make Your Components Really RAD with Visual Studio .NET Property Browser
Components in Visual Studio
This is very normal, since each time you are closing the form and opening it again you are having a new instance from the form MyPropsX, so the best way would be to save your properties in any kind of a database (sql, access, textfiles,...)

How to use AutomationProperties.Name?

Question
Can anyone please explain (preferrably with a code example) how the AutomationProperties.Name property is used programmatically and declaratively with XAML?
Explanation
I understand that the Coded UI Builder in Visual Studio 2010, for instance, takes a Window's name as SearchProperty.
Since my Window's name changes, I would like to have a constant SearchProperty that my Coded UI Tests can rely on.
In the code example below, I don't want the window title to be hard-coded as "Properties of Pipe 1" since that changes.
Code example
[GeneratedCode("Coded UITest Builder", "10.0.30319.1")]
public class UIListViewPropertiesTable1 : WpfTable
{
public UIListViewPropertiesTable1(UITestControl searchLimitContainer) :
base(searchLimitContainer)
{
#region Search Criteria
this.SearchProperties[WpfTable.PropertyNames.AutomationId] = "listViewProperties";
this.WindowTitles.Add("Properties of Pipe 1");
#endregion
}
#region Properties
public WpfText NameOfComponent
{
get
{
if ((this.mNameOfComponent == null))
{
this.mNameOfComponent = new WpfText(this);
#region Search Criteria
this.mNameOfComponent.SearchProperties[WpfText.PropertyNames.Name] = "Pipe 1";
this.mNameOfComponent.WindowTitles.Add("Properties of Pipe 1");
#endregion
}
return this.mNameOfComponent;
}
}
#endregion
#region Fields
private WpfText mNameOfComponent;
#endregion
}
Links
Here is an example: How To: Get automation working properly on data bound WPF list or combo box. I wasn't able to adapt it for a Window.
You can change the attached property AutomationProperties.Name either in XAML using:
AutomationProperties.Name = "new name"
or in code using:
Button.SetValue(AutomationProperties.NameProperty, "new value");
or
AutomationProperties.SetName(Button, "new value");
You can pass the Window Title as parameter to its parent and set this parameter while initializing.
I do this way and works fine.
There is a way to work around that but its a bit ugly.
We will be using the fact that the the proprty that contains the reference to the window is cached and not looked up every time.
uimap class is a partial class and you can have code in the uimap.cs file that stil counts as a part of the uimap class.
Add a method there that accepts as a parameter the window title, and performs the search, and that puts the found window into the UIListViewPropertiesTable1 property of the generated code.

Persisting object changes from child form to parent form based on button press

I have created a form that is used for both adding and editing a custom object. Which mode the form takes is provided by an enum value passed from the calling code. I also pass in an object of the custom type. All of my controls at data bound to the specific properties of the custom object. When the form is in Add mode, this works great as when the controls are updated with data, the underlying object is as well. However, in Edit mode, I keep two variables of the custom object supplied by the calling code, the original, and a temporary one made through deep copying. The controls are then bound to the temporary copy, this makes it easy to discard the changes if the user clicks the Cancel button. What I want to know is how to persist those changes back to the original object if the user clicks the OK button, since there is now a disconnect because of the deep copying. I am trying to avoid implementing a internal property on the Add/Edit form if I can. Below is an example of my code:
public AddEditCustomerDialog(Customer customer, DialogMode mode)
{
InitializeComponent();
InitializeCustomer(customer, mode);
}
private void InitializeCustomer(Customer customer, DialogMode mode)
{
this.customer = customer;
if (mode == DialogMode.Edit)
{
this.Text = "Edit Customer";
this.tempCustomer = ObjectCopyHelper.DeepCopy(this.customer);
this.customerListBindingSource.DataSource = this.tempCustomer;
this.phoneListBindingSource.DataSource = this.tempCustomer.PhoneList;
}
else
{
this.customerListBindingSource.DataSource = this.customer;
this.phoneListBindingSource.DataSource = this.customer.PhoneList;
}
}
You could always add a function to your object (Customer), either as "Copy(Customer cust)" or "Update(Customer cust)" and consume the changes that way.
The other way would be to have a wrapper class around Customer EditableCustomer, which takes a customer object in its constructor EditableCustomer(Customer root) and uses that to hold the changes. In the final event just call a function like "UpdateRoot()" and populate the changes back to the original customer, failing to call this will be the same as a discard.
You wont be able to use deep copies directly but this will allow you to control this type of situation, and actually allow you to control both edits and undo's dynamically.

Categories