Set dependency properties in XAML when the base cl

2020-03-03 09:54发布

问题:

As per the title really, how can you set a dependency property in XAML when the base class is generic? When trying to do this I get a NullReferenceException, setting the property from code behind works fine. It also works when the base class is not generic. I'm using .NET4

Here is some sample code to demonstrate:

WindowBase.cs

using System.Windows;

namespace GenericDependencyPropertyTest
{
    public class WindowBase<ViewModel> : Window
    {
        public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
        "Header", typeof(string), typeof(WindowBase<ViewModel>), new PropertyMetadata("No Header Name Assigned"));

        public string Header
        {
            get { return (string)GetValue(HeaderProperty); }
            protected set { SetValue(HeaderProperty, value); }
        }
        protected virtual ViewModel Model { get; set; }
    }
}

MainWindow.xaml

<local:WindowBase x:Class="GenericDependencyPropertyTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:GenericDependencyPropertyTest"
        x:TypeArguments="local:IMyViewModel"
        Title="MainWindow" Height="350" Width="525" Header="Test">
    <Grid>

    </Grid>
</local:WindowBase>

MainWindow.xaml.cs

namespace GenericDependencyPropertyTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : WindowBase<IMyViewModel>
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        protected override IMyViewModel Model
        {
            get
            {
                return base.Model;
            }
            set
            {
                base.Model = value;
            }
        }
    }
}

IMyViewModel.cs

namespace GenericDependencyPropertyTest
{
    public interface IMyViewModel
    {
    }
}

回答1:

My guess is that the problem lies with the owner type of the dependency property (typeof(WindowBase<ViewModel>). Each closed generic type will be a completely different runtime type so while the compiler is happy that the property exists the WPF runtime can't find it when it looks the property up against the (different) type in its internal storage.

As you found, the non-generic class works because the owner type and the runtime type are the same.

You can get the behaviour you want by pushing the DPs to a non-generic base but still derive your views from a generic class to get the strongly typed models

public class WindowBase : Window
{
    public static readonly DependencyProperty HeaderProperty = DependencyProperty.Register(
        "Header", typeof(string), typeof(WindowBase), new PropertyMetadata("No Header Name Assigned"));

    public string Header
    {
        get { return (string)GetValue(HeaderProperty); }
        protected set { SetValue(HeaderProperty, value); }
    }
}

public class WindowBase<ViewModel> : WindowBase
{
    protected ViewModel Model { get; set; }
}