Не удается получить внутреннюю привязку XAML для работы со свойством зависимостей


У меня есть пользовательский элемент управления "CtrlComments", этот элемент управления имеет следующий XAML (это супер basic).

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:wpftoolkit="http://schemas.microsoft.com/wpf/2008/toolkit" 
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" 
x:Name="ucRoot">
<Grid>
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="ID: " />
        <TextBlock Text="{Binding Path=Deployment.Id}" />
    </StackPanel>
</Grid>
Код, стоящий за этим, выглядит следующим образом, это голые основы, чтобы заставить элемент управления функционировать. Ключ-это DependencyObject typeof (DeploymentDto), который имеет свойство int с именем Id, которое мы хотим показать в нашем окне согласно приведенной выше привязке XAML.
public partial class CtrlComments : UserControl, INotifyPropertyChanged
{
    public static readonly DependencyProperty DeploymentProperty =
        DependencyProperty.Register("Deployment", typeof(DeploymentDto),
                                    typeof(CtrlComments), new PropertyMetadata(new DeploymentDto()));

    public DeploymentDto Deployment
    {
        get
        {
            return (DeploymentDto)GetValue(DeploymentProperty);
        }
        set
        {
            SetValue(DeploymentProperty, value);
            OnPropertyChanged(new PropertyChangedEventArgs("Deployment"));
        }
    }

    public CtrlComments()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, e);
    }
}

Наша проблема заключается в том, Несмотря на то, что привязка между родительским элементом управления и моим пользовательским элементом управления через свойство dependency работает (проверено) и метод OnPropertyChanged срабатывает, TextBlock в моем XAML не обновляется.

Я заметил, что при запуске метода OnPropertyChanged обработчик событий null означает, что никто не уведомляется о том, что произошло изменение свойства.

Хотя я не понимаю, почему это так. Если бы вы могли помочь объяснить, в чем мы ошибаемся, это было бы чрезвычайно ценно.

Спасибо!

1   2   2015-03-05 09:14:49

1 ответ:

Я попытался воспроизвести вашу проблему и, делая это, я понял, что проблема для меня была в следующей строке в CtrlComments:

this.DataContext = this;

Отбрасывание этой строки просто заставило меня работать. Также обратите внимание (как написал @Aron в комментариях), что OnPropertyChanged из INotifyPropertyChanged не следует вызывать, находясь в сеттере DependencyProperty. По крайней мере, мне вообще не нужно внедрять INPC.

В файле XAML, где вы используете UserControl, скорее всего, будет еще один DataContext set (на более высоком уровне, возможно, в Window), и поэтому я предполагаю, что он не наследуется пользовательскому элементу управления, если уже установлен там (или перезаписан). Ниже приведен мой рабочий код, но, возможно, я неправильно понял, что именно вы делаете. Если это так, пожалуйста, расширьте свой вопрос, чтобы включить, как вы используете UserControl, так как это ключ к ответу на вопрос, если это не работает :)

CtrlComments.xaml:

<UserControl x:Class="WpfApplication1.CtrlComments"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <StackPanel Orientation="Horizontal">
      <TextBlock Text="ID: "/>
      <TextBlock Text="{Binding Path=Deployment.Id}"/>
    </StackPanel>
  </Grid>
</UserControl>

CtrlComments.код XAML.cs:

namespace WpfApplication1
{
  public partial class CtrlComments : UserControl
  {
    public static readonly DependencyProperty DeploymentProperty =
      DependencyProperty.Register("Deployment", typeof(DeploymentDto), typeof(CtrlComments), new PropertyMetadata(new DeploymentDto { Id = 5 }));

    public DeploymentDto Deployment
    {
      get { return (DeploymentDto)GetValue(DeploymentProperty); }
      set
      {
        SetValue(DeploymentProperty, value);
      }
    }

    public CtrlComments()
    {
      InitializeComponent();
    }
  }
}

Главное окно.xaml:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        xmlns:local="clr-namespace:WpfApplication1"
        DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <StackPanel>
    <local:CtrlComments x:Name="testUC" Height="100" Deployment="{Binding Deployment}"/>
    <Button Click="Button_Click" Height="50" Width="100"/>
  </StackPanel>
</Window>

Главное окно.код XAML.cs:

namespace WpfApplication1
{
  public partial class MainWindow : Window, INotifyPropertyChanged
  {
    public MainWindow()
    {
      InitializeComponent();
    }

    private DeploymentDto deployment = new DeploymentDto { Id = 2 };
    public DeploymentDto Deployment
    {
      get { return deployment; }
      set { deployment = value; OnPropertyChanged("Deployment"); }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
      Deployment = new DeploymentDto { Id = new Random().Next(100) };
    }

    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string propName)
    {
      if (PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propName));
      }
    }
  }
}

DeploymentDto:

public class DeploymentDto
{
  public int Id { get; set; }
}

Довольно некрасиво привязывать MainWindow.DataContext к своему коду, но поскольку он используется только для примера, я надеюсь, что это нормально :)