WPF 自定义控件在视图模型子属性更改时重新呈现
WPF Custom Control rerendering on view model subproperties change
WPF 自定义控件能否跟踪视图模型子属性 更改以自动重新呈现自身?
假设我有一个具有两个属性的模型:
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();
}
}
}
其中 ViewModel
是:
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;
}
我想在 WPF 自定义控件(AffectsRender
,无 SubPropertiesDoNotAffectRender
)上有一个依赖项 属性,以便以控件自动重新呈现 [=] 的方式引用模型17=] 和 Last
属性 变化:
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());
}
}
这是测试它的代码片段:
<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>
不幸的是,它不起作用——更改没有传播到自定义控件。能否高效完成? SubPropertiesDoNotAffectRender
是什么意思?
为此,您的 FullName
class 必须是 Freezable
并且您的 First
和 Last
属性必须是依赖属性。
你可以看看 DependencyObject
的 current implementation:
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();
}
}
此机制最初不是为了观察绑定视图模型的子属性。它有助于通过观察 Freezable
s` 属性并在更改其属性和子属性时触发适当的操作来简化 FrameworkElement
s 的测量、排列和渲染。
您可以找到一个不错的博客 post here,它解释了 WPF 中保留的图形系统如何工作以及如何使用您感兴趣的功能。
WPF 自定义控件能否跟踪视图模型子属性 更改以自动重新呈现自身?
假设我有一个具有两个属性的模型:
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();
}
}
}
其中 ViewModel
是:
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;
}
我想在 WPF 自定义控件(AffectsRender
,无 SubPropertiesDoNotAffectRender
)上有一个依赖项 属性,以便以控件自动重新呈现 [=] 的方式引用模型17=] 和 Last
属性 变化:
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());
}
}
这是测试它的代码片段:
<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>
不幸的是,它不起作用——更改没有传播到自定义控件。能否高效完成? SubPropertiesDoNotAffectRender
是什么意思?
为此,您的 FullName
class 必须是 Freezable
并且您的 First
和 Last
属性必须是依赖属性。
你可以看看 DependencyObject
的 current implementation:
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();
}
}
此机制最初不是为了观察绑定视图模型的子属性。它有助于通过观察 Freezable
s` 属性并在更改其属性和子属性时触发适当的操作来简化 FrameworkElement
s 的测量、排列和渲染。
您可以找到一个不错的博客 post here,它解释了 WPF 中保留的图形系统如何工作以及如何使用您感兴趣的功能。