I am implementing an attached behavior in a WPF application. I need to pass a type parameters to the behavior, so I can call a method void NewRow(Table<T> table)
on SqliteBoundRow
. If I was instantiating an object in XAML, I would pass a type parameters using x:TypeArguments
, but I don't see a way to do this when setting an attached behavior, because it uses a static property.
Code for the attached behavior looks like this:
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 looks like this:
<DataGrid DataGridBehavior.IsEnabled="True">
<!-- DataGridBehavior needs a type parameter -->
</DataGrid>
My current solution is to wrap DataGridBehavior in a a derived class which specifies the type parameters.
The simplest solution is for you to declare another Attached Property
, but of type Type
to hold the parameter value for you. In this case, you would set the Type
property before your IsEnabled Attached Property
:
<DataGrid DataGridBehavior.TypeParameter="{x:Type SomePrefix:SomeType}"
DataGridBehavior.IsEnabled="True" ... />
Looking again at your code, it seems as though your IsEnabled
property does nothing except adding a new row to your table... in that case, there's no reason why you couldn't replace it with the TypeParameter Attached Property
and use that one to add the new row instead.
I don't think WPF provides an elegant syntactic way to do what you want. So I was just about to post a similar answer to the one from Sheridan. That is you can provide an additional property of type Type to determine the generic type. However, Sheridan beat me to it. Below is some sample code of how you can do this with reflection:
Xaml
<DataGrid behaviors:DataGridBehavior.InnerType="namespace:SqliteBoundRow"
behaviors:DataGridBehavior.IsEnabled="True">
</DataGrid>
Code behind
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
{
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With