Здесь новичок. Я пытался осмыслить привязку данных и хотел попробовать двухстороннюю привязку флажка в представлении к логическому значению в отдельном классе, который я назвал «Состояние». Дело в том, чтобы гарантировать, что они всегда синхронизированы.
Итак, я установил флажок в представлении и привязал его к вышеупомянутому логическому свойству в классе State, вместе с кнопкой, которая обходит флажок и напрямую переключает логическое свойство (метко названное «Ниндзя!»). Дело в том, чтобы проверить, что привязка данных флажка реагирует на изменение свойства. Однако я не могу понять, как должен вызываться метод OnPropertyChanged при изменении свойства.
Вот что у меня есть на данный момент:
<CheckBox x:Name="checkBox" Content="CheckBox" HorizontalAlignment="Left" Margin="232,109,0,0" VerticalAlignment="Top" IsChecked="{Binding Checked, Mode=TwoWay}"/>
<Button x:Name="button" Content="Ninja!" HorizontalAlignment="Left" Margin="228,182,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
И код для класса "State", который я сделал:
namespace TestTwoWayBinding
{
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged(Checked);
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Checked));
}
}
}
}
И код программной части в представлении для инициализации и обработки событий:
namespace TestTwoWayBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private State _state;
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
}
private void button_Click(object sender, RoutedEventArgs e)
{
_state.Toggle();
}
}
}
Насколько я понимаю, OnPropertyChanged ожидает String propertyName, но я не знаю, к чему это приведет. Когда я ввожу имя свойства (Checked), это, естественно, относится к логическому, а не к строке. Что я не получаю? А что еще я делаю не так, ведь флажок не регистрирует изменение свойства, когда я меняю его с помощью кнопки?
3 ответа
Два ответа, в которых предлагается передать строковый литерал "Checked"
, будут работать, но ИМХО - не лучший способ это сделать. Вместо этого я предпочитаю использовать [CallerMemberName]
при реализации метода OnPropertyChanged()
. (Я понятия не имею, о чем этот третий ответ ... похоже, он не имеет ничего общего с этим вопросом, и я предполагаю, что он был просто скопирован / вставлен из другого места).
Вот пример того, как я бы написал ваш класс State
:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
public void Toggle()
{
Checked = !Checked;
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Ключевым моментом здесь является то, что параметр, помеченный как [CallerMemberName]
, будет автоматически заполнен правильным значением от вызывающей стороны, просто не передавая никакого значения. Значение по умолчанию null
присутствует только для того, чтобы компилятор разрешил вызывающей стороне не передавать значение.
Обратите внимание, что я также упростил метод Toggle()
. Нет необходимости использовать оператор if
для преобразования одного значения bool
в другое; вот для чего нужны булевы операторы.
Я также изменил метод OnPropertyChanged()
, чтобы он стал потокобезопасным, т. Е. Не вылетал, если какой-то код отменил подписку последнего обработчика от события PropertyChanged
между временем, когда поле события сравнивается с {{X2} } и время фактического возникновения события. Как правило, это не проблема, поскольку к этим свойствам почти всегда обращаются только из одного потока, но от этого достаточно легко защититься, и это хорошая привычка.
Обратите внимание, что в C # 6 у вас есть возможность просто написать PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
для тела метода. Не все еще используют новый компилятор 100% времени, поэтому я просто упомянул это как необязательный выбор для вас.
Естественно, вам также необходимо правильно установить DataContext
, как показано в одном из других ответов:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
Хотя лично я не уверен, что возился бы с конструктором. Похоже, у вас нет другого кода, который бы устанавливал checkBox.IsChecked
, поэтому мне кажется, что вы в любом случае всегда будете получать значение по умолчанию. Кроме того, вы не можете создать свой класс модели представления в XAML, если у него нет параметризованного конструктора; в будущем вы можете захотеть настроить свой DataContext
таким образом. Например.:
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
И в конструкторе окна:
public MainWindow()
{
InitializeComponent();
_state = (State)this.DataContext;
}
См. Также соответствующие вопросы и ответы Автоматически INotifyPropertyChanged. Вопрос действительно о чем-то другом - они хотят реализовать интерфейс без необходимости явно писать что-либо в установщике свойств - но, что бы там ни было, ответы, которые они получили, действительно больше касаются вашего сценария, где это просто вопрос < em> упрощение реализации средства задания свойств вместо того, чтобы делать его полностью автоматическим.
Должен признаться, я бы подумал, что уже был другой вопрос, с помощью которого можно было бы отметить ваш как дубликат. И я нашел много связанных вопросов. Но ничего, что напрямую фокусировалось бы на том, «как мне реализовать и использовать модель представления, которая реализует INotifyPropertyChanged
?», О чем действительно, кажется, и заключается ваш вопрос.
Приложение :
Я провел еще несколько поисков, и хотя ни один из них не выглядел как бы точным дубликатом как таковой, все они содержат полезную информацию, которая помогает решить вопрос о реализации INotifyPropertyChanged
:
Использование атрибутов… INotifyPropertyChanged
INotifyPropertyChanged для модели и модели просмотра
BindableBase против INotifyChanged
Как написать «ViewModelBase» в MVVM (WPF)
Метод OnPropertyChanged
ожидает в качестве аргумента имя свойства Checked
- в данный момент вы передаете его значение!
Это означает, что измените объявление свойства Checked
на:
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
Вы очень близки. Вам нужно внести 2 небольших изменения, и ваш тест работает:
- Назначьте
DataContext
вашего окна переменной _state. - Поместите строку «Проверено» в
OnPropertyChanged
и передайтеpropertyName
вPropertyChangedEventArgs
в методеOnPropertyChanged
.
Итак, ваш MainWindow ctor становится:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
А файл класса State выглядит так:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Как новичок, я рекомендую вам узнать больше о Model-View-ViewModel MVVM Шаблон дизайна. Это общий шаблон с WPF, который помогает разделить проблемы (не допуская бизнес-логики к логике пользовательского интерфейса).
Похожие вопросы
Связанные вопросы
Новые вопросы
c#
C# (произносится как «see Sharp») — это высокоуровневый мультипарадигменный язык программирования со статической типизацией, разработанный Microsoft. Код C# обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, которое включает в себя .NET, .NET Framework, .NET MAUI и Xamarin среди прочих. Используйте этот тег для ответов на вопросы о коде, написанном на C#, или о формальной спецификации C#.