Привязка элемента управления WPF не всегда обновляет пользовательский интерфейс

#c# #wpf #data-binding

#c# #wpf #привязка к данным

Вопрос:

У меня есть кнопка, IsEnabled свойство которой привязано в моей модели представления к значению, указывающему, были ли изменены данные в текущем представлении. До сих пор эта привязка работала нормально, пока я не попытался добавить функциональность перетаскивания в ItemsControl в представлении.

Функция перетаскивания работает нормально и делает все, что должна. Когда он завершает любые необходимые манипуляции с данными, он устанавливает свойство модели представления IsModified true равным . Я проверил, что значение действительно установлено true равным .

Проблема, с которой я сталкиваюсь, заключается в том, что при изменении IsModified свойства из моего Drop метода свойство кнопки IsEnabled не обновляется; когда IsModified установлено значение true во время операции перетаскивания, кнопка остается отключенной. Если я нажимаю на кнопку, она внезапно обновляется и становится включенной, требуя, чтобы я нажал кнопку второй раз, чтобы действительно сделать то, что задумано.

Drop Метод вызывается при Drop событии элемента ItemsControl. Вызывается ли это из другого потока или что-то, что не информирует пользовательский интерфейс об изменении свойства? Я пытался найти вспомогательные документы, но у меня возникли небольшие проблемы.

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

Рассматриваемый код довольно прост.

XAML:

 <ItemsControl.ItemContainerStyle>
    <Style TargetType="ContentPresenter">
        <EventSetter Event="DragEnter" Handler="IcFields_DragEnter"/>
        <EventSetter Event="Drop" Handler="IcFields_Drop"/>
    </Style>
</ItemsControl.ItemContainerStyle>

...
...
...

<Button Margin="5" Padding="10,0,10,0" IsEnabled="{Binding IsModified}"
    Command="{Binding SaveChangesCommand}">Save Changes</Button>
 

где IcFields_Drop обработчик разрешает вносить изменения в модель и, в конце концов, устанавливает IsModified значение true .

Редактировать:

Вот сокращенный пример реализации. Используйте приведенный выше XAML в качестве XAML для этого примера.

C # — Code-Behind

 private void IcFields_Drop(object sender, DragEventArgs e)
{
    ViewModel.Drop();   
}
 

C # — Модель представления

 private bool isModified;
public bool IsModified
{
    get { return isModified; }
    set { SetProperty(ref isModified, value); }
}

public void Drop()
{
    //PSEUDO: Do some drag/drop logic for the items attached to the ItemsControl.
    ...
    ...

    IsModified = true;
}
 

Комментарии:

1. Попробуйте это IsEnabled="{Binding IsModified, UpdateSourceTrigger=PropertyChanged}"

2. К сожалению, @ikerbera не справился с задачей.

3. IsEnabled="{Binding IsModified, UpdateSourceTrigger=PropertyChanged}, Mode=OneWay" . Я обычно использую Mode=TwoWay , но в данном случае это кажется немного излишним. Взгляните на это

4. @ikerbera Mode=TwoWay — это бессмыслица, поскольку элемент управления никогда активно не обновляет свое свойство IsEnabled, следовательно, нет направления привязки от цели к источнику. UpdateSourceTrigger=PropertyChanged также бессмысленно, потому что это влияет только на привязки TwoWay или OneWayToSource.

5. Реализует ли ваша модель представления (поскольку код не показан) inotifypropertychanged ?

Ответ №1:

Вместо того, чтобы привязывать IsEnabled свойство, вы должны вернуть a bool из CanExecute метода вашей команды, чтобы указать Button , должно ли оно быть включено. Затем вы должны вызвать метод, который вызывает CanExecuteChanged событие команды в вашем Drop() методе.

Большинство ICommand реализаций включают RaiseCanExecuteChanged() метод или аналогичный, который вы можете вызвать, чтобы обновить статус команды:

 public void Drop()
{
    ...
    SaveChangesCommand.RaiseCanExecuteChanged();
}
 

Комментарии:

1. Это отличный момент. Я IsEnabled привязал, когда мне это действительно не нужно, так что я могу, по крайней мере, отказаться от этого. В настоящее RaiseCanExecuteChanged() время в моей реализации нет метода, поэтому я подумаю о его добавлении. На данный момент, однако, похоже, что что-то происходит в конце моего перетаскивания; он не обновляется до тех пор, пока не будет нажата в любом месте экрана. IsEnabled Изучаю это.

2. @pseudorian: вы могли бы взглянуть на то, как это реализовано, например, в MVVMLight .

3. Это действительно сработало для меня, спасибо! Я все еще не уверен, почему только Drop событие вызывает эту проблему, поэтому я собираюсь продолжить ее изучение, но, по крайней мере, это работает, и у меня есть немного лучшая ICommand реализация для работы.