使用DependencyProperty进行TextBlock内联绑定

时间:2015-10-28 14:41:55

标签: c# wpf mvvm

我正在使用MVVMLight。这就是我从示例here获得的内容。

#include "GameManager.h"
#include "Input.h"
#include "Player"

#include <iostream>
#include <string>

using namespace std;

const int maxPlayerCnt = 10;
static Player p1, p2, morePlayers[maxPlayerCnt];

int main()
{
    GameManager game;
    game.Game(p1, p2, morePlayers);
    return 0;
}

问题是绑定属性InlineList永远不会更新。我没有看到我添加到集合ObservableCollection的任何文本。当我在OnPropertyChanged方法中放置一个断点时,它永远不会被击中。我知道我的数据上下文设置正确,因为其他绑定控件工作。 可能是什么问题?

1 个答案:

答案 0 :(得分:-1)

好的,你只需要在你的BindableTextBlock中添加这些原始解决方案。我们在这里做的是我们为收集更改时添加处理程序(意味着添加新值),我们只做那个设置集合时。因此,对于您在xaml中的绑定,您在VM fires集合中对集合所做的每个更改都会在textblock中更改事件,而后者只是将值附加到内联。

private void CollectionChangedHandler(object sender, NotifyCollectionChangedEventArgs e)
{
    Inlines.AddRange(e.NewItems);
}

private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
    if (e.NewValue == null) return;

    var textBlock = (BindableTextBlock)sender;
    textBlock.InlineList.CollectionChanged += textBlock.CollectionChangedHandler;
}

出于历史原因编辑之前

好的,我看到了发生了什么,所以首先解释一个例子。

首先介绍一些关于wpf的基本概念:

为了让您的视图通知ViewModel中的绑定变量(或者此时的任何DataContext),您必须使用已更改属性的名称进行RaisePropertyChanged事件或使用“#”;以某种方式这样做:) - 像ObservableCollection。

所以有些用例:

您有一个带字段的属性 - &gt;通常的做法就是这样:

private ICollection<Inline> _inlineList;

public ICollection<Inline> InlineList
{
    get
    {
        return _inlineList;
    }
    set
    {
        _inlineList = value;
        RaisePropertyChanged("InlineList");
    }

}

这可确保在为InlineList设置新值时,将通知视图

或者就你的情况而言:

private ICollection<Inline> _inlineList;

public ICollection<Inline> InlineList
{
    get { return _inlineList; }
    set { Set(() => InlineList, ref _inlineList, value); }
}

如果你查看Set方法的描述,你会发现它正在设置值并提升属性(还有一些东西)

您希望自动更新并使用 ObservableCollection - &gt;我这样用它:

private ObservableCollection<ClientFilter> clientFilters;

public IEnumerable<ClientFilter> ClientFilters
{
    get
    {
        if (this.clientFilters == null)
        {
            this.clientFilters = new ObservableCollection<ClientFilter>();
        }

        return this.clientFilters;
    }
    set
    {
        if (this.clientFilters == null)
        {
            this.clientFilters = new ObservableCollection<ClientFilter>();
        }

        SetObservableValues<ClientFilter>(this.clientFilters, value);
    }
}

方法SetObservableValues位于我的主ViewModel中,并且正在执行此操作:

public static void SetObservableValues<T>(
    ObservableCollection<T> observableCollection,
    IEnumerable<T> values)
{  
    if (observableCollection != values)
    {
        observableCollection.Clear();

        foreach (var item in values)
        {
            observableCollection.Add(item);
        }
    }
}

此方法确保如果对obs集合的引用不相同,它将清除旧的并重用它,因为绑定时绑定到引用的常见错误是然后更改引用本身而不是值,反过来不会更新UI上的任何内容,你认为绑定被破坏了:)

因此,如果您希望它正常运行,您只需添加/删除Collection / Enumerable ClientFilters

现在的解决方案

因此,我不能100%确定您想要达到的目标,但是您可以采取哪些措施来实现绑定工作

您的ViewModel

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Documents;

namespace WpfApplication3.ViewModel
{
public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {

    }

    private ICollection<Inline> _inlineList;

    public ICollection<Inline> InlineList
    {
        get { return _inlineList; }
        set { Set(() => InlineList, ref _inlineList, value); }
    }

    public RelayCommand SendClicked
    {
        get
        {
            return new RelayCommand(() =>
            {
                InlineList = new List<Inline>
                {
                    new Run("This is some bold text") { FontWeight = FontWeights.Bold },
                    new Run("Some more text"),
                    new Run("This is some text") { TextDecorations = TextDecorations.Underline }
                };
            });
        }
    }
}
}

您的自定义控件 - &gt; BindableTextBlock

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;

namespace WpfApplication3
{
    public class BindableTextBlock : TextBlock
    {
        public ICollection<Inline> InlineList
        {
            get { return (ICollection<Inline>)GetValue(InlineListProperty); }
            set { SetValue(InlineListProperty, value); }
        }

        public static readonly DependencyProperty InlineListProperty =
            DependencyProperty.Register("InlineList", typeof(List<Inline>), typeof(BindableTextBlock), new UIPropertyMetadata(null, OnPropertyChanged));

        private static void OnPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (e.NewValue == null) return;

            var textBlock = (BindableTextBlock)sender;

            textBlock.Inlines.AddRange((ICollection<Inline>)e.NewValue);
        }
    }
}

你的XAML

在您的页面或窗口上(取决于平台)

DataContext="{Binding Main, Source={StaticResource Locator}}"

然后在里面

<StackPanel>
    <Button Command="{Binding SendClicked}">SendClicked</Button>
    <local:BindableTextBlock Background="Black" Foreground="AliceBlue"
                             Width = "Auto" Height="Auto" 
                             InlineList="{Binding InlineList}" 
                             >
    </local:BindableTextBlock>
</StackPanel>

所有假设你有来自MVVM Light的ViewModelLocator注册你的视图模型

使用GalaSoft.MvvmLight.Ioc; 使用Microsoft.Practices.ServiceLocation;

namespace WpfApplication3.ViewModel
{
    public class ViewModelLocator
    {
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register<MainViewModel>();
        }

        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public static void Cleanup()
        {
        }
    }
}

<强> ALTERNATIVE

或者你也可以这样命令:

public RelayCommand SendClicked
{
    get
    {
        return new RelayCommand(() =>
        {
            _inlineList = new List<Inline>();
            InlineList.Add(new Run("This is some bold text") { FontWeight = FontWeights.Bold });
            InlineList.Add(new Run("Some more text"));
            InlineList.Add(new Run("This is some text") { TextDecorations = TextDecorations.Underline });
            RaisePropertyChanged("InlineList");
        });

    }
}

但你必须使用另一种定义属性的选项,如我文章开头所述。

当然,您可以通过其他方式进行此操作。

再提一个建议 - &gt;在视图模型中使用UI元素被认为是不好的做法而不是MVVM的精神,所以在这个代码IMO中强烈建议改变体系结构。

帖子太长了(像往常一样),如果你需要附加说明,请告诉我。 干杯和快乐的编码;)