在自定义 WPF 控件中强制重绘自定义绘制的 UIElement
Force a repaint of a custom drawn UIElement in a custom WPF control
我正在开发自定义 WPF 控件。此控件的主要目的是在可滚动区域中可视化数以千计的图形基元。控件模板的核心部分如下所示:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ItemVisualizer}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<local:ItemAreaElement Grid.Row="0" Grid.Column="0" x:Name="PART_ItemArea" />
<ScrollBar Grid.Row="0" Grid.Column="1" x:Name="PART_ScrollBarVert" Orientation="Vertical" Maximum="100" />
<ScrollBar Grid.Row="1" Grid.Column="0" x:Name="PART_ScrollBarHorz" Orientation="Horizontal" Maximum="100" />
<Rectangle Grid.Row="1" Grid.Column="1" x:Name="PART_SizeGrip" Focusable="False" Fill="#F0F0F0" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
ItemAreaElement 负责绘制项目。为了简单起见,我们可以认为它的核心部分是这样的:
class ItemAreaElement : FrameworkElement
{
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
for (int i = 0; i < _data.ItemCount; i++)
{
drawingContext.DrawLine(_penLine, new Point(0, i * 10), new Point(100, i * 10));
}
}
}
每当整个 ItemVisualizer 控件中的相关 属性 更改时,我都需要重新绘制 ItemAreaElement。但是,我没有找到在 WPF 中执行此操作的方法。众所周知的 Dispatcher 对象技巧在我的例子中不起作用:
private static void OnItemCountPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
ItemVisualizer vis = (ItemVisualizer)source;
vis._itemArea.Dispatcher.Invoke(delegate { }, DispatcherPriority.Render);
}
,其中 _itemArea 是对在 OnApplyTemplate() 中获取的 ItemAreaElement 的本地引用:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.Template != null)
{
_itemArea = this.Template.FindName("PART_ItemArea", this) as ItemAreaElement;
_itemArea.Grid = this;
}
}
是否有其他方法可以在我的构造中强制更新 UIElement?或者,也许,我需要重新设计整个控件才能使其成为可能?
通常在 ItemCount
属性 的注册中指定 FrameworkPropertyMetadataOptions.AffectsRender
就足够了:
public static readonly DependencyProperty ItemCountProperty =
DependencyProperty.Register(
"ItemCount", typeof(int), typeof(ItemVisualizer),
new FrameworkPropertyMetadata(FrameworkPropertyMetadataOptions.AffectsRender));
如果这没有帮助,您可以通过在 ItemAreaElement 上调用 InvalidVisual()
来强制重绘:
var vis = (ItemVisualizer)source;
vis._itemArea.InvalidVisual();
我正在开发自定义 WPF 控件。此控件的主要目的是在可滚动区域中可视化数以千计的图形基元。控件模板的核心部分如下所示:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ItemVisualizer}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<local:ItemAreaElement Grid.Row="0" Grid.Column="0" x:Name="PART_ItemArea" />
<ScrollBar Grid.Row="0" Grid.Column="1" x:Name="PART_ScrollBarVert" Orientation="Vertical" Maximum="100" />
<ScrollBar Grid.Row="1" Grid.Column="0" x:Name="PART_ScrollBarHorz" Orientation="Horizontal" Maximum="100" />
<Rectangle Grid.Row="1" Grid.Column="1" x:Name="PART_SizeGrip" Focusable="False" Fill="#F0F0F0" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
ItemAreaElement 负责绘制项目。为了简单起见,我们可以认为它的核心部分是这样的:
class ItemAreaElement : FrameworkElement
{
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
for (int i = 0; i < _data.ItemCount; i++)
{
drawingContext.DrawLine(_penLine, new Point(0, i * 10), new Point(100, i * 10));
}
}
}
每当整个 ItemVisualizer 控件中的相关 属性 更改时,我都需要重新绘制 ItemAreaElement。但是,我没有找到在 WPF 中执行此操作的方法。众所周知的 Dispatcher 对象技巧在我的例子中不起作用:
private static void OnItemCountPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
ItemVisualizer vis = (ItemVisualizer)source;
vis._itemArea.Dispatcher.Invoke(delegate { }, DispatcherPriority.Render);
}
,其中 _itemArea 是对在 OnApplyTemplate() 中获取的 ItemAreaElement 的本地引用:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.Template != null)
{
_itemArea = this.Template.FindName("PART_ItemArea", this) as ItemAreaElement;
_itemArea.Grid = this;
}
}
是否有其他方法可以在我的构造中强制更新 UIElement?或者,也许,我需要重新设计整个控件才能使其成为可能?
通常在 ItemCount
属性 的注册中指定 FrameworkPropertyMetadataOptions.AffectsRender
就足够了:
public static readonly DependencyProperty ItemCountProperty =
DependencyProperty.Register(
"ItemCount", typeof(int), typeof(ItemVisualizer),
new FrameworkPropertyMetadata(FrameworkPropertyMetadataOptions.AffectsRender));
如果这没有帮助,您可以通过在 ItemAreaElement 上调用 InvalidVisual()
来强制重绘:
var vis = (ItemVisualizer)source;
vis._itemArea.InvalidVisual();