Description
When creating WPF applications, you use binding. It’s a very nice workflow except for one point, where I want to bing the ContentProperty of a Label or a TextBlock. If I set this up in the xaml code with {Binding}, then I don’t have the “feeling” of the designer since there is no text until it compiles.
You can use “SetBinding” method for this programmatically in code, and having this in mind I decided that I need an UIModel class that encapsulates the logic of it.
Solution
First create a class that stores the UI Element, the dependency property whose property will be set and the property info of the UIElement that is set as a property in child class.
public class UIControledElement { //ui element from the window public FrameworkElement Element { get; set; } //dependency property to affect. Set in the attribute in the class public DependencyProperty LanguageProperty { get; set; } //property info public PropertyInfo UiModelProperty { get; set; } }
Create a base class that will act as a template for other classes. It will provide a Register method that will create UIControledELements based on the properties described in Register function.
public class UIModel where TWindow : Window { //I need the context which is the MainWindow to get the tagged properties private static TWindow context; //Because the register method below is static, this element is also static private static List UIControledElements = new List(); public UIModel(TWindow context) { UIModel.context = context; UIModel.context.ContentRendered += Context_ContentRendered; } //Making sure that all elements are loaded and rendered, afterwards we make the changes required private void Context_ContentRendered(object sender, EventArgs e) { this.UpdateDisplay(); } //this method is called in the child class to register a property. Because at this point the object doesn;t exist //we need to call it statically. In here we also store the DependencyProperty that we will alter protected static UIControledElement Register(string PropertyInfo, DependencyProperty Property, Type Owner) { var propertyInfo = Owner.GetProperty(PropertyInfo, BindingFlags.Public | BindingFlags.Instance); if (propertyInfo != null) { var uicontroled = new UIControledElement { UiModelProperty = propertyInfo, LanguageProperty = Property }; UIModel.UIControledElements.Add(uicontroled); return uicontroled; } return null; } //this method returns the UIElement protected FrameworkElement GetValue(UIControledElement Element) { return Element.Element; } //this methods sets the UIElement protected void SetValue(UIControledElement Element, FrameworkElement Value) {Element.Element = Value;} //in this method we get the value from the attribute and call the function SetProperty private void UpdateDisplay() { foreach (var item in UIModel.UIControledElements) { DisplayAttribute Attribute = item.UiModelProperty.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute; if (Attribute == null) return; SetProperty(item.Element, item.LanguageProperty, Attribute.Name); } } //this method alters the DependencyProperty of the UIElement with the value from an attribute private void SetProperty(FrameworkElement Element, DependencyProperty Property, object value) { Type depencendyObjectType = typeof(DependencyObject); MethodInfo SetValueMethodInfo = depencendyObjectType.GetMethod("SetValue",new Type[] { typeof(DependencyProperty), typeof(object) }); SetValueMethodInfo.Invoke(Element, new object[] { Property, value }); } }
Usage
We create a child class. UIControledElements references the element ExampleText.
public class UINumberModel: UIModel < MainWindow > { private static UIControledElement exampleText = Register("ExampleText", Label.ContentProperty, typeof(UINumberModel)); [Display(Name = "NewLabelContent")] public FrameworkElement ExampleText { get => GetValue(exampleText); set => SetValue(exampleText, value); } public UINumberModel(MainWindow context): base(context) {} }
In main you will create a new instance of this class. At initializing the base class will search for UIControledElements. It will detect that the property “ExampleText” was stored and it will change the Label.ContentProperty with the value provided in DisplayAtribute
public MainWindow() { InitializeComponent(); UINumberModel m = new UINumberModel(this) { ExampleText = LabelIO }; }