Приостановить привязку элементов управления
У меня есть ряд элементов управления, которые привязываются к значениям, которые меняются каждую секунду или около того. Время от времени мне нужно "приостановить" элементы управления, чтобы они не обновляли свои привязки данных (в любом направлении). Затем мне нужно "отключить" элементы управления, чтобы они могли обновлять источник данных с их значениями и получать в будущем обновления от источника как обычно. Как это сделать?
Связывание с образцом:
<TextBox Text="{Binding UpdateSourceTrigger=LostFocus, Mode=TwoWay, Path=myData}">
Ответы
Ответ 1
Вам необязательно приостанавливать привязку. Другой, и, возможно, более простой способ сделать это - приостановить уведомление об изменении в модели представления. Например:
private HashSet<string> _ChangedProperties = new HashSet<string>();
private void OnPropertyChanged(string propertyName)
{
if (_Suspended)
{
_ChangedProperties.Add(propertyName);
}
else
{
PropertyChangedEventHandler h = PropertyChanged;
if (h != null)
{
h(this, new PropertyChangedEventArgs(propertyName));
}
}
}
private bool _Suspended;
public bool Suspended
{
get { return _Suspended; }
set
{
if (_Suspended == value)
{
return;
}
_Suspended = value;
if (!_Suspended)
{
foreach (string propertyName in _ChangedProperties)
{
OnPropertyChanged(propertyName);
}
_ChangedProperties.Clear();
}
}
}
Это будет (если он будет отлажен и протестирован, что я еще не сделал) перестает поднимать события PropertyChanged
, когда Suspended
установлен на true
, а когда Suspended
установлен на false
снова, он будет поднять событие для каждого свойства, которое было изменено во время его приостановки.
Это не остановит изменения привязанных элементов управления от обновления модели представления. Я утверждаю вам, что если вы позволяете пользователю редактировать свойства на экране одновременно с тем, что вы меняете их в фоновом режиме, вам нужно взглянуть более внимательно, и это не является обязательным.
Ответ 2
Чтобы иметь дело с источником, установите UpdateSourceTrigger
как Explicit
.
<TextBox Name="myTextBox" Text="{Binding UpdateSourceTrigger=Explicit, Mode=TwoWay, Path=myData}">
Затем в коде за ссылкой используется служба, которая может иметь дело с фактическим обновлением в соответствии с вашими условиями.
BindingExpression be = myTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
Это позволит вам указать, в какой момент данные вернутся к источнику из целевого объекта.
Целевая задача может быть решена путем обращения к той же службе, на которую ссылается, которая знает, когда вызывать событие INotifyPropertyChanged.PropertyChanged
в вашей ViewModel.
class Data : INotifyPropertyChanged
{
Manager _manager;
public Data(Manager manager)
{
_manager = manager;
}
public event PropertyChangedEventHandler PropertyChanged;
String _info = "Top Secret";
public String Information
{
get { return _info; }
set
{
_info = value;
if (!_manager.Paused)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Information"));
}
}
}
}
Ответ 3
Прежде всего вам нужно создать явное привязку:
Binding binding = new Binding("Content");
binding.Source = source;
binding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
binding.Mode = BindingMode.TwoWay;
txtContent.SetBinding(TextBox.TextProperty, binding);
Затем, когда вам нужно приостановить привязку twoway, вам нужно уничтожить старую привязку и создать новую привязку oneway с явным триггером (в этом случае источник привязки не будет обновляться при изменении какого-либо свойства):
BindingOperations.ClearBinding(txtContent, TextBlock.TextProperty);
Binding binding = new Binding("Content");
binding.Source = source;
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
binding.Mode = BindingMode.OneWay;
txtContent.SetBinding(TextBox.TextProperty, binding);
Когда вам нужно возобновить привязку twoway, вы можете указать явный источник обновлений (если вам это нужно), чем уничтожить привязку oneway и создать привязку twoway.
BindingExpression be = txtContent.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
BindingOperations.ClearBinding(txtContent, TextBlock.TextProperty);
Binding binding = new Binding("Content");
binding.Source = source;
binding.UpdateSourceTrigger = UpdateSourceTrigger.LostFocus;
binding.Mode = BindingMode.TwoWay;
txtContent.SetBinding(TextBox.TextProperty, binding);
Ответ 4
Если вы сохраняете ссылку на представление в классе контроллера, вы можете запустить событие из модели представления, когда хотите приостановить сбор данных, в котором контроллер очистит DataContext представления. и когда вы готовы снова начать отправку принимаемых данных, reset DataContext Views в Model View.
Ответ 5
Если элемент управления, который вы хотите приостановить, имеет собственный DataContext (ViewModel), просто сохраните его и отключите DataContext.
Если элемент управления имеет унаследованный DataContext, установка контроля DataContext на null блокирует наследование. Затем, чтобы возобновить связывание обновлений, вы используете метод ClearValue, чтобы очистить DataContext DependencyProperty, чтобы снова наследовать наследование.
Вы можете получить фантазию и использовать VisualBrush для снятия снимок экрана, который вы приостанавливаете, прежде чем очистить свой DataContext, поэтому пользователь не видит, что элемент управления пуст.
Ответ 6
Мое решение закончилось следующим образом, чтобы предотвратить обновление текста, пока пользователь пытается его изменить.
XAML:
<TextBox Grid.Row="0" Grid.Column="1" TextAlignment="Right" VerticalAlignment="Center" Text="{Binding Path=MinimumValueInDisplayUnit, StringFormat=0.########}" MinWidth="100" Margin="4" GotFocus="TextBox_OnGotFocus" LostFocus="TextBox_OnLostFocus"/>
<TextBox Grid.Row="0" Grid.Column="2" TextAlignment="Right" VerticalAlignment="Center" Text="{Binding Path=MaximumValueInDisplayUnit, StringFormat=0.########}" MinWidth="100" Margin="4" GotFocus="TextBox_OnGotFocus" LostFocus="TextBox_OnLostFocus"/>
Код позади:
private void TextBox_OnGotFocus([CanBeNull] object sender, [CanBeNull] RoutedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb == null) return;
BindingExpression expression = tb.GetBindingExpression(TextBox.TextProperty);
if (expression == null) return;
// disable updates from source
BindingOperations.ClearBinding(tb, TextBlock.TextProperty);
tb.SetBinding(TextBox.TextProperty, new Binding(expression.ParentBinding.Path.Path) { Mode = BindingMode.OneWayToSource, UpdateSourceTrigger = UpdateSourceTrigger.Explicit , FallbackValue = tb.Text});
}
private void TextBox_OnLostFocus([CanBeNull] object sender, [CanBeNull] RoutedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb == null) return;
BindingExpression expression = tb.GetBindingExpression(TextBox.TextProperty);
if (expression == null) return;
// send current value to source
expression.UpdateSource();
// enable updates from source
BindingOperations.ClearBinding(tb, TextBlock.TextProperty);
tb.SetBinding(TextBox.TextProperty, new Binding(expression.ParentBinding.Path.Path) { Mode = BindingMode.TwoWay, UpdateSourceTrigger = UpdateSourceTrigger.LostFocus });
}
Обратите внимание, что я назначаю текущий текст как резервное значение привязки OneWayToSource, чтобы иметь начальное значение (иначе текстовое поле будет пустым после сфокусировки)