ComboBox changed before Window is loaded - c#

I have a WPF / XAML Window that contains a ComboBox that is giving me problems.
The window's ComboBox is firing off the SelectionChanged event.
The Debugger callstack shows me that SelectionChanged is being called (indirectly) from the Window Constructor.
The problem is that the window has an event Window_Loaded, which does some final initialization of data-members. Because this final initialization isn't done yet, the SelectionChanged event fails with a null-reference exception.
There are several ways I could solve this, but I'd like to know the "most correct" way.
I could fully initialize all my data members in the constructor. This violates the concept of keeping constructors minimal.
I could code the SelectionChanged event handler to properly deal with some data-members being null. This is coding to deal with only a startup problem that will never occur once the Window is fully constructed.
I could make the data-members Lazy-Loaded, so they are not initialized by Window_Loaded, but rather when they are first accessed. Seems like a bit of work to solve a problem that could be solved more simply.
I assume I'm not the first person to deal with UI-events prior to the Window Loaded event. What is the preferred way to handle this?

I had a similar problem and while tracing through it I had an "a-ha" moment. I had a default value as "IsSelected", an OnChange event, and a custom loadSettings method. I blamed the settings method at first, but it turned out that having a default value selected triggered the OnChange event before a handful of the controls were loaded, including the parent combobox which was triggering the null reference. Once I removed the "IsSelected" and allowed the default value to be null/empty it worked fine and my loadSettings method took care of setting the default or last used value.

I usually deal with the (endlessly irritating) SelectionChanged problem like this:
bool mySettingSelectionChangedInCode;
private void SetMySettingComboBox(string value)
{
mySettingSelectionChangedInCode = true;
mySettingComboBox.SelectedItem = value;
mySettingSelectionChangedInCode = false;
}
private void mySettingComboBox_SelectionChanged(object sender, EventArgs args)
{
if (mySettingSelectionChangedInCode)
return;
//...
}

The most proper way would be to build your application using MVVM pattern. In that case you would not have to deal with those problems. But I realize that it is not always possible to just move to MVVM unless the project is in its very beginning state.
Anyway, the problem you describe I would solve by defining a flag like IsInitialized in your window and set it to true once you've completed the initialization in the Loaded event handler. Then, I would check that flag in the SelectionChanged handler and if it is False then return from method without doing anything (ignore the call).

This worked for me:
if (System.Windows.Application.Current.MainWindow.IsInitialized == true)
{
//Do something
}

This could happen because you've set the SelectedValue of the combobox after setting the ItemsSource property.

Related

Fody.PropertyChanged doesn't fire on inherited Property

I have a Control which inherits from NumericUpDown in WinForms.
I want to handle changes to the DecimalPlaces-Property of NumericUpDown, therefore I have both tried declaring a
void OnDecimalPlacesChanged()
{
MessageBox.Show("Moeeep");
}
and also manually subscribing the PropertyChanged-event in the ctor manually. It seems to just not fire when I update DecimalPlaces and I have no idea why.
Since Control does not implement INotifyPropertyChanged, only your class does. Because of that, the Control doesn't raise any event when the DecimalPlaces changes. No framework can inject that into their code.
For now, the best thing you have is to override the UpdateEditText method. It is called when the DecimalPlaces property changes. Note that this is of course not the only reason the method is called, so you might have a problem there...

Doubts with Attach property

Well, the title is not very descriptive because they do not how to name what I'm looking for. I'll try to explain it as best I can.
In the .xaml file, a control (Suppose a textbox), if you type "Text", this can not be used again property. If we write by hand, the compiler displays an error. Good already explained why I think it's now easier.
Suppose now that I have two (DependencyProperty.RegisterAttached ... ...) with the name "Propiedad_1" and "Propiedad_2" is possible, if I'm already using one, the other can not be used in the same control and vice versa?
2) Another question, within a dependency property of type string, is it possible to check if the String changed at some point (around trying to not use a variable and then comparing), I need to avoid spending a TexBox and avoid the textbox.TextChanged event.
Thanks!
EDIT
This is what I have now.
public static readonly DependencyProperty FilterSourceProperty =
DependencyProperty.RegisterAttached("FilterSource", typeof (TextBox), typeof (ListViewExtension),
new FrameworkPropertyMetadata(null, OnTextBoxTextChanged));
public static TextBox GetFilterSource(DependencyObject dObj)
{
return (TextBox) dObj.GetValue(FilterSourceProperty);
}
public static void SetFilterSource(DependencyObject dObj, TextBox value)
{
dObj.SetValue(FilterSourceProperty, value);
}
private static void OnTextBoxTextChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs e)
{
var listView = dObj as ListView;
var textBox = e.NewValue as TextBox;
if ((listView == null) || (textBox == null)) return;
textBox.TextChanged += delegate(object sender, TextChangedEventArgs tcea)
{
var view = CollectionViewSource.GetDefaultView(listView.ItemsSource);
if (view == null) return;
view.Filter += item =>
{
...
...
...
...
};
};
}
In .XAML
<TextBox Name="TxtFilter"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Text="{Binding Filter, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" CharacterCasing="Upper"/>
<ListView Margin="0,5,0,0"
ItemsSource="{Binding Articles}"
IsSynchronizedWithCurrentItem="True"
SelectedItem="{Binding SelectedArticle}"
tools:ListViewExtension.FilterSource="{Binding ElementName=TxtFilter}">
In ViewModel
public string Filter
{
get { return _filter; }
set
{
if (_filter == value) return;
_filter = value;
RaisePropertyChanged();
}
}
What I need now is change the use of a "TextBox" by a string.
tools:ListViewExtension.FilterSource="{Binding Filter}"
Your question is fairly hard to understand, due to the lack of a good, minimal, complete code example as well as the poor English. The latter is understandable (though you must still do what you can to ensure you're communicating as well as possible), but the former definitely should be addressed.
Lacking those improvements, I will guess that your questions (both…for future reference, please do not post two different questions in the same post) amount to the following:
I have two attached properties, which I would like to make mutually exclusive in the XAML editor. Is this possible?
No. The behavior you're seeing applies only to a single property. The editor will complain if you try to set the same property more than once. But it's easy for it to do that, since all it has to do is check whether that property was already used in the element.
For two different properties that are supposed to be mutually exclusive, there's not any feasible way to modify the editor or compiler's behavior to check this.
As an alternative, consider implementing the two mutually-exclusive values as a single property, where that property can accept two different subclasses of a given type, and where those subclasses each represent one of the two mutually exclusive property types.
Can I optimize property updates, so that if a new value is assigned that is actually the same as the current value, no "property changed" event is raised?
Whether this is possible depends on how your code is actually written. In WPF, binding is supported through the use of DependencyProperty or INotifyPropertyChanged. Neither of these would imply a TextChanged event in an object for a Text property, which you stated is the event you don't want raised.
Note that in general, DependencyObject.SetValue() will suppress change notifications if the effective value (after coercion) has not actually changed. Note also that in most other cases, extra change notifications would not normally be a real performance issue.
Lacking a good code example, not much more advice on that second question can be offered.
If you feel that these answers don't reasonably or usefully address your questions, please improve your post so that it is more understandable.
EDIT:
With respect to the first question, and based on the code snippet you've provided (not complete), I would say that the simplest approach would be to make FilterSource have type object instead of TextBox, and then in OnTextBoxTextChanged(), check the type of the new value and handle appropriately. I.e. if it's a TextBox, do what you're doing now (mostly…see (*) below), and if it's a string instance, just configure the view's filter directly instead of putting the configuration into an event handler.
(*) note:
I see at least two areas of improvement in your OnTextBoxTextChanged() method:
There is no need to rebuild the Filter event handler just because the text's changed. Instead, you can just call Refresh() on the view. So in that approach, you would implement the event handler for the Filter event to always retrieve the TextBox.Text property value for filtering. You would subscribe to the event once, and then the event handler for TextChanged would just call Refresh().
In the string scenario, you'd use a Filter event handler that just filters using the string value, with no need to handle the (non-existent, of course) TextChanged event.
The bigger issue is that you only ever subscribe to the TextChanged event. If you only ever change the FilterSource property once, you'll never notice a problem, but it is a problem. If the property value is ever changed again, you should be unsubscribing the old event handler before subscribing a new one. If you make the change I describe above, where the TextChanged event handler is only calling Refresh(), the impact of this bug will be significantly reduced. But it's still a bug.
end of note.
As far as the second part of your question goes, I don't see a problem that needs solving. It's not clear whether you're concerned about the TextBox.Text property or the FilterSource property, but I think that neither property should generate change notifications if the newly set property value is the same as the old.
If you think differently, please provide a better code example (both minimal and complete) that illustrates clearly what actual problem occurs, along with a clear, precise explanation of what that problem is: how the code currently behaves, and how that's different from how you want it to.
Taking all of the above into account, I think your OnTextBoxTextChanged() method should look more like this:
private static void OnTextBoxTextChanged(DependencyObject dObj, DependencyPropertyChangedEventArgs e)
{
ListView listView = dObj as ListView;
if (listView == null) return;
var view = CollectionViewSource.GetDefaultView(listView.ItemsSource);
if (view == null) return;
if (e.NewValue is TextBox)
{
TextBox newValue = (TextBox)e.NewValue;
view.Filter += item =>
{
string filterString = newValue.Text;
// filter based on filterString, etc.
};
textBox.TextChanged += delegate(object sender, TextChangedEventArgs tcea)
{
view.Refresh();
};
}
else if (e.NewValue is string)
{
string filterString = (string)e.NewValue;
view.Filter += item =>
{
// filter based on filterString, etc.
};
}
else return;
}
For this to work, you'll of course have to change the type of your attached property from TextBox to object.
In the above, I did not bother to address the issue of unsubscribing from the TextChanged or Filter events. If you desire to fix that particular problem, it is relatively simple: you need to unsubscribe the old handlers from the events (for TextChanged, only if e.OldValue is TextBox is true of course).
Of course, to do this you'll need to store the old event handler delegate instances on a per-ListView object basis, e.g. in a dictionary or maybe even just having a private attached property (similar to the FilterSource property, but not visible to other code). That way, you can retrieve the delegate instances later to unsubscribe them from their events.

Prevent or bypass event if control's property is set server side

I have an ASP.NET checkbox that based on certian logic I need to set its checked value to true server-side. This is easy enough, but it fires the wired up MyCheckBox_CheckedChanged(object sender, EventArgs e) event as well.
That event's code is 100% ok, as long as it is called when the user checks the check box from the client. However, when I'm just trying to set that checkbox to be Checked = true; server-side I don't want the code in the event to run.
The cleanest way I would like to approach this was to inspect the sender object on the event and see if this event was really called by the client or not (like: http://msdn.microsoft.com/en-us/library/aa457091.aspx). However this did not yield any distinguishing information. It still looked like the checkbox being selected called it.
My 2nd thought is to set some session value and inspect this to basically return if not being called by the user. I can easily do this, but I don't like using 'public flags' of sorts unless absolutely necessary. I'd rather (if at all possible) inspect the sender or arguments on that event to determine if that property was set server side on reload of data from the database, or if the user actually selected it.
I saw this Prevent postback on server-side property change and it was not a direct solution to my question, but rather a work around not applicable to my situation.
Is there a way to determine this was set solely server side so that I can bypass running this code?
Add a private boolean variable dontFire to the top of your class with a default value = false.
Then use the following:
dontFire = True;
myCheckbox.Checked = True;
dontFire = False;
Then in the myCheckBox.CheckedChanged event put this at the top:
if (dontFire) return;
Problem solved.
If I am correct you are trying to set the checked property of check box true on load/initialize of the page.
Here are a couple of approaches you may follow:
Another approach to deal with the situation you mentioned could be to dynamically bind the controls (your checkbox's) event on the page load only if you are not setting the checkbox's checked property to true.
Use a simple page property as a flag to signify if the checked property had been set server side, and use it inside the check box's change event handler. You do not need to use session, as you may not need to persist this value across post back.
If possible put the logic to set the checked property of the check box to true in the check box's change event handler, and based on whether or not you are setting the property you can allow/disallow the handler code.
This would be my recommended solution:
public bool Check_CheckBox
{
set
{
checkbox1.Checked = value;
if (value)
{
checkbox1.CheckedChanged -= new EventHandler(this.Check_Clicked);
}
}
get
{
return checkbox1.Checked;
}
}
set the Check_CheckBox property to true based on your logic and it will do the trick.
Hope this helps.

2-way binding in Winforms app makes entering text in a TextBox painfully slow

I've set-up 2-way binding between my form (it has 32 controls) and an instance of my class but each character entered in a TextBox has that 1/2 second delay which makes the application almost unusable.
When I use DataSourceUpdateMode.Never, the problem does not occur which clearly indicates the 2-way binding is the culprit.
Note that if I set DataSourceUpdateMode.Never for each control but one, the lag exists for that one control so it doesn't seem to be the number of bound controls that causes the issue.
parameterTagRecord = new PETParameterTagRecord(TagID);
baseTagNameTB.DataBindings.Add("Text", parameterTagRecord,
"BaseTagName", true, DataSourceUpdateMode.OnPropertyChanged);
And an extract of my class:
public class PETParameterTagRecord : PETBaseObject, INotifyPropertyChanged
{
private string _baseTagName = Constants.NullString;
public event PropertyChangedEventHandler PropertyChanged;
public string BaseTagName
{
get { return _baseTagName; }
set
{
_baseTagName = value;
NotifyPropertyChanged("BaseTagName");
}
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
What am I doing wrong?
It shouldn't be that slow, but there's an option where you can have the textbox change on key press or on lost focus. Try setting it to lost focus. Also in your setter, be sure to check that _baseTagName != value before setting and raising the event. That will slow things up a bunch as well.
So, first try changing your binding like this:
baseTagNameTB.DataBindings.Add("Text", parameterTagRecord,
"BaseTagName", true, DataSourceUpdateMode.OnValidation);
See this MSDN link: http://msdn.microsoft.com/en-us/library/system.windows.forms.datasourceupdatemode.aspx. This means that instead of every keypress causing the new string value to be pushed into the property, it will only do so on Validation (which happens as part of the control losing focus).
Second, change your property implementation to match this:
public string BaseTagName
{
get { return _baseTagName; }
set
{
if (_baseTagName != value) {
_baseTagName = value;
NotifyPropertyChanged("BaseTagName");
}
}
}
Right now you're raising the event whether the property has actually changed or not. That is also detrimental to performance.
I ran into the same exact issue with BindingSource. This has has nothing to do with the update mode or notifications being fired too often (though indirectly, it does). The current implementation causes every single bound element to refresh whenever any property changes. So the reason OnValidation is less of an issue is obvious, it happens less frequently.
Fairly easy to check, too. Add two counters, increase each whenever a getter is accessed or NotifyProperChanged is called. In my case, with roughly 40 elements, I'd be at 1/40 after loading the form. Add a character in a textbox, suddenly at 2/80. Keeping the key pressed, my app stopped being responsive. Once it finally caught up, the count stood at something ridiculous like 50/2000. All from one single element changing.
I might be wrong, but I don't see how this makes sense or could be the desired implementation. Why would I want to update the whole form when one element changes, defeats the point of binding specific elements in the first place.

How do you manually refresh a window in WPF?

I'm using the ICommand interface to perform binding on a couple of buttons in my application, the Run and Close button. I'm using a helper method that was mentioned in this question to hook up the ICommand to a delegate as follows:
_someCommand = new DelegateCommand(this.DoSomething, this.CanDoSomething);
private ICommand _someCommand;
public ICommand SomeCommand
{
get { return _someCommand; }
}
private void DoSomething(object state)
{
// do something here
}
private bool CanDoSomething(object state)
{
// return true/false here is enabled/disable button
}
This appears to work just fine as, in my implementation, CanDoSomething returns the value of a property that I have in my application.
If I set the initial value of the property to true, then the button is enabled and false it is disabled.
I have a series of events that are raised from a BackgroundWorker in the application layer back to the ViewModel that change the value of the property to true or false based on the current state of the application.
The current problem I'm having is that the button is not "re-enabling" when I set the value to true after the work has completed. If I click somewhere within the window, it will update. So, therefore, I'm thinking than manually refreshing the window will solve my problem, at least for the interim. This feels a bit gross to do it this way, but I'm kind of at a loss for what else I could try.
If anyone has any suggestions, I'm all ears.
Thanks for the help!
Ian
Edit -
A little bit more information on the application itself. It uses a background worker in the application thread to handle the "work". The application is a simple utility to manage the creating of tables and loading of data into the tables. We use a lot of pre-defined SQL scripts to setup our test environment, so this is a simple utility that allows us to do that sort of thing based on parameters provided by the user in the UI.
Hopefully that helps, because when I re-read my question it read as if I were doing everything in the UI thread, which is not the case. Progress reports are raised back up to the UI thread and everything is updated as expected, except the button..
CommandManager.InvalidateRequerySuggested() may be the answer - it tells all the commands to check whether they are enabled or not.
You have to raise the CanExecuteChanged event:
http://msdn.microsoft.com/en-us/library/system.windows.input.icommand.canexecutechanged.aspx
This may be more useful than the other answer in cases where you know you should re-evaluate a single control, and re-evaluating all the controls would be costly. The other answer is simpler if you don't have a case like that, though.

Categories