如何获取发送路由事件的控件的父控件的列索引?

时间:2015-10-30 17:43:45

标签: c# wpf xaml routed-events

我的表单上有一个UserControl Button点击后,会在新行或列中添加UserControl或另一个UserControl的另一个实例。我要做的是点击Button时,找出包含UserControl的{​​{1}}所在的列,然后将新Button添加到该列同一栏。代码如下:

UserControl

我遇到的问题是获取 ++rowCount; int currentColumn = Grid.GetColumn(e.Source as UIElement); AnswerNode answerNode = new AnswerNode(); answerNode.buttonAddQuestionNode.MouseLeftButtonDown += AddQuestionNode_DifferentLevelEvent; answerNode.Margin = new Thickness(0, 5, 0, 5); RowDefinition gridRowNew = new RowDefinition(); gridRowNew.Height = new GridLength(70); Grid.SetRow(answerNode, rowCount); Grid.SetColumn(answerNode, currentColumn); MainGrid.RowDefinitions.Add(gridRowNew); MainGrid.Children.Add(answerNode); 的列索引会导致e.Source中的按钮索引 - 在本例中为UserControl - 而不是1本身的列索引。我如何访问UserControl的列索引,该列索引是被点击的UserControl的父级?

1 个答案:

答案 0 :(得分:0)

Without a good, minimal, complete code example that clearly illustrates the question, showing exactly what you've tried and how you want the event handling to work, it's difficult to know for sure what the best answer would be. That said, from what little code you've provided and your problem description, it sounds like you have subscribed to the event in the wrong control object. Specifically, you have declared your event handler in the Button object directly, rather than subscribing to the Button.Click event in the UserControl.

For a routed event, you can always get the original source of the event, i.e. the control where the event was initially signaled via the RoutedEventArgs.OriginalSource property. The RoutedEventArgs.Source property in turn will provide the reference to the control object to which the event has been routed and is currently handling the event. If you want to handle the event as seen by the UserControl object, then you need to subscribe to the event from the UserControl object, not the Button object.

Here is a short, complete code example that illustrates the basic technique:

XAML:

UserControl1.xaml

<UserControl x:Class="TestSO33441926RoutedEvent.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <TextBlock Text="Click here: "/>
    <Button x:Name="button1" Content="Click me!" Grid.Column="1"
            HorizontalAlignment="Left" VerticalAlignment="Top"
            Click="Button_Click"/>
  </Grid>
</UserControl>

MainWindow.xaml

<Window x:Class="TestSO33441926RoutedEvent.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:TestSO33441926RoutedEvent"
        Title="MainWindow" Height="350" Width="525">
  <Grid x:Name="grid1">
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition/>
      <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <TextBlock Text="MainWindow column 1"/>
    <TextBlock Text="MainWindow column 2" Grid.Column="1"/>
    <l:UserControl1 x:Name="userControl1" Grid.Column="2"
                    Button.Click="UserControl1_Click"/>
  </Grid>
</Window>

C#:

UserControl1.xaml.cs

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        App.ReportClick((FrameworkElement)e.Source);
    }
}

MainWindow.xaml.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void UserControl1_Click(object sender, RoutedEventArgs e)
    {
        App.ReportClick((FrameworkElement)e.Source);
    }
}

App.xaml.cs

public partial class App : Application
{
    public static void ReportClick(
        FrameworkElement frameworkElement, [CallerMemberName]string callerName = null)
    {
        MessageBox.Show(
            "Caller name: \"" + callerName + "\"\n" +
            "Sender name: \"" + frameworkElement.Name + "\"\n" +
            "Sender column: " + Grid.GetColumn(frameworkElement));
    }
}

Since neither event handler sets RoutedEventArgs.Handled to true, the event will be sent to each handler in turn, starting with the original source of the event and moving up the visual object graph to each parent in turn.

As such, you can see that when the ReportClick() method is called by each event handler, the e.Source object is different in each event handler, depending on where that handler was subscribed.

For your own question, the key is here, in the MainWindow.xaml source code:

    <l:UserControl1 x:Name="userControl1" Grid.Column="2"
                    Button.Click="UserControl1_Click"/>

Note that the Button.Click event is the one being subscribed, but that the code is subscribing from the UserControl1 object. This has the result that the event handler void UserControl1_Click(object, RoutedEventArgs) is called with the UserControl1 object as the sender and source of the event.

Thus, when the column index of the sender of the event is retrieved by calling Grid.GetColumn(UIElement), the column index of the UserControl1 object is returned, not the column index of the original source of the event (i.e. the Button object that was actually clicked).


As an aside: in the bit of code your posts shows, there seems to be a fair amount of code-behind that is probably better-implemented as XAML declarations. Naturally, just about anything you can do in XAML, you can also accomplish in code-behind. But that doesn't mean it's a good idea. Addressing that defect in your code is not likely to relate directly to the question you're asking, nor would changing the implementation in that way fix the problem. But you should definitely consider seriously following the WPF/XAML paradigm more closely and put as much of your implementation in XAML as you can.