3

在这个问题的帮助下,我在 FlowPanelLayout 中设置了 UserControls: 对于每个 DataTable,将 UserControl 添加到 FlowLayoutPanel

我现在正在尝试实现一个单击事件,该事件允许我在已选择的 UserControl 周围放置一个边框。我已经这样做了:

        private void User_Load(object sender, EventArgs e)
    {
        flowlayoutpanelUsers.HorizontalScroll.Visible = false;

        // Load and Sort Users DataTable
        DataTable datatableUsers = UserMethods.GetUsers().Tables["Users"];
        datatableUsers.DefaultView.Sort = "Name";
        DataView dataviewUsers = datatableUsers.DefaultView;

        // Loop Through Rows and Add UsersGrid to FlowLayoutPael
        foreach (DataRowView datarowviewUsers in dataviewUsers)
        {
            var UsersGrid = new UsersGrid
            {
                Username = datarowviewUsers["Username"].ToString(),
                User = datarowviewUsers["Name"].ToString(),
                Admin = datarowviewUsers["Administrator"].ToString(),
            };
            flowlayoutpanelUsers.Controls.Add(UsersGrid);
            UsersGrid.MouseClick += new MouseEventHandler(user_click);
        }
    }

    private UsersGrid selectedUser;

    void user_click(object sender, EventArgs e)
    {
        if (selectedUser != null)
            selectedUser.BorderStyle = BorderStyle.None;
        selectedUser = (UsersGrid)sender;
        selectedUser.BorderStyle = BorderStyle.FixedSingle;
    }

我的问题是,它仅在我单击 UserControl 中的空白区域时才有效,但在用户单击两个标签或图像时无效。我如何使它也适用于所有子对象?

另外,我怎样才能使用选定的 UserControl 来做其他事情,比如打开一个显示该选定用户的所有详细信息的表单?

4

3 回答 3

8

I have a few suggestions for you. At the bottom of my response I included code that demonstrates my suggestions.

Suggestion 1: Fixing MouseClick in your UC
When you register the MouseClick event for a UserControl (UC) you're doing so for the UserControl itself, not for any controls that you place on the UserControl such as your Labels, etc. If you click one of these child controls the click won't be 'seen' by the underlying UC.

To fix this register the MouseClick event for all your child controls; you can even register the same MouseClick event handler you have for the UserControl itself.

Suggestion 2: Setting your UC's BorderStyle
I'd move your code for setting the UC's BorderStyle into the UC itself. Create public property IsSelected that's set to true when the UC is selected. In the property's setter update the UC's BorderStyle property depending on the value of the property.

Exposing an IsSelected property for your UC can be handy: you can query a group of these UCs to see which ones are selected rather than trying to track this status outside of the control like through a Form-level variable.

Edit in response to your comment:
Here's an example of how you might query the UCs in a FlowLayoutPanel to see if any are selected and if one is found how you might take some action. In this case the action is to call an EditUser method that takes as parameters values you get from properties in the selected UC:

var selectedUC = flowLayoutPanel.Controls.Cast<UserControl1>().FirstOrDefault(uc => uc.IsSelected);
if (selectedUC != null) {
    // Use the properties of the UC found to be selected as parameters is method EditUser.
    EditUser(selectedUC.Name, selectedUC.Username, selectedUC.Administrator);
}

Suggestion 3: Managing selection in a group of your UCs
If you want to unselect all UCs in a group except for the one that the user clicks (i.e. selects) you'll need to create an event in your UC that fires when a UC is clicked. The handler for this event explicitly sets IsSelected to false for all UCs in a set (such as in a container type control such as a Form, FlowLayoutPanel, etc.), the MouseClick handler in the UC that was clicked will then set the clicked UC's IsSelected to true.

It's worth considering creating another UserControl type that manages a group of your UCs. This new UserControl can encapsulate the code for the creation and state management of sets of your UC and would faciliate using your UCs in other projects as well as keeping the code of Forms hosting your UCs a bit cleaner.


I figured that rather than include a series of disjointed code snippets for each of my suggestions I'd include what I'm hoping is the minimum amount of code to allow you to reproduce what I'm talking about.

Create a new Visual Studio Winform project and use the following for class Form1:

public partial class Form1 : Form
{
    public Form1() {
        InitializeComponent();

        flowLayoutPanel = new FlowLayoutPanel {
            Dock = DockStyle.Fill,
        };
        this.Controls.Add(flowLayoutPanel);
        // Add several sample UCs.
        for (int i = 0; i < 10; i++) {
            var uc = new UserControl1();
            uc.WasClicked += UsersGrid_WasClicked;
            flowLayoutPanel.Controls.Add(uc);
        }
    }

    FlowLayoutPanel flowLayoutPanel;

    // Event handler for when MouseClick is raised in a UserControl.
    void UsersGrid_WasClicked(object sender, EventArgs e) {
        // Set IsSelected for all UCs in the FlowLayoutPanel to false. 
        foreach (Control c in flowLayoutPanel.Controls) {
            if (c is UserControl1) {
                ((UserControl1)c).IsSelected = false;
            }
        }
    }
}

Next add a UserControl to the project. Keep the name UserControl1 and add a couple Labels and a PictureBox. Use this code for class UserControl1:

public partial class UserControl1 : UserControl
{
    public UserControl1() {
        InitializeComponent();
        this.Load += UsersGrid_Load;
    }

    // Event fires when the MouseClick event fires for the UC or any of its child controls.
    public event EventHandler<EventArgs> WasClicked;

    private void UsersGrid_Load(object sender, EventArgs e) {
        // Register the MouseClick event with the UC's surface.
        this.MouseClick += Control_MouseClick;
        // Register MouseClick with all child controls.
        foreach (Control control in Controls) {
            control.MouseClick += Control_MouseClick;
        }
    }

    private void Control_MouseClick(object sender, MouseEventArgs e) {
        var wasClicked = WasClicked;
        if (wasClicked != null) {
            WasClicked(this, EventArgs.Empty);
        }
         // Select this UC on click.
         IsSelected = true;
    }

    private bool _isSelected;
    public bool IsSelected {
        get { return _isSelected; }
        set {
            _isSelected = value;
            this.BorderStyle = IsSelected ? BorderStyle.Fixed3D : BorderStyle.None;
        }
    }
}
于 2012-09-27T15:07:10.277 回答
1

我知道这很旧,但我来到这里寻找有关如何在 Container 中进行 UC 选择的一些指示。杰伊的回答效果很好。

只有一个更新:UsersGrid_Load 方法将只参与顶级控件,容器的子项将不会参与 WasClicked 事件。

private void UsersGrid_Load(object sender, EventArgs e) {
    // Register the MouseClick event with the UC's surface.
    this.MouseClick += Control_MouseClick;
    // Register MouseClick with all child controls.
    RegisterMouseEvents(Controls);
}

将递归方法 RegisterMouseEvents 添加到 UserControl

    private void RegisterMouseEvents(ControlCollection controls)
    {
        foreach (Control control in controls)
        {
            // Subscribe the control to the 
            control.Click += Control_MouseClick;
            if (control.HasChildren) RegisterMouseEvents(control.Controls);
        }
    }
于 2016-07-12T16:28:31.333 回答
0

您可以尝试订阅GotFocusUserControl 上的事件。

警告:

由于此事件使用冒泡路由,因此接收焦点的元素可能是子元素,而不是实际附加事件处理程序的元素。检查事件数据中的 Source 以确定获得焦点的实际元素。

UIElement.GotFocus 事件

更新

这个问题可能是合适的:.Net (Windows Forms) 用户控件的 Click event

于 2012-09-26T18:45:08.483 回答