Reputation: 3750
Can WPF Custom Control trace view model subproperty changes to automatically rerender itself?
Let’s say that I have a model with two properties:
public class FullName : ViewModel
{
string _first;
string _last;
public string First
{
get { return _first; }
set
{
_first = value;
RaisePropertyChanged();
}
}
public string Last
{
get { return _last; }
set
{
_last = value;
RaisePropertyChanged();
}
}
}
Where ViewModel
is:
public abstract class ViewModel : INotifyPropertyChanged
{
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e) =>
PropertyChanged?.Invoke(this, e);
public event PropertyChangedEventHandler PropertyChanged;
}
I would like to have a Dependency Property on the WPF Custom Control (AffectsRender
, no SubPropertiesDoNotAffectRender
) to reference model in such a way that control automatically rerenders on First
and Last
property changes:
public class Tag : Control
{
public static readonly DependencyProperty FullNameProperty =
DependencyProperty.Register("FullName", typeof(FullName), typeof(Tag),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public FullName FullName
{
get { return (FullName)GetValue(FullNameProperty); }
set { SetValue(FullNameProperty, value); }
}
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
if (FullName == null)
return;
FontFamily courier = new FontFamily("Courier New");
Typeface courierTypeface = new Typeface(courier, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
FormattedText ft2 = new FormattedText(FullName.First + " " + FullName.Last,
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
courierTypeface,
14.0,
Brushes.Black);
drawingContext.DrawText(ft2, new Point());
}
}
Here is the snippet to test it:
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication3"
mc:Ignorable="d"
Title="MainWindow" Height="139.9" Width="249.514">
<StackPanel>
<StackPanel.DataContext>
<local:FullName>
<local:FullName.First>John</local:FullName.First>
<local:FullName.Last>Doe</local:FullName.Last>
</local:FullName>
</StackPanel.DataContext>
<local:Tag FullName="{Binding}" Height="20"/>
<TextBox Text="{Binding First, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Last, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Window>
Unfortunately, it does not work – changes are not propagating to the custom control. Can it be done efficiently? What this SubPropertiesDoNotAffectRender
is about then?
Upvotes: 3
Views: 667
Reputation: 15227
For this to work, your FullName
class has to be a Freezable
and your First
and Last
properties have to be dependency properties.
You could take a look at the current implementation of the DependencyObject
:
internal void NotifySubPropertyChange(DependencyProperty dp)
{
InvalidateSubProperty(dp);
// if the target is a Freezable, call FireChanged to kick off
// notifications to the Freezable's parent chain.
Freezable freezable = this as Freezable;
if (freezable != null)
{
freezable.FireChanged();
}
}
This mechanism was originally not intended for observing the sub-properties of a bound view-model. It helps to simplify the FrameworkElement
s measuring, arranging and rendering by observing the Freezable
s` properties and triggering appropriate actions on changing their properties and sub-properties.
You can find a nice blog post here that explains how does the retained graphics system in WPF work and how to use the feature you're interested in.
Upvotes: 1