如何将命令绑定到TextBlock中动态创建的按钮或超链接?

时间:2017-03-08 07:12:50

标签: c# wpf mvvm

我没有找到任何可以帮助我解决这个问题的东西。我正在使用“MVVM”来构建WPF测试应用程序。当消息显示在ListBox中时,应用程序应该根据特定的正则表达式模式读取消息并创建链接/按钮,并允许用户单击它们以采取进一步的操作。

我遇到的问题是在将消息绑定到ListBox项之后创建这样的按钮时,我希望绑定工作或抛出错误,但这些都没有发生,现在我很困惑为什么。我唯一的猜测就是“绑定”无法找到命令并且无法慷慨地失败。

所以关于我做错了什么的想法,或者我怎么能做到这一点?

所以这是代码:

XAML PART

<ListBox x:Name="lsbMessages" Grid.Row="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ItemsSource="{Binding Messages}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock local:TextBlockExtension.FormattedText="{Binding}" />
                    <TextBlock Text="{Binding Path=Date}"/>                        
                </StackPanel>
            </DataTemplate>

        </ListBox.ItemTemplate>
    </ListBox>

背后的代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        DataContext = new TestViewModel();
    }
}

视图模型:

public class TestViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Message> _messages;
public ObservableCollection<Message> Messages
{
    get
    {
        if (_messages == null)
        {
            _messages = new ObservableCollection<Message>();
        }

        return _messages;
    }
}

private string _message;
public string Message {
    get
    {
        return this._message;
    }

    set
    {
        if (value != this._message)
        {
            this._message = value;
            NotifyPropertyChanged();
        }
    }
}

private List<string> _filters = new List<string>();

public IAsyncCommand ChannelCommand { get; set; }

public TestViewModel()
{            
    ChannelCommand = new AsyncCommand(ChannelSelected);


    PropertyChanged += TestViewModel_PropertyChanged;

    _filters.Add(@"(?:#(\w+))");
    _filters.Add(@"(?:@(\w+)(?:\s))");
    _filters.Add(@"(#(\d{1,2}\/\d{1,2}\/\d{1,4})(\s\d{1,2}:\d{1,2}(?:PM|AM)?))");
}

private async void TestViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    switch (e.PropertyName)
    {                
        case "Message":
            await RunFilters();
            break;
    }
}

private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

public async Task ChannelSelected(object parameter)
{
    System.Windows.MessageBox.Show(parameter.ToString());
}

public async Task RunFilters()
{
    foreach(string filter in _filters)
    {
        await Filter(filter);

    }
}

public async Task Filter(string filter)
{
    Regex reg = new Regex(filter);
    var matches = reg.Matches(Message);

    foreach (Match match in matches)
    {
        if (!Finds.Contains(match.Value))
        {
            Finds.Add(match.Value);
        }
    }
}

public async Task AddTags(Message message)
{
    foreach (string filter in _filters)
    {
        Regex reg = new Regex(filter);
        var matches = reg.Matches(Message);

        foreach (Match match in matches)
        {
            Tag tag = new Tag()
            {
                Type = "Channel"
                ,
                Value = match.Value
            };

            message.Tags.Add(tag);
        }

    }
}
}

TextBlock EXTENSION:

public static class TextBlockExtension
{
    public static Message GetFormattedText(DependencyObject obj)
    { return (Message)obj.GetValue(FormattedTextProperty); }

    public static void SetFormattedText(DependencyObject obj, Message value)
    { obj.SetValue(FormattedTextProperty, value); }

    public static readonly DependencyProperty FormattedTextProperty =
        DependencyProperty.RegisterAttached("FormattedText", typeof(Message), typeof(TextBlockExtension),
        new PropertyMetadata(null, (sender, e) =>
        {
            Message message = e.NewValue as Message;
            var textBl = sender as TextBlock;
            if (textBl != null)
            {
                textBl.Inlines.Clear();
                foreach (Tag tag in message.Tags)
                {
                    Regex regx = new Regex($"({tag.Value})", RegexOptions.IgnoreCase);
                    var str = regx.Split(message.Body);

                    for (int i = 0; i < str.Length; i++)
                        if (i % 2 == 0)
                            textBl.Inlines.Add(new Run { Text = str[i] });
                        else
                        {                                
                            Hyperlink link = new Hyperlink { CommandParameter = str[i]};
                            link.Inlines.Add(str[i]);
                            switch (tag.Type)
                            {
                                case "channel":
                                    Binding myBinding = new Binding("DataContext.ChannelCommand")
                                    {                                            
                                        RelativeSource = new RelativeSource() { AncestorType = typeof(Window), Mode=RelativeSourceMode.FindAncestor }
                                    };
                                    link.SetBinding(Hyperlink .CommandProperty, myBinding);                                        
                                    break;
                            }

                            textBl.Inlines.Add(link);
                        }
                }
            }
        }));
}

对于命令实现,我使用了此处描述的代码https://msdn.microsoft.com/en-us/magazine/dn630647.aspx,并添加了以下支持参数:

    private readonly Func<object, Task> _commandWithParameter;


    public AsyncCommand(Func<object, Task> command)
    {
        _commandWithParameter = command;
    }
    public override Task ExecuteAsync(object parameter)
    {
        if (_commandWithParameter != null)
        {
            return _commandWithParameter(parameter);
        }
        else
        {
            return _command();
        }
    }

希望我能在这里得到一切。

0 个答案:

没有答案