PropertyChangedCallback для DependencyProperty срабатывает только один раз

У меня точно такая же проблема, как у этого парня на форуме Silverlight, и принятый ответ: :

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

Это отличное объяснение, но не ответ на решение этой проблемы. Я могу найти в Google много предложений для WPF, но не для Silverlight.

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

public partial class MyGrid : UserControl
{
    public MyGrid()
    {
        InitializeComponent();
    }

    public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register(
          "Shapes", typeof(ObservableCollection<ModelItem>), typeof(MyGrid), new PropertyMetadata(OnShapesPropertyChanged));

    public ObservableCollection<ModelItem> Shapes
    {
        private get { return (ObservableCollection<ModelItem>)GetValue(ShapesProperty); }
        set { SetValue(ShapesProperty, value); }
    }

    private static void OnShapesPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
    {
        ((MyGrid)o).OnShapesPropertyChanged(e); //Fire Only Once
    }

    private void OnShapesPropertyChanged(DependencyPropertyChangedEventArgs e)
    {
        dg.ItemsSource = e.NewValue as ObservableCollection<ModelItem>;
    }


}

//--------
public class ViewModel : INotifyPropertyChanged
{
    public Model Model { get; set; }
    public RelayCommand cmd;
    public ObservableCollection<ModelItem> ModelItemCollection
    {
        get
        {
            return  Model.ModelItem;
        }
    }

    public ViewModel()
    {
        Model = new Model();
        Model.PropertyChanged += Model_PropertyChanged;
    }

    void Model_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {

        System.Diagnostics.Debug.WriteLine(e.PropertyName);

        if (PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs("ModelItemCollection"));
        }
    }

    public ICommand AddCmd
    {
        get { return cmd ?? (cmd = new RelayCommand(a => Model.ModelItem.Add(new ModelItem {Name = "asd"}))); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

///----------------------

public class Model: INotifyPropertyChanged
{
    public ObservableCollection<ModelItem> ModelItem { get; set; }

    public Model()
    {
        ModelItem = new ObservableCollection<ModelItem>();
        ModelItem.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(ModelItem_CollectionChanged);
    }

    void ModelItem_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs("ModelItem"));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

public class ModelItem
{
    public String Name { get; set; }
}

Даже при явном вызове PropertyChanged() ничего не обновляется.

Каков обходной путь, чтобы сообщить DependencyProperty, что в ObservableCollection есть измененные элементы?


person Patrick Desjardins    schedule 07.09.2011    source источник
comment
Я думаю, что то, что вы здесь видите, связано с путаницей в использовании ObservableCollection. Вы не видите событие изменения свойства, потому что сама ObservableCollection не изменяется после ее инициализации, а вы собираетесь добавлять или удалять элементы внутри ObservableCollection. Таким образом, свойство (то есть ObservableCollection) не изменяется само по себе и не вызывает событие. Но если вы привязываетесь к ObservableCollection (для ItemSource или чего-то еще), вы должны получать изменения при добавлении или удалении элементов из коллекции. Я неправильно понимаю вашу проблему?   -  person Kevek    schedule 07.09.2011
comment
Я могу без проблем добавить элемент и увидеть, как он отображается на экране. Моя проблема касается только того факта, что DependencyProperty не срабатывает, а значение не установлено. Вот почему в Model_PropertyChanged я явно добавил PropertyChanged(this, new PropertyChangedEventArgs(ModelItemCollection), поэтому при добавлении элемента этот метод (Model_PropertyChanged) запускается, но все же: я хочу, чтобы ModelItemCollection перепривязывалась. Этого не происходит.   -  person Patrick Desjardins    schedule 07.09.2011
comment
Вы когда-нибудь пытались использовать UpdateTarget()?   -  person Tigran    schedule 07.09.2011
comment
@ Даок А, понятно. Тогда я считаю, что вам следует последовать совету Тиграна и попробовать UpdateTarget, вот пример, чтобы вы могли увидеть, как это реализовать.   -  person Kevek    schedule 07.09.2011
comment
С какого объекта? Можно поточнее пожалуйста, я попробую.   -  person Patrick Desjardins    schedule 07.09.2011
comment
Хорошо, но ViewModel, содержащая коллекцию, не имеет элемента управления представлением. Он использует Binding для взаимодействия с представлением.   -  person Patrick Desjardins    schedule 07.09.2011


Ответы (1)


Псевдокод:

BindingOperations.GetBindingExpressionBase(dependencyObject, dependencyProperty).UpdateTarget();

Посмотрите здесь: принудительное обновление привязки WPF...

Попробуйте это, обычно работает :)

person Tigran    schedule 07.09.2011
comment
Мне все еще нужно что-то, чтобы уведомить представление о том, что модель изменилась. Этот вызов может быть выполнен только на стороне представления, поскольку для этого требуется ссылка на объект и свойство. - person Patrick Desjardins; 08.09.2011
comment
@Daok это действительно обновляет представление. Этот вызов уведомляет все цели, привязанные к исходному свойству зависимостей, о том, что им необходимо обновить, чтобы получить новую информацию. - person Kevek; 08.09.2011
comment
dependencyObject и dependencyProperty находятся в представлении. Простите меня за мое невежество, но как я могу вызвать BindingOperations.GetBindingExpressionBase с двумя вещами, к которым у меня нет доступа (изменения выполняются на стороне модели)? - person Patrick Desjardins; 08.09.2011
comment
@Daok: правильно, изменения сделаны на стороне модели, но у вас есть (я надеюсь) ModelView, который должен быть мостом между моделью и представлением. Поместите этот код в область ModelView. - person Tigran; 08.09.2011
comment
Да, у меня есть ModelView, но этот не знает, что такое View, он просто предоставляет свойство, которое View будет привязывать к нему... - person Patrick Desjardins; 08.09.2011