Does MouseLeftButtonDown sender object really not pass a referance to object that triggered it? c# Silverlight
I recently created a Silverlight 3 app in which I created some UI elements in the code behind and added them at run-time dynamically.
I was hoping to just use the built-in MouseButtonEventArgs or the sender object to get a reference to the instance that was clicked, however I noticed once I started that this was not the case. I was not able to access any properties of the object that triggered the event and program against it.
void myFunc(object sender, MouseButtonEventArgs e)
{
//Can't do this :(
sender.someProperty = someValueToUpdate;
开发者_C百科 //or this
MyClass foo = sender as MyClass;
foo.someProperty = someValueToUpdate;
}
I ended up just writing a CustomEventArgs object to pass an instance, but it surprised me that this wasn't a default behavior.
Can anyone shed some light as to WHY the sender object doesn't contain a reference to the object that triggered the event?
Also, here is what I did to get that instance.
myObject.myEvent += new CustomEvent(myFunc);
...
void myFunc(object sender, CustomEventArgs e)
{
e.MyProperty = someValueToUpdate;
}
...
public class MyClass
{
public MyProperty = 0;
public event CustomEvent myEvent;
protected virtual void MyEventMethod(CustomEventArgs e)
{
if (myEvent != null){myEvent(this, e);}
}
public MyClass ()
{
this.MouseLeftButtonDown += new MouseButtonEventHandler(this_MouseLeftButtonDown);
}
void rect_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
CustomEventArgs e2 = new CustomEventArgs(this);
MyEventMethod(e2);
}
}
public class CustomEventArgs : EventArgs
{
private readonly MyClass myProperty;
public CustomEventArgs(MyClass myProperty) { this.myProperty = myProperty; }
public MyClass MyProperty { get { return myProperty; } }
}
public delegate void CustomEvent(object sender, CustomEventArgs e);
The MouseEventArgs
has a OriginalSource
property. Its this property which holds a reference to the object that originally triggered it.
The sender
argument quite rightly is set to the instance of the object against which you attached the event handler. Perhaps a simple experiment will make how this hangs together clearer. In Visual Studio create a Silverlight Application. Make the content of the MainPage.xaml look like this:-
<UserControl x:Class="SilverlightApplication1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid x:Name="LayoutRoot" Background="White" MouseLeftButtonDown="MouseHandler">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel x:Name="OuterPanel" MouseLeftButtonDown="MouseHandler" Margin="5">
<StackPanel x:Name="TopPanel" MouseLeftButtonDown="MouseHandler">
<TextBlock Text="First Top Item" />
<TextBlock Text="Second Top Item" />
</StackPanel>
<StackPanel x:Name="BottomPanel" MouseLeftButtonDown="MouseHandler">
<TextBlock Text="First Bottom Item" />
<TextBlock Text="Second Bottom Item" />
</StackPanel>
</StackPanel>
<ListBox x:Name="lstOutput" Grid.Column="1" Margin="5" />
</Grid>
</UserControl>
And in MainPage.xaml.cs add this code:-
private void MouseHandler(object sender, MouseButtonEventArgs e)
{
FrameworkElement s = sender as FrameworkElement;
TextBlock o = e.OriginalSource as TextBlock;
string text = (o != null) ? o.Text : "Not from a text block";
lstOutput.Items.Add(String.Format("Sender: {0}, Text block: {1}", s.Name, text));
}
Note how this same handler is attached to three different items in the XAML but not to the TextBlocks themselves. Clicking the "First Top Item" gets you this:-
Sender: TopPanel, Text block: First Top Item
Sender: OuterPanel, Text block: First Top Item
Sender: LayoutRoute, Text block: First Top Item
The handler fires 3 times once for each item it is attached to as can be seen by the sender being different for each one. However the OrignalSource it the TextBlock that was actually clicked on despite it not having any handler attached. Also note that the OriginalSource remains the same as it bubbles up the ancestor elements.
Click on the area below the Stack panels. You only get:-
Sender: LayoutRoot, Text block: Not from a text block
Of interest also is that clicking in the Listbox results in no items being added at all, you might expect to the same ase the above line. Clearly ListBox handles the mouse down and therefore sets the event args Handled
property to True
preventing further bubbling.
From the msdn documentation:
For a bubbling event, the sender parameter identifies the object where the event is handled, not necessarily the object that actually received the input condition that initiated the event.
I.e. since it's a bubbling event, maybe you should try something like
void myFunc(object sender, MouseButtonEventArgs e)
{
var theUIElement = sender as TheUIElementOfWhichImInterested;
if (theUIElement != null)
{
// set properties on the element
}
}
精彩评论