简体   繁体   中英

In WPF/C#, how do I access elements of dynamically generated controls or otherwise add click events to them?

I am using a custom control class to build similar-looking controls with different details and OnClick actions. Other than looking pretty, the controls only function is to toggle a yellow button so it looks like a modern togglebutton control (like in Android settings or whatever).

The main output looks like this: 带有自定义控件的主输出

What I'm trying to do is figure out how to bring the "onclick" logic out of the control and into the main file so I can properly respond to it. I originally had it respond to it's own click events on both the rectangle and ellipse like so:

    public partial class FixerBox : UserControl
    {
        Thickness btnDotOn = new Thickness(-60, 0, 0, 0);
        Thickness btnDotOff = new Thickness(0, 0, -60, 0);
        SolidColorBrush dotOn = new SolidColorBrush(Color.FromRgb(84, 130, 53));
        SolidColorBrush dotOff = new SolidColorBrush(Color.FromRgb(207, 178, 76));
        SolidColorBrush bkOn = new SolidColorBrush(Color.FromRgb(226, 240, 217));
        SolidColorBrush bkOff = new SolidColorBrush(Color.FromRgb(255, 242, 204));
        
        public bool IsOn { get; set; } = false;
        
        public FixerBox(Dictionary<string,string> deets)
        {
            InitializeComponent();
            btnOff();
            this.Name = deets["Name"];
            this.FixerTitle.Text = deets["Title"];
            this.FixerDesc.Text = deets["Description"];
            this.FixerTags.Text = deets["Tags"];
            this.FixerImg.Source = new BitmapImage(new Uri(deets["Img"], UriKind.Relative));
        }

        public void btnOn() 
        {
            IsOn = true;
            FixBtnBk.Fill = bkOn;
            FixBtnBk.Stroke = dotOn;
            FixBtnDot.Fill = dotOn;
            FixBtnDot.Margin = btnDotOn;
        }
        public void btnOff() 
        {
            IsOn = false;
            FixBtnBk.Fill = bkOff;
            FixBtnBk.Stroke = dotOff;
            FixBtnDot.Fill = dotOff;
            FixBtnDot.Margin = btnDotOff;
        }

        private void FixBtnBk_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //if (isOn)
            //{
            //  btnOff();
            //}
            //else
            //{
            //  btnOn();
            //}
        }
        private void FixBtnDot_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            FixBtnBk_MouseLeftButtonDown(sender, e);
        }

    }

I've commented out the click events for now so I can try to trigger the same effect from outside:

        // The click event I'm trying to build
        private void fixClick(object sender, EventArgs e)
        {
            StatusBox.Text = "CLICK WORKS";
            FixerBox whichFix = (FixerBox)sender;
            if (whichFix.IsOn)
            {
                whichFix.btnOff();
            }
            else
            {
                whichFix.btnOn();
            }
        }

        // The constructor of my app
        public MainWindow()
        {
            InitializeComponent();
            // fixNames is a dictionary containing details of each control
            var fixNames = fixers.FixerNames();
            foreach (string key in fixNames)
            {
                var temp = fixers.GetFix(key);
                // Be sure to pass this along as well
                temp["Name"] = key;
                fixerBoxes[key] = new FixerBox(temp);
                fixerBoxes[key].FindName("FixBtnBk") = new EventHandler(fixClick);
                FixersArea.Children.Add(fixerBoxes[key]);
            }           
        }

Above is my main app constructor where I generate all the controls. The issue is that on the line where I'm setting the event handler, I can't figure out how to actually make it work. FindName should get the specific control, but it doesn't HAVE a click event or mousedown or anything. How can I attach my custom click event to each control?

The reason I'm doing it this way is because along with toggling the button, I want it to actually make system changes and registry updates in response to which control was clicked. I could keep all the responses in a class and include them in the custom control, but that seems sloppy both from a system resources viewpoint and a programming viewpoint.

Instead, if I can figure out how to access the various elements of the dynamic controls and add click events to them (or any equivalent workaround), that seems better.

how do I access elements of dynamically generated controls or otherwise add click events to them. What I'm trying to do is figure out how to bring the "onclick" logic out of the control and into the main file so I can properly respond to it.

As mentioned in my comments above, you could create public events that you could subscribe to and respond to those events as needed.

In your FixerBox class:

  • Add event: public event RoutedEventHandler MouseLeftBtnDown;

  • Invoke the event when needed:

     private void FixerBox_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { MouseLeftBtnDown?.Invoke(this, new RoutedEventArgs()); }

In your class you want to subscribe to this new event:

foreach (string key in fixNames)
            {
                var temp = fixers.GetFix(key);
                // Be sure to pass this along as well
                temp["Name"] = key;
                fixerBoxes[key] = new FixerBox(temp);
                fixerBoxes[key].MouseLeftBtnDown += fixClick;
                FixersArea.Children.Add(fixerBoxes[key]);
            }  

Your routine signature would need to be changed as well:

 private void fixClick(object sender, RoutedEventArgs e)

This should get you started, if you have questions please let me know.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM