#c# #wpf #data-binding
#c# #wpf #привязка данных
Вопрос:
В настоящее время я пытаюсь привязать свойство enable двух кнопок к одному объекту, объявленному в коде, но, похоже, я не могу заставить его работать. Мой код до сих пор:
XAML:
<UserControl x:Class="TestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TestApp"
mc:Ignorable="d">
<Button Name="AddButton" Background="DarkBlue" Foreground="LightGray" BorderBrush="LightGray"
BorderThickness="2" Content=" "
Click="AddPopup" ToolTip="Add Content" IsEnabled="{Binding User}"/>
<Button ToolTip="Save" Name="SaveButton" Click="Connect" Background="DarkBlue"
Foreground="LightGray"
BorderBrush="LightGray" BorderThickness="2" IsEnabled="{Binding User}">
<Image Source="/Resources/save.png"/>
</Button>
C#:
public partial class MainWindow : UserControl
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
StartWork();
MainBackgroundWork();
EndWork();
}
private void StartWork(){
//other code here
User = true;
}
private void EndWork(){
//other code here
User = false;
}
private bool _User;
public bool User { get { return _User; } set { _User = value; EditOrNotChanged(); } }
public event PropertyChangedEventHandler PropertyChangedContent;
protected void EditOrNotChanged([CallerMemberName] string propertyName = "")
{
PropertyChangedContent?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
startWork() включает обе кнопки, но EndWork(), похоже, не отключает их.
Я просмотрел несколько руководств, но, похоже, не могу определить, что не так. Любая помощь будет оценена.
Спасибо.
Ответ №1:
startWork() включает обе кнопки, но EndWork(), похоже, не отключает их
На основании чего вы делаете это утверждение? Потому что опубликованный вами код завершит оба вызова еще до того, как окно будет показано пользователю. Как бы они даже сказали, что включенное состояние любой кнопки изменилось? В любом случае…
Проблема, наиболее тесно связанная с вашей заявленной проблемой, заключается в том, что, хотя контекст данных является объектом window, этот объект не реализуется INotifyPropertyChanged
. У него есть событие, которое оно вызывает, но а) это событие не вызывается PropertyChanged
, и б) даже если бы оно было, без указания интерфейса в объявлении, фреймворк никогда не стал бы искать событие.
Следующее немного ближе к правильному, но, что важно, теоретически решает конкретную проблему привязки данных:
public partial class MainWindow : UserControl, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
StartWork();
MainBackgroundWork();
EndWork();
}
private void StartWork(){
//other code here
User = true;
}
private void EndWork(){
//other code here
User = false;
}
private bool _User;
public bool User { get { return _User; } set { _User = value; EditOrNotChanged(); } }
public event PropertyChangedEventHandler PropertyChanged;
protected void EditOrNotChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Обратите внимание, что приведенное выше не устраняет другие серьезные проблемы с кодом:
- A
UserControl
никогда не должен устанавливать свой собственныйDataContext
. Этот контекст предназначен только для установки клиентского кода. - Свойства, объявленные в a
UserControl
, должны быть реализованы как свойства зависимостей, а неINotifyPropertyChanged
свойства. Если вы хотите последнее, вы можете создать отдельный объект модели представления, который реализуетINotifyPropertyChanged
, а затем использовать этот объект модели представления для контекста данных одного или нескольких элементов внутриUserControl
(но неUserControl
самого … см. Предыдущий пункт). - Вы не должны выполнять какие-либо длительные операции (например, что-то, что выиграло бы от изменения включенного состояния любых элементов управления) в конструкторе. Ограничьте конструктор только тем, что требуется исключительно для инициализации. Вы можете подписаться на
Loaded
событие и в этом обработчике запустить любую работу после инициализации, которая может занять много времени. - Вы не должны блокировать код при выполнении какой-либо длительной операции. Было бы намного лучше выполнить этот код в
async
методе, используяawait Task.Run(...)
для самой длительной операции.