Привязка DataTemplate дает цели неверное / неустановленное / нулевое значение - PullRequest
0 голосов
/ 14 ноября 2018

Я хотел бы распространять DataContext вверх от динамически созданного DataTemplate, размещенного с ContentControl, такого как следующий:

var myFactory = new FrameworkElementFactory(controlTypeToDisplay);//= typeof(MyControl)
var dctxBinding = new Binding
{
    Path = new PropertyPath("DataContext.Dctx"),
    Mode = BindingMode.OneWayToSource,
    RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(ContentControl), 1)
};
myFactory.SetBinding(FrameworkElement.DataContextProperty, dctxBinding);

return new DataTemplate() { VisualTree = myFactory };

Однако результат такой привязки равен nullхотя DataContext устанавливается в конструкторе MyControl.DataContext из MyControl определенно не устанавливается в ноль дальше, конструктор вызывается перед установщиком Dctx.Как я могу исправить привязку, чтобы свойства MyControl DataContext и Dctx всегда были синхронизированы?


Полный минимальный пример проблемы (при правильной работе должны отображаться два текстовых блока "FooBar")):

//MyControl.xaml
<Grid>
    <TextBlock Text="{Binding}"/>
</Grid>
//MyControl.xaml.cs
public MyControl()
{
    InitializeComponent();
    DataContext = "FooBar";
    this.DataContextChanged += MyControl_DataContextChanged;
}

private void MyControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
    MessageBox.Show("An unexpected change");
}

//MainWindow.xaml
<StackPanel>
    <ContentControl ContentTemplate="{Binding DataTemplate}"/>
    <TextBlock Text="{Binding Dctx, TargetNullValue='&lt;null&gt;'}" />
</StackPanel>

//MainWindow.xaml.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
    private Type controlTypeToDisplay = typeof(MyControl);
    public DataTemplate DataTemplate
    { get {/*see first listing*/ } }

    private object _dctx;
    public object Dctx
    {
        get { return _dctx; }
        set { _dctx = value; RaisePropertyChanged(); }
    }

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

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged([CallerMemberName]string caller = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
    }
}

1 Ответ

0 голосов
/ 14 ноября 2018

DataContext корневого элемента в ContentTemplate для ContentControl - это ContentControl 'Content.Свяжите свойство Content со своим DataContext:

<ContentControl Content="{Binding Dctx}" ContentTemplate="{Binding DataTemplate}"/>

Вы также должны установить DataContext самого ContentControl (или родительского окна) где-нибудь и добавить некоторый визуальный элемент кDataTemplate:

public partial class MainWindow : Window, INotifyPropertyChanged
{
    public DataTemplate DataTemplate
    {
        get {
            var myFactory = new FrameworkElementFactory(typeof(TextBlock));
            myFactory.SetBinding(TextBlock.TextProperty, new Binding(".")); //bind to the DataContext
            return new DataTemplate() { VisualTree = myFactory };
        }
    }

    private object _dctx = new object(); //set the Dxtc property somewhere
    public object Dctx
    {
        get { return _dctx; }
        set { _dctx = value; RaisePropertyChanged(); }
    }

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

    public event PropertyChangedEventHandler PropertyChanged;
    public void RaisePropertyChanged([CallerMemberName]string caller = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(caller));
    }

}
...