通过数据绑定属性动态创建的WPF UserControl

问题描述 投票:0回答:1

我正在尝试创建 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 数据绑定工作正常,但我的控制可能在错误的时间创建内容。有人可以帮忙吗?谢谢!

wpf xaml data-binding user-controls
1个回答
0
投票

为了在

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

© www.soinside.com 2019 - 2024. All rights reserved.