Reputation: 3570
I want to get a reference to the TextBox
bound to a given property name.
I would like to do this without changing the view.
Is there a proper way to do this using Caliburn Micro?
If not, what is a "good enough" way?
public class MweViewModel : PropertyChangedBase
{
public MweViewModel() : base()
{
PropertyChanged += (object sender, PropertyChangedEventArgs e) =>
{
// Find control (i.e. TextBox) bound to property with name e.PropertyName
TextBox textBox = ...
};
}
}
Upvotes: 2
Views: 1725
Reputation: 8656
I'm not necessarily sure this is the most sensible approach (it's not something I've tried to do myself), looking at the Documentation, there's the mention of a ViewModelBinder
class that's responsible for fixing up the various bindings of properties, methods, etc. to their respective ViewModels
.
The BindProperties
function on the ViewModelBinder
is responsible for resolving the bindings between your properties, and the UI elements to which they're eventually bound. You could, define your own function based on the existing code, which kept track of all the bindings being established, so you would have a record of them you could use elsewhere in your program.
Using the existing code would give you something like this:
ViewModelBinder.BindProperties = (namedElements, viewModelType) =>
{
var unmatchedElements = new List<FrameworkElement>();
foreach (var element in namedElements)
{
var cleanName = element.Name.Trim('_');
var parts = cleanName.Split(new[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
var property = viewModelType.GetPropertyCaseInsensitive(parts[0]);
var interpretedViewModelType = viewModelType;
for (int i = 1; i < parts.Length && property != null; i++)
{
interpretedViewModelType = property.PropertyType;
property = interpretedViewModelType.GetPropertyCaseInsensitive(parts[i]);
}
if (property == null)
{
unmatchedElements.Add(element);
// Log.Info("Binding Convention Not Applied: Element {0} did not match a property.", element.Name);
continue;
}
var convention = ConventionManager.GetElementConvention(element.GetType());
if (convention == null)
{
unmatchedElements.Add(element);
// Log.Warn("Binding Convention Not Applied: No conventions configured for {0}.", element.GetType());
continue;
}
var applied = convention.ApplyBinding(
interpretedViewModelType,
cleanName.Replace('_', '.'),
property,
element,
convention
);
if (applied)
{
// Log.Info("Binding Convention Applied: Element {0}.", element.Name);
}
else
{
// Log.Info("Binding Convention Not Applied: Element {0} has existing binding.", element.Name);
unmatchedElements.Add(element);
}
}
return unmatchedElements;
};
At the point when the binding is being added (when applied
is set), you have all the information you require. You could then store specific bindings (e.g those relating to a TextBox
).
You might use something like a static dictionary (there may be something far more appropriate depending on your requirement):
ViewModel Type Bound Property List of Bound elements
| | |
| | |
Dictionary<Type, Dictionary<PropertyInfo, List<FrameworkElement>>>
You would have to be careful about null/sanity checks.
There are a few other solutions that use helper methods to grab the bound properties/controls, although they often have to traverse the visual tree, this way, you're doing it at the point the binding is actually created.
Upvotes: 1