【问题标题】:WPF Data Binding and IValueConverterWPF 数据绑定和 IValueConverter
【发布时间】:2010-09-15 13:08:17
【问题描述】:

为什么我在WPF的绑定表达式中使用转换器时,更新数据时值没有更新。

我有一个简单的 Person 数据模型:

class Person : INotifyPropertyChanged
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

我的绑定表达式如下所示:

<TextBlock Text="{Binding Converter={StaticResource personNameConverter}" />

我的转换器如下所示:

class PersonNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Person p = value as Person;
        return p.FirstName + " " + p.LastName;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

如果我在没有转换器的情况下绑定数据,效果会很好:

<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=LastName}" />

我错过了什么?

编辑: 只是为了澄清一些事情,对于需要实现的 INotifyPropertyChanged 接口,Joel 和 Alan 都是正确的。实际上我确实实现了它,但它仍然不起作用。

我无法使用多个 TextBlock 元素,因为我正在尝试将窗口标题绑定到全名,并且窗口标题不采用模板。

最后,添加复合属性“FullName”并绑定到它是一个选项,但我仍然想知道为什么当绑定使用转换器时不会发生更新。即使我在转换器代码中放置了一个断点,当对基础数据进行更新时,调试器也无法到达那里:-(

谢谢, 乌里

【问题讨论】:

  • 您的帖子有误吗?您的绑定应该是: ??
  • 看来我今天学到了一两件事。查看我的更新。
  • 窗口标题是依赖属性吗?我现在没有时间看。也许这就是它没有为你更新的原因。

标签: c# wpf data-binding converter


【解决方案1】:

您还可以使用 MultiBinding.. 绑定到 Person 对象,FirstName 和 LastName。这样,只要 FirstName 或 LastName 引发属性更改事件,值就会立即更新。

<MultiBinding Converter="{IMultiValueConverter goes here..}">
    <Binding />
    <Binding Path="FirstName" />
    <Binding Path="LastName" />
</MultiBinding>

或者,如果您只使用 FirstName 和 LastName,则将 Person 对象从绑定中剥离,如下所示:

<MultiBinding Converter="{IMultiValueConverter goes here..}">
    <Binding Path="FirstName" />
    <Binding Path="LastName" />
</MultiBinding>

MultiValueConverter 看起来像这样:

class PersonNameConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
            return values[0].ToString() + " " + values[1].ToString();
    }

    public object ConvertBack(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
            throw new NotImplementedException();
    }
}

当然,选择的答案也可以,但是 MultiBinding 更优雅...

【讨论】:

  • 我同意。我完全忘记了MultiBinding。而且,经过一番思考,格式化全名确实应该是 ValueConverter 的工作(而不是 Person),因为它接收 CultureInfo 对象。
  • 啊,谢谢,我一直在尝试用 IValueConverter 破解来传递两个参数...不知道 IMultiValueConverter。
【解决方案2】:

(参见下面的编辑;最新:#2)

它不会更新,因为您的 Person 对象无法通知任何东西 FirstNameLastName 的值已更改。 See this Question.

下面是你如何实现INotifyPropertyChanged。 (已更新,请参阅编辑 2

using System.ComponentModel;

class Person : INotifyPropertyChanged {
    public event PropertyChangedEventHandler PropertyChanged;

    string _firstname;
    public string FirstName {
        get {
            return _firstname;
        }
        set {
            _firstname = value;
            onPropertyChanged( "FirstName", "FullName" );
        }
    }

    string _lastname;
    public string LastName {
        get {
            return _lastname;
        }
        set {
            _lastname = value;
            onPropertyChanged( "LastName", "FullName" );
        }
    }

    public string FullName {
        get {
            return _firstname + " " + _lastname;
        }
    }

    void onPropertyChanged( params string[] propertyNames ) {
        PropertyChangedEventHandler handler = PropertyChanged;

        if ( handler != null ) {
            foreach ( var pn in propertyNames ) {
                handler( this, new PropertyChangedEventArgs( pn ) );
            }
        }
    }
}

编辑 1

实际上,由于您在更新名字和姓氏之后,并且Path=FirstName 和这样的工作都很好,我认为您根本不需要转换器。多个TextBlocks 一样有效,并且在您本地化为从右到左的语言时实际上可以更好地工作。

编辑 2

我想通了。没有通知属性已更新,因为它绑定到对象本身,而不是这些属性之一。即使我制作了PersonDependencyObject 并制作了FirstNameLastName DependencyProperties,它也不会更新。

必须使用FullName 属性,我已经更新了上面Person 类的代码以反映这一点。然后你可以绑定Title。 (注意:我已将Person 对象设置为WindowDataContext。)

Title="{Binding Path=FullName, Mode=OneWay}"

如果您正在编辑 TextBox 中的名称并希望立即反映更改的名称,而不是在 TextBox 失去焦点时,您可以这样做:

<TextBox Name="FirstNameEdit"
    Text="{Binding Path=FirstName, UpdateSourceTrigger=PropertyChanged}" />

我知道您不想使用 FullName 属性,但任何可以实现您想要的东西都可能有点像 Rube Goldberg 设备。例如在Window 类本身上实现INotifyPropertyChangedPerson 属性,让Window 监听PropertyChanged 事件以触发WindowPropertyChanged 事件,并使用如下所示的相对绑定。当然,您还可以在InitializeComponent() 之前设置Person 属性或在设置Person 属性之后触发PropertyChanged,以便它显示出来。 (否则在InitializeComponent() 期间它将是null,并且需要知道它何时是Person。)

<Window.Resources>
    <loc:PersonNameConverter
        x:Key="conv" />
</Window.Resources>
<Window.Title>
    <Binding
        RelativeSource="{RelativeSource Self}"
        Converter="{StaticResource conv}"
        Path="Person"
        Mode="OneWay" />
</Window.Title>

【讨论】:

    【解决方案3】:

    为了更新绑定,您的人员类需要实现 INotifyPropertyChanged 以让绑定知道对象的属性已更新。您还可以通过提供 fullName 属性来避免额外的转换器。

    using System.ComponentModel;
    
    namespace INotifyPropertyChangeSample
    {
        public class Person : INotifyPropertyChanged
        {
            private string firstName;
            public string FirstName
            {
                get { return firstName; }
                set
                {
                    if (firstName != value)
                    {
                        firstName = value;
                        OnPropertyChanged("FirstName");
                        OnPropertyChanged("FullName");
                    }
                }
            }
    
            private string lastName;
            public string LastName
            {
                get { return lastName; }
                set
                {
                    if (lastName != value)
                    {
                        lastName = value;
                        OnPropertyChanged("LastName");
                        OnPropertyChanged("FullName");
                    }
                }
            }
    
            public string FullName
            {
                get { return firstName + " " + lastName; }
            } 
    
            #region INotifyPropertyChanged Members
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void OnPropertyChanged(string name)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
    
            #endregion
        }
    }
    

    您的绑定现在将如下所示:

    <TextBlock Text="{Binding Person.FullName}" />
    

    【讨论】:

    • 向 Person 类添加 FullName 属性可以解决问题,但这不是我要寻找的解决方案。
    【解决方案4】:

    我没查过但是你也可以试试下面的

    <TextBlock Text="{Binding Path=/, Converter={StaticResource personNameConverter}}" />
    

    【讨论】:

    • 快速简单的选择,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-14
    • 1970-01-01
    • 1970-01-01
    • 2012-12-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多