Привязка WPF TextBlock к ViewModel с использованием расширения TextBlock не работает

Я привязываю модель представления к TextBlock. Свойство Inlines не является DependencyProperty, поэтому я создал класс TextBlockExtensions и создал присоединенный DependencyProperty с именем BindableInlines.

Ниже приведена моя модель просмотра.

public class MainWindowModel: INotifyPropertyChanged
{
    private buttonAddNewTextCommand _btnAddNewTextCommand;

    public ObservableCollection<Inline> ProcessTrackerInlines { get; set; }

    public ICommand btnAddNewTextCommand
    {
        get
        {
            return _btnAddNewTextCommand;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public MainWindowModel()
    {
        _btnAddNewTextCommand = new buttonAddNewTextCommand(this);
        loadProcessTracker();
    }

    public void addErrorLine(String errorMessage)
    {
        addText(errorMessage, Brushes.Red, true);
    }

    public void addNewTextLine()
    {
        String message = Guid.NewGuid().ToString();
        var rand = new Random();
        byte[] brushes = new byte[4];
        rand.NextBytes(brushes);
        ProcessTrackerInlines.Add(new LineBreak());
        ProcessTrackerInlines.Add(addText(message, new SolidColorBrush(Color.FromArgb(brushes[0], brushes[1], brushes[2], brushes[3])), true));
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("ProcessTrackerInlines"));
        }
    }

    private void loadProcessTracker()
    {
        ProcessTrackerInlines = new ObservableCollection<Inline>();
        var rand = new Random();
        byte[] brushes = new byte[4];
        for(int i=0; i<5; i++)
        {
            rand.NextBytes(brushes);
            ProcessTrackerInlines.Add(addText($"{Guid.NewGuid().ToString()}\r\n", new SolidColorBrush(Color.FromArgb(brushes[0], brushes[1], brushes[2], brushes[3])), true));
        }

    }

    private Run addText(String textToAdd, Brush foreground = null, Boolean? addTime = null)
    {
        if (addTime ?? false)
        {
            textToAdd = addCurrentTime(textToAdd);
        }
        if (foreground != null)
        {
            return new Run(textToAdd) { Foreground = foreground };
        }
        else
        {
            return new Run(textToAdd);
        }
    }

    private String addCurrentTime(String prefixText)
    {
        return $"[{DateTime.Now.ToString("h:mm ss tt")}] {prefixText}";
    }
}

public class buttonAddNewTextCommand : ICommand
{
    private MainWindowModel owner;

    public buttonAddNewTextCommand(MainWindowModel _object)
    {
        owner = _object;
    }

    public Boolean CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        owner.addNewTextLine();
    }

    public event EventHandler CanExecuteChanged;
}

Мой TextBlockExtensions класс:

public class TextBlockExtensions
{
    public static IEnumerable<Inline> GetBindableInlines(DependencyObject obj)
    {
        return (IEnumerable<Inline>)obj.GetValue(BindableInlinesProperty);
    }

    public static void SetBindableInlines(DependencyObject obj, IEnumerable<Inline> value)
    {
        obj.SetValue(BindableInlinesProperty, value);
    }

    public static readonly DependencyProperty BindableInlinesProperty =
        DependencyProperty.RegisterAttached("BindableInlines", typeof(IEnumerable<Inline>), typeof(TextBlockExtensions), new PropertyMetadata(null, OnBindableInlinesChanged));

    private static void OnBindableInlinesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var Target = d as TextBlock;

        if (Target != null)
        {
            Target.Inlines.Clear();
            Target.Inlines.AddRange((System.Collections.IEnumerable)e.NewValue);
        }
    }
}

И мой XAML:

<Window x:Class="MVVMTextBlock.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:MVVMTextBlock"
        xmlns:viewModels="clr-namespace:MVVMTextBlock.ViewModels"
        xmlns:extensions="clr-namespace:MVVMTextBlock"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.DataContext>
        <viewModels:MainWindowModel/>
    </Window.DataContext>
    <Grid>
        <TextBlock extensions:TextBlockExtensions.BindableInlines="{Binding ProcessTrackerInlines, Mode=OneWay}" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Width="578" Margin="25,0,0,0" Height="398"/>
        <Button Content="Add New Text" HorizontalAlignment="Left" VerticalAlignment="Top" Width="123" Margin="638,10,0,0" Command="{Binding btnAddNewTextCommand, Mode=OneWay}"/>
    </Grid>
</Window>

При нажатии кнопки метод MainWindowModel.addNewTextLine() срабатывает, а TextBlockExtensions.OnBindableInlinesChanged() - нет. Таким образом, TextBlock не обновляется. Я уже изменил BindableInlinesProperty на ObservableCollection, но это не сработало. Что я делаю неправильно?


person El Bayames    schedule 02.01.2021    source источник
comment
Обратный вызов OnBindableInlinesChanged вызывается только тогда, когда Binding создает новое значение, чего нельзя сказать о добавлении или удалении элементов из исходной коллекции. Ваше прикрепленное свойство также должно позаботиться о том, чтобы целевой элемент зарегистрировал обработчик CollectionChanged в случае, если e.NewValue является INotifyCollectionChanged.   -  person Clemens    schedule 02.01.2021
comment
Я бы рекомендовал упростить это, сделав ProcessTrackerInlines обычным списком и назначая новое значение свойства каждый раз, когда коллекция изменяется.   -  person Clemens    schedule 02.01.2021
comment
Я ответил на аналогичный вопрос несколько дней назад. Пожалуйста, посмотрите здесь #65448972" title="привязка wpf внутри шаблона элемента списка к исходному источнику наблюдаемой коллекции"> stackoverflow.com/questions/65447022/.   -  person Anton    schedule 03.01.2021