Почему привязка не работает для TestClass, но установка того же свойства работает? - PullRequest
0 голосов
/ 15 октября 2019

Так что я, вероятно, не очень хорошо понимаю связывание, и я придумал этот тестовый проект, в котором я могу показать свою проблему простым способом. Где мне нужно изменить проект, чтобы привязка обновляла представление?

Вот мой проект на тот случай, если вы хотите его загрузить: https://1drv.ms/u/s!AqdZJMIRBGu7z1V8K1jXN9BQWFQ-?e=oxdlhL

А вот код:

TestClass.cs:

 public class TestClass
 {
     public string Text { get; set; } = "Default";
 } 

TestClassView.xaml:

<UserControl x:Class="Test.TestClassView" ...>
   <Grid>
      <TextBlock Text="{Binding TestClass.Text}" />
   </Grid>
</UserControl>

TestClassView.xaml.cs:

public partial class TestClassView : UserControl
{
    public TestClass TestClass 
    {
        get { return (TestClass)GetValue(TestClassProperty); }
        set { SetValue(TestClassProperty, value); }
    }
    public static readonly DependencyProperty TestClassProperty = DependencyProperty.Register("TestClass", typeof(TestClass), typeof(TestClassView), new PropertyMetadata(new TestClass() { Text = "Default"}));

    public TestClassView()
    {
        InitializeComponent();

        DataContext = this;
    }

MainWindow.xaml:

<Window x:Class="Test.MainWindow" ...>
   <Grid>
      <WrapPanel Margin="10">
         <Button Content="Test" Click="Button_Click" />
         <local:TestClassView x:Name="TestClassView" TestClass="{Binding TestClass}" />
      </WrapPanel>
   </Grid>
</Window>

MainWindow.xaml.cs:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private TestClass testClass = new TestClass() { Text = "DefaultProperty" };
    public TestClass TestClass { get { return testClass; } set{ testClass = value; OnPropertyChanged(); } }
    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        TestClass = new TestClass() { Text = "Test1" };
        MessageBox.Show("Test 1 Done");
        await Task.Delay(1000);
        TestClassView.TestClass = new TestClass() { Text = "Test2" };
        MessageBox.Show("Test 2 Done");
    }
}

Поэтому, когда я нажимаю на кнопку, сначала я устанавливаю экземпляр TestClass MainWindow, который связан с вызовом DependencyProperty TestClassView. TestClass. Я также вызываю PropertyChanged, но свойство TestClassView не обновляется.

Через секунду во второй части обработчика события click фактически устанавливается свойство TestClassView TestClass, и представление будет обновлено.

Так как поменять код, чтобы привязка работала?

Ответы [ 2 ]

1 голос
/ 15 октября 2019

В вашей привязке вы привязываете к свойству TestClass:

<TextBlock Text="{Binding TestClass.Text}" />

, поэтому для свойства необходимо вызвать PropertyChanged. Бесполезно поднимать его с какого-то другого предмета.

private string text = "Default";
public string Text 
{ 
    get => return this.text;
    set 
    { 
        this.text = value;
        this.NotifyPropertyChanged();
    } 
 } 
1 голос
/ 15 октября 2019

Я бы сказал, что TestClassView не нуждается в TestClass свойстве. Его использование такое же, как у свойства DataContext.

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

<UserControl x:Class="Test.TestClassView" ...>
   <Grid>
      <TextBlock Text="{Binding Text}" />
   </Grid>
</UserControl>

и Window:

<Window x:Class="Test.MainWindow" ...>
   <Grid>
      <WrapPanel Margin="10">
         <Button Content="Test" Click="Button_Click" />
         <local:TestClassView x:Name="TestClassView" DataContext="{Binding TestClass}" />
      </WrapPanel>
   </Grid>
</Window>

public partial class MainWindow : Window, INotifyPropertyChanged
{
    private TestClass testClass = new TestClass() { Text = "DefaultProperty" };
    public TestClass TestClass { get { return testClass; } set { testClass = value; OnPropertyChanged(); } }

    public MainWindow()
    {
        InitializeComponent();
        DataContext = this;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName]string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        TestClass = new TestClass() { Text = "Test1" };
        MessageBox.Show("Test 1 Done");
        await Task.Delay(1000);
        TestClass = new TestClass() { Text = "Test2" };
        MessageBox.Show("Test 2 Done");
    }
}

В исходном примере привязка не работает, поскольку элемент управления TestClassView прерывает распространение DataContextустановив DataContext = this; в конструкторе. Это плохая практика, которая приводит к большему количеству проблем, чем решает.

Установка DataContext = this; в Window приемлема для упражнений, так как она позволяет быструю настройку, нообычно представления используют отдельные классы ViewModel для DataContext.

...