WPF Getter/Setter Not Being Called - wpf

I have the following code. The PropertyChanged event is being called but the getter and setter is not. I can't for the life of me see why not. I have other properties where the same thing is happening but the values are being set. This is a custom control, if that makes a difference. When I put a breakpoint at the get/set I get nothing. However, the propertychanged is returning the correct value. Any help is greatly appreciated.
/// <summary>
/// Identifies the PlacementTarget dependency property.
/// </summary>
public static readonly DependencyProperty PlacementTargetProperty =
DependencyProperty.RegisterAttached(
"PlacementTarget",
typeof(UIElement),
typeof(FunctionPanel),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, PlacementTargetPropertyChanged)
);
/// <summary>
/// Gets or setsthe PlacementTarget of tooltips for all child controls.
/// </summary>
public UIElement PlacementTarget
{
get { return (UIElement)GetValue(PlacementTargetProperty); }
set { SetValue(PlacementTargetProperty, value); }
}
/// <summary>
/// Sets the value of the PlacementTarget dependency property.
/// </summary>
/// <param name="target">Target object.</param>
/// <param name="value">Value argument.</param>
public static void SetPlacementTarget(DependencyObject target, UIElement value)
{
target.SetValue(PlacementTargetProperty, value);
}
/// <summary>
/// Gets the value of the PlacementTarget dependency property.
/// </summary>
/// <param name="target">Target object.</param>
public static UIElement GetPlacementTarget(DependencyObject target)
{
return (UIElement)target.GetValue(PlacementTargetProperty);
}
/// <summary>
/// This method is called when the PlacementTarget dependency property changes value.
/// </summary>
/// <param name="sender">Sender object.</param>
/// <param name="e">Event arguments.</param>
private static void PlacementTargetPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ToolTipService.SetPlacementTarget(sender, (UIElement)e.NewValue);
}
Additional Info
So essentially PlacementTargetPropertyChanged is handling it fine. I guess the issue is that this particular DP isn't actually propagating to the children. Here's the XAML snippet:
<controls:FunctionPanel BetweenShowDelay="0"
InitialShowDelay="500"
IsError="False"
Message="MESSAGE"
Placement="Right"
PlacementTarget="{Binding RelativeSource={RelativeSource Self}}"
ShowDuration="60000" />
All of the DPs except PlacementTarget are getting to the children elements. I've tried removing the binding but it made no difference.
I'm verifying the other DPs by using Snoop to check the values. I have created a InitialShowDelay DP that is of type Int. When I set the value to 5000, all child controls inherit that and tooltips delay 5 seconds before appearing. I can verify that the binding is sending the correct control by putting a breakpoint on my PlacementTargetPropertyChanged method.
Update 1
So it seems that PlacementTarget is working fine. The default value of my Placement attached property, which is set to "right", is being ignored. When I set this through XAML, it still doesn't work. However, if I set the value to Top, Left, or Bottom, the tooltips appear in the correct place.

XAML will invoke the SetValue and GetValue methods directly, which is why you shouldn't place any logic in your static Set/Get methods, but use a property changed handler instead.

The Setter and Getter are only the .NET Wrappers for your coding. When Binding from XAML the GetXXXX and SetXXX will be called and not the Property Getter and Setter.
You can also access the DependencyProperty PropertyChanged Callback, this function will be called everytime the Property is changed.

I've found the reason why the getters and setters didn't appear to be firing. In my placement dependency property, I was setting the default value to PlacementMode.Right. WPF is ignoring the default value. I was then setting it to Placement="Right" in the XAML. Because this value was the same as the default, it was again ignored. By changing the default value to PlacementMode.Relative, I can now successfully set the value to "Right" in the XAML. This does prevent me from using Relative if I want, but I guess that could be solved by converting my properties to an object, which would then have a default of null.

Related

WPF MVVM Dependency Properties

I have the following situation:
I have a User Control with just a single Grid inside.
The Grid has its first column as a checkbox column, which is bound to the IsSelected property of CustomerModel
ItemsSource for the Grid is bound to List< CustomerModel>
When the user checks any of the CheckBoxes the corresponding IsSelected property of CustomerModel is getting updated
Query:
I added a dependency property to the UserControl named "SelectedCustomerItems", and I want it to return a List< CustomerModel> (Only for IsSelected = true)
This UserControl is placed on another Window
The Dependency Property "SelectedCustomerItems" is bound to "SelectedCustomers" property inside the WindowViewModel
But I am not getting the SelectedCustomer Items through this dependency property. Breakpoint is not hitting in Get{} Please suggest....
Implement your DPs this way:
#region SomeProperty
/// <summary>
/// The <see cref="DependencyProperty"/> for <see cref="SomeProperty"/>.
/// </summary>
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register(
SomePropertyPropertyName,
typeof(object),
typeof(SomeType),
// other types here may be appropriate for your situ
new FrameworkPropertyMetadata(null, OnSomePropertyPropertyChanged));
/// <summary>
/// Called when the value of <see cref="SomePropertyProperty"/> changes on a given instance of <see cref="SomeType"/>.
/// </summary>
/// <param name="d">The instance on which the property changed.</param>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnSomePropertyPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as SomeType).OnSomePropertyChanged(e.OldValue as object, e.NewValue as object);
}
/// <summary>
/// Called when <see cref="SomeProperty"/> changes.
/// </summary>
/// <param name="oldValue">The old value</param>
/// <param name="newValue">The new value</param>
private void OnSomePropertyChanged(object oldValue, object newValue)
{
}
/// <summary>
/// The name of the <see cref="SomeProperty"/> <see cref="DependencyProperty"/>.
/// </summary>
public const string SomePropertyPropertyName = "SomeProperty";
/// <summary>
///
/// </summary>
public object SomeProperty
{
get { return (object)GetValue(SomePropertyProperty); }
set { SetValue(SomePropertyProperty, value); }
}
#endregion
You must understand that a DependencyProperty isn't just a property with a bunch of junk added, its a hook into the WPF Binding system. This is a vast, complex system that lives below the sea level upon which your DP daintily floats. It behaves in ways that you will not expect, unless you actually learn it.
You are experiencing the first revelation all of us had with DPs: Bindings do NOT access DependencyProperty values via the property accessors (i.e., get and set methods). These property accessors are convenience methods for you to use from code only. You could dispense with them and use DependencyObject.GetValue and DependencyObject.SetValue, which are the actual hooks into the system (see the implementation of the getters/setters in my example above).
If you want to listen for change notification, you should do what I have done above in my example. You can add a change notification listener when registering your DependencyProperty. You can also override the "Metadata" of inherited DependencyProperties (I do this all the time for DataContext) in order to add change notification (some use the DependencyPropertyDescriptor for this, but I've found them to be lacking).
But, whatever you do, do NOT add code to the get and set methods of your DependencyProperties! They won't be executed by binding operations.
For more information about how DependencyProperties work, I highly suggest reading this great overview on MSDN.

ComboBox not changing when bound value of SelectedItem changes

I have a ComboBox that has its ItemSource and SelectedItem properties bound to a view model. And I have the following block of code that is the callback for a data query against a DomainContext:
/// <summary>
/// Stores (readonly) - Stores available for ship to store.
/// </summary>
public ObservableCollection<StoreEntity> Stores
{
get { return _stores; }
private set { _stores = value; RaisePropertyChanged("Stores"); }
}
/// <summary>
/// SelectedStore - Currently selected store.
/// </summary>
public StoreEntity SelectedStore
{
get { return _selectedStore; }
set { _selectedStore = value; RaisePropertyChanged("SelectedStore"); }
}
/// <summary>
/// When stores are completely loaded.
/// </summary>
/// <param name="a_loadOperations"></param>
protected void OnStoresLoaded(LoadOperation<StoreEntity> a_loadOperations)
{
Stores.AddRange(a_loadOperations.Entities);
SelectedStore = a_loadOperations.Entities.FirstOrDefault();
}
In this example, Stores is a ObservableCollection<StoreEntity> (AddRange is an extention method) and is bound to ItemSource, and SelectedStore is a StoreEntity and is bound to SelectedItem.
The problem here is that the ComboBox is not changing its selection to reflect the change in SelectedItem.
Edits:
I've even tried the following, though I think that a_loadOperation.Entities is already a realized set:
/// <summary>
/// When stores are completely loaded.
/// </summary>
/// <param name="a_loadOperations"></param>
protected void OnStoresLoaded(LoadOperation<StoreEntity> a_loadOperations)
{
var entities = a_loadOperations.Entities.ToArray();
Stores.AddRange(entities);
SelectedStore = entities.First();
}
Thanks
If you are trying to get a change to your viewmodel (specifically the SelectedStore property) to be reflected in your combo box, you could:
Confirm the binding worked. Check it was set up properly in the XAML, and check the Output window to see if there is a message saying the Binding failed
Confirm your DataContext is set properly (it probably is since you are getting the combo box `ItemsSource` from `Stores`
Subscribe to your PropertyChanged event and confirm that it is being raised when the property changes
If that doesn't work, it may be a bug with the ComboBox. I have seen cases where the order of specifying the properties in XAML makes the difference (ex: you should set ItemsSource first and SelectedItem second). I have also seen a binding fail until I added Mode=TwoWay (even though in your example you are trying to get the binding to update from your view model to your UI). Try confirming that your ComboBox XAML is like this:
<ComboBox ItemsSource="{Binding Stores}" SelectedItem="{Binding SelectedStore, Mode=TwoWay}" />
Order shouldn't matter since XAML is declarative, but I have personally seen it matter with ComboBoxes in Silverlight.
I had SelectedItem bound to Stores instead of SelectedStore. Ooops!

set a dependency property in xaml

I have a custom UserControl which exposes the following dependency property: CanEdit. The property was created using a snippet and the generated code is:
#region CanEdit
/// <summary>
/// CanEdit Dependency Property
/// </summary>
public static readonly DependencyProperty CanEditProperty =
DependencyProperty.Register("CanEdit", typeof(bool), typeof(RequisitionItem),
new PropertyMetadata((bool)false));
/// <summary>
/// Gets or sets the CanEdit property. This dependency property
/// indicates ....
/// </summary>
public bool CanEdit {
get { return (bool)GetValue(CanEditProperty); }
set { SetValue(CanEditProperty, value); }
}
#endregion
I'm trying to set this property to True on the parent UserControl, like this:
<RequisitionItem CanEdit="True" />
but the property stays False.
Why is that?
Assuming that you mean the child items have the property still set to false this sounds like an inheritance issue.
See this page on value inheritance, there is one section called Making a Custom Property Inheritable, which might offer some help.

Bindable richTextBox still hanging in memory {WPF, Caliburn.Micro}

I use in WFP Caliburn.Micro Framework.
I need bindable richTextbox for Document property. I found many ways how do it bindable richTextBox.
But I have one problem. From parent window I open child window. Child window consist bindable richTextBox user control.
After I close child window and use memory profiler view class with bindabelrichTextBox control and view model class is still hanging in memory. -> this cause memory leaks.
If I use richTextBox from .NET Framework or richTextBox from Extended WPF Toolkit it doesn’t cause this memory leak problem.
I can’t identified problem in bindable richTextBox class.
Here is ist class for bindable richTextBox:
Base class can be from .NET or Extended toolkit.
/// <summary>
/// Represents a bindable rich editing control which operates on System.Windows.Documents.FlowDocument
/// objects.
/// </summary>
public class BindableRichTextBox : RichTextBox
{
/// <summary>
/// Identifies the <see cref="Document"/> dependency property.
/// </summary>
public static readonly DependencyProperty DocumentProperty = DependencyProperty.Register("Document",
typeof(FlowDocument), typeof(BindableRichTextBox));
/// <summary>
/// Initializes a new instance of the <see cref="BindableRichTextBox"/> class.
/// </summary>
public BindableRichTextBox()
: base()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="BindableRichTextBox"/> class.
/// </summary>
/// <param title="document">A <see cref="T:System.Windows.Documents.FlowDocument"></see> to be added as the initial contents of the new <see cref="T:System.Windows.Controls.BindableRichTextBox"></see>.</param>
public BindableRichTextBox(FlowDocument document)
: base(document)
{
}
/// <summary>
/// Raises the <see cref="E:System.Windows.FrameworkElement.Initialized"></see> event. This method is invoked whenever <see cref="P:System.Windows.FrameworkElement.IsInitialized"></see> is set to true internally.
/// </summary>
/// <param title="e">The <see cref="T:System.Windows.RoutedEventArgs"></see> that contains the event data.</param>
protected override void OnInitialized(EventArgs e)
{
// Hook up to get notified when DocumentProperty changes.
DependencyPropertyDescriptor descriptor = DependencyPropertyDescriptor.FromProperty(DocumentProperty, typeof(BindableRichTextBox));
descriptor.AddValueChanged(this, delegate
{
// If the underlying value of the dependency property changes,
// update the underlying document, also.
base.Document = (FlowDocument)GetValue(DocumentProperty);
});
// By default, we support updates to the source when focus is lost (or, if the LostFocus
// trigger is specified explicity. We don't support the PropertyChanged trigger right now.
this.LostFocus += new RoutedEventHandler(BindableRichTextBox_LostFocus);
base.OnInitialized(e);
}
/// <summary>
/// Handles the LostFocus event of the BindableRichTextBox control.
/// </summary>
/// <param title="sender">The source of the event.</param>
/// <param title="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
void BindableRichTextBox_LostFocus(object sender, RoutedEventArgs e)
{
// If we have a binding that is set for LostFocus or Default (which we are specifying as default)
// then update the source.
Binding binding = BindingOperations.GetBinding(this, DocumentProperty);
if (binding.UpdateSourceTrigger == UpdateSourceTrigger.Default ||
binding.UpdateSourceTrigger == UpdateSourceTrigger.LostFocus)
{
BindingOperations.GetBindingExpression(this, DocumentProperty).UpdateSource();
}
}
/// <summary>
/// Gets or sets the <see cref="T:System.Windows.Documents.FlowDocument"></see> that represents the contents of the <see cref="T:System.Windows.Controls.BindableRichTextBox"></see>.
/// </summary>
/// <value></value>
/// <returns>A <see cref="T:System.Windows.Documents.FlowDocument"></see> object that represents the contents of the <see cref="T:System.Windows.Controls.BindableRichTextBox"></see>.By default, this property is set to an empty <see cref="T:System.Windows.Documents.FlowDocument"></see>. Specifically, the empty <see cref="T:System.Windows.Documents.FlowDocument"></see> contains a single <see cref="T:System.Windows.Documents.Paragraph"></see>, which contains a single <see cref="T:System.Windows.Documents.Run"></see> which contains no text.</returns>
/// <exception cref="T:System.ArgumentException">Raised if an attempt is made to set this property to a <see cref="T:System.Windows.Documents.FlowDocument"></see> that represents the contents of another <see cref="T:System.Windows.Controls.RichTextBox"></see>.</exception>
/// <exception cref="T:System.ArgumentNullException">Raised if an attempt is made to set this property to null.</exception>
/// <exception cref="T:System.InvalidOperationException">Raised if this property is set while a change block has been activated.</exception>
public new FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
}
Thank fro help and advice.
Qucik example:
Child window with .NET richTextBox
<Window x:Class="WpfApplication2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<RichTextBox Background="Green"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="13"
Margin="4,4,4,4"
Grid.Row="0"/>
</Grid>
</Window>
This window I open from parent window:
var w = new Window1();
w.Show();
Then close this window, check with memory profiler and it memory doesn’t exist any object of window1 - richTextBox. It’s Ok.
But then I try bindable richTextBox:
Child window 2:
<Window x:Class="WpfApplication2.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:WpfApplication2.Controls"
Title="Window2" Height="300" Width="300">
<Grid>
<Controls:BindableRichTextBox Background="Red"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto"
FontSize="13"
Margin="4,4,4,4"
Grid.Row="0" />
</Grid>
</Window>
Open child window 2, close this child window and in memory are still alive object of this child window also bindable richTextBox object.
I suspect that since instances of DependencyPropertyDescriptor are likely to be cached at application level, the references to ValueChanged delegate (the anonymous delegate in OnInitialized method) might leak instances of the BindableRichTextBox.
In the code shown there isn't, indeed, any call to DependencyPropertyDescriptor.RemoveValueChanged to remove the handler.
You might consider using the DependencyProperty.Register overload, which supports a PropertyMetadata parameter; this allows you to specify a proper PropertyChangedCallback for the property (see http://msdn.microsoft.com/en-us/library/cc903933(v=VS.95).aspx#metadata):
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.Register("Document",
typeof(FlowDocument), typeof(BindableRichTextBox),
new PropertyMetadata(null,
new PropertyChangedCallback(OnDocumentChanged)
)
);
public static void OnDocumentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((BindableRichTextBox)d).SetBaseDocument((FlowDocument)e.NewValue);
}
public new FlowDocument Document
{
get { return (FlowDocument)GetValue(DocumentProperty); }
set { SetValue(DocumentProperty, value); }
}
private SetBaseDocument(FlowDocument document) {
base.Document = (FlowDocument)GetValue(DocumentProperty);
}
Maybe is better to create user control with richTextBox.
Richtextbox wpf binding

Editable ComboBox

I want to create editable combo box with the following properties:
Bind the text property to my data model.
The data model may override the changes in the GUI, even in Selection changed. E.g. I can choose from 1, 2, 3 I choose 2, but some component down under changes it to 3.
Update the data model on the following events:
Selection Changed
Lose focus
Enter pressed (should behave the same as lost focus).
I've been able to create such control, but it's pretty ugly (using many hacks) and I hoped there's a simpler way…
Thanks in advance
Ok, I here's what I've done, and its not that ugly:
/// <summary>
/// Editable combo box which updates the data model on the following:
/// 1. Select## Heading ##ion changed
/// 2. Lost focus
/// 3. Enter or Return pressed
///
/// In order for this to work, the EditableComboBox requires the follows, when binding:
/// The data model value should be bounded to the Text property of the ComboBox
/// The binding expression UpdateSourceTrigger property should be set to LostFocus
/// e.g. in XAML:
/// <PmsEditableComboBox Text="{Binding Path=MyValue, UpdateSourceTrigger=LostFocus}"
/// ItemsSource="{Binding Path=MyMenu}"/>
/// </summary>
public class PmsEditableComboBox : ComboBox
{
/// <summary>
/// Initializes a new instance of the <see cref="PmsEditableComboBox"/> class.
/// </summary>
public PmsEditableComboBox()
: base()
{
// When TextSearch enabled we'll get some unwanted behaviour when typing
// (i.e. the content is taken from the DropDown instead from the text)
IsTextSearchEnabled = false;
IsEditable = true;
}
/// <summary>
/// Use KeyUp and not KeyDown because when the DropDown is opened and Enter is pressed
/// We'll get only KeyUp event
/// </summary>
protected override void OnKeyUp(KeyEventArgs e)
{
base.OnKeyUp(e);
// Update binding source on Enter
if (e.Key == Key.Return || e.Key == Key.Enter)
{
UpdateDataSource();
}
}
/// <summary>
/// The Text property binding will be updated when selection changes
/// </summary>
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
base.OnSelectionChanged(e);
UpdateDataSource();
}
/// <summary>
/// Updates the data source.
/// </summary>
private void UpdateDataSource()
{
BindingExpression expression = GetBindingExpression(ComboBox.TextProperty);
if (expression != null)
{
expression.UpdateSource();
}
}
}
The simplest way to do this is to use the UpdateSourceTrigger property on the binding. You may not be able to match your current behavior exactly, but you may find that it is comparable.
The UpdateSourceTrigger property controls when the target of the binding updates the source. Different WPF controls have different default values for this property when bound.
Here are your options:
UpdateSourceTrigger.Default = Allow target control to determine UpdateSourceTrigger mode.
UpdateSourceTrigger.Explicit = Only update source when someone calls BindingExpression.UpdateSource();
UpdateSourceTrigger.LostFocus = Automatically update binding source whenever the target looses focus. This way a change can be completed, and then the binding is updated after the user moves on.
UpdateSourceTrigger.PropertyChanged = Whenever the DependencyProperty on the target changes values the source is updated immediatly. Most UserControls do not default to this property because it requires more binding updates (can be a performance issue).

Resources