C# (另一个)附加属性绑定不起作用

C# (另一个)附加属性绑定不起作用,c#,.net,wpf,xaml,C#,.net,Wpf,Xaml,起初我试图制作UWP应用程序,但后来意识到它对我来说太有限了,现在我尝试将现有代码移植到经典的WPF。不过,我使用的是.NET Core 3.1、Caliburn、Kinnara/ModernWpf、Microsoft.NETCore.Windows.ApiSets、Microsoft.Windows.SDK.Contracts和System.Windows.Interactivity.WPF,而不是最后一个。 所以,基本上,它应该为我提供相当平滑的WPF移植,但正如我所发现的,Caliburn

起初我试图制作UWP应用程序,但后来意识到它对我来说太有限了,现在我尝试将现有代码移植到经典的WPF。不过,我使用的是.NET Core 3.1、Caliburn、Kinnara/ModernWpf、Microsoft.NETCore.Windows.ApiSets、Microsoft.Windows.SDK.Contracts和System.Windows.Interactivity.WPF,而不是最后一个。 所以,基本上,它应该为我提供相当平滑的WPF移植,但正如我所发现的,Caliburn的附加属性视图。模型不起作用。我尝试创建自己的附加属性并将其绑定到VM的测试属性,但也没有成功,该属性从未被访问,附加属性的值为null

基本代码如下:

这不是实际情况,在实际情况中,我在NavigationView.ContentTemplate中使用ContentPresenter,但实际情况应该是相同的。我用ContentPresenter outside和NavigationView测试了ofc。另外,如果我使用TextBlock而不是ContentPresenter,那么绑定工作正常,并且附加属性的值与预期的一样。什么是错误的?是什么让ContentPresenter与众不同

视图模型:

XAML

应使用ContentControl替换ContentPresenter:


ContentPresenter仅用于在ControlTemplate中显示ContentControl的内容。它不会继承DataContext,这就是绑定不起作用的原因。

经过大量测试、试用和谷歌搜索,我找到了答案


Kinnara/ModernWpf使用的是ContentPresenter,它会删除DataContext。另外,它的ContentPresenter/ContentControl没有在模板到ContentTemplate、ContentTemplateSelector和ContentStringFormat中绑定,因此我设置的ContentTemplate从未实际使用过,因为它没有被控件绑定。修复源代码后,一切正常。

我发现的第一件事是我应该使用ContentControl而不是ContentPresenter。我不明白他们之间有什么区别。。。不过,如果我在NavigationView.ContentTemplate中使用ContentControl,附加的属性绑定仍然无法工作。但是,如果像本例中那样使用ContentControl,那么在外部,一切都很正常,属性都设置好了,视图也找到了。很多人喜欢Caliburn Micro,但根据我的经验,它的魔力只是让开发在某些东西不起作用时成为一种痛苦的体验。我强烈建议放弃它,使用更传统的MVVM方法。@Keithernet我刚刚尝试了更传统的MVVM方法,那是另一种痛苦的经历,因为WPF是纯粹的魔法。黑色的。我已经下载并引用了Caliburn的源代码,所以至少在那里我可以追踪到一个比我更有经验的XAML人员是如何完成事情的。但是,尽管如此,当我甚至不能让一些简单的案例正常工作时,它仍然是无用的。呃,而且看起来卡利本与此无关,因为正如我已经说过的,它都是附加属性,出于某种原因无法正常工作。如果caliburn的附件还没有初始化,那么它就可以正常工作,这太奇怪了。我一分钟前才找到它。我现在正在发布我的答案。为什么要发布我的答案的副本而不是接受它?再恰当不过了,因为这不是你答案的副本。实际上,对于我的案例,您的回答并不完整,因为正如我在问题中所说的,主要问题是NavigationView中的AttachedProperty,即使我将ContentPresenter替换为ContentControl也无法解决,因为我还向ContentTemplate添加了绑定。但如果你愿意的话,好吧,我会接受你的回答,不管怎样。
    public class ShellViewModel : Conductor<IScreen>.Collection.OneActive
    {
        public ShellViewModel()
        {
            ActiveItem = new SomeOtherViewModelDerivedFromScreen();
            Task.Delay(3000).ContinueWith(task => OnPropertyChanged(new PropertyChangedEventArgs("HelloWorld")));
            // This is getting called ok, though AFAIK it's not even necessary to raise PropertyChanged
        }
    ...

        public string HelloWorld => "Hello world"; // Breakpoint here, never gets called
    ...
    }
    public static class Test
    {
        public static readonly DependencyProperty TestProperty = 
            DependencyProperty.RegisterAttached("Test", typeof(object), typeof(Test), 
                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, 
                TestChangedCallback));

        public static object GetTest(UIElement element)
        {
            // Breakpoint here, never gets called
            return element.GetValue(TestProperty);
        }

        public static void SetTest(UIElement element, object value)
        {
            // Breakpoint here, never gets called
            element.SetValue(TestProperty, value);
        }
        private static void TestChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            // Breakpoint here, never gets called
        }
    }
<Window x:Class="RecipeCalculator.Views.ShellView"
        ... >
    <Grid>
        <ContentPresenter x:Name="TestCP" t:Test.Test="{Binding Path=HelloWorld}" Content="{Binding ActiveItem}"/>
    </Grid>
</Window>
public partial class ShellView : Window
    {
        public ShellView()
        {
            InitializeComponent();
            this.Loaded += OnLoaded;
        }

        private void OnLoaded(object sender, RoutedEventArgs e)
        {
            var val = TestCP.GetValue(ViewModels.Test.TestProperty);
            // Breakpoint here, val is null.
        }
    }
<ContentControl x:Name="TestCP" t:Test.Test="{Binding Path=HelloWorld}" 
                Content="{Binding ActiveItem}"/>