I have an attached property to use in a datagrid to can use the SelectedItems in my view model. The code is this:
public class DataGridSelectedItemsAttachedProperty
{
#region SelectedItems
///
/// SelectedItems Attached Dependency Property
///
public static readonly DependencyProperty SelectedItemsProperty =
DependencyProperty.RegisterAttached("SelectedItems", typeof(IList),
typeof(DataGridSelectedItemsAttachedProperty),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
new PropertyChangedCallback(OnSelectedItemsChanged)));
public static IList GetSelectedItems(DependencyObject d)
{
return (IList)d.GetValue(SelectedItemsProperty);
}
public static void SetSelectedItems(DependencyObject d, IList value)
{
d.SetValue(SelectedItemsProperty, value);
}
private static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid miDg = (DataGrid)d;
miDg.SelectionChanged += dataGrid_SelectionChanged;
miDg.Unloaded += dataGrid_Unloaded;
}
private static void dataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid miDg = (DataGrid)sender;
//Get list box's selected items.
IEnumerable miDgSelectedItems = miDg.SelectedItems;
//Get list from model
IList ModelSelectedItems = GetSelectedItems(miDg);
//Update the model
ModelSelectedItems.Clear();
if (miDg.SelectedItems != null)
{
foreach (var item in miDg.SelectedItems)
ModelSelectedItems.Add(item);
}
SetSelectedItems(miDg, ModelSelectedItems);
}
private static void dataGrid_Unloaded(object sender, RoutedEventArgs e)
{
DataGrid miDg = sender as DataGrid;
miDg.SelectionChanged -= dataGrid_SelectionChanged;
miDg.Unloaded -= dataGrid_Unloaded;
}
#endregion
}
The problem is that this datagrid is in a tab control the event unload is fired, so the event is unsubcribe and then the SelectedItems is not notified to the view model anymore.
So I would like to know how to solve this problem, perhaps unsubscribe the events in another place instead of the unload event?
Thanks.
when I close the user control is when the attached property will be recolected because no object is referencing it.
This is false. If you remove the code that unregisters the events, any controls using the attached property will live forever. Why? Because the event handlers you register are static. That means the control will contain a reference to something static preventing the garbage collector from ever collecting it.
The first potential solution to this problem is to use the weak event pattern when registering events. It's for the reason above that I always use the weak event pattern when registering events for my own attached properties.
The annoying thing about this solution is that it requires a rather large amount of boilerplate code. You have to create a new WeakEventManager
implementation for every new type of event. Then to receive the weak events, you have to implement an interface (EDIT: unless you are using .NET 4.5 or higher), and that means you can't have a static handler. So then you need class that implements the IWeakEventListner
interface, and create and manage instances of that class in your attached property events.
Therefore, the solution I would recommend for you is to actually subclass the DataGrid
class and add this functionality as a normal dependency property. If you do it that way, you won't have to register events at all (there are protected methods you can override), and there's no worries about potential memory leaks. The reason I would recommend this solution is because in my experience I have needed to override the DataGrid
class for numerous other reasons, many of them could be achieved with attached properties, but a few of them cannot.
The real problem is that the WPF DataGrid implementation is rather half-baked (my personal opinion). There are bugs, default behaviors that I don't like, and incomplete or unimplemented features (such as support for Copy, but not Paste; or the particular issue I think you are trying to solve: a bindable SelectedItems). It's possible to fix all these issues most easily by simply subclassing DataGrid.