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.
I have faced the same question, but come to the conclustion that it is unnecessary to unsubscribe from events in this case (thanks for comments from Álvaro García and Blechdose that point me in this direction).
Actualy memory leaks because of an event handler are a one way problem. The cause of this problem is described here: https://stackoverflow.com/a/4526840/12797700. By using this code miDg.SelectionChanged += dataGrid_SelectionChanged; you add a link to the object that stores the dataGrid_SelectionChanged method into the miDg object. Because of that GC cannot remove the object that stores dataGrid_SelectionChanged method while the miDg object is alive.
However, the static object know nothing about the miDg object and GC can remove the miDg object even if the event is handled.
You can download the test project demonstrating this behavior using the next link. It also demonstrates how to replicate the memory leaks problem by handling an event.
https://github.com/Drreamer/AttachedPropertyMemoryTest
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.
Related
I am extending a control's capabilities. I want to know if there are any advantages in using a casted sender vs this keyword in an event. For example:
public class CustomTextBox : TextBox
{
public CustomTextBox()
{
Loaded += CustomTextBox_Loaded;
}
void CustomTextBox_Loaded(object sender, RoutedEventArgs e)
{
//use either
var c = (CustomTextBox)sender;
//or
var c2 = this;
//do whatever...
}
}
I believe using this might be more efficient (No casting necessary).
usually events are handled outside the class which publishes them, in the subscriber class. In such a setup, it may be desired to get the reference of publisher in subscriber & that is when type casting the sender to get the reference of publisher comes handy.
I agree, in my opinion, if you can get the reference of publisher without type casting thats better, you should use this.
But since you are extending a control, please check if its really necessary to use event of the base class. An event is for the outside world, and not for child classes.
If the event pattern has been implemented correctly in the base control class, I would expect a virtual method responsible for raising which you can override while extending the control, like this -
class CustomTextBox : TextBox
{
protected override void OnClick(EventArgs e)
{
//place your code here if you want to do the processing before the contol raises the event. Before call to base.OnClick(e);
base.OnClick(e); // call to base funciton fires event
//place your code here if you want to do the processing after the contol raises the event. After call to base.OnClick(e);
}
}
Hope it helps.
I doubt you would find any measurable performance difference either way, especially for an event like Loaded that is only going to be raised once during the lifetime of the control.
That said, it seems to me you should go ahead and use this, just because it's more convenient and expressive. If your method is already in the code of the class of the sender, why not? What possible gain could there be in code comprehension, ease of authoring, maintainability, or any other common goal in programming to using the sender parameter instead of just using this?
I have my class where I define my event:
public class EventRaiserUtility
{
public event EventHandler updateList;
public void updateListEvent()
{
if (updateList != null)
{
updateList(this, EventArgs.Empty);
}
}
public static EventRaiserUtility raiser = new EventRaiserUtility();
}
and this is where I raise my event:
EventRaiserUtility.raiser.updateListEvent();
and finally this is where I'm trying to create the listener:
...
EventRaiserUtility.raiser.updateList += new EventHandler(raiser_updateList);
//placed in the init method of another class
...
private void raiser_updateList(object sender, EventArgs e)
{
connType = MainWindowViewModel.getTTC();
}
Simply: this event has to notify when a list is updated and then update another list, with getTTC() with raiser_updateList.
But raiser_updateList is never called. Why? All my 3 snippets of code are in 3 different classes (same project), but this isn't a problem... right?
You're creating a new EventRaiserUtility just before you call updateListEvent (which should have a capital U to follow .NET conventions, by the way; ditto updateList => UpdateList) - but you're creating a separate EventRaiserUtility in order to subscribe to the event. They're different objects, so have different event subscribers. If you always create a new object before raising the event, there could never be any subscribers.
You should have a single EventRaiserUtility stored in an instance variable in the containing class - you'd create that on construction, then subscribe to the event in one place an raise it in another... but because they'd be talking about the same EventRaiserUtility object, you wouldn't lose the subscription.
(It's not clear that this utility class actually has much value, to be honest - why aren't you just declaring the event in your class directly? And why declare your own delegate when EventHandler has exactly the same signature?)
As far as I can see - you are subscribing to the event of one instance of EventRaiserUtility, but raising event from another instance which has no subscribers
you need one object to really own the event. Maybe that is the EventRaiserUtility, but you'd still need to make the same instance available in both classes. Without knowing the relationship between those classes
Is there a good strategy to exchange the receiver of multiple events (let's say an object instance A) during runtime to another instance B (or multiple instances)? For example think of a menu bar with a bunch of operations that can be performed on the currently selected of multiple objects.
Now one option would be to connect all object's handlers to the click events and let the handlers filter out the relevant calls (by checking if the current instance is selected) or registering/unregistering the events on selection.
Another one would be to register the events to an object functioning as proxy like this (rough code):
class ClickEventProxy
{
private static ClickEventProxy selectedInstance; // <-- changend on selection
public event EventHandler SomeEventToForward;
public static void RaiseSomeEventToForward(object sender, EventArgs e)
{
if (selectedInstance.ClickedAddNewFrame != null)
selectedInstance.ClickedAddNewFrame(sender, e);
}
...
}
The sender-site would look like SomeSource.Click += ClickEventProxy.RaiseSomeEventToForward; and all receivers would subscribe to their instance of the proxy.
However handling the instances (e.g. by a global <object, proxy instance> dictionary) is a bit unconvenient and the whole thing looks a bit clumsy. So my question: Is there a more programmatic way to do so? Or is it itself bad practice proxying events by introducing another step and one should rather remove and readd the handlers? (Maybe this could be made better by using custom events and altering the invocation list...)
Previously I had been using
this.CommandBindings.Add(
new CommandBinding(ApplicationCommands.Copy, this.cmdCopy_Executed, this.cmdCopy_CanExecute))
where cmdCopy_Executed is a non-static function, but I've seen folks using
static MyControl()
{
CommandBinding binding =
new CommandBinding(ApplicationCommands.Save, CommandHandler);
CommandManager.RegisterClassCommandBinding(typeof(MyControl), binding);
}
private static void CommandHandler(object target, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Command Handled!");
}
where the CommandBinding is static. Is one preferred over another?
The latter is more of a global handler, versus the former which is per instance.
Also, the RegisterClassCommandBinding cannot be unregistered, so you are stuck with it once you register. Generally, when using this it's best to call virtual methods on your control so their behavior can be changed or by-passed.
With CommandBindings you can remove any bindings that are no longer needed. This can also be done by external users of your control. So you may add a command binding that is required, but someone could easily do element.CommandBindings.Clear().
So there are differences, and each has their place. If you want it to be easily customizable, I'd go with the former.
I just implemented Clone from ICloneable and realized that the event subscriptions from my source instance also followed. Is there a good way to clear all those?
Currently I am using a couple of these loops for every event I have to clear everything.
foreach (var eventhandler in OnIdChanged.GetInvocationList())
{
OnIdChanged -= (ItemEventHandler) eventhandler;
}
foreach (var eventhandler in OnNameChanged.GetInvocationList())
{
...
This works fine but clutters the code a bit. Mostly worried to get event dangling.
I think you could just set OnIdChanged = null in your cloned object.
After you have created the clone you just call the ClearEvents method on the clone.
public class ClonedObject
{
public event EventHandler OnIdChanged;
public event EventHandler OnNameChanged;
public void ClearEvents()
{
OnIdChanged = null;
OnNameChanged = null;
}
}
Presumably, if you truly wanted to clone an object, you wanted to keep those event subscriptions.
If you're cloning objects that shouldn't be subscribed to events, it appears you should consider refactoring your code. Have your controller or similar objects subscribe to the events with a reference to a dedicated data object and have your data objects store that data without referencing the events; clone the data objects and place them in appropriate controller objects as necessary.
Ultimately, I'm suggesting you get around the issue by not subscribing to events to which you don't need to subscribe. Look at the problem space from a different angle.