This question already has answers here:
Is there a notification mechanism for when a dependency property has changed?
(4 answers)
Closed 3 years ago.
Our Silverlight app contains a third-party control, which contains some ScrollBars (among other things). In order to troubleshoot a problem, I want to be able to stop in the debugger whenever the third-party control modifies the Minimum or Maximum properties of any of its scrollbars. Then I'll be able to look at the stack trace and learn more about what's going on.
If I was interested in the ScrollBars' Value property, that would all be easy -- ScrollBar has a ValueChanged event, so I could just add some throwaway code that hooks that event on the ScrollBar, set a breakpoint inside my event handler, and debug away. But there are no corresponding CLR events for MinimumChanged or MaximumChanged, so it won't be that simple.
I ran across a blog post that talks about using DependencyPropertyDescriptor to get dependency property change events, but unfortunately, DependencyPropertyDescriptor doesn't exist in Silverlight.
How can I get to the point where I can set a breakpoint that fires whenever the ScrollBar's Minimum and Maximum properties change?
The following idea springs to my mind:
Create a user control with a dependency property. (The XAML within the user control won't actually be used, we just need the dependency property.)
Bind the user control's dependency property to the Minimum or Maximum property of the third party control (assuming they are also dependency properties).
In the code-behind of the user control, add a PropertyChangedCallback to the dependency property and drop a breakpoint in that.
This approach should allow you to set a breakpoint that fires whenever the Minimum or Maximum property changes. However, I can't guarantee that you'll get a stacktrace that will help you.
The code-behind of the user control could look something like this:
public partial class DPContainer : UserControl
{
public static readonly DependencyProperty DebugValueProperty =
DependencyProperty.Register("DebugValue", typeof(object), typeof(DPContainer), new PropertyMetadata(DebugValue_Changed));
public DPContainer()
{
InitializeComponent();
}
public object DebugValue
{
get { return GetValue(DebugValueProperty); }
set { SetValue(DebugValueProperty, value); }
}
private static void DebugValue_Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// Drop a breakpoint in this method.
}
Assuming that you have a ScrollBar with x:Name="someScrollBar", you could then add something like the following to your XAML:
<local:DPContainer DebugValue="{Binding Path=Minimum, ElementName=someScrollBar}" />
Related
I have a custom control in the early stages of development as I endeavour to learn about wpf custom control development. The custom control inherits from ItemsControls which gives me access to an ItemsSource property to which I am binding an enumerable collection.
Currently I have a simple two project solution comprising my custom control in one and a test project in the other to test the former. In my test project I have a simple mainwindow onto which I have put my custom control and bound its ItemsSource.
<WpfControls:VtlDataNavigator x:Name="MyDataNavigator"
ItemsSource="{Binding ElementName=MainWindow, Path=Orders}" />
In the loaded event of the main window (which implements INotifyPropertyChanged) I instantiate the Orders collection. The customcontrols gets initialised before the main window loads but I can see from examining the Live Visual Tree in visual studio that once the main form loads the custom controls Items Source property is indeed set to Orders. Now of course I'd actually like to count the orders and have my custom control display that (it's a simple data navigator so what I'm after is the record count). I know how to get the count but how do I know when the itemsSource has changed so that I can react to it and get the count. There's no itemsSourceChanged event that I can see.
I've seen this blog article, but I'm wondering if there is a more straightforward approach to this as it seems such an obvious thing to want to know about.
You can do that using OverrideMetaData.
Try this:
public class Class1 : ItemsControl
{
static Class1()
{
ItemsSourceProperty.OverrideMetadata(typeof(Class1),
new FrameworkPropertyMetadata(null, OnItemSourceChanged));
}
private static void OnItemSourceChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Debug.WriteLine("Why you haz Changed me!");
}
}
The ItemsSource is a DependencyProperty, and when creating DPs you can optionally specify a "property changed" event. Unfortunately ItemsSource is locked away in the base class, so I started wondering if there might be a way to add your own event to an existing DP. I came across this article that looks promising. Basically you would do something like this (untested so read the article!):-
var dpd = DependencyPropertyDescriptor.FromProperty(
VtlDataNavigator.ItemsSourceProperty,
typeof(VtlDataNavigator));
if (dpd != null)
{
dpd.AddValueChanged(
vtlDataNavigatorInstance,
delegate
{
var count = VtlDataNavigatorInstance.ItemsSource.Count; // Or whatever...
});
}
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.
I need to implement a Ruler and I have a CustomControl RangeSlider which is derived from Control and has several DependencyProperties (e.g. Minimum, Maximum, LowerValue, UpperValue).
public static readonly DependencyProperty LowerValueProperty =
DependencyProperty.Register("LowerValue",
typeof(double),
typeof(RangeSlider),
new FrameworkPropertyMetadata(0d, OnLowerValueChanged, CoerceLowerValue));
public double LowerValue
{
get { return (double)base.GetValue(LowerValueProperty); }
set { base.SetValue(LowerValueProperty, value); }
}
In Xaml, the Ruler has a sub control Scale (derived from Canvas), which needs the same Properties to draw the Scaling. If LowerValue or UpperValue changed, the Scale should repaint. So I add
public static readonly DependencyProperty LowerValueProperty =
RangeSlider.LowerValueProperty.AddOwner(typeof(Scale), new FrameworkPropertyMetadata(1d));
When I move the thumb, something should raised in Scale. First I implemented OnRender(DrawingContext) and added to the LowerValueProperty in RangeSlider FrameworkPropertyMetadataOptions. But it doesn't matter, nothing happens. Moreover I do not want to use OnRender(DrawingContext), because it's not available in Silverlight.
Furthermore I wonder why the OnLowerValueChanged-Method in RangeSlider is raised but not the setter of LowerValue (at least the debugger does not break in there).
So, is there a way for Silverlight & WPF to raise a method automatically in Scale, when LowerValue in RangeSlider changed (except raising from OnLowerValueChanged)?
Note: The above code is the WPF-Code. In Silverlight there are of course some difference, because Silverlight does not support among other things Coercion.
Because the code should run in Silverlight and WPF, I do not want to use OnRender(DrawingContext).
If I understand correctly you want to be able to perform some logic in propertiers, that are just wrappers around GetValue & SetValue methods. Problem is they are only used when you create XAML and specify bindings for those properties. Behind the scenes they are never called anymore, instead GetValue & SetValue are used.
Thats why I think (still not sure I understand question clearly) you have to either use event handlers for ValueChanged event (for example) inside your control and there is no other way to intercept calls to GetValue & SetValue.
Or you can use PropertyChangedCallback with DependencyProperties that is called after DPs are changed and place your code there
I've built a custom control in WPF that inherits from ListBox. In this I have implementet my own property that is a BindingList. To make this property bindable I've implemeneted it as a DependencyProperty:
public BindingList<CheckableListItem> CheckedItems
{
get
{
return (BindingList<CheckableListItem>)GetValue(MultiComboBox.CheckedItemsProperty);
}
set
{
SetValue(MultiComboBox.CheckedItemsProperty, value);
}
}
public static readonly DependencyProperty CheckedItemsProperty;
I register this DependencyProperty in a static constructor inside my custom control:
CheckedItemsProperty = DependencyProperty.Register("CheckedItems",
typeof(BindingList<CheckableListItem>),
typeof(MultiComboBox),
new FrameworkPropertyMetadata(new BindingList<CheckableListItem>()));
(MultiComboBox is the name of my custom control. CheckableListItem is a simple class I've written just for this purpose).
This BindingList is then updated inside the custom control (never outside) as the user interacts with it.
When I use my custom control in XAML I bind to the CheckItems property with the mode "OneWayToSource". I'm using the MVVM pattern and the property in the ViewModel that I'm binding to is also a BindingList. The ViewModel never affects this list, it just reacts at the changes that the custom control make to the list. The property in the ViewModel looks like this:
private BindingList<CheckableListItem> _selectedItems;
public BindingList<CheckableListItem> SelectedItems
{
get
{
return _selectedItems;
}
set
{
if (value != _selectedItems)
{
if (_selectedItems != null)
{
_selectedItems.ListChanged -= SelectedItemsChanged;
}
_selectedItems = value;
if (_selectedItems != null)
{
_selectedItems.ListChanged += SelectedItemsChanged;
}
OnPropertyChanged("SelectedItems");
}
}
}
As you can see I'm listening to changes made to the list (these changes always occur inside my custom control), and in the "SelectedItemsChanged"-method I update my Model accordingly.
Now...this works great when I have one of these controls in my View. However, if I put two (or more) of them in the same View strange things start to happen. This will of course mean that I'll have two lists with selected items in my ViewModel. But if do something in the View that changes one of the lists, both lists are affected! That is, the event handlers for the event ListChanged is triggered for both list if changes are made to any one of them!
Does anyone recognize this problem and/or have a solution to it? What is wrong with my implementation?
My first though is that the DependencyProperty is static. Normally that means shared between all instances. But I guess DependencyProperties work in some other "magical" way so that might not be the problem.
Any tips or hints is appreciated!
I had a similar problem with a collection-type dependency property. My solution was taken from the MSDN article on Collection-Type Dependency Properties. It was adding the following line
SetValue(OperatorsPropertyKey, new List<ListBoxItem>()); //replace key and type
in the constructor of my control because it seems that a collection-type dependency property constructor is being called only once no matter how many instances your control containing this collection has (static eh).
This sounds like you bound both/all the Views to the same ViewModel. That would explain that changes to one cause changes in the other.
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.