DataContext, DataBinding and Element Binding in Silverlight - silverlight

I'm having one hell of a time trying to get my databinding to work correctly. I have reason to believe that what I'm trying to accomplish can't be done, but we'll see what answers I get.
I've got a UserControl. This UserControl contains nothing more than a button. Now within the code behind, I've got a property name IsBookmarked. When IsBookmarked is set, code is run that animates the look of the button. The idea is that you click the button and it visually changes. We'll call this UserControl a Bookmark control.
Now I have another control, which we'll call the FormControl. My FormControl contains a child Bookmark control. I've tried to do databinding on my Bookmark control, but it's not working. Here's some code to help you out.
This is the XAML and Loaded event handler of my control. As you can see it contains a child element that is a custom control (bookmark). So once this control loads, it's DataContext is set to an new instance of an Employee object. Silverlight also sets the DataContext property of my child bookmark control to the same instance. I've verified this by debugging. If my parent has a valid DataContext set then why can't my child control (bookmark) property databind to it?
<UserControl ......>
<q:Bookmark x:Name="BookMarkControl1" IsBookmarked="{Binding IsSiteBookmarked}" />
</UserControl>
public void Control_Loaded(object sender, EventArgs e)
{
DataContext = new Employee { IsSiteBookmarked = True };
}
This is my custom control below. Obviously it contains more than this, but for readability I've trimmed it down to the property I'm trying to databind to.
//this is the bookmark control. I've included this control within another control, and I'm trying to databind to properties within my parents DataContext
public partial class Bookmark : UserControl
{
bool _IsBookmarked= false;
public bool IsBookmarked
{
get {return _IsBookmarked;}
set {
_IsBookmarked= value;
SwitchMode(value);
}
}
}
UPDATE
Got some javascript errors that I should mention. Firebug reports a AG_E_PARSER_BAD_PROPERTY_VALUE exception. It doesn't seem like my databinding is even working yet.

Make your IsBookmarked property on the Bookmark control a dependency property.
I presume Control_Loaded is a part of your FormControl, in which case I'm not sure you are using DataContext properly. Best double check that.
UPDATE: Yes, you are using the DataContext properly. AG_E_PARSER_BAD_PROPERTY_VALUE indicates you need to make the IsBookmarked property a dependency property, like so:
Public Property IsBookmarked() As Boolean
Get
Return Me.GetValue(IsBookmarkedProperty)
End Get
Set(ByVal value As Boolean)
Me.SetValue(IsBookmarkedProperty, value)
End Set
End Property
Public Shared ReadOnly IsBookmarkedProperty As DependencyProperty = DependencyProperty.Register("IsBookmarked", GetType(Boolean), GetType(Bookmark), New PropertyMetadata(New PropertyChangedCallback(AddressOf OnIsBookmarkedPropertyChanged)))
Private Shared Sub OnIsBookmarkedPropertyChanged(ByVal d As DependencyObject, ByVal e As DependencyPropertyChangedEventArgs)
Dim cntrl As Bookmark = TryCast(d, Bookmark)
cntrl.SetIsBookmarked(e.NewValue)
End Sub
If you only need to store the value for later use, then you don't need to do anything in the OnIsBookmarkedPropertyChanged procedure, But I put some code there as an example anyway.
Good Luck!

I don't recall the exact order in which databinding is evaluated (and I'm too lazy to go look it up), but as I recall, it initially happens BEFORE the form's Loaded event fires, and without making the IsBookmarked property a dependency property, or at least using INotifyPropertyChanged, it may have trouble establishing the datacontext appropriately. I'd recommend either implementing INotifyPropertyChanged or making IsBookmarked a dependency property. DataBinding is tough enough to get right in the best of circumstances (see my long, bad-tempered rant about it here), and you'll just be making it more difficult on yourself if you aren't setting up your properties in the way that it expects.

The control exposes a IsSiteBookmarked property(which I believe should be a DependencyProperty) but the control is binding to a IsBookmarked which is not shown. Is this intentional? Have you checked your Visual Studio output window for binding errors?
Addition 1:
Since you have fixed the typo in your question and added that there is an error being reported.
Start by clearing up the AG_E_PARSER_BAD_PROPERTY_VALUE problem. Is there a line number and start position in the error message? Start looking there. One strategy is to start taking out XAML until there is no longer an error. This will narrow down the offending code.
Running in debug, mode check for binding errors in the output window.
You might want to also post the Employee class code, especially the IsSiteBookmarked property.

Typically when doing databinding to an object you will want to leverage the INotifyPropertyChanged interface and implement that so that the control can properly invalidate it's property value. Unless you use INotifyPropertyChanged with Mode=TwoWay then any code that changes your DataContext's IsSiteBookmarked will have no effect.

Related

Updating source using DependencyProperty in custom usercontrol

I got a binding source not being updated when the targeted DependencyProperty of a custom UserControl changes.
The source is a ViewModel loaded with MEF into the DataContext of my custom UserControl.
My binding looks like this in the root XAML of MyUserControl
// MyUserControl.xaml
<MyUserControlBase x:Class="MyUserControl" MyDependencyProperty="{Binding ViewModelProperty}">
If i use the MyDependencyProperty with a FrameworkPropertyMetadata and use the callback function when the property changes i can do the following which works fine
// MyUserControlBase.cs
private static void MyDependencyProperty_Changed(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
{
var filter = (UserControl )depObj;
var vm = (ViewModel)filter.DataContext;
vm.ViewModelProperty= (VMPropType)args.NewValue;
}
Registration of the DependencyProperty in MyUserControlBase which inherit UserControl.
// MyUserControlBase.cs
public static readonly DependencyProperty MyDependencyPropertyProperty = DependencyProperty.Register("MyDependencyProperty",
typeof(VMPropType),
typeof(MyUserControlBase),
new FrameworkPropertyMetadata(null, MyDependencyProperty_Changed));
But why can't i get the simple two way binding to work when done in the XAML?
Output window show no binding failurs. I can see the ViewModel.ViewModelProperty getter is being called. Just not the setter once MyDependencyProperty changes.
It don't appear to be a problem with the ordering of events. The ViewModel is created long before DependencyProperty is changed for the first time.
I've also looked at Setting up binding to a custom DependencyProperty inside a WPF user control
But his problem seems slightly different since i actualy inherit from my own Base class which holds the DependencyProperty.
Thank you for any help.
Edit: with link to example solution.
Solution zip here
Sorry for the scary looking link, I don't know alot of quick fileshareing sites. If its bad post comment and I will remove it asap, but it seems ok.
Answer updated / irrelevant info deleted
After looking at the code in your sample I saw what the problem is. You have this "selector" control. In the BindDependencies method you set a binding on the MyControlBase.MyDependencyProperty. But at the same time you also bind this property to your control's datacontext (in MyUserControl.xaml). Effectively this means that in the BindDependencies method you are overwriting the binding to your viewmodel thus it is not active after that method - you can see this yourself by breaking into before and after the SetBinding call and calling BindingOperations.GetBindingExpression(control, MyUserControlBase.MyPropertyDependencyProperty) - the bindings are different. This is why your code does not work. I guess you will have to find another way of transferring the value there :)
Some additional info
In WPF a property can be a target of only one binding. This is actually very logical - if a property is bound to two sources - which one should it use? The fact that the binding is marked as OneWayToSource does not really matter in this situation as it still counts as a binding that targets the control's property.
One way you can investigate and see if it works for you is the MultiBinding - it does allow to bind the control's property to several sources by implementing your own IMultiValueConverter. You can find more info on this on MSDN in the link provided.
Do you expose the ViewModelProperty as a regular property..
Usually you do the following....
public static readonly DependencyProperty ViewModelPropertyProperty = DependencyProperty.Register("ViewModelProperty",
typeof(VMPropType),
typeof(MyUserControlBase),
new FrameworkPropertyMetadata(null, ViewModelProperty_Changed));
public VMPropType ViewModelProperty {
get { return (VMPropType)GetValue(ViewModelPropertyProperty); }
set { SetValue(ViewModelPropertyProperty,value); }
}
EDIT -- After comment.
When are you checking that the property has changed? By default WPF bindings only update the values when they lose focus, you change change the UpdateSourceTrigger property of the binding to alter this behaviour.

WPF binding to a function?

Let me start by saying I am very new to WPF so be gentle.
It seems like this should be easy but I am just missing something. I have implemented INotifyPropertyChanged on a few classes/properties and started binding them to elements in XAML but now I have a little more complex binding to make then updating text or changing a color. What I need is when the Alarm property of my object is set to true I need to change colors, start an animation, create and display other elements in the control. I thought I could just call a function in my control when the property is changed but since WPF hides how an element is "bound" to the model's property I am not sure how to wire that up. Is there a better way to perform this type of more complex response to a property change? If not are there any samples out there? I have not been able to find anything close to what I am looking for but I may not be searching with the correct terms.
What I need is when the Alarm property of my object is set to true I
need to change colors, start an animation, create and display other
elements in the control.
Change colors: Bind the Color/Foreground of the element you want to change the color of, to the boolean that sets the alarm, and add an IValueConverter in the binding that returns a Brush based on the boolean.
Start an animation: Use a (data)trigger on whichever element needs to be animated, in that trigger, use a Storyboard to define the animation you want.
create and display other elements in the control: that really depends on how well you did your MVVM, if the elements are a visualisation of an ObservableCollection through a ListBox/ListView/ItemsControl (which it should), wire up a Command to whatever sets the alarm on/off (the Button class has a Command property built in, other UIElements may require the use of System.Windows.Interactivity) and in the method that this Command will point to, add a new item to the ObservableCollection, the ItemsControl will automatically reflect the change by adding a new control.
Of course this is just raw information, and you're probably not familiar will all these things, that's when Google comes into play ;)
HTH,
Bab.
For complex behaviour in response to a property changed event, you should use the following approach: (I'm typing this freestyle so pardon any minor syntax errors)
class MyClass : INotifyPropertyChanged
{
//Presumably you've already done this part
private object _myProperty = null;
public object MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
NotifyPropertyChanged("MyProperty");
}
}
public MyClass()
{
this.PropertyChanged += My_PropertyChanged;
}
private void My_PropertyChanged( object sender, PropertyChangedEventArgs e)
{
if( e.PropertyName == "MyProperty" )
{
//Do Something complicated
}
}
Your timer / alarm just needs to update the bound property when it elapses. The property should then raise the PropertyChanged event to notify the GUI to update.
You should take a look at DataTriggers that get fired when a property changes in the view model. The EnterActions and ExitActions will allow you to play a Storyboard when the value of the property changes. Here's an example of how it is used MSDN. You can use a content control and style the Template or ContentTemplate to add all of your elements and have it control the Visibility or Opacity of the other elements.
I don't think you are looking at this right. Your class has logic, does calculation, enforces constraints, and enforces relationships among properties. If you have an alarm hopefully there is some business logic to deal with this and should be done in the class (not the UI). A UI is not built to handle an alarm it is built to display status and actions of that alarm. You will make new control visible in an alarm situation and hide other. On the animation I think you might want to throw an event that you listen for to start the animation. The idea there is that when an alarm is thrown to can register additional handles - you want to separate the business logic from the UI (not pull the business logic into the UI).

S/L 4 & IDataErrorInfo - How to force re-validation of a control (when a related control is touched)

I have two controls bound to properties MinCartValue and MaxCartValue. MinCartValue must be less than MaxCartValue. To achieve this validation I have implemented the the IDataErrorInfo interface, and run the above check in the this[columnName] method if either MinCartValue or MaxCartValue are touched. ValidatesOnDataErrors=True is set in the binding of both controls.
The validation works correctly, highlighting each control when a change to its property value violates the rule. The problem is that once a control is flagged as invalid, if the user corrects the problem by altering the other control's value, the first control remains flagged as invalid. This is understandable because the IDataErrorInfo method was not doing validation on the first control's property.
So what I need is a way to force property #1 to be re-validated (or a way to clear the invalid state) when property #2 is validated, and vice versa. I have tried calling RaisePropertyChanged within my this[columnName] method but it does nothing. Also tried setting the property to its own value to try to trick it to validate itself, but again nothing happens.
Thanks
I would recommend looking at the INotifyDataErrorInfo interface (introduced in Silverlight 4). It's able to async-notify if properties become invalid, so I think the framework is better about respecting this across many properties instead of expecting that the property currently being changed is the only one whose validity may be changing.
I had two DateTime properties (DateFrom and DateTo) that needed to be validated against each other. In the setters for these properties I just raised a PropertyChanged event for both DateTo and DateFrom. Worked like a charm.
I'm not sure if I'm understanding your problem exactly, but perhaps this may help. Providing some example XAML and the binding property code would help.
It sounds like an issue of your code depending on the default UpdateSourceTrigger, which in the case of TextBox controls is their focus/unfocus. You can set in the XAML the UpdateSourceTrigger attribute by adding UpdateSourceTrigger=Explicit to your binding where your validation occurs. Then in each TextBox (MinCartValue, MaxCartValue), add an event handler to the TextChanged event.
In the code-behind in the event handler, you can do something like this:
private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
{
TheTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
TheTextBox in this case would be one of your cart controls. The UpdateSource() method is a way to manually update the binding value, which should trigger your validation. This method provides away to tie into a trigger to update values and raising properties have changed outside of the default scope (using text changed intead of focus and unfocus on TextBox in this instance).
Here's how I solved it. Let's say Property1 and Property2 are codependent. I'm not familiar with MVVM (yet), but you're probably extending your entity class to implement IDataErrorInfo. In this case you can also extend On[Property]Changed method and report change in codependent property:
partial class YourEntity : IDataErrorInfo
{
public string this[string columnName]
{
//Your validation logic
}
public string Error
{
//WPF doesn't use it anyway
get { return string.Empty; }
}
partial void OnProperty1Changed()
{
OnPropertyChanging("Property2");
OnPropertyChanged("Property2");
}
partial void OnProperty2Changed()
{
OnPropertyChanging("Property1");
OnPropertyChanged("Property1");
}
}
In this case the update in either one of this properties makes both bound controls re-evaluate themselves.
EDIT2: It appears that you should use OnPropertyChang* instead of ReportPropertyChang*. ReportPropertyChanged will notify the entity framework that there are pending changes in the model, but in fact all you're trying to do is inform the view. You don't want to update the database with the property that didn't really change. ReportPropertyChang* will also fail on computed fields that have no mappings in the database.
EDIT1: Found out that it's essential to call ReportPropertyChanging before ReportPropertyChanged.

How to update binding dynamically when source object changes?

What I have?
I have Frame in XAML, (binding works properly).
<Frame Name="frmMainContent"
DataContext="MyPageInformation"
Source="{Binding ElementName=thisPage, Path=MyPageInformation.UserControlPath}"
NavigationUIVisibility="Hidden"></Frame>
In the code behind I have a DependencyProperty of my class, PageInformation
public static DependencyProperty PageInformationProperty = DependencyProperty.Register("MyPageInformation", typeof(PageInformation), typeof(Administration));
public PageInformation MyPageInformation
{
get { retur n (PageInformation)GetValue(PageInformationProperty); }
set{ SetValue(PageInformationProperty, value); }
}
What I want?
The Frame should update its binding whenever value of MyPageInformation changes.
Can somebody tell me how I can achieve this?
Thanks in advance!
You don't have to make the PageInformationProperty a dependency property just for this binding. Implement INotifyPropertyChanged in the code behind.
Also since you are actually binding to "UserControlPath", make sure that this property actually sends change notifications.
Well, the first thing I'll tell you is that all binding errors appear on the Output window. So you need to look at it and find out if you have any errors.
The next thing.. for bindings to update automatically, you either need to make the property it is "binded" to, either a dependency property, or implement INotifyPropertyChanged for other classes.
Be sure that the property that you exactly bind to, is either one of these cases.

WPF - CanExecute doesn't fire when raising Commands from a UserControl

I've got a button strip usercontrol that I want to have on most of my forms.
I've added the commands as follows ...
public ICommand Create
{
get
{
return buttonCreate.Command;
}
set
{
buttonCreate.Command = value;
}
}
I've set these as dependency properties so I can bind to them ...
public static readonly DependencyProperty CreateCommandProperty =
DependencyProperty.Register(
"Create",
typeof(ICommand),
typeof(StandardButtonStrip),
new PropertyMetadata((ICommand)null));
I'm then binding my usercontrol to a command ...
<commoncontrols:StandardButtonStrip HorizontalAlignment="Stretch" Create="{Binding CreateCommand}" />
I'm setting up the command as follows ...
_viewModel.CreateCommand = new DelegateCommand<object>(OnCreateCommand, CanCreate);
but despite the fact that i'm always returning true on my CanCreate method the button is disabled ... if I put a break point on return true it never fires!
public bool CanCreate(object parm)
{
return true;
}
I've tried this to see if it will refresh the binding, but no joy!
_viewModel.CreateCommand.RaiseCanExecuteChanged();
I think the problem is down to the user control and how I'm passing the Command up as a property, but not sure ...
The way this looks, you have a dependency property on a view model. If you are really using MVVM, that is definetly not the way to go about it (not because of religious adherence to a pattern, but because it's not the optimal way).
First of all, is your view model a DependencyObject?
If it is, you should downgrade it to a class which implements INotifyPropertyChanged. Why? Because the Button's Command property is a DependencyProperty itself (inherited from ButtonBase) and supports databinding already.
If it isn't, then a dependency property on it will not work, which is fine, because you shouldn't have dependency properties on your view model in the first place.
What you should do, is have the view model as the DataContext for your control (I'm guessing you already have that set up). Then change your view model's CreateCommand to a plain ICommand, and bind the createButton's Command property like this (in your default StandardButtonStrip style)
<Button Name="createButton" HorizontalAlignment="Stretch" Command="{Binding CreateCommand}" />
This way, it will still be reusable, you just need to ensure that any view model you associate with your user control has a property CreateCommand of type ICommand (and that view model will be inherited down to the button control by default - one of the nicest things the wpf guys thought up).
So to recap, you should do it the other way around, more or less.
Hope this was helpful, cheers.
One caveat to the accepted answer -
Using a delegate command I could only get this to work if I created a new
Command<T> : DelegateCommand<T> and hooked up the Command Manager.
event EventHandler ICommand.CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
Have you overridden any of the user control's functionality?
One common problem is overriding a method without calling the base's implementation. For example, if you've overridden OnContentChanged() without calling base.OnContentChanged(), you may have accidentally supressed the firing of the ContentChanged event.

Resources