Using Visual Studio 2012 Express.
I have a UserControl with more properties which can be set from the properties window at design-time.
The name of the UserControl is "Drum".
One property is named IsSlaveOf, and is defined as follows:
[Category("Custom")]
public Drum IsSlaveOf
{
get
{
return _isSlaveOf;
}
set
{
_isSlaveOf = value;
}
}
As you can see this property can be set to a reference of an instance of "Drum".
In my project there are several instances of the UserControl "Drum".
In the properties window at design time I can choose from a drop-down list one of the other instances that exist in my project.
So far so good.
"Drum" contains another property called IsMasterOf, and is defined as follows:
[Category("Custom")]
public Drum[] IsMasterOf
{
get
{
return _isMasterOf;
}
set
{
_isMasterOf= value;
}
}
Notice that this property is an array, than means, you should have the possibility to choose more instances in the properties window.
The problem is that in the properties window you will now see a "Drum Collection Editor", where it is not possible to choose existing instances of "Drum", it is only possible to create new instances.
If I try to enter the name of an existing instance I get a message that tells me that this component already exists.
So far I have worked it out by manually setting the property in the Designer.cs file for my main form, but this is obviously not the correct way to go.
How can you make this property so it will be possible to choose existing instances from the properties window?
Any help is appreciated!
Related
I have a razor component. The component have a parameter, EventCallback to be precise, named "ValueChanged".
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
I refer to the event exactly in one razor file like this:
<Autocomplete DataSource="#Data" Columns="Id,Name" ValueChanged="OnUnitBrandIdChanged" />
I renamed (via F2, AKA refactor) the property name to "OnValueChanged". The razor file was not automatically updated (I guess a bug in Visual Studio). So I updated it manually. So now both points of interest looks like this:
[Parameter]
public EventCallback<string> OnValueChanged { get; set; }
and
<Autocomplete DataSource="#Data" Columns="Id,Name" OnValueChanged="OnUnitBrandIdChanged" />
The code compiles and runs, then the runtime crashes with following error message
Unhandled exception rendering component: Object of type 'Woof.Blazor.Components.Autocomplete' does not have a property matching the name 'ValueChanged'.
I wasted 4 hours investigating the case.
First, I changed the name "OnValueChanged" to "SomethingElse" - to make sure, the string "ValueChanged" doesn't exist in my source code at all.
Of course I get the same error message.
Then I searched ALL files in my project, including hidden and binary files for the string "ValueChanged". Of course I found the string in obj, bin and .vs directories. So I deleted those files.
After compiling the code again, I get the same error message. The one with 'ValueChanged' reference.
I created completely new Blazor project. I copied all my code files, triple-checking NONE of them contains "ValueChanged" string.
When the new project is run - I get the same error message.
I thought maybe Visual Studio has written the reference in a file outside the project directory in a hidden place. So I remove all temporary files from the project, published it on GitHub and send to my coworker. He cloned the project, run it and got the same error message.
I created the property with the name "ValueChanged", dummy property, object type. Completely unused and redundant. Of course program runs without errors.
It appears the reference to that name is hidden somewhere but I have no idea where. No Windows tool is able to search "ValueChanged" string within project directory. I even suspected the Visual Studio could hide the reference by encrypting and / or compressing the content, but again I triple-checked I removed ALL binary files, all non plain text files.
All for nothing.
Then I created completely new Blazor project. Created a test component, created an event, bound dummy event handler, compiled and run. Everything worked. Then I refactored my test program in the exact same way as the original one. It worked, no problems at all. I even used exactly the same names, types, directory structure, namespaces, I even added the parameter named "Value" to make my test case more similar to the production code. The result is the new project behaves normally. I can rename any parameter and it just works.
My old project doesn't behave normally even when it's almost rewritten from scratch WITHOUT the string "ValueChanged" occurring in any file ONCE. The same error message. Like with Visual Studio and Blazor the common logic no longer applies. The name doesn't exist, but when I compile the code, it suddenly appears in temporary files with g.cs extension.
It's most probably a horrific bug in Visual Studio / .NET Core, but to report it I should be able to reproduce it, but in this case - I can't. Any clues?
BTW, Of course I tried to debug it and set the breakpoint on the cursed property setter. It gets triggered, however the only items in my call stack are "External code", so it's completely useless.
Note the following:
You ordinarily define a parameter property in a component if this component is a child component which is bound to a parent component, in that case the parent component is bound to a Value property that should be defined in the child component, and decorated with the Parameter attribute as well. Thus, your child component should look like this
private string _value;
[Parameter]
public string Value
{
get { return _value ?? string.Empty; }
set
{
if (Value != value)
{
_value = value;
}
}
}
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
The following is the usage of the component in a parent component
<ChildComponent #bind-Value="value" />
#code
{
private string value;
}
Note: The code above (ParentComponent) embed the child component defined before, and bind the local variable called value to the Value property of the child component. When you want to bind to a property of a Component from a parent component you use the #bind directive + hyphen + the name of the property, as for instance: #bind-Password="password". In the bounded component (the child component) you should define the property (in the last instance it should be Password) and a delegate parameter property (in the last instance it should be
[Parameter]
public EventCallback<string> PasswordChanged { get; set; }
)
As you can see, we must have this pair. BUT ValueChanged or PasswordChanged are constructs which the compiler use to produce, behind the scene, code which enables communication between a parent component and a child component. To be more precise, the compiler produces code that enable two way data-binding between the components... You can't use ValueChanged or PasswordChanged as an attribute for event handlers. The only thing you can do is trigger the delegates. Thus you can put an input element in your child component, and bind the value attribute of the input element to the Value property of the component, like this:
<input type="text" value="#Value" #oninput="OnValueChanged" />
This is a one-way binding from the Value property of the child component to the value attribute of the element. We also have to update the Value property, in that case, whenever the user type a char... For this we need to define an event handler, which is called after each hit on the keyboard, like this:
private Task OnValueChanged(ChangeEventArgs e)
{
Value= e.Value.ToString();
return ValueChanged.InvokeAsync(Value);
}
As you can see, the OnValueChanged method assign the value entered into the text box (this is done at each hit on the keyboard) into the Value property, and then.....................
triggers the ValueChanged delegate, passing it the value of the Value property. This is what you should do with ValueChanged, nothing else. Now, when the delegate is triggered, the value of the private value field in the parent component is updated with the value of the Value property of the child Component. This procedure is called two ways data-binding between Components
Is there any functionality to use a global variable in form properties window?
public partial class MainForm : Form
{
string title = "This is title";
// constructor,etc.
}
No, there isn't such functionality in the designer. The nearest you can get is using resources. You can't wire your resources directly in the designer (unless you are doing localization tricks), but you can edit your Designer.cs file and change:
this.MainForm.Text = "whatever";
for
this.MainForm.Text = Properties.Resources.MainFormTitle;
// or whatever other resource property you wish
This only works for resources (they are considered Global Objects by the designer and respected upon serializing): if you try to set any other variable (not a resource-generated property), it'll get overriden when the form is serialized again (upon saving).
Otherwise, you can just set your properties in the constructor, after InitializeComponents(), but they won't be seen in design-time unless you are inheriting that form.
I have a non-static property inside a non-static MainForm class:
public string SelectedProfile
{
get { return (string)cbxProfiles.SelectedItem; }
set { cbxProfiles.SelectedItem = value; }
}
I would like to get the value of this property, from another non-static class. Using MainForm.SelectedProfile gives an error saying "An object reference is required for the non-static field, method or property".
Usually I would solve this problem by making SelectedProfile static, but I can't, since cbxProfiles (a ComboBox control) can't be made static.
So how do I access the property's value without making it static?
You access non-static members the same way you always do: by using a reference to an instance of the object.
So whatever code you want to be able to use that property, you need to pass it a reference to a MainForm object.
As said in the compilation error, you need to have a reference of the existing MainForm instance to act on it.
// You surely do this somewhere in your code
MainForm mainForm = new MainForm();
// ...
// Use the reference to your mainForm to access its public properties
String selectedProfile = mainForm.SelectedProfile;
I might be late to the party but my solution might help someone someday down the road. You can directly access an open form's controls (even private ones) using Application.OpenForms[n]...
For example, let's assume you have created a MainForm and then created a comboBox such that it is inside MainForm => Tab (named tabControl) => TabPage (named tabPageMain) => Panel (named pnlMain) => ComboBox (named cmbSeconds). Then you can access this last control as follows:
ComboBox combo = Application.OpenForms[0].Controls["tabControl"].Controls["tabPageMain"].Controls["pnlMain"].Controls["cmbSeconds"] as ComboBox;
string SelectedProfile = (string)combo.SelectedItem;
// OR
bool isMaximized = Application.OpenForms[0].WindowState == FormWindowState.Maximized;
i.e. you've got to traverse the path from the top-level form down to that particular control. Document Outline view of Visual Studio (View menu => Other Windows => Document Outline) might help you in this bcoz you might be overlooking some transparent containers in between.
Use it cautiously. For example, if the handles of any of the referenced controls are not yet created, you might see runtime exceptions.
I'm in a mess with visibility between classes. Please, help me with this newbie question.
I have two controls (DatePickers from default WPF toolbox) which are in different windows, so in different classes. I can easily access these controls properties like datePicker1.Text from within its native class, i.e. in its native window, but when I try to reach datePicker1.Text from another window I get nothing.
I try to assign value of one datePicker to another, using reference to the window in my code:
string testStr;
...
AnotherWindow aw = new AnotherWindow();
testStr = aw.datePicker2.Text;
datePicker1.Text = testStr;
and it doesn't work
also I tried to do it through public property of a class, like:
public partial class AnotherWindow : Window
{
....
public string dateNearest
{
get { return datePicker2.Text; }
set { datePicker2.Text = value; }
}
....
and then use it in another window:
string testStr;
...
AnotherWindow aw = new AnotherWindow();
testStr = aw.dateNearest;
but also no value assigned.
Please, help me to understand this basic issue. I know there are other ways of accessing values in WPF like databinding, but I would like to understand basics first.
Unfortunately, the basics of WPF are data bindings. Doing it any other way is 'going against the grain', is bad practice, and is generally orders of magnitude more complex to code and to understand.
To your issue at hand, if you have data to share between views (and even if it's only one view), create a view model class which contains properties to represent the data, and bind to the properties from your view(s).
In your code, only manage your view model class, and don't touch the actual view with its visual controls and visual composition.
I'm using VS 2010 beta 2 right now which crashes regularly doing the simplest WPF coding, like trying to duplicate your question's code :) : but consider :
Is it possible that using this syntax will "do the right thing" :
public string dateNearest
{
get { return this.datePicker2.Text; }
set { this.datePicker2.Text = value; }
}
Edit 1 : Okay, I got a WPF replication of your code that didn't crash : using the above syntax I can both get and set the property in the "other window."
Edit 2 : The code also works using your original code :) Which, seemed to me to be "proper" the first time I read it. Are you setting that property before you read it ? : to my knowledge a DateTimePicker's Text property will be an empty string by default when first created.
Edit 3 : in response to Rem's request :
the main window has a button, 'button1 : which tests setting and getting the Public Property DTContent defined in the instance of the second Window named : 'WindowAdded : here's the 'Click event handler for that button in the main window's code :
private void button1_Click(object sender, RoutedEventArgs e)
{
WindowAdded wa = new WindowAdded();
wa.DTContent = DateTime.Now.ToString();
Console.WriteLine("dt = " + wa.DTContent);
}
Edit 4 : a better "real world" example : most cases you are going to want to create that instance of another window, and hold on to it, for re-use: imho : not have it exist only within the scope of a button's Click event. So consider, please :
Somewhere in the scope of the main window's code define a "place-holder" for the window(s) you will add : private WindowAdded wa;
In the event you select as most appropriate for creating the instance of that window : create the instance, and assign to your "place-holder" variable : then re-use it as needed. In WinForms I most often create required secondary windows that I will need to re-use references to the instances of to access something on them in the main form's load or shown events.
Discussion : of course, if your intent is to create "temporary" windows, and you don't need to re-use that reference to the new window's instance again, then creating it in the scope of some function is fine.
And, if the only thing you ever need to access on your second Window is the DateTimePicker, then you use the same technique suggested above, but create and hold to a reference to the instance of the DateTimePicker only.
As the others already pointed out, this is probably not the way to go, but you can use:
<object x:FieldModifier="public".../>
To set the object public.
See msdn for more info.
I have a created a UserControl with a combobox in it. This combobox is populated from a xml, when this is not present, it is loaded from resource file.
It works fine in the program, but it can't be displayed in designer - it says: "Object reference not set to an instance of an object."
In the class responsible for loading the list from xml the null reference check is skipped for reasons beyond my understanding...
public SortedDictionary<string, string> Countries
{
get
{
if (object.ReferenceEquals(countries, null))
{
GetCountryList();
}
return countries;
}
}
Populating of the comboBox goes like this:
comboBoxCountry.DataSource = new BindingSource(Program.language.Countries, null);
Program.language is initialized in Program, but it does not help for the Designer.
The question is, how (when, at what event) should I populate the ComboBox (=load list from xml) to be able to display my control in designer.
If possible, you want to check for this.DesignMode and then simply not load the ComboBox at design-time.
Does GetCountryList() set a member variable? If so, move that call to a method. Property get accessors and the ToString() method are assumed pure: the program state before and after must be identical. Violating this assumption can cause all sorts of problems, especially designer/debugger/runtime inconsistency. Various rants have taken place, but the best thing to do is understand the assumption, follow it, and let it work to your advantage as you debug.