Better Practice: CheckBox DataBindings vs CheckedChanged event - c#

I have a CheckBox that, when checked/unchecked will toggle the Enabled property of some other controls. I did have my code looking something like this:
checkBox.CheckedChanged += new EventHandler((o, e) =>
{
control1.Enabled = checkBox.Checked;
control2.Enabled = checkBox.Checked;
});
But today I started playing with DataBindings and discovered I could do this:
control1.DataBindings.Add("Enabled", checkBox, "Checked");
control2.DataBindings.Add("Enabled", checkBox, "Checked");
They seem to behave the same, but I suspect one is preferred over the other. Or perhaps one has some unexpected behavior that may trip me up later.
Is one way better than the other?

The first one is checked at compiled time, so I'd go with that one. I assume that if the "Enabled" property in the second example was not valid you would get a runtime error.

You should notice that there is another difference:
with data binding (method 2), if the object implements INotifyPropertyChanged, and if the object.Enabled is changed outside the UI layer, the checkbox.checked state will get changed automatically.

Related

One method successfully toggles a control's "Enabled" property - a similar method fails. Why?

In my code behind I have two method to enable or disable a group of controls depending upon the value of a field on a form. One of these methods works as expected, and the other doesn't ... quite. The one that works depends upon the value of a Checkbox, while the other depends upon the value of a drop-down list (I believe that this difference is irrelevant - I mention it only for completeness). I've simplified these methods for readability, but the only significant difference from the production system is that they set a number of controls, not just one.
private void SetControlsFromDropDown(int statusID)
{
// This method doesn't work
bool enable = (statusID == (int)ReqStatus.CompletedOK)
this.myTextBox.Enabled = enable;
}
private void SetControlsFromCheckBox(bool enable)
{
// This method works
cboMyDropDown.Enabled = enable;
}
Where the first method fails is that it sets the controls correctly when the form is loaded. However, when the drop-down list changes, the method is called and the value of the bool variable "enable" is correctly set, and the code runs through as expected (and a watch on the Enabled property of the controls that are being set shows that they are toggled as expected) - BUT BUT BUT the controls remain firmly unchanged in the interface. So, if they were initially set to Enabled = false they remain disabled even though the method might have set them to Enabled = true.
What is causing me conniptions is that if I put setting of this.myTextoxBox into the second method, it toggles correctly.
I get the impression I haven't explained myself very clearly. In essence, two more-or-less identical methods, called from similar events, operating in arguably indistinguishable ways, behave differently in real-time. One will toggle the Enabled property of a group of controls ad lib., while the other will toggle it once, never to be toggled again.
Any thoughts gratefully received.
Edward
The problem was caused by a failure of the brain. I was getting an incorrect value from the drop-down list. Apologies for any time wasted.
I'm not really sure about this, but try to remove the 'this'
Just put the following:
myTextBox.Enabled = enable;

How can I avoid events firing during InitializeComponent?

I have a simple WPF page with a couple of RadioButtons, each RadioButton is registered with a Checked event handler so that when the selection is changed something can happen. By default I want to have one of these RadioButtons selected, so I have set the IsChecked property to True in the xaml. Something like this:
<RadioButton Checked="Radio_Checked" IsChecked="True">One</RadioButton>
<RadioButton Checked="Radio_Checked">Two</RadioButton>
The problem with this is that during InitializeComponent the IsChecked property causes the event to fire, this causes a null reference exception because my event handler attempt to use elements that have not been initialized yet.
Currently I have gotten around the issue by checking if the page IsInitialized within my handler as follows:
private void Radio_Checked(object sender, RoutedEventArgs e)
{
if (this.IsInitialized)
{
if(MyRadioButton.IsChecked.GetValueOrDefault())
{
//SomeOtherElement is not initialized yet so it is null
SomeOtherElement.Visibility = Visibility.Visible;
}
}
}
I would like to avoid having to use if (this.IsInitialized) in all my event handlers, as this is something I never had to do in WinForms.
So my question is, can I handle this a different way without having to add extra code to all my event handers?
To be honest, I'm surprised that you aren't checking for null in your handlers anyway... checking for IsInitialised is just a slight variation on checking for null. Handling null values is just part of good programming and let's face it, it's not really adding a lot of code.
So to answer your question, I would say 'No, there is no way around checking for null (or IsInitialised) in your event handlers if you don't want NulReferenceExceptions to occur'.
However, when using the MVVM methodology, we don't use many events, preferring instead to use data binding and ICommand instances where possible. When we do need to use events, we generally use them in Attached Properties, but there you will still need to check for null values.
You can remove the event handler from the xaml and add it after InitializeComponent();
radioButton1.Checked+=Radio_Checked;
Every element is created in that order it is in your XAML.
<RadioButton x:Name="MyRadioButton" ...>
<YourElement x:Name="SomeOtherElement" ...>
I assume in your XAML the RadioButton is placed before the other element you are referencing. On creation of an element in InitializeComponent all properties are set and also all events are fired. So SomeOtherElement does not exist in that moment.
The solution is quiet simple:
<YourElement x:Name="SomeOtherElement" ...>
<RadioButton x:Name="MyRadioButton"...>
Set SomeOtherElement before your RadioButtons.
If there are reasons to not switch the order of your elements in your XAML, then use the already mentioned null check:
if (SomeOtherElement != null)
{
SomeOtherElement.Visibility = Visibility.Visible;
}

In which event should one set dynamic control properties?

This article states that Page_PreInit should be used to
create or re-create dynamic controls.
For example:
Button button = new Button();
somePanel.Controls.Add(button);
Good. I understand.
However, it also says:
If the request is a postback, the values of the controls have not yet
been restored from view state. If you set a control property at this
stage, its value might be overwritten in the next event.
Huh?
Does this mean that all I should do is create the button, but not set any members of the button?
For example:
Button button = new Button() { CommandArgument="arg" };
somePanel.Controls.Add(button);
Does this mean that setting CommandArgument in this event is incorrect/not recommended/might cause an error/unexpected behavior?
Assuming it is incorrect, this would lead me to think that one would have to do something like this:
protected void Page_PreInit(object sender.....)
{
somePanel.Controls.Add((new Button());
}
protected void Page_Init(object sender.....)
{
foreach(Button button in somePanel.Controls)
button.CommandArgument = "arg";
}
is this the right way?
Finally, in which event should one set dynamic control properties?
There is no single answer for that last question as depending on the nature of the property it may or may not make sense to set a value in a specific method.
If the request is a postback, the values of the controls have not yet
been restored from view state. If you set a control property at this
stage, its value might be overwritten in the next event.
Might is the keyword here. If you consider some properties that may change as a form goes through various states then this is where you have to be careful of what may get overwritten as well as the question of whether or not this is a bad thing as it may be that the updated value should persistent and in other cases the original value may be better such as if someone wants to reset the form to its initial state.
My suggestion would be to do some trial and error to see what works as I can remember working with dynamic controls that could be tricky in some me cases to manage properly.

Custom event on object to change button state

I have 3 buttons on my Data Entry form, OK, APPLY, CANCEL.
This form is used to edit a doctor's details, things like first name, phone # etc...
I have one object doctorObj which at any given time is either empty (a new doctor) or an object pulled from a Linq query.
I deep clone the doctorObj to EditCopyDoctor which is of the same type but used for form editing (so if Cancel is hit, the database do not need to update).
What I want to achieve is observe the EditCopyDoctor for changes against the original doctorObj so
If(doctor.Changed() && doctor.IsNotNew)
{
ApplyButton.Enabled = true;
}
else
{
ApplyButton.Enabled = false;
}
So I thought writting an event to trigger when something changed on EditCopyDoctor is the best way to do it but I'm not sure how.
I can of course put the ApplyButton.Enabled code in the TextChanged events from the form but I was wondering if there are any quicker ways to do this, I don't really want to do this as there are 10+ textbox and other controls.
Since your "Doctor" type sounds like it's generated by LINQ to SQL/Entities you should find that it already implements the INotifyPropertyChanged interface. Therefore, you should just need to watch the PropertyChanged event and act accordingly.

Disabling a TextBox in C# .NET using CSLA

I am trying to disable a number of text boxes intended for displaying data (not edit) in one of my UserControls. However, for some reason I can not get the textBoxes to disable properly.
I've set "ApplyAuthorization on readWriteAuthorization" to true and the textBoxes are databound to the correct properties.
I've also added the following lines to the CanWriteProperty of my object:
if (propertyName == OpeningDateProperty.Name) return false;
if (propertyName == ChangeDateProperty.Name) return false;
if (propertyName == CloseDateProperty.Name) return false;
return base.CanWriteProperty(propertyName);
I can't figure out what I'm doing wrong here. I've implemented pretty much the same thing recently in other UserControls without any problems...
I am using Windows Forms in C# .NET (Visual Studio 2008)
EDIT: The code snippets and the properties are taken from my customer object. The date represent opening, last change and closure of the customer account. They are never supposed to be edited at all and in fact in the old sollution they are represented by textLabels, however we now want to use a text box and make the property's CanWriteProperty false.
I realise that the information might be sort of scarce, but I am looking for what I might have forgotten in this process.
EDIT: We are using CSLA as well and I guess (I'm new at this whole thing) this has something to do with why we want to do it like this.
EDIT (Sollution): As you can see in my answer below, the problem was that I had not set up the CurrentItemChanged event like I should have.
If you're trying to get them to be read only, then just set the .ReadOnly property to true.
Alternatively, if you're never ever using these textboxes for editing, then maybe just use a Label instead?
EDIT: Ahh it appears this more of a CSLA-framework question than a pure windows forms question. I've never even heard of CSLA before this question, but it looks interesting.
If you are databinding to properties of the control just bind the "ReadOnly" property of the textbox to the "CanWrite" property of your business object.
i think you mean ReadOnly property
To make this work you need to do the following:
Make sure the TextBox is databound to the right property in the correct way
Set up the needed checks for each textBox in the CanWriteProperty override in your root object
if (propertyName == OpeningDateProperty.Name) return false;
Make sure the rootBindingsource's CurrentItemChanged event is set up right
private void rootBindingSource_CurrentItemChanged(object sender, EventArgs e)
{
readWriteAuthorization1.ResetControlAuthorization();
}
Make sure the texBox's "ApplyAuthorization on ReadWriteAuthorization" is set to true
This solved the problem for me.

Categories

Resources