INotifyPropertyChanged проблема - PullRequest
       12

INotifyPropertyChanged проблема

7 голосов
/ 26 января 2009

Сначала я хочу сказать, что приведенный ниже пример - упрощение. Предположим, вы связали WPF-контроль.

<Window Title="Window1" Height="300" Width="300">
<Grid>
    <StackPanel>
        <TextBox Text="{Binding Name}" Margin="10"/>
        <Button HorizontalAlignment="Center" 
        Content="Click Me" Margin="5" 
        Padding="2" Click="OnButtonClick" />
    </StackPanel>
</Grid>
</Window>

Окно привязано к классу Person, который реализует INotifyPropertyChanged и имеет установщик имени в форме

    public string Name 
    {
        get { return _name; }
        set 
        {
            _name = "Some Name";
            OnPropertyChanged("Name");
        }
    }

т.е. _name присваивается «Some Name» всякий раз, когда пользователь пытается изменить его из пользовательского интерфейса. Но этот образец не работает. Я изменил имя в TextBox на какое-то значение, нажимая вкладку, заставляя фокус переместиться на кнопку, и значение в TextBox остается неизменным, хотя событие PropertyChanged было запущено.

Не могли бы вы объяснить, почему это происходит? Как я понимаю, PropertyChanged событие заставляет пользовательский интерфейс перечитывать значения из свойств и отображать их, но в моем примере значение в текстовом поле с привязкой к данным не обновляется.


Снова. Я понимаю, что это плохая реализация свойства, но я хочу повторить, что это упрощение. Это просто образец. Но в любом случае PropertyChanged сигнализирует, что свойство было изменено и должно быть обновлено, но это не так.

Ответы [ 7 ]

9 голосов
/ 26 января 2009

Событие PropertyChanged игнорируется TextBox, поскольку оно является инициатором события.

Некоторые уточнения:

TextBox (или привязка к текстовому полю) знает, что это инициатор, потому что он получает событие PropertyChanged в том же вызове. Выполняя асинхронный вызов, текстовое поле (или привязка) не может узнать, что это инициатор, поэтому оно будет обрабатывать событие, как если бы кто-то еще его обновил

Если вы добавите 2-е текстовое поле в свой пользовательский интерфейс, вы увидите, что 2-е текстовое поле изменяется, когда вы редактируете 1-е, и наоборот.

4 голосов
/ 29 октября 2010

Обходной путь обходного конвертера, предложенный Хайнци (описан здесь ), не работает, когда UpdateSourceTrigger равен PropertyChanged . Но что, если это то, что нам нужно?

Кажется, что асинхронное связывание делает свое дело, например ::10000

SelectedIndex="{Binding SelectedIndex, IsAsync=True}"
2 голосов
/ 01 сентября 2009

Как уже отмечал Bubblewrap, это сделано специально - текстовое поле предполагает, что если оно устанавливает свойство привязки для некоторого значения, установщик не изменит значение. Согласно Microsoft , они не изменят это поведение, поскольку это нарушит существующий код.

Если вы хотите изменить значение (да, для этого есть вполне веские причины), вы должны использовать обходной путь, например, добавив фиктивный конвертер. В блоге есть запись (не написанная мной), подробно описывающая эту технику.

0 голосов
/ 26 января 2009

Замена сеттера в форме

        set 
        {
            _name = "Some Name";
            Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.DataBind, 
                (SendOrPostCallback)delegate { OnPropertyChanged("Name"); },
                null);
        }

решает проблему, но она все еще открыта. Почему я должен делать асинхронный вызов вместо синхронной сигнализации о том, что мое свойство было изменено.

0 голосов
/ 26 января 2009
public string MyField  
{  
    get { return _myField; }  
    set {   
        if (_myField == value)  
            return;  

        _myField = value;   

        OnPropertyChanged("MyField");  
    }  
}  

Это правильная реализация свойства.

При изменении свойства убедитесь, что точно такой же экземпляр объекта привязан к элементу управления. В противном случае об изменении будет сообщено, но элемент управления никогда не получит его, поскольку элемент управления не связан должным образом.

0 голосов
/ 26 января 2009

Если я не ошибаюсь, поведение связывания по умолчанию для свойства Text в TextBox - это TwoWay, так что это должно работать. Вы можете заставить его быть двухсторонним в XAML следующим образом:

<Window Title="Window1" Height="300" Width="300">
  <Grid>
    <StackPanel>
        <TextBox Text="{Binding Name, Mode=TwoWay}" Margin="10"/>
        <Button HorizontalAlignment="Center" 
        Content="Click Me" Margin="5" 
        Padding="2" Click="OnButtonClick" />
    </StackPanel>
  </Grid>
</Window>

Обратите внимание на Mode=TwoWay в объявлении Binding.

Если это не сработает, то я подозреваю, что в коде, генерирующем событие, или создается свойство, возникает исключение, и вы должны искать это.


Возможно, вы делаете вызов, чтобы изменить значение в потоке, который не является потоком пользовательского интерфейса. Если это так, то вам нужно либо выполнить маршалинг вызова, чтобы вызвать событие свойства изменено в потоке пользовательского интерфейса, либо внести изменения в значение в потоке пользовательского интерфейса.

Когда объект связан с элементом пользовательского интерфейса, изменения в объекте, которые могут повлиять на пользовательский интерфейс, должны быть сделаны в потоке пользовательского интерфейса.

0 голосов
/ 26 января 2009

Причина в том, что вы жестко закодировали «Some Name» в установщике. Когда вы изменили значение textBox, вызывается фактически установщик, и он снова устанавливает «Some Name» в качестве propertyValue, так что кажется, что оно не изменяется в пользовательском интерфейсе. Поставьте _name = value и все будет работать так, как вы ожидали,

...