Why my control's properties won't change outside its class? - c#

I'm new in C# but not new to coding --being doing it for almost two decades--, and have a problem with properties in a custom control I'm building, which inherits from a Panel. When I put my properties, I can see them in the Designer properties list and can even set them, but when running my little application, it seems these properties values are not used. The same if I change a property programatically: no error but my control does nothing, it is like they are not properly set. However, if I do it programatically whithin the class, they do work. My guess is that something in my properties set/get stuff is not right. Please see the following code chunk of how I'm doing it:
public class ColorStrip : Panel
{
// properties
// ------------------------------------------
// size of color clusters (boxes)
private int _clusterSize = 20;
// controls if show the buttons panel
private Boolean _showButtons;
// property setters/getters
// ------------------------------------------
// clusterSize...
public int clusterSize
{
get { return _clusterSize; }
set { _clusterSize = value; }
}
// showButtons...
public Boolean showButtons
{
get { return _showButtons; }
set { Console.Write(_showButtons); _showButtons = value; }
}
....
So in my form, for instance in the load or even in a click event somewhere, if I put colorStrip1.showButtons = false; or colorStrip1.showButtons = true; whatever (colorStrip1 would be the instance name after placing the control in the form in design mode)... console.write says always 'false'; Even if I set it in the design properties list as 'true' it will not reflect the settled value, even if I default it to true, it will never change externally. Any ideas? Non of the methods get the new and externally settled property value neither, obviously the getter/setter thing is not working. Seems to me I'm not doing right the way I set or get my properties outside the class. It works only inside it, as a charm...Any help...very appreciate!
Cheers
lithium
p.s. TO CLARIFY SOLUTION:
Setting the property in this case didn't work because I was trying to use a new set value within the constructor, which seems can't get the new values since it is, well, building the thing. If I change the property value in Design mode > Property editor or in code externally to the object, say in it's parent form's load event, it will change it but readable for all methods except the constructor, of course :)

It's likely an issue of the order of execution. Your property setter just sets a variable, but doesn't actually trigger anything on the control to update the state related to this variable (e.g. adding or showing the buttons I assume).
When you set the property befre the rest of the initialization is done, the value is being used, otherwise it isn't because during the initial go the default value is still the property value.
You need to act on the setter, here's some pseudocode to illustrate:
set {
_showButtons = value;
if (alreadyInitialized) {
UpdateButtons();
}
}
Note: make sure to first set the value, then act - otherwise you end up using the old value (just like your Console.Write() is doing).

The quoted code doesn't look problematic. Are you sure you're referencing the same instance of ColorStrip? Also, check your .Designer.cs file to ensure that the code setting the property is there.
In fact, try simplifying your code by using auto-implementing properties:
public int clusterSize { get;set;}
public Boolean showButtons {get;set;}
public ColorStrip() { ... clusterSize = 20; ... }

Related

Property doesn't change the value if it assign in a JSInvokable function in Blazor

I have an interesting problem. I'm creating a Markdown Editor component for Blazor. The component is working fine. Now, I discover an interesting issue related to a JSInvokable. So, the component has a Value property
[Parameter]
public string Value { get; set; }
When I add the component to a page like this
<MarkdownEditor Value="#markdownValue"
ValueChanged="#OnMarkdownValueChanged"
ValueHTMLChanged="#OnMarkdownValueHTMLChanged" />
#code {
string markdownValue = "Test";
}
So, in the JavaScript, every time the content changes, I call the JSInvokable via DotNetObjectReference.
The function is looking like that (here the full code)
[JSInvokable]
public Task UpdateInternalValue(string value)
{
Value = value;
return ValueChanged.InvokeAsync(Value);
}
When I debug the function, I can see the new value in the value
Then, I can see the Value parameter is changed with the new value
But a line after that Value has again the initial value and not the new value from the parameter.
I don't know if this is something related to the JSInvokable function. The interesting thing is in the some function I have another variable ValueHTML that changes its value as expected.
This is a variation on "Component changing its Parameter value" , which you shouldn't do.
ValueHTML works because you don't specify it in the owning component. But the await there enables the StateHasChanged related logic to refresh Value from he surrounding component.
I think you need to do
// Value = value;
and let
await ValueChanged.InvokeAsync(Value);
make the change, through the owning component.
So, based on the comment from Henk, I googled "Component changing its Parameter value" and I found the solution.
In the component, I have to change the Value parameter like that
[Parameter]
public string Value {
get => _value;
set
{
if (_value == value) return;
_value = value;
ValueChanged.InvokeAsync(value);
}
}
private string _value;
Then, in the page, I call the component with a bind-Value
<MarkdownEditor #bind-Value="#markdownValue"
ValueHTMLChanged="#OnMarkdownValueHTMLChanged" />
In UpdateInternalValue:
Value = value;
//....
ValueHTMLChanged.InvokeAsync(ValueHTML);
return ValueChanged.InvokeAsync(Value);
You set Value. You shouldn't do this - This is bad practice.
You async invoke the ValueHTMLChanged event callback.
This calls OnMarkdownValueHTMLChanged in the parent.
This triggers a StateHasChanged event in the parent.
The Renderer calls SetParamatersAsync on the component
Value is set back to the old value of markdownValue. It hasn't yet been updated!
You're now back in the code block and async invoke the ValueChanged event callback.
repeat steps 3....5 In 6 markdownValue is set to the new value so shows correctly.
You're living in the async world: things don't get run in a nice sequential order.
Don't set Value. I think, if I've read the code right, it will get updated as part of calling ValueChanged and the triggered component rendering process.

Accessing components of another form

I have two Forms in my application. A Form has the following fields: txtPower, txtTension and txtCurrent. I would like to access the values ​​filled in these TextBox through another Form. In the second Form I instantiated an object of the first Form (MotorForm), however I do not have access to the TextBox.
public MacroForm()
{
InitializeComponent();
MotorForm motorForm = new MotorForm();
motorForm.Show();
}
Is there any way?
Please do not expose the controls in your form. Never. (Unless you have a really good reason.)
If the problem is simple enough not to use MVVM (or the like) in your program (which you should consider for every program that's but trivial), you should expose the values of the instantiated form via properties. Think
public string Power
{
get { return txtPower.Text; }
set
{
if(ValidatePower(value))
{
txtPower.Text = value;
}
else
{
// throw ??
}
}
}
If we can make a sensible assumption about the type of the value we could extend this to
public double Power
{
get
{
// parse the value
// validate the value
// throw if not valid ??
// return the value
}
set
{
// validate the value
// set the value in the text box
}
}
If you exposed the txtPower object, you'd make the instantiating class depend on implementation details of the instantiated class, which is virtually never a good thing.
It seems that your problem is a perfect situation for using ShowDialog for opening your form.
To accomplish this, you need to change the Modifiers property of the controls you want to access on MotorForm and set them to Public. And also set the DialogResult property of your form somewhere to a desired value i.e OK. Anyway the easier way to do this is to set it on the button that is supposed to close the form. Suppose OK or CANCEL buttons.
Then you can create your form this way:
MotorForm motorForm = new MotorForm();
if(motorForm.ShowDialog() == DialogResult.OK)
{
string myValue = motorForm.txtPower.Text; //you can access your values this way
}

(ASP.NET User control) - problems setting properties

I am creating several .NET User Controls and I am trying to figure out the best way to go about setting properties. I have an address control and I am trying to create a property called ShowCountry which will either hide or show the control's country ddl.
I have been trying to set most of my properties similar to the below code:
public bool ShowCountry
{
get { return (bool)ViewState["ShowCountry"]; }
set
{
ViewState["ShowCountry"] = value;
pnlCountry.Visible = value;
}
}
How would I set a default value for this property? When I run my page with the control on it, it instantly errors out in the "get{}" when ShowCountry is used in one of my functions because I never set ShowCountry="false" in the control's tag. If I set this property when declaring the control everything works fine.
Also is what I am doing with the ViewState a good way to keep property values across postbacks?
Could someone show me how they would write this property?
The specs are:
Must keep value across postbacks, Must default to false
you can try this in order to avoiding error..
public bool ShowCountry
{
get {
if(ViewState["ShowCountry"] != null ){
return (bool)ViewState["ShowCountry"];
}
else {
//return the default value
return false;
}
}
set
{
ViewState["ShowCountry"] = value;
pnlCountry.Visible = value;
}
}
i think view state is best approach alternatively you can use hidden field in order to save value against post back.

Why does MonoTouch.Dialog use public fields for some Element options, and public properties for others

I am trying to get a StringElement's 'Value' to update in the UI when I set it after already setting up the DVC.
e.g:
public partial class TestDialog : DialogViewController
{
public TestDialog() : base (UITableViewStyle.Grouped, null)
{
var stringElement = new StringElement("Hola");
stringElement.Value = "0 Taps";
int tapCount = 0;
stringElement.Tapped += () => stringElement.Value = ++tapCount + " Taps";
Root = new RootElement("TestDialog")
{
new Section("First Section")
{
stringElement,
},
};
}
}
However the StringElement.Value is just a public field, and is only written to the UICell during initialization when Element.GetCell is called.
Why isn't it a property, with logic in the setter to update the UICell (like the majority of Elements, e.g. EntryElement.Value):
public string Value
{
get { return val; }
set
{
val = value;
if (entry != null)
entry.Text = value;
}
}
EDIT :
I made my own version of StringElement, derived from Element (basically just copied the source code from here verbatim)
I then changed it to take a class scoped reference to the cell created in GetCell, rather than function scoped. Then changed the Value field to a property:
public string Value
{
get { return val; }
set
{
val = value;
if (cell != null)
{
// (The below is copied direct from GetCell)
// The check is needed because the cell might have been recycled.
if (cell.DetailTextLabel != null)
cell.DetailTextLabel.Text = Value == null ? "" : Value;
}
}
}
It works in initial testing. However I am not sure on whether taking a reference to the cell is allowed, none of the other elements seem to do it (they only take references to control's placed within the cells). Is it possible that multiple 'live'* cell's are created based on the one MonoTouch.Dialog.Element instance?
*I say live to indicate cells currently part of the active UI. I did notice when navigating back to the dialog from a child dialog the GetCell method is invoked again and a new cell created based on the Element, but this is still a 1-1 between the element and the live cell.
For the main question:
Why does MonoTouch.Dialog use public fields for some Element options, and public properties for others?
I've been through the code, and I don't think there's a consistent reason for use of either.
The Dialog project was not part of the MonoTouch project initially - I don't think Miguel knew how useful it was going to turn out when he started wrote and grew it - I think he was more focussed on writing other apps like TweetStation at the time.
I know of several people (including me!) who have branched the code and adapted it for their purposes. I would guess at some future point Xamarin might write a 2.0 version with stricter coding standards.
Taking references to live cells
For limited use you can do this... but in general don't.
The idea of the table view is that cells get reused when the user scrolls up and down - especially in order to save memory and ui resources. Because of this is a long list, multiple elements might get references to the same cell.
If you do want to cache a cell reference then you probably should override GetCell() so that it never tries to reuse existing cells (never calls DequeueReusableCell)
Alternatively, you could try to change some code in the base Element class in order to find out if the Element has a current attached cell - this is what CurrentAttachedCell does in my branch of Dialog https://github.com/slodge/MvvmCross/blob/master/Cirrious/Cirrious.MvvmCross.Dialog/Dialog/Elements/Element.cs (but that branch has other added functions and dependencies so you probably won't want to use it for this current work!)

Two-way bind a "virtual" list of strings to a column

I have a list of Strings.
Well, conceptually. They are stored somewhere else, but I want provide an object which acts like a list (and provides any necessary events on top of that), with properties that I could bind to.
I want to establish a two-way binding over this data, to display it as a modifiable column in a DataGrid. I have the following problems with that:
I can't make a two-way binding because the binding needs a path (i.e. I can't have it look like {Binding} or {Binding Path=.} in the column, must be {Binding Path=someField"} to be made modifiable if I got this right, which sounds reasonable).
I don't exactly know how the proxy collection object should look like, in terms of interfaces (would IEnumerable + INotifyCollectionChanged sufficient?)
Is there any solution which doesn't involve creating one proxy object per every String in the collection? Could you suggest an efficient design?
To keep the discussion on the rails, let's assume I want to bind to something like this:
class Source {
public String getRow(int n);
public void setRow(int n, String s);
public int getCount();
public void addRow(int position, String s);
public void removeRow(int position);
}
That's not exactly my case, but when I know how to bind to this, I think I'll be able to handle any situation like this.
I'm OK with having to provide an adapter object on top of that Source, with any necessary interfaces and events, but I don't want to have one adapter object per row of data.
While making an adapter for the Source is relatively clear, then, unfortunatelly, the core of the second problem ('not wrapping every string in a miniobject') is a clash built into the .Net and WPF..
The first thing is that the WPF does provide you with many ways of registering 'on data modified' callbacks, but provides no way of registering callbacks that would provide a value. I mean, the "set" phase is only extendable, not interceptable, and the "get" - nothing at all. WPF will simply keep and return whatever data it has once cached.
The second thing is that in .Net the string is ... immutable.
Now, if ever you provide a string directly as a pathless binding or as a datacontext to any control, you are screwed in a dead end. The problem is, that WPF actually passes only the actual value of the binding, without the information of "where it came from". The underlying control will be simply given the string instance, and will have no sane way of modifying it as the string cannot change itself. You will not be even notified about such attempt, just like with read-only properties. What's more - if you ever manage to intercept such a modification attempt, and if you produce a proper new string, the WPF will never ask you again for the new value. To update the UI, you'd have to mannually, literally, force the WPF to re-ask you by for example changing the original binding so it points elsewhere (to the new value) or set the datacontext (to the new instance). It is doable with some VisualTree scanning, as every 'changed' callback gives you the DependencyObjects (Controls!), so yo ucan scan upwards/downwards and tamper with their properties.. Remember that option - I'll refer to this in a minute.
So, everything boils down to the fact that to get a normal 2-way binding you do not have to have a Path, you "just" have to have a mutable underlying data object. If you have immutable one - then you have to use a binding to a mutable property that holds the immutable value..
Having said that, you simply have to wrap the strings some how if you want to modify them.
The other question is, how to do that. There's a plenty of ways to do it. Of course, you can simply wrap them like Joe and Davio suggested (note to Joe: INotify would be needed there also), or you can try to do some XAML tricks with attached properties and/or behaviours and/or converters to do that for you. This is completely doable, see for example my other post - I've shown there how to "inject a virtual property" that pulled the data completely from elsewhere (one binding+converter performed the wrapping on the fly, second binding extracted the values from the attached-wrapper). This way you could create a "Contents" property on the string, and that property could simply return the string itself, and it'd be completely 2-way bindable with no exceptions.
But.. it would NOT work 2-way-ish.
Somewhere at the root of your binding/behaviour/conveter chain, there will be an immutable string. Once your smart autowrapping binding chain fires with 'on modified' callback you will be notified with pair of old/new values. You will be able to remap the values to new and old strings. If you implemented everything perfectly, the WPF will simply use the new value. If you tripped somewhere, then you will have to push the new value artificially back to the UI (see the options I'd asked you to remember). So, it's ok. No wrapper, old value was visible, it was changeable, you've got new value, the UI displays new value. How about storage?
Somewhere in the meantime you've been given a old/new value pair. If you analyze them, you'll get old/new strings. But how do you update the old immutable string? Can't do. Even if autowrapping worked, even if UI worked, even if editing seemed to work, you are now standing with the real task: you onmodified callback was invoked and you have to actually update that immutable string piece.
First, you need your Source. Is it static? Phew. What a luck! So surely it is instanced. In the on-modified callback we got only a old+new string.. how to get the Source instance? Options:
scan the VisualTree and search for it in the datacontexts and use whatever was found..
add some more attached properties and binding to bind a virtual "Source" property to every string and read that property from the new value
Well doable, but smells, but no other options.
Wait, there's more: not only the old/new value and an instance of Source are needed! You also need the ROW INDEX. D'oh! how to get that from the bound data? Again, options:
scan the VisualTree and search for it (blaargh)...
add some more attached properties and bindings to bind a virtual "RowIndex" property to every (blaaergh)...
At this point of time, while I see that all of this seems implementable and actually might be working properly, I really think that wrapping each string in a small
public class LocalItem // + INotifyPropertyChanged
{
public int Index { get; }
public Source Source { get; }
public string Content
{
get { Source...}
set { Source... }
}
}
will simply be more readable, elegant and .. SHORTER to implement. And less error-prone, as more details will be explicit instead of some WPF's binding+attached magic..
I find your approach a little weird.
DataGrids are usually used to display Rows. Rows consist of data that belongs together.
You could for instance easily map a row to a certain class. This means that the columns in your datagrid represent properties in your class.
What you're trying to do is the opposite, you're trying to get a relation between the column values instead of the row values.
Wouldn't it be easier to have a collection of your class which you can then bound the column to?
For instance
class MyClass : INotifyPropertyChanged
{
// Remember to actually implement INotifyPropertyChanged
string Column;
}
If you would have an ObservableCollection of MyClass you could bind the DataGrid to this collection. Whenever the property which I called "Column" changes, you could update your special list.
You can do this by hooking up some events. With the implementation of INotifyPropertyChanged, your columns will be updated if you update the "Column"-value directly.
I have this bit of code I use to bind a list of custom object to a DataContextMenu. You can alter it to use a list of strings and bind it to what you need
class SampleCode
{
class Team
{
private string _TeamName = "";
private int _TeamProperty1 = 0;
ObservableCollection<Territory> _Territories = new ObservableCollection<Territory>();
public Team(string tName)
{
this.TeamName = tName;
}
public ObservableCollection<Territory> Territories
{
get { return _Territories; }
set { _Territories = value; }
}
public string TeamName
{
get { return _TeamName; }
set { _TeamName = value; }
}
public int TeamProperty1
{
get { return _TeamProperty1; }
set { _TeamProperty1 = value; }
}
}
class Territory
{
private string _TerritoryName = "";
Team _AssociatedTeam = null;
public Territory(string tName, Team team)
{
this.TerritoryName = tName;
this.AssociatedTeam = team;
}
public Team AssociatedTeam
{
get { return _AssociatedTeam; }
set { _AssociatedTeam = value; }
}
public string TerritoryName
{
get { return _TerritoryName; }
set { _TerritoryName = value; }
}
public void Method1()
{
//Do Some Work
}
}
class MyApplication
{
ObservableCollection<Team> _Teams = new ObservableCollection<Team>();
ContextMenu _TeritorySwitcher = new ContextMenu();
public MyApplication()
{
}
public void AddTeam()
{
_Teams.Add(new Team("1"));
_Teams.Add(new Team("2"));
_Teams.Add(new Team("3"));
_Teams.Add(new Team("4"));
foreach (Team t in _Teams)
{
t.Territories.Add(new Territory("1", t));
t.Territories.Add(new Territory("2", t));
t.Territories.Add(new Territory("3", t));
}
SetContextMenu();
}
private void SetContextMenu()
{
HierarchicalDataTemplate _hdtTerritories = new HierarchicalDataTemplate();
_hdtTerritories.DataType = typeof(Territory);
HierarchicalDataTemplate _hdtTeams = new HierarchicalDataTemplate();
_hdtTeams.DataType = typeof(Team);
FrameworkElementFactory _TeamFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TeamFactory.Name = "txtTeamInfo";
_TeamFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TeamProperty1"));
FrameworkElementFactory _TerritoryFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TerritoryFactory.Name = "txtTerritoryInfo";
_TerritoryFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TerritoryProperty1"));
_hdtTeams.ItemsSource = new Binding("Territories");
_hdtTeams.VisualTree = _TeamFactory;
_hdtTerritories.VisualTree = _TerritoryFactory;
_hdtTeams.ItemTemplate = _hdtTerritories;
_TeritorySwitcher.ItemTemplate = _hdtTeams;
_TeritorySwitcher.ItemsSource = this._Teams;
}
}
}
Lazy solution
Derive from ObservableCollection<string> and let that collection be populated from the Source. In the derived class, register to collection change events and update the source accordingly. Bind the DataGrid column to the observable collection.
This should be pretty straightforward to write, but has a big drawback of duplicating all data in the collection.
More efficient solution
Create an adapter (as you suggested) and implement IList<string> and INotifyCollectionChanged. Let the list operations fall through directly to the source. Bind the DataGrid column to the adapter.
This approach would require some tedious boilerplate, but it's a thin layer between the WPF control and your Source.
This really depends on how you're implementing the UI. Bea Stollnitz did an excellent post of virtualizing the ItemsSource for the WPF DataGrid at http://bea.stollnitz.com/blog/?p=344 . With work I used this to edit as well as display data.
The easiest way is by placing the string in a wrapper class.
public class Wrapper
{
public string Content{get;set;}
}
Then you use the string via the wrapper class. This was the list items remain the same but the content changes.
The problem is when you do this without that then an old string is being deleted and a new one is created and the collection is confused.
Start with an ObservableCollection<string>. Then set the bindable control's ItemsSource to the ObservableCollection.

Categories

Resources