【问题标题】:Does MouseLeftButtonDown sender object really not pass a referance to object that triggered it? c# SilverlightMouseLeftButtonDown 发送者对象真的没有传递对触发它的对象的引用吗? c# Silverlight
【发布时间】:2010-02-01 18:59:46
【问题描述】:

我最近创建了一个 Silverlight 3 应用程序,其中我在后面的代码中创建了一些 UI 元素,并在运行时动态添加它们。

我希望只使用内置的 MouseButtonEventArgs 或 sender 对象来获取对被点击的实例的引用,但是我注意到一旦我开始,情况并非如此。我无法访问触发事件的对象的任何属性并针对它进行编程。

 void myFunc(object sender, MouseButtonEventArgs e)
    {
        //Can't do this :(           
        sender.someProperty = someValueToUpdate;
        //or this
        MyClass foo = sender as MyClass;
        foo.someProperty = someValueToUpdate;

    }

我最终只是编写了一个 CustomEventArgs 对象来传递一个实例,但令我惊讶的是这不是默认行为。

谁能解释一下为什么发送者对象不包含对触发事件的对象的引用?

另外,这是我为获得该实例所做的。

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);

【问题讨论】:

  • 您是否附加了调试器并查看“sender”的值是多少?
  • 是的,它只是一个对象,我可以用 var foo = sender as MyClass; 委托一个类型;但该对象的更新属性不起作用。

标签: c# silverlight


【解决方案1】:

MouseEventArgs 有一个OriginalSource 属性。它的 this 属性包含对最初触发它的对象的引用。

sender 参数非常正确地设置为您附加事件处理程序的对象的实例。也许一个简单的实验会让这两者之间的联系更加清晰。在 Visual Studio 中创建 Silverlight 应用程序。使 MainPage.xaml 的内容如下所示:-

<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>

并在 MainPage.xaml.cs 添加以下代码:-

    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));
    }

请注意同一处理程序如何附加到 XAML 中的三个不同项目,而不是附加到 TextBlock 本身。点击“First Top Item”可以得到这个:-

Sender: TopPanel, Text block: First Top Item
Sender: OuterPanel, Text block: First Top Item
Sender: LayoutRoute, Text block: First Top Item

处理程序为它所附加的每个项目触发 3 次,从发送者可以看出每个项目都不同。然而,尽管没有附加任何处理程序,但 OrignalSource 是实际单击的 TextBlock。另请注意,OriginalSource 保持不变,因为它会冒泡祖先元素。

单击堆栈面板下方的区域。你只会得到:-

Sender: LayoutRoot, Text block: Not from a text block

有趣的是,单击列表框根本不会添加任何项目,您可能希望与上述行相同。显然 ListBox 处理鼠标按下,因此将事件参数 Handled 属性设置为 True 防止进一步冒泡。

【讨论】:

    【解决方案2】:

    来自 msdn 文档:

    对于冒泡事件,发送者 参数标识其中的对象 事件被处理,不一定 实际收到的对象 启动的输入条件 事件。

    即因为这是一个冒泡事件,也许你应该尝试类似的东西

    void myFunc(object sender, MouseButtonEventArgs e)
    {
    
        var theUIElement = sender as TheUIElementOfWhichImInterested;
        if (theUIElement != null)
        {
            // set properties on the element
        }
    }
    

    【讨论】:

    • 您对文档的解释不正确。如果事件处理程序直接附加到该类型对象的鼠标事件,则发送方将永远属于TheUIElementOfWhichImInterested 类型。冒泡不会导致处理程序的单个附件多次触发。看我的回答。
    • 这不是我对文档的解释。如果将相同的事件处理程序附加到不同的 UI 元素,则 if 部分中的代码将仅在事件冒泡通过类型为 TheUIElementOfWhichImInterested 的对象时执行。我相信上面引用的文档可以回答为什么发送者对象不包含对触发事件的对象的引用,正如 OP 所要求的那样。而且您已经给出了如何找到它的解决方案:我现在将一直使用该属性... ;-)
    猜你喜欢
    • 1970-01-01
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2015-08-18
    • 2017-08-19
    • 2011-06-02
    • 1970-01-01
    相关资源
    最近更新 更多