WPF - Binding in XAML to an object created in the code behind - c#

Can an object created in the code (ie C#) be used for binding in the XAML?
For example:
public class MyForm
{
private MyComplexObject complexObject;
public MyForm()
{
InitializeComponent();
}
public OnButtonClick(object sender, RoutedEventArgs e)
{
complexObject = new MyComplexObject();
}
}
complexObject is not created till a button is clicked. But once that button is clicked I would like to have a text box that is bound to complexObject.ID start showing the Id.
I would like to do that in the XAML if that is possible.
Can this be done? If so, how?

Yes, this can be done, binding to a property that you update with the desired value. I'd suggest you look into the MVVM pattern (Model-View-ViewModel), which is really useful for structuring this nicely working with WPF. Check out this video for a nice overview:
MVVM video
Using MMVM you would create a class which would be the ViewModel class. This one would typically be set to the DataContext of the View. Having done so you could add dynamic references to the other properties of the class, e.g. binding your text field to some property holding the Id og the ComplexObject. If your ViewModel class had a property ComplexObject, which again had a property ID you'd simply bind to the object like this:
<TextBlock Text="{Binding ComplexObject.ID}" />
Having this you could trigger creation of your ComplexObject from mouse click, which you should ideally set up as a command binding. Also note that the ViewModel class (or whoever is holding the ComplexObject needs to notify the View when the object has been set. This can either be done by making the ComplexObject a DependencyProperty or by making the class holding the property implement the INotifyPropertyChanged interface - giving it the PropertyChanged function to trigger the changed event. I prefer the latter.

One possibility would be to have your XAML bind to a property on your code behind. The getter for that property would return complexObject.ID, if complexObject != null. Otherwise, it returns something "default", whether that's null or 0 or default(ID's type). Similarly, the setter for that property would assign value to complexObject.ID if complexObject is, again, not null.
public int ID
{
get
{
if (complexObject != null)
return complexObject.ID;
return 0; // or null or some appropriate default
}
set
{
if (complexObject != null)
complexObject.ID = value;
}
}

Related

Dotvvm run-time binding to 'object' type

i have this class
public class Property{
public string Name {get;set;}
public object Value {get;set;}
}
i want to create list of the above class List<Property> and dynamically add Mark Up Controls Code only
, so as their website they have an example HERE and what i did to that example is adding a public property of type Property to the TextBoxWithLabel class and changed the setter of the above example for binding as follows:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set {
SetValue(TextProperty, value);
Property.Value = value;
}
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
when i run the app and type something in the input field, the Value property of Type Property still null and here is where i'm stuck
i tried also to debug setter and it turns out it does not reach there so there is problem with run-time binding, which is 'as their example' this line of code
textBox.SetBinding(TextBox.TextProperty, GetValueBinding(TextProperty));
any help will appreciated :)
EDIT:
for more clarification,i have page called MainAppPage
and Markup Control with code behind called ContentControl
simply , MainAppPage passes List<Property> to ContentControl using this in MainAppPage
<controls:ContentControl Instance="{value: ClassObject}"/> then ContentControl start iterating through List<Property> and creating InputField's that derive from HtmlGenericControl
InputField's rendering like a charm in ContentControl
only thing is not working is binding , so again, how to bind Property.Value to InputField.Text so any changes happens in UI from user reflects on Property.Value after the InputField gets unfocused like any other MVVM pattern ?
DotVVM does not assign to the property usning the setter, is sets the underlying property store in DotvvmBindableObject instead. It's very simmilar what WPF does with their DependencyProperty, it's needed to represent the data bindings. You can actually completely omit the C# property declaration, declaring the field TextProperty and calling the DotvvmProperty.Register is enough to declare a property for dotvvm.
Other "problem" is that the controls do not store any data, everything has to be persisted in the view model. You can only use the control properties to data-bind a view model property. I think we are running here into a XY problem, I can only tell why your code does not work, but I have no idea what are actually trying to do...
Anyway, if you just want to "bind" your control to a view model property, have a look at https://www.dotvvm.com/docs/tutorials/control-development-markup-controls-with-code/2.0. You can declare the property like that:
[MarkupOptions(AllowHardCodedValue = false)]
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DotvvmProperty TextProperty
= DotvvmProperty.Register<string, TextBoxWithLabel>(c => c.Text, "");
Use it in the markup of you control
#baseType FullName.Of.YourControl
{{value: _control.Text}}
And use the control in your page (or other control)
<cc:YourControl Text="{value: _this.Property.Value}" />

WPF Databinding - Getter/Setter for intermediary object not getting hit

My WPF TextBox uses a class instance (called "SelectedDocument") as its datacontext. This class implements INOtifyPropertyChanged.
The SelectedDocument instance owns another object of type "CellContent" (named "Description"), exposed via a property.
CellContent also implements INotifyPropertyChanged.
The CellContent class has a string property ("TextValue") that can be bound to.
I'm binding the TextBox's Text property to that TextValue property. Like so:
<TextBox DataContext="{Binding SelectedDocument}" Text="{Binding Path=Description.TextValue" />
No issues with the databinding - it works both ways. But for that binding to work, WPF presumably has to hit the Description property in SelectedDocument every time to retreive the CellContent object:
public CellContent Description
{ get; set; }
(This property is more complicated in my code.) Then WPF can reach inside the actual CellContent object and get/set TextValue.
The problem: the Description property is never hit. It looks like WPF is bypassing it, and has created a direct connection to the TextValue property inside of the Description object. I want to hit the Description getter and setters every time so that I can execute some extra code there.
How do I do that?
If you want the Document to be marked dirty whenever a child's property is changed, you could subscribe to the child's PropertyChanged event.
I'm assuming your CurrentDocument looks something like this.
public class Doc
{
public Doc()
{
_description = new CellContent();
// subscribe to changes in child
_description.PropertyChanged += DescriptionChanged;
}
private void DescriptionChanged(object sender, PropertyChangedEventArgs e)
{
Debug.Write($"I'm a dirty dirty document. Property {e.PropertyName} has changed");
}
private CellContent _description;
public CellContent Description
{
get
{
Debug.Write("I assure you this is called every time a getter of the child properties is called");
return _description;
}
// If you have a setter, don't forget to -= unsubscribe and resubscribe += after changing
}
}
Try to to raise PropertyChanged event for Description every time when you change TextValue property.

"Binding" wpf Combox selectedValue to an integer?

I just started a new wpf project in hopes that I could learn a new technique as opposed to using winForms all the time.
I seem to be having way too much difficulty binding the selected value of a comboBox to an integer variable in my "MainWindow" class.
I have been looking at a host of "simple" examples from sites like codeproject, but they all seem way too complicated to just return the selected value of a comboBox. I am used to setting the "SelectedValueChanged" property and just setting a variable, which takes just a few clicks, like so:
public int value;
public void comboBox_SelectedValueChanged()
{
value = comboBox.SelectedValue();
}
Is there a similarly sweet, simple, and short way to properly "bind" the selected comboBox item to an integer?
I am trying to understand how to use INotifyPropertyChanged but I keep getting errors when I try to use it. This is what I have so far, but to be honest, I'm not sure where I am going with it:
// Combo Box Value
public class ComboValue
{
#region Members
int valueToReturn;
#endregion
# region Properties
public int numWeeks
{
get { return valueToReturn; }
}
#endregion
}
// Veiw Model Class
public class ComboValueViewModel:INotifyPropertyChanged
{
#region Construction
public ComboValueViewModel()
{
}
#endregion
}
and I've never used "#region" before, I have no clue what that is.
Could someone fill me in if I'm headed down the right path here?
You don't mention how much you know of MVVM but here goes. Your view will have an associated ViewModel class. In here you'll expose a property containing the items to bind to the combobox, e.g.:
public List<ComboValue> ComboItems { get; set; }
If you populate this property in the VM's constructor, then a List<> is probably sufficient; however you'll often see an ObservableCollection<> used for this kind of thing - this comes into its own if you need to add or remove items within your VM code - your view will react to such changes and update the list accordingly. This won't happen with a List<>.
As for INotifyPropertyChanged, I haven't implemented this pattern in the above code snippet. Again, it's not strictly necessary if you populate the collection in the VM constructor and won't be re-assigning that property again. However it's good practice to use the INPC pattern on your VM properties. Without it, if you were to reassign that property elsewhere in your code, e.g.:-
ComboItems = aNewListOfItems;
then the view wouldn't be made aware of the property change, and the ComboBox wouldn't update. If you need this to happen then implement the INPC pattern on the property, e.g.:-
public List<ComboValue> ComboItems // or ObservableCollection<ComboValue>
{
get
{
return _comboItems;
}
set
{
if (_comboItems != value)
{
_comboItems = value;
OnPropertyChanged("ComboItems");
}
}
}
As you are working with a ComboBox, your VM should also expose a property that you bind to the control's SelectedItem property. This property should implement INPC, e.g.:-
public ComboValue SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
}
As you select items in the combo, the VM's SelectedItem property will change to reflect the current selection.
Finally, your XAML should end up looking something like this:-
<ComboBox ItemsSource="{Binding ComboItems}" SelectedItem="{Binding SelectedItem}" />
Hope this gives you a little "primer" into WPF binding! (Code snippets taken from memory so may not be 100% correct!).
Edit
Your ComboValue class exposes a numWeeks property. As it stands, you'll probably find that your ComboBox displays a list of ComboValue type names. To get the number to appear, the easiest thing is just to override .ToString() in your class and return the value of numWeeks. For more advanced formatting of items in controls such as this, you'll typically specify an ItemTemplate (again, plenty of examples can be found via Google!).

Dependency Property Binding and updating to a custom control -- It updates, but apparently not both ways?

ViewModel:
public class MyViewModel : INotifyPropertyChanged
{
public string MyText { ... }
}
XAML:
<my:MySpecialTextBox Text="{Binding MyText}" />
Custom Control:
public class MySpecialTextBox : TextBox
{
static MySpecialTextBox()
{
TextProperty.OverrideMetadata(typeof(MySpecialTextBox),
new FrameworkPropertyMetadata
{
BindsTwoWayByDefault = true,
DefaultValue = string.empty,
PropertyChangedCallback = OnTextPropertyChanged
});
}
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MySpecialTextBox;
if (control != null)
{
control.Text = SomeAdjustedValue((string)e.NewValue);
}
}
}
The problem is that while the DependencyProperty in the custom control does adjust properly, it does not update the ViewModel. I realize that this seems as if it should be a CoerceValueCallback due to the naming of SomeAdjustedValue, but Coercion does not change the ViewModel value either. I can't seem to update the value in my ViewModel if it was the trigger for the OnTextPropertyChanged callback to begin with... I did a debug trace and it does not go through the ViewModel a second time with the new value. Not sure what to do here to fix this.
In the FrameworkPropertyMetadata, there are different constructors you can use. Use one with the FrameworkPropertyMetadataOptions parameter. The FrameworkPropertyMetadataOptions.BindsTwoWayByDefault option will turn on two way binding by default, otherwise it's just one way.
Edit: So you did, I should stop trying to answer questions when I'm sick.
A disclaimer first- this smells like logic that should live inside of the ViewModel, not in the UI binding.
That said, if you are set on doing it this way, I think that you need to first check whether the "Adjusted Value" is different than the one already provided (to avoid looping indefinitely), then use DependencyProperty.SetValue to set the value of the dependency property on the control, rather than just setting it's Text property.
TextBox.Text is a binding, and you are replacing that binding with a string value in OnTextPropertyChanged, so the property is no longer bound to your datasource.
I think you would need to get the binding on TextBox.Text and update the source, however I wouldn't recommend doing that since you'd be mixing your Business Logic layer with your UI layer.
If you only want a display some custom formatting, I would do it in a Converter so it doesn't actually change your data source's value. If you want to change the actual data source value, I would do that with the ViewModel's PropertyChanged event
Did you try this?
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MySpecialTextBox;
if (control != null)
{
control.SetCurrentValue(TextBox.TextProperty, SomeAdjustedValue((string)e.NewValue));
}
}
The SetCurrentValue() method ensures that the binding is preserved, where a simple SetValue() which is what is called under the hood if you use the Text property setter will remove any binding.

Update binding without DependencyProperty

I have a lot of existing business objects with many properties and collections inside which I want to bind the userinterface to. Using DependencyProperty or ObservableCollections inside these objects is not an option. As I know exactly when I modify these objects, I would like to have a mechanism to update all UI controls when I do this. As an extra I also don't know which UI controls bind to these objects and to what properties.
Here is a simplified code of what I tried to do by now:
public class Artikel
{
public int MyProperty {get;set;}
}
public partial class MainWindow : Window
{
public Artikel artikel
{
get { return (Artikel)GetValue(artikelProperty); }
set { SetValue(artikelProperty, value); }
}
public static readonly DependencyProperty artikelProperty =
DependencyProperty.Register("artikel", typeof(Artikel), typeof(MainWindow), new UIPropertyMetadata(new Artikel()));
public MainWindow()
{
InitializeComponent();
test.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
artikel.MyProperty += 1;
// What can I do at this point to update all bindings?
// What I know at this point is that control test or some of it's
// child controls bind to some property of artikel.
}
}
<Grid Name="test">
<TextBlock Text="{Binding Path=artikel.MyProperty}" />
</Grid>
This is, I tried to pack my object into a DependencyProperty and tried to call UpdateTarget on this, but didn't succeed.
What could I do to update the corresponding UI controls?
I hope I described my situation good enough.
Using INotifyPropertyChanged is a good alternative to DependencyProperties.
If you implement the interface you can raise the PropertyChanged event with null as parameter to notify the UI that all properties changed.
(I'm going to assume you can't add INotifyPropertyChanged to your business objects either, and that you don't want to add another "view of the data model" layer of wrapper objects a la MVVM.)
You can manually update bound properties from their data source by calling BindingExpression.UpdateTarget().
myTextBlock.GetBindingExpression(TextBlock.TextProperty).UpdateTarget();
To update all bindings on a control or window, you could use something like this:
using System.Windows.Media;
...
static void UpdateBindings(this DependencyObject obj)
{
for (var i=0; i<VisualTreeHelper.GetChildrenCount(obj); ++i)
{
var child = VisualTreeHelper.GetChild(obj, i);
if (child is TextBox)
{
var expression = (child as TextBox).GetBindingExpression(TextBox.TextProperty);
if (expression != null)
{
expression.UpdateTarget();
}
}
else if (...) { ... }
UpdateBindings(child);
}
}
If you're binding a diverse set of properties then rather than handling them individually as above, you could combine the above with this approach to enumerate all dependency properties on a control and then get any BindingExpression from each; but that relies on reflection which will not be particularly performant.
As a footnote, you can also use BindingExpression.UpdateSource() if you want to explicitly write back to the data source. Controls usually do this anyway when their value changes or when they lose focus, but you control this and do it by hand with {Binding Foo, UpdateSourceTrigger=Explicit}.
As I know exactly when I modify these objects, I would like to have a mechanism to update all UI controls when I do this.
You will find that the most straightforward and maintainable way to deal with this is to implement view model classes for each class you want to present in the UI. This is probably true if you can modify the underlying classes, and almost certainly true if you can't.
You don't need to be using dependency properties for this. Dependency properties are only necessary on the targets of binding, which is to say the controls in the UI. Your view model objects are the source; they need only implement INotifyPropertyChanged.
Yes, this means that you will need to build classes that contain a property for each property exposed in the UI, and that those classes will need to contain observable collections of child view models, and you'll have to instantiate and populate those classes and their collections at runtime.
This is generally not as big a deal as it sounds, and it may be even less of one in your case. The traditional way to build a view model that's bound to a data model is to build properties like this:
public string Foo
{
get { return _Model.Foo; }
set
{
if (value != _Model.Foo)
{
_Model.Foo = value;
OnPropertyChanged("Foo");
}
}
}
But if, as you've claimed, you know when the objects are being updated, and you just want to push the updates out to the UI, you can implement read-only properties, and when the underlying data model gets updated make the view model raise PropertyChanged with the PropertyName property of the event args set to null, which tells binding, "Every property on this object has changed; update all binding targets."

Categories

Resources