Listen to DependencyProperty changed event and get the old value - wpf

I have the following code to subscribe to property changed event for VisiblePosition property of Column class:
DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(ColumnBase.VisiblePositionProperty, typeof(Column));
if (dpd != null)
{
dpd.AddValueChanged(col, ColumnVisiblePositionChangedHandler);
}
Here is the definition of the ColumnVisiblePositionChangedHandler method:
static internal void ColumnVisiblePositionChangedHandler(object sender, EventArgs e)
The problem is I need to get the old value of the property. How do I do that?
Thanks,

Unfortunately, you don't get old value information when registering property changed event handler this way.
One workaround is to store property value somewhere (this is your 'old' value) and then compare it to current value in the event handler.
Another workaround is to create your own dependency property (DP) and create binding between your DP and the control's DP. This will give you change notification in the WPF style.
Here is an article about this.

You can do that when you register your dependency property in the attached event handler. Please find below the syntax for a dependency property and how to get the old value on PropertyChanged event handler:
//Declaration of property
public static readonly DependencyProperty MyNameProperty =
DependencyProperty.Register("MyName",
typeof(PropertyType),
typeof(ClassName),
new PropertyMetadata(null,
new PropertyChangedCallback(MyNameValueChanged)));
//PropertyChanged event handler to get the old value
private static void MyNameValueChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
{
object oldValue = eventArgs.OldValue; //Get the old value
}

Related

Why does PropertyChangedCallback method not execute from Dependency Property OverrideMetadata?

I have a class called CustomGrid which derives from the Grid class. I am attempting to run a method when there are changes made to the grid's parent window's title by using OverrideMetadata on the Window class's TitleProperty. My approach to this problem, however, does not seem to work despite having another PropertyChangedCallback method I implemented, that works, using the same approach (OverrideMetadata) for the grid's MarginProperty:
public class CustomGrid : Grid
{
static CustomGrid()
{
Type ownerType = typeof(CustomGrid);
MarginProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnMarginPropertyChanged)));
Window.TitleProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(new PropertyChangedCallback(OnTitlePropertyChanged)));
}
private static void OnMarginPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// This executes when the grid's margin changes.
}
private static void OnTitlePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// This does not execute when the parent window's title changes.
}
}
Why does the OnTitlePropertyChanged method not execute when the grid's parent window's title is changed? Thanks.
The callback method is not called because you did not set the Window.Title property on a CustomGrid instance.
The expression
Window.TitleProperty.OverrideMetadata(
typeof(CustomGrid),
new FrameworkPropertyMetadata(OnTitlePropertyChanged));
registers the OnTitlePropertyChanged callback for the type CustomGrid. This means the callback is called whenever the dependency property is set on instances of CustomGrid, but only on those instances, not any objects like e.g. the MainWindow.

How do i get the itemsourcechanged event? Listbox

How can i get the itemsourcechangedevent in listbox?
For eg. the itemsource changes from null to ListA then to ListB
I know there is no such event. But is there any workaround for this?
Thanks in advance :)
A commonly used (answered) approach is use the PropertyChangedTrigger from the Blend SDK. However I don't like recommending the use of other SDKs unless there is a clear indication the SDK is already in use.
I'll assume for the moment that its in code-behind that you want listen for a "ItemsSourceChanged" event. A technique you can use is to create a DependencyProperty in your UserControl and bind it to the ItemsSource of the control you want to listen to.
private static readonly DependencyProperty ItemsSourceWatcherProperty =
DependencyProperty.Register(
"ItemsSourceWatcher",
typeof(object),
typeof(YourPageClass),
new PropertyMetadata(null, OnItemsSourceWatcherPropertyChanged));
private static void OnItemsSourceWatcherPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
YourPageClass source = d As YourPageClass;
if (source != null)
source.OnItemsSourceWatcherPropertyChanged();
}
private void OnItemsSourceWatcherPropertyChanged()
{
// Your code here.
}
Now given that your ListBox has a name "myListBox" you can set up watching with:-
Binding b = new Binding("ItemsSource") { Source = myListBox };
SetBinding(ItemsSourceWatcherProperty, b);
There is no ItemsSourceChanged event in Silverlight.
But, there is a workaround. Use RegisterForNotification() method mentioned in this article to register a property value change callback for ListBox's ItemsSource property.

Attached Property and Binding

I'm creating an attached behavior in order to set a regular property of a class:
public class LookupHelper
{
public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.RegisterAttached("ItemsSource", typeof(object), typeof(LookupHelper), new UIPropertyMetadata(null, OnItemsSourceChanged));
private static void OnItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as MyControl;
if(control == null)
return;
control.ItemsSource = (IEnumerable)e.NewValue;
}
public static object GetItemsSource(GridColumn column)
{
return column.GetValue(ItemsSourceProperty);
}
public static void SetItemsSource(GridColumn column, object value)
{
column.SetValue(ItemsSourceProperty, value);
}
}
Here, ItemsSource property on MyControl is a regular property, so I can not bind it in Xaml, hence this attached behavior.
Now, when I use this attached property using string or objects it works and breakpoint I set is hit, but when I set it with Binding markup, it never runs. Why isn't this working?
<MyControl ctrl:LookupHelper.ItemsSource="DataSource"/>; //It works
<MyControl ctrl:LookupHelper.ItemsSource="{Binding Path=MyDataSource}"/>; //Does not work
What I need to do is to set the ItemsSource property to the value specified by the Binding.
In your Get and Set methods, you're defining the receiving object as GridColumn where it should be DependencyObject.
You might also want to change the type of your DP from object to IEnumerable since your casting to that in your change handler.
Can you please post the markup you are using? Also, If the actual property exists on an object and makes sense there then I think you should be using a regular dependency property on that object instead of an attached property on a helper class.
Edit
From MSDN:
The signature for the GetPropertyName accessor must be:
public static object GetPropertyName(object target)
and the signature for the SetPropertyName accessor must be:
public static void SetPropertyName(object target, object value)
In your case, is GridColumn the correct target type?

Dependency Property and ViewModel initialization problem

I am trying to add my own ItemsSource to provide a list of GraphViewModels to a chart. I dont think I have it quite right though, as when I create my first GraphViewModel and add it to the Graphs, my DP is updated, but OnGraphsCollectionChanged is not called.
How is this supposed to work? If I add graphs to my VM property via a button tied to a command then all is good.
Here is the DP code, can anyone explain how this is supposed to work or what Im doing wrong to display my data during initialization?
public static readonly DependencyProperty GraphsProperty =
DependencyProperty.Register("ItemsSource",
typeof(ObservableCollection<GraphViewModel>),
typeof(DynamicPlotter),
new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeGraphs)));
public ObservableCollection<GraphViewModel> ItemsSource
{
get { return (ObservableCollection<GraphViewModel>)GetValue(GraphsProperty); }
set
{
SetValue(GraphsProperty, value);
ItemsSource.CollectionChanged += new NotifyCollectionChangedEventHandler(OnGraphsCollectionChanged);
}
}
public static void ChangeGraphs(DependencyObject source, DependencyPropertyChangedEventArgs eventArgs)
{
(source as DynamicPlotter).UpdateGraphs((ObservableCollection<GraphViewModel>)eventArgs.NewValue);
}
private void UpdateLineGraphs(ObservableCollection<GraphViewModel> grphs)
{
this.ItemsSource = grphs;
}
private void OnGraphsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
// This never gets called when you set the ItemsSource, but is where all the work is done
}
CollectionChanged only gets called when the collection changes, if the collection is already populated prior to being set you will never get your notification, until something gets added/removed.
Secondly, if you are setting the dependency property from xaml the getter/setter is not used, the dependency mechanism uses its own internal setter routines. You should attach your collectionChanged event in your ChangeGraphs property callback function as this is called whenever the property is set/changed. You can use this to unhook the old collectionChanged event as well, the event arguments will give you an old and new value.
But really, it is an observable collection you should not have to know when the collection changes as you should be binding to the collection and when it changes the binding mechanism will update your ui.
I would change my code to look like this
public ObservableCollection<GraphViewModel> ItemsSource {
get { return (ObservableCollection<GraphViewModel>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ItemsSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(ObservableCollection<GraphViewModel>), typeof(DynamicPlotter), new UIPropertyMetadata(null, (o, e) => { ((DynamicPlotter)o).ItemsSourceChanged(); }));
private void ItemsSourceChanged() {
if (this.ItemsSource != null){
//attach the collection changed listener, this will listen to all FUTURE collection changes, items that are added and removed
this.ItemsSource.CollectionChanged +=new NotifyCollectionChangedEventHandler(ItemsSource_CollectionChanged);
//do some inital processing with the items that are in the collection already when it is set
this.UpdateGraphs(this.ItemsSource);
}
private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e){
//this will get called if an item gets added or removed from the collection
}

wpf how to tell when databinding has finished?

I've got a custom control which has a DependencyProperty MyAnimal - I'm binding an Animal Property on my ViewModel to the MyAnimal DependencyProperty.
I've stuck a TextBox on the Control so I can trigger an Event - whenever I trigger the event the MyAnimal property has been set - however if I put a break point on the Setter of the MyAnimal property it never gets fired!
I guess I'm missing something fundamental about WPF Dependency Properties/Binding?!
And so my question is, if I can't use the Setter how can I find out when its been set? If I put if I put a break point after InitializeComponent() its null and I had a look to see if theres an Event a can hook up to - DatabindingFinished or similar? but can't see what it would be ...
Can anyone assist please?
Thanks,
Andy
public partial class ControlStrip
{
public ControlStrip()
{
InitializeComponent();
}
public Animal MyAnimal
{
get
{
return (Animal)GetValue(MyAnimalProperty);
}
set
{
SetValue(MyAnimalProperty, value);
}
}
public static readonly DependencyProperty MyAnimalProperty =
DependencyProperty.RegisterAttached("MyAnimal", typeof (Animal), typeof (ControlStrip));
private void TextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
var myAnimal = MyAnimal;
MessageBox.Show(myAnimal.Name);
}
}
The setter methods are never called by the runtime. They go directly to the DependencyProperty. You will need to add an additional argument to your call to RegisterAttached(). There you can add a PropertyChangedCallback.
Here is some sample code:
public static readonly DependencyProperty MyAnimalProperty =
DependencyProperty.RegisterAttached("MyAnimal", typeof (Animal), typeof (ControlStrip), new PropertyMetadata(AnimalChanged));
private static void AnimalChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// Do work here
}
The setter is only there for your use - you actually can leave the property off entirely, since DataBinding uses the actual DependencyProperty itself, not the CLR property.
If you need to see when the property changes, you will need to specify PropertyMetadata on your dependency property, and provide a PropertyChangedCallback.
For details, I recommend reading Dependency Property Metadata.

Resources