Controlling event-driven navigation in a WPF application - c#

I think the title for this question is probably wrong, but I'm not sure quite how to phrase it. I have a C# 4.0 (VS2010) WPF application.
This application consists of a single window with a header including the basics (logos, captions, etc) and a set of navigation buttons (back, retry, next, etc). The rest of the window is comprised of a listbox that is populated with one or more usercontrols based on what mode the app is currently in.
The way the code is currently written when the mode changes the listbox is cleared, all new user controls are added, and the buttons are set to their required state. This is fine for the initial state of each window mode but I'm having trouble deciding a good approach to update the navigation buttons as the contents of the controls change.
For example one screen is a configuration screen and there are three user controls contained within the listbox. These controls are custom classes that inherit from UserControl. Additionally they implement an interface that defines a method 'bool Validate' which determines if the control has been completely filled out.
This same scenario could apply to lots of other situations but this is a generic use case that is pretty straightforward to understand. When the screen initially loads the 'Next' button, whose visibility is controled by the parent window, is visible but disabled as the child controls can't possibly yet be valid. At some point as the user fills out arbitrary data within one or more controls each one would return true if its Validate method was called.
At the point where all controls are valid, the next button would then become enabled. Fairly straightforward design.
The problem is each control doesn't know what screen it is on, and this is by design. I don't want the controls having to be aware of each other and updating a button status in the parent window. I also don't want the parent window to run a polling thread to call Validate every second because in some cases the validation could be complex.
I'm thinking that the change event of each control within the UserControl (text boxes, radio buttons, etc) would all call a trigger a private validate event and this would set some public property on the interface or class.
f I can do that is there a way for the parent window to respond in an event-driven manner to the change of that property? I'm not looking to do this in WPF, doing this in C# code is preferable as I don't want to get into the complexity of WPF quite yet. I'm just not sure, other than constant polling, how to tell when every control's 'IsValid' property will have synchronized all to 'true', if that is even a good approach.
EDIT:
Okay, here is another way to ask the question. I have a List of something (in this case a list of an interface) and want to be able to respond to a public property change on each item in the list so I can take an action when all items are (bool in this case) true. The above explains the use case, but this is a more generic version of the question.
EDIT:
#Vincent "you might do it in an even simpler way with a custom "ValidatedChanged()" event that you can hook in the same way."
It turns out that this is really what I was looking for. The property notification approach seems to be more for ease of use with data-bound controls. I read a lot of posts on this site about how to implement that but it really wasn't what I wanted. I just wanted my objects to notify that an event occured, which happened to be a property change, but that is beside the point. I found documentation on implementing an event in an interface and I have it working now. Thanks for pointing me in the right direction and helping me realize what is really is that I needed.

So you have a ListBox which contains all your controls, and when all controls are validated, the Next button should be enabled ?
If so, when one of your control validates, you might search all sons of the ListBox to check them for validation, using VisualTreeHelper.GetChildren to get them all.
If you don't want / can't have a handle on the ListView, you might find it by searching up the visual tree starting from the control that just validated.
Each 'Validated' event of each control would be handled by a 'CheckIfAllValidated' event handler, and when all are validated, you could raise a 'AllValidated' events that would be handled by the button (and maybe some other controls as well) to enable it.
Edit : I understood that you did not want each component to know about their children, but notice that even the quite common PropertyChanged event has a 'sender' fields that tells who did raise the event. So any listener of a PropertyChanged on, say, the 'validated' property, can go up the visual tree, stop when it encounters a ListView, then search downstairs if all control that have a validated property do have this property set to true...
Edit 2 :
To be more clear about how to do it, either in your window new or on the window loaded event
or maybe on the ContentRendered Event, depending on how your controls are loaded, you
might use once that code to hook a handler to all your controls :
For Each ThisControl In MainListView.
Dim ThisControlType = ThisControl.GetType
Dim ThisControlPropertyChangedEvent = ThisControlType.GetEvent("PropertyChanged")
' you might wanna check here if event is not null / nothing
ThisControlPropertyChangedEvent.AddEventHandler(ThisControl, New PropertyChangedEventHandler(AddressOf APropChanged))
Next
and you write the APropChanged somehow like that :
Public Sub APropChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs)
If e.PropertyName = "Validated" Then
Dim ValidatedForAll = True
For Each ThisControl In MainListView.Items
Dim ThisControlType = ThisControl.GetType
Dim ThisControlValidatedProperty = ThisControlType.GetProperty("Validated")
'you might wanna check for non null here
If Not ThisControlValidatedProperty.GetValue(ThisControl, Nothing) Then
ValidatedForAll = False
Exit For
End If
Next
If ValidatedForAll Then
MessageBox.Show("Yeeppee") ' you might send an event instead.
End If
End If
End Sub
Edit 3 : you might do it in an even simpler way with a custom "ValidatedChanged()" event that you can hook in the same way.

Related

FocusManager.FocusedElement and MVVM

I'd like to control which button is focused in my view. User must be able to perform a job without need to use mouse. And job is going through all elements. Depending on element, some buttons will appears, some disappears.
I could do it with dependency properties. To example, if there are buttons Previous and Next, then I can provide IsPreviousFocused and IsNextFocused to set focus to them after certain event. Events can be: showing window for the first time (something should have focus already), reaching 1 element (hiding Previous button, setting IsNextFocused), reaching last element (opposite), etc.
This looks reasonable more or less.
But, if I have, to example, 10 buttons (different operations), then all of them will have to have dependency property!
So I was thinking about much easier approach: when I hide button, there will be no focus
if(FocusManager.FocusedElement == null) { ... }
If I can detect, when there are no focus, then I can try to set it to one of the buttons somehow. This way, I don't really need any complicated focus management
Question is: how to deal with FocusManager.FocusedElement in MVVM scenario? How to detect when there is no focus (when window is shown first time, when certain button is clicked and become invisible, etc)?
P.S.: I actually hate ms for making another technology without thinking fully into it; focus is very basic feature (and everybody care about it in their software), but there is no direct support for it (in xaml); looks like "oh, its too complicated, lets skip it" solution.
You could control your focus from your ViewModel by using the approach shown here:
Set focus on textbox in WPF from view model (C#)

WPF - Common event fired for value changed

Ok. This seems like an incredibly basic use case but fore some reason I am having an issue finding a common solution across control types.
Heres the use case:
A form is presented to the user with any editable controls. (Text box, combo, grid etc.).
The user edits a value in the control and tabs out.
Expectation is that I can wire to an event like Lost Focus and do "foo" with the changed value.
The user then gives focus back to the control and tabs out without making an edit.
Expectation is that whatever event I am wired to I can check if the value has been changed.
Is there one common event across controls that will only fire when the user has finished editing( such as tab out or enter ) and allow me to check previous state vs. current state?
Jason, you may want to look into Binding and DependencyProperties in WPF instead of tracking events in your form. You would bind a class to your form which exposes properties to be changed. Using DependancyProperties a single event is fired called "PropertyChanged".
Unfortunately is is a broad topic, but you will really get the full benefit of the WPF programming model. Searches on "dependency properties in wpf" will give you some good examples.
I think maybe this is Focus issue. There exist two different focus types: keyboard focus and logical focus. The the control that has keyboard focus is the one that has the caret, in what the user press a key and the control process that input. The a control may have the logical focus but not having the keyboard focus. Please check this in the MSDN article "Input Overview". About the other question, maybe you could process the TabControl.SelectedItemChanged for taking the event when a tab item selection changed.
Hope this is helpful to you...
What you may be interested in is implementing INotifyPropertyChanging (not just INotifyPropertyChanged).
As noted in the answer of this question, the INotifyPropertyChangING does not prevent you from changing a value, but to detect WHEN something DOES change, and what it's new value is going to be.
Hope it helps in your solution needs.
As the previous answers suggested, you would be better off by implementing a view - viewmodel structure, with your viewmodel having implemented INotifyPropertyChanged, and thus allowing you to bind to properties that will announce their changes to the UI.
If you don't want to do this, you can eventually subscribe on your input elements to the PreviewKeyUp event, check if the Tab key has been pressed and proceed from there.

How to reset all items in a groupbox, but not any items outside said groupbox?

I'm working a WPF application which has a number of groupboxes, each containing 3-4 controls a piece. Each of these groupboxes contains a reset button.
Instead of explicitly writing a line for each item the groupbox reset buton should reset is there a way to have the reset button only reset all items for the groupbox it's contained upon?
When researching this issue I found the following post which seems relevant but I'm unsure how to extend Nathan's answer to do this: What is the best way to clear all controls on a form C#?
You can use this question as an source of methods you need.
First of all you need to find groupbox that contains button. To do that use sender argument form click event it is reference to button that was clicked and call FindVisualParent method from one of answers in that question. This will give you groupbox.
Then Find all ui elements by FindVisualChildren<DependencyObject> method. This will give you super set of controls that are to be cleared.
Then it is just a loop over this as in question that you referenced. Note that that question was intended for winforms not wpf so you will have to revise the controldefaults dictionary which handles grouping controls ~(Panel GroupBox) which is not needed here (and would not work in wpf).
You might add a TAG to each control telling which property is beeing edited in the control, and then on reset press, you travel up the button to find containing GroupBox, then down to find all controls contained in this GroupBox, and then you find, with the TAGs, the name of the properties that needs reset. You then reset the properties using Reflexion.
Maybe an issue is that the default value might not be obvious. But you could store the default values for all your bindings in a static object, and copy the value of the property for this reset object into the current status object.
Rq : you could also use reflexion to get the bindings for all control of current GroupBox, and then get the bounded properties from the binding, so no need for a TAG. BUT if a control has several bindings, you won't know which one to reset. In my case, controls within GroupBoxes have also a color binding to indicate wether they have default value (green) or non-default (red), hence the need for a TAG. (BUT the color indicator are read-only so reflexion could, in fact, also be used with a little more use of Reflexion... was just too lazy to change a working logic when i saw that :-) )
I don't know about your bindings so obviously i cannot tell what's best, i hope this thoughs can help.

What is a good way to base the enabled state of the OK button in a dialog on valid control entries

Even if I associate the button with a class derived from ICommand, I am still left with figuring out how the button should trigger the CanExecute method and refresh its enabled state. I do know about the CanExecuteChanged event for which a button with an associated command registers, but see the following paragraph for why this is troublesome.
On a plain old dialog consisting of some 10-15 controls, it seems haphazard to have to process every change notification for every single one of those controls, triggering the CanExecuteChanged event on the button's command, causing the button's enabled state to be affected by the CanExecute method's return value. Even stating what needs to be done in the last sentence was quite cumbersome.
There must be a better way of coding a WPF dialog, so that the confirmation button (e.g., OK) is grayed out until all controls have valid information and is enabled at that point in time (i.e., when all controls are properly filled in). Sample code, ideas and pointers to articles would be immensely appreciated.
Thanks
I don't see anything haphazard here. Since your condition is "all controls have valid information", this can occur after any control is edited, and therefore you need to listen to change notifications from all controls.
On a plain old dialog consisting of some 10-15 controls, it seems
haphazard to have to process every change notification for every
single one of those controls,
I don't think so. Every Textbox, checkbox changed event is handled by the same handler, say SetState(), which calculates the overall state of the dialog. Every time a control is edited, the entire state is recalculated.
until all controls have valid information
Then that object would have a boolean property EnableOKButton, let's say, which is set according to the updated state. Then that property is bound to the button's Enabled property so it automagically changes - without dealing with extraneous events.

Detecting default button on a control

This seems very simple, but I can find nothing on a web concerning the behaviour I want to add to my custom control.
My custom control is a textBox with a list of choices. When the text entered by the user is not part of the list, a popup will appear with the list allowing the user to select a correct choice.
Sometimes, there may be a default button on the container in wich the custom control has been added. If so, when the enter key has been pressed, if the text is wrong, The popup must been displayed. If there is no default button, on enter, nothing must happen even if the text is wrong.
To be able to create this behaviour, I must be able to detect the presence of a defaultbutton in the container, and it must be done inside the c# code of the cutom control.
I hope the description is clear enough.
Thanks in advance
Have you thought about implementing an MVVM approach and the Command pattern? So long as your view model knows what the choices are, you can bind the default button to a command. So long as the commands CanExecute handler returns false, i.e., an appropriate choice has not been entered/selected, the button will be disabled and won't respond to the user pressing enter.
Since I was unable to know what other controls I had from the custom control I chose to go like this:
I made a recursive function to find the first parent using FrameworkElement.Parent
Having the parent, I could take a look at every controls it contains.
As soon as I saw a button, I had to verify if IsDefault.
For this one, I used the VisualTreeHelper GetChildrenCount(DependencyObject obj_Parent) and GetChild(DependencyObject obj_Parent, int childIndex). Recursivity once again...
It works very well even though it means more code to execute.

Categories