Pergunta

Por que é que quando eu uso um conversor na minha expressão de ligação em WPF, o valor não é atualizado quando os dados são atualizados.

Eu tenho um modelo de dados Pessoa simples:

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

minha expressão olhares de ligação como este:

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

meu conversor esta aparência:

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();
    }
}

Se eu ligar os dados sem um conversor ele funciona muito bem:

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

O que eu estou ausente?

EDIT: Só para esclarecer algumas coisas, tanto Joel e Alan estão corretas sobre a interface INotifyPropertyChanged que precisa ser implementado. Na realidade eu realmente implementá-lo, mas ele ainda não funciona.

Eu não posso usar vários elementos TextBlock porque eu estou tentando ligar o título da janela para o nome completo, e não o título da janela não ter um modelo.

Finalmente, é uma opção para adicionar uma propriedade composto "FullName" e se ligam a ele, mas eu ainda estou querendo saber por que a atualização não acontece quando o uso obrigatório de um conversor. Mesmo quando eu colocar um break point no código conversor, o depurador só não fica lá quando uma atualização é feita para os dados subjacentes: - (

Obrigado, Uri

Foi útil?

Solução

(ver edições abaixo; mais recente: Número 2)

Não é atualizar porque seu objeto Person não é capaz de notificar qualquer coisa que o valor de FirstName ou LastName mudou. Veja esta Pergunta .

E aqui está como você implementar INotifyPropertyChanged. ( Atualizado, consulte Editar 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 ) );
            }
        }
    }
}

Editar 1

Na verdade, desde que você está após o primeiro nome eo nome da última atualização e Path=FirstName e essas obras muito bem, eu não acho que você vai precisar do conversor em tudo. TextBlocks múltipla são tão válido, e pode realmente funcionar melhor quando você está de localizações para um idioma da direita para a esquerda.

Editar 2

Eu percebi isso. Não está sendo notificado de que as propriedades tenham atualizado porque ele é vinculativo para o objeto em si, não é um daqueles propriedades. Mesmo quando eu fiz Person um DependencyObject e fez FirstName e LastName DependencyProperties, não seria atualizar.

Você irá tem que usar uma propriedade FullName, e eu atualizar o código da classe Person acima para refletir isso. Depois, você pode vincular a Title. ( Nota:. eu definir o objeto Person como Window do DataContext)

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

Se você estiver editando os nomes em uma TextBox e quer que o nome mudou reflete imediatamente em vez de quando o TextBox perde o foco, você pode fazer isso:

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

Eu sei que você não quer usar uma propriedade FullName, mas nada que pudesse realizar o que você quer provavelmente seria um pouco de um dispositivo de Rube Goldberg. Tais como a implementação INotifyPropertyChanged e uma propriedade Person na própria classe Window, tendo o Window ouvir sobre o evento PropertyChanged para disparar evento Window do PropertyChanged, e usando uma ligação como o seguinte relativa. Você também teria defina a propriedade Person antes InitializeComponent() ou incêndio PropertyChanged depois de definir a propriedade Person para que ele aparece, é claro. (Caso contrário, será null durante InitializeComponent() e precisa saber quando é um 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>

Outras dicas

Você também pode usar um MultiBinding .. Bind para o objeto Pessoa, o nome e sobrenome. Dessa forma, o valor é atualizado assim que FirstName ou LastName lança o evento de propriedade alterada.

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

Ou se você só usar o nome e sobrenome, tira o objeto Pessoa da ligação a algo como isto:

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

E os olhares MultiValueConverter como este:

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();
    }
}

Mas, claro, a resposta selecionada funciona bem, mas um MultiBinding funciona mais elegante ...

Para que a ligação a ser atualizado, suas necessidades de classe pessoa para implementar INotifyPropertyChanged para deixar o know vinculativo que as propriedades do objeto foram udpated. Você também pode salvar-se do conversor adicional, fornecendo uma propriedade 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
    }
}

A sua ligação vai ficar assim:

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

Eu não verificá-lo, mas você também pode tentar o seguinte

<TextBlock Text="{Binding Path=/, Converter={StaticResource personNameConverter}}" />
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top