WPF MVVM Dependency Properties - wpf

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.

Related

WPF Getter/Setter Not Being Called

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.

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

User-friendly way to display a Visual in a WPF application?

Looking for a simple, elegant way to display any given Visual to the user. The only way I can think of off my head is to slap it in a brush and paint it on a Rectangle that's in a ScrollViewer. Not exactly the best option.
You could create a wrapper that inherits from FrameworkElement that would either host your Visual or a generic wrapper that will host any object deriving from Visual.
Take a look at the example in Visual.AddVisual or, if you want to host more than one visual, take a look at the (partial) example in Using DrawingVisual Objects
I don't see a way how you could do that since a Visual has neither a position nor a size. Perhaps stick to FrameworkElement and create a style for it?
My (current) answer is to slap it in an XpsDocument and display it in a DocumentViewer. I could, I suppose, do it a little less complex, but I already have the infrastructure to do it this way. Its not 100%, but it works.
First, a behavior so that I can bind to DocumentViewer.Document (its a friggen POCO, urgh):
public sealed class XpsDocumentBinding
{
#region Document
/// <summary>
/// The <see cref="DependencyProperty"/> for <see cref="Document"/>.
/// </summary>
public static readonly DependencyProperty DocumentProperty =
DependencyProperty.RegisterAttached(
"Document",
typeof(XpsDocument), //
typeof(XpsDocumentBinding),
new UIPropertyMetadata(null, OnDocumentChanged));
/// <summary>
/// Gets the value of the <see cref="DocumentProperty">Document attached property</see> on the given <paramref name="target"/>.
/// </summary>
/// <param name="target">The <see cref="DependencyObject">target</see> on which the property is set.</param>
public static XpsDocument GetDocument(DependencyObject target)
{
return (XpsDocument)target.GetValue(DocumentProperty);
}
/// <summary>
/// Sets the <paramref name="value"/> of the <see cref="DocumentProperty">Document attached property</see> on the given <paramref name="target"/>.
/// </summary>
/// <param name="dependencyObject">The <see cref="DependencyObject">target</see> on which the property is to be set.</param>
/// <param name="value">The value to set.</param>
public static void SetDocument(DependencyObject target, XpsDocument value)
{
target.SetValue(DocumentProperty, value);
}
/// <summary>
/// Called when Document changes.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void OnDocumentChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var viewer = sender as DocumentViewer;
if (viewer == null)
throw new InvalidOperationException(
"This behavior is only valid on DocumetViewers.");
var doc = e.NewValue as XpsDocument;
if (doc == null)
return;
viewer.Document = doc.GetFixedDocumentSequence();
}
#endregion
}
Then in my model I expose my visual as an XpsDocument
var pack = PackageStore.GetPackage(_uri);
if (pack != null)
return new XpsDocument(pack, CompressionOption.SuperFast, _uri.AbsoluteUri);
MemoryStream ms = new MemoryStream(2048);
Package p = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
PackageStore.AddPackage(_uri, p);
XpsDocument doc = new XpsDocument(p, CompressionOption.SuperFast, _uri.AbsoluteUri);
var writer = XpsDocument.CreateXpsDocumentWriter(doc);
var collator = writer.CreateVisualsCollator();
// write the visuals using our collator
collator.BeginBatchWrite();
collator.Write(Visual);
collator.EndBatchWrite();
p.Flush();
return doc;
Just add a DocumentViewer, bind the result of the conversion method to it via the behavior, and there it is. I'm sure there's a shortcut in here somewhere...

WindowsFormsHost and DependencyProperty

I've got a Windows Forms control that I'm attempting to wrap as a WPF control using the WindowsFormsHost class; I would like to bind the legacy control to a view-model. Specifically, the control exposes a grid property, GridVisible, that I would like to bind a view-model to. I'm using a private, static backing field and a static, read-only property to represent the dependency property (functionally the same as a static, public field, but less mess). When I attempt to set the control's GridVisible property via XAML, it's not updating. Ideas? What am I doing incorrectly?
DrawingHost Class
/// <summary>
/// Provides encapsulation of a drawing control.
/// </summary>
public class DrawingHost : WindowsFormsHost
{
#region Data Members
/// <summary>
/// Holds the disposal flag.
/// </summary>
private bool disposed;
/// <summary>
/// Holds the grid visible property.
/// </summary>
private static readonly DependencyProperty gridVisibleProperty =
DependencyProperty.Register("GridVisible", typeof(bool),
typeof(DrawingHost), new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender |
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
/// <summary>
/// Holds the pad.
/// </summary>
private readonly DrawingPad pad = new DrawingPad();
#endregion
#region Properties
/// <summary>
/// Get or set whether the grid is visible.
/// </summary>
public bool GridVisible
{
get { return (bool)GetValue(GridVisibleProperty); }
set { SetValue(GridVisibleProperty, pad.GridVisible = value); }
}
/// <summary>
/// Get the grid visible property.
/// </summary>
public static DependencyProperty GridVisibleProperty
{
get { return gridVisibleProperty; }
}
#endregion
/// <summary>
/// Default-construct a drawing host.
/// </summary>
public DrawingHost()
{
this.Child = this.pad;
}
/// <summary>
/// Dispose of the drawing host.
/// </summary>
/// <param name="disposing">The disposal invocation flag.</param>
protected override void Dispose(bool disposing)
{
if (disposing && !disposed)
{
if (pad != null)
{
pad.Dispose();
}
disposed = true;
}
base.Dispose(disposing);
}
}
XAML
<UserControl x:Class="Drawing.DrawingView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:local="clr-namespace:Drawing">
<local:DrawingHost GridVisible="True"/></UserControl>
One of the first rules of dependency properties is to never include any logic in the get and set except the GetValue and SetValue calls. This is because when they are used in XAML, they do not actually go through the get and set accessors. They are inlined with the GetValue and SetValue calls. So none of your code will be executed.
The appropriate way to do this is set up a call-back using the PropertyMetadata parameter in the DependencyProperty.Register method. Then in the call-back you can then execute any extra code.

Resources