Reputation: 18857
Let's say I have a property on a class that needs to be rendered using a Hyperlink control. The hyperlink is to be bound to a command on the view model such that clicking on it triggers some action. Something like this:
<Style x:Key="HyperlinkStyle" TargetType="{x:Type igDP:CellValuePresenter}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type igDP:CellValuePresenter}">
<Border BorderBrush="{StaticResource DataGridCellOuterBorder}" BorderThickness="1,0,0,0" >
<TextBlock Margin="5">
<Hyperlink
Command="{Binding ElementName=dataGrid, Path=DataContext.Commands[GetSolutionSummaryCmd], Mode=OneTime}"
CommandParameter="{Binding Path=(igDP:DataRecord.DataItem), Mode=OneTime}">
<TextBlock Text="{TemplateBinding Value}"/>
</Hyperlink>
</TextBlock>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How do I ensure that when data items are removed from the grid, the binding between dataGrid.DataContext.Command[GetSolutionSummaryCmd], an implementation of ICommand, and each data record with a hyperlink column is destroyed, so data item can be garbage collected? Otherwise, I see a potential for memory leaking here.
Also, GetSolutionSummaryCmd is an instance of RelayCommand implemented like this:
public class RelayCommand : ICommand
{
readonly protected Predicate<object> _canExecute;
readonly protected Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
: this(canExecute, execute, true)
{
}
public RelayCommand(Predicate<object> canExecute, Action<object> execute, bool isCommandAllowed)
{
_canExecute = canExecute;
_execute = execute;
IsAllowed = isCommandAllowed;
}
public void RaiseCanExecuteChanged()
{
if (this.CanExecuteChanged != null)
this.CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
public virtual bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public event EventHandler CanExecuteChanged;
public virtual void Execute(object parameter)
{
_execute(parameter);
}
#endregion
}
In contrast to this, I need to be able to raise CanExecuteChanged.
Upvotes: 2
Views: 716
Reputation: 18857
I used the suggestions here to implement a solution for this problem. Leak was definitely being caused by hyperlink elements used in the above Style. ANTS profiler indicated this with a positive instance count for System.Windows.EffectiveValueEntry[] between memory snapshots. And when I look at the object reference graph for this class, there was always a reference to a hyperlink instance within the chain.
Underlying data was changed so that hyperlinks can always be executed when clicked. This means that CanExecuteChanged event of ICommand does not have to be raised allowing me to define some kind of NoReferenceRelayCommand class like this:
public class NoReferenceRelayCommand : ICommand
{
protected readonly Action<object> _execute;
public NoReferenceRelayCommand(Action<object> execute)
{
Guard.ThrowIfArgumentIsNull(execute);
_execute = execute;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
public void Execute(object parameter)
{
_execute(parameter);
}
#endregion
}
Profiling solution over 10 hours shows that instance number of System.Windows.EffectiveValueEntry does not increase.
Upvotes: 2