To begin with, this answer seriously lacks theoretical support (i.e. explaining why). I would very much second this answer instead. It is much under-voted because it was posted more than a bit later.
Your approach should definitely also be guided by a bit of philosophy.
MVVM (or How to separate business logic from presentation logic)
Let's see what you have come up with. You have a UserControl
with DependencyProperty
-ies named Departments
, People
, Teams
, etc. How is this UserControl
not exposing business logic when it's crowded with Domain-specific nouns? Programming is a lot more about names than you may think. Contrast: TextBox.Text
and TextBox.Address
as names for properties. The second case suddenly introduces a pre-disposition. An expectation that the value of this property serves a very specific purpose. Never underestimate the names of members and the conceptual communication weight involved.
What is a UserControl?
Based on your linked answer:
A UserControl is simply an easy way to create a Control using
composition. UserControls are still Controls, and therefore should
solely be concerned with matters of UI.
But, is it? Based on this explanation, a proper PersonPicker
control would have been:
<PersonPicker
FirstItemsSource="{Binding PersonPickerModel.Departments}"
SecondItemsSource="{Binding PersonPickerModel.Teams}"
ThirdItemsSource="{Binding PersonPickerModel.People}"
FirstSelectedItem="{Binding PersonPickerModel.SelectedDepartment}"
SecondSelectedItem="{Binding PersonPickerModel.SelectedTeam}"
ThirdSelectedItem="{Binding PersonPickerModel.SelectedPerson}"
IsFirstItemSelectionEnabled="{Binding PersonPickerModel.IsDepartmentSelectionEnabled}"
IsSecondItemSelectionEnabled="{Binding PersonPickerModel.IsDepartmentSelectionEnabled}"
</PersonPicker>
Oh, by the way, the control should also have been named something like ThreeComboBoxControl
or ThreeHierarchiesControl
. You named it PersonPicker
because that is what you want it for. While fully re-usable, the control is not a UserControl
... it is a facility. If you need a facility for your very own needs, you sometimes have to concede: Do you really want a reusable UserControl
, or a reusable business tool? If you decide that it is a business tool, it makes perfect sense to have a specific viewmodel for it.
View-models are over-interpreted
Binding, in WPF, does not care about class types, only about property names. As long as an object of type PersonPickerModel
exists in your bound DataContext
, in your example, and it carries properties with the bound names (and, of course, the property types are compatible), everything will work properly, regardless of whether your PersonPickerModel
is named as such, or any other way. You can even define it as an object.
In short, what you have come up with is an intermediate between a UserControl
and a business tool. Even if only for the correctness of it, I suggest you reconsider, what it is that you need between the two. If you need a business tool, keep the names, create a tailor-made viewmodel. Think about this... you have a Window
or something else, and:
<PersonPicker DataContext="{Binding PersonPickerViewModel}"/>
The bindings can be placed inside the control's xaml code because you are only ever going to use it for the very specific reason of picking people. Why do you have to be so verbose in every place where you are going to reuse your control? Just pass it the ViewModel and leave the rest to that.
But there is a really clearer reason why this is better than what you are already have.
Your solution is imparting a false sense of reusability.
Designing a truly abstract "proper" UI control is hard and not to be underestimated. Look what you have come up with, three adjacent combo-boxes. What do they represent? Departments? Teams? No, it's collections. Why three? Why not four, or, even better, a variable number. A proper control pretending to cover your specific needs would have to offer a variable number of collection handling. What are these collections? Simple strings? More complicated viewmodels?
So far, we have pretty much come up with a MultiComboBox
control. Think about that, a variable number of ComboBoxes, their ItemsSource bound to a different collection, as per your needs, ViewModels can be used for that. You know how to use a ComboBox
and you definitely understand its conceptual representation, so you will not have a hard time juggling multiple of these. Thereafter, you can expose them in the code-behind through collections of ItemsSource
and SelectedItem
properties, accessible by index, maybe? You have dozens of possibilities (and loads of responsibilities, of course) because a UserControl
is abstract.
Designing a UserControl
means that nobody cares that you have Departments, Teams or People. Creating "non-generally-but-partially-reusable" UserControl
s is a bad practice but a good workaround so use that to your benefit when MVVM-ing around.
EDIT:
I'm not certain... but it feels like the selection in one ComboBox
affecting the items of another could be considered a pure UI concern
and something which belongs in the code-behind- depending on the
selection logic, I guess.
Tomorrow, your requirements suddenly change! You don't want ComboBox
es, you want ListBox
es. Think about it, more beautiful. You select a Department in the first list, second list immediately shows you the Teams, you avoid an additional click (at the cost of some more UI space). So you decide to build another control, one with three list boxes (or ListView
s for that matter, or anything else). Do you write the filtering logic again? Copy-paste it, maybe?
Whatever it is that you are referring to as "filtering logic" is emphatically not a UI concern. Most basic UserControl
s are that basic for a good reason: they try to avoid assumptions about what you would want to do, as much as possible. They simply try to convey the user's reactions to you, the programmer. Any additional "initiative" only robs you of flexibility.
A Button
is the abstraction for "producing clicks". A ComboBox
is an abstraction for making a selection from a collection. A ListBox
too. A ListBox
does not offer anything more than a ComboBox
in terms of abstractions, it only has a different fanciness in terms of presentation. That is your keyword, there! Presentation (as in Windows Presentation Foundation).
Whenever you compose a UserControl
from these basic UserControls, you are practically risking breaking this elegantly meaningless abstraction. Your UserControls
should only "chain" abstractions together. In that sense, the UserControl
you are trying to create offers the abstraction of making three selections at once. A filtering logic is, again, emphatically NOT a UI concern. My simple way to argue about this was that you might need to change the UI and the filtering logic would have to be re-written, instead of simply being attached. That's your other keyword there. Attached, like ViewModels attach to controls.
The only job of your controls should be to model and abstract away user interaction, and user interaction does not include filtering lists anymore than the remote control of a TV includes logic for producing two-digit values (as when you click two numbers in sequence). You press the number 1 twice in a row, within 3-4 seconds, which takes you to channel 11. Do you think this wait for a second input within 3-4 seconds is "coded" into the remote control, so that the number 11 is sent to the TV after 3 seconds? Or... the signal for number 1 actually simply travels in two occurrences, 3-4 seconds apart, with the TV deciding what to do next, instead?
Well, your UserControl
is the remote control, the TV is your model. It receives two notifications about the pressing of button 1, one now, one in three seconds. The UserControl
has to ask you for collections and convey potential selections from items of the collections to you, the programmer. This is, pretty much, where its responsibilities end. The rest is... philosophy ;)