我正在尝试创建 WPF UserControl,其设计应基于某些数据绑定属性。
简单举例一下我想要实现的目标: 让我们拥有包含我的控件的窗口:
<Window>
<controls:DynamicControl TextValue="{Binding Message}" />
</Window>
我的用户控件应该采用此绑定的Message字符串并根据消息的值创建内容。为了简单演示,我从这个值中提取单词,并用交替的颜色创建 Run:
但是,问题是我可能错误地渲染了我的 UserControl,因为在实际场景中我的窗口看起来像这样:
(我还向 Window 添加了普通的 TextBlock(蓝色边框),只是为了表明 TextBlock 的数据绑定正常工作。红色的是我的 DynamicControl)
<Window>
<TextBlock Text="{Binding Message}" />
<controls:DynamicControl TextValue="{Binding Message}" />
</Window>
我的 UserControl DynamicControl 看起来像这样:
XAML:
<UserControl x:Class="MyControls.DynamicControl">
<TextBlock Name="RootTextBlock" />
</UserControl>
代码:
namespace MyControls;
public partial class DynamicControl : UserControl
{
public DynamicControl()
{
InitializeComponent();
Loaded += OnLoaded;
}
public static readonly DependencyProperty TextValueProperty =
DependencyProperty.Register(nameof(TextValue), typeof(string), typeof(DynamicControl),
new PropertyMetadata(default(string)));
public string TextValue
{
get => (string)GetValue(TextValueProperty);
set
{
Console.WriteLine($"DynamicControl.TextValue.set(); value=[{value}]");
SetValue(TextValueProperty, value);
}
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Console.WriteLine($"DynamicControl.OnLoaded; TextValue=[{TextValue}]");
CreateContent();
}
protected override void OnRender(DrawingContext drawingContext)
{
Console.WriteLine($"DynamicControl.OnRender; TextValue=[{TextValue}]");
base.OnRender(drawingContext);
CreateContent();
}
private void CreateContent()
{
RootTextBlock.Inlines.Clear();
if (string.IsNullOrEmpty(TextValue))
return;
var words = TextValue.Split(' ');
int num = 1;
foreach (var word in words)
{
RootTextBlock.Inlines.Add(new Run() { Text = word + " ", Background = new SolidColorBrush(num % 2 == 0 ? Colors.Salmon : Colors.Gold)});
num++;
}
}
}
我试图在 OnRender 和 Loaded 事件中调用我的 CreateContent() 方法(不知道是否可以,只是尝试过)。问题是,当调用 OnRender 和 OnLoaded 时,TextValue 字符串为空(未从数据绑定填充 - 不明白为什么)
我在控制台中看到这个:
Window.Message.set(); value=[This is test message]
DynamicControl.OnRender; TextValue=[]
DynamicControl.OnLoaded; TextValue=[]
DynamicControl.OnRender; TextValue=[]
因此,对于正常的 TextBlock 数据绑定工作正常,但我的控制可能在错误的时间创建内容。有人可以帮忙吗?谢谢!
为了在
OnRender
属性更改时调用 TextValue
,请设置 FrameworkPropertyMetadataOptions.AffectsRender
选项:
public static readonly DependencyProperty TextValueProperty =
DependencyProperty.Register(
nameof(TextValue),
typeof(string),
typeof(DynamicControl),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.AffectsRender));
public string TextValue
{
get => (string)GetValue(TextValueProperty);
set => SetValue(TextValueProperty, value);
}
但是,没有必要在每次呈现控件时重新创建内容。仅当
TextValue
属性发生变化时才足够,即在 PropertyChangedCallback
: 中
public static readonly DependencyProperty TextValueProperty =
DependencyProperty.Register(
nameof(TextValue),
typeof(string),
typeof(DynamicControl),
new PropertyMetadata(null, TextValuePropertyChanged));
public string TextValue
{
get => (string)GetValue(TextValueProperty);
set => SetValue(TextValueProperty, value);
}
private static void TextValuePropertyChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((DynamicControl)o).CreateContent();
}
请注意,在依赖属性包装器的 setter 中调用
Console.WriteLine
或除 SetValue
之外的任何其他内容都是毫无意义的。当通过 Binding 设置属性时,框架会绕过 setter 并直接调用 SetValue
。