传递类型参数到附加的行为

4
我正在实现一个附加属性在WPF应用程序中。我需要传递类型参数到该行为,以便我可以调用SqliteBoundRow上的方法void NewRow(Table<T> table)。如果我在XAML中实例化对象,我将使用x:TypeArguments传递类型参数,但是当设置附加行为时,我不知道如何做到这一点,因为它使用静态属性。
附加行为的代码如下:
public abstract class SqliteBoundRow<T> where T : SqliteBoundRow<T>
{
    public abstract void NewRow(Table<T> table);
}

public class DataGridBehavior<T> where T:SqliteBoundRow<T>
{
    public static readonly DependencyProperty IsEnabledProperty;
    static DataGridBehavior()
    {
        IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled",
            typeof(bool), typeof(DataGridBehavior<T>),
            new FrameworkPropertyMetadata(false, OnBehaviorEnabled));
    }
    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }
    public static bool GetIsEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsEnabledProperty);
    }
    private static void OnBehaviorEnabled(DependencyObject dependencyObject,
        DependencyPropertyChangedEventArgs args)
    {
        var dg = dependencyObject as DataGrid;
        dg.InitializingNewItem += DataGrid_InitializingNewItem;
    }
    private static void DataGrid_InitializingNewItem(object sender,
        InitializingNewItemEventArgs e)
    {
        var table = (sender as DataGrid).ItemsSource as Table<T>;
        (e.NewItem as T).NewRow(table);
    }

}

XAML看起来像这样:

<DataGrid DataGridBehavior.IsEnabled="True">
    <!-- DataGridBehavior needs a type parameter -->
</DataGrid>

我的当前解决方案是将DataGridBehavior包装在一个派生类中,该派生类指定了类型参数。


这里有一个相关问题 - tdr
2个回答

6

最简单的解决方案是声明另一个Attached Property,但类型为Type,以保存参数值。在这种情况下,在设置IsEnabled Attached Property之前,您需要设置Type属性:

<DataGrid DataGridBehavior.TypeParameter="{x:Type SomePrefix:SomeType}" 
    DataGridBehavior.IsEnabled="True" ... />

再次查看您的代码,似乎您的IsEnabled属性除了向表中添加新行之外没有任何作用...在这种情况下,您可以使用TypeParameter Attached Property代替它,并使用该属性来添加新行。


如果我定义了一个附加属性TypeParameter,那么我就不能在DataGrid_InitializingNewItem中将其用作类型参数。也许可以通过反射来实现,但我宁愿不走这条路。 - tdr
这只是几行代码,我碰巧回答了一个问题,展示了如何从Type对象创建一个对象:在C#中定义集合时使用字符串作为类名 - Sheridan

3
我认为WPF没有提供一种优雅的语法方式来实现你想要的功能。所以我刚刚准备发表与Sheridan类似的答案。也就是说,你可以提供一个额外的Type属性来确定泛型类型。然而,Sheridan比我先写了。下面是一些使用反射实现此操作的示例代码:

Xaml

 <DataGrid behaviors:DataGridBehavior.InnerType="namespace:SqliteBoundRow"
      behaviors:DataGridBehavior.IsEnabled="True">
</DataGrid>

后端代码

public abstract class DataGridBehavior
{
    private static readonly ConcurrentDictionary<Type, DataGridBehavior> Behaviors = new ConcurrentDictionary<Type, DataGridBehavior>();
    public static readonly DependencyProperty IsEnabledProperty;
    public static readonly DependencyProperty InnerTypeProperty;
    static DataGridBehavior()
    {
        IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled",
            typeof(bool), typeof(DataGridBehavior),
            new FrameworkPropertyMetadata(false, OnBehaviorEnabled));

        InnerTypeProperty = DependencyProperty.RegisterAttached("InnerType",
            typeof(Type), typeof(DataGridBehavior));
    }
    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }
    public static bool GetIsEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsEnabledProperty);
    }

    public static void SetInnerType(DependencyObject obj, Type value)
    {
        obj.SetValue(InnerTypeProperty, value);
    }
    public static Type GetInnerType(DependencyObject obj)
    {
        return (Type)obj.GetValue(InnerTypeProperty);
    }

    private static void OnBehaviorEnabled(DependencyObject dependencyObject,
          DependencyPropertyChangedEventArgs args)
    {
        var innerType = GetInnerType(dependencyObject);
        if (innerType == null)
            throw new Exception("Missing inner type");

        var behavior = Behaviors.GetOrAdd(innerType, GetBehavior);
        behavior.OnEnabled(dependencyObject);
    }

    private static DataGridBehavior GetBehavior(Type innerType)
    {
        var behaviorType = typeof(DataGridBehavior<>).MakeGenericType(innerType);
        var behavior = (DataGridBehavior)Activator.CreateInstance(behaviorType);
        return behavior;
    }

    protected abstract void OnEnabled(DependencyObject dependencyObject);

}

public class DataGridBehavior<T> : DataGridBehavior
    where T : SqliteBoundRow
{
    protected override void OnEnabled(DependencyObject dependencyObject)
    {
        //dg.InitializingNewItem += DataGrid_InitializingNewItem;
    }

    private static void DataGrid_InitializingNewItem(object sender,
        InitializingNewItemEventArgs e)
    {
        //var table = (sender as DataGrid).ItemsSource as Table<T>;
        //(e.NewItem as T).NewRow(table);
    }

}

public class SqliteBoundRow
{
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接