Home > Software engineering >  Get and act on dynamically-added controls in WPF
Get and act on dynamically-added controls in WPF

Time:10-05

I'm programatically adding a series of controls based on a dictionary describing the controls various settings. Here are two of the entries as an example (I truncated the rest to save space):

    // Translation array from fix shortname to various data about them
        private Dictionary<string, Dictionary<string, string>> fixers = new Dictionary<string, Dictionary<string, string>>
        {
            ["F1"] = new Dictionary<string,string> {
                ["PrefName"] = "KillF1UnhelpfulHelp",
                ["Img"] = @"/graphics/F1key.png",
                ["Title"] = @"Diable F1 ""Help"" function",
                ["Description"] = @"
                    Have you ever hit the F1 key by accident and had a distracting and unhelpful window or webpage open as a result? 
                    Windows set the F1 key to a generic help function that basically never helps and always gets in the way. 
                    Enable this control to disable that obnoxious design choice. Note that some programs still respond to F1 on their own accord, 
                    but this will stop the default Windows behavior in things like Windows Explorer at least.
                    ",
                ["Tags"] = "#Keyboard,#Rage"
            },
            ["CMD"] = new Dictionary<string, string>
            {
                ["PrefName"] = "RestoreAdminCMDContext",
                ["Img"] = @"/graphics/CMD.png",
                ["Title"] = @"Restore ""Open Admin CMD Window Here"" to Windows Explorer",
                ["Description"] = @"
                    When you need to run commands in CMD, it's usually in a specific folder. Windows used to have an option when you CTRL Right Click 
                    to show ""Open CMD HERE"" on a folder. This restores that function AND it's at administrative level (and you don't need to CTRL CLICK to see it)
                    ",
                ["Tags"] = "#Windows Explorer,#TimeSaver"
            },
}

The controls themselves are just custom boxes with a few grid components, buttons, etc. Fairly simple. They are currently correctly generated and then added to the window as follows:

Code that adds them:

        public MainWindow()
        {
            InitializeComponent();
            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);
                FixersArea.Children.Add(fixerBoxes[key]);
            }

            FixerBox f1box = (FixerBox)FixersArea.FindName("F1");
            StatusBox.Text  = "Tags are:"  f1box.FixerTags;
        }

This is the XAML for the window where the controls are being added:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="60"/>
            <RowDefinition />
        </Grid.RowDefinitions>

        <TextBlock Grid.Row="0" Name="StatusBox" Width="600" MinHeight="40" Background="Beige"/>
        <ScrollViewer Grid.Row="1" ScrollViewer.VerticalScrollBarVisibility="Visible">
            <WrapPanel Name="FixersArea" Orientation="Horizontal" Width="auto" ></WrapPanel>
        </ScrollViewer>

    </Grid>

And this is the output:

main window rendered with custom controls

Everything works as expected, but how do you reference a dynamically generated custom control? I tried a few ways of referencing it by name or some other value and testing by printing out some of the text to the status box:


            FixerBox f1box = (FixerBox)FixersArea.FindName("F1");
            StatusBox.Text  = "Tags are:"   f1box.FixerTags;

But this results in an exception saying that f1box was null. So the finder isn't working. I found a few methods online for finding a child element by name, but those didn't work either and are fairly long so I'll exclude them.

The bottom line is that I'll need to get/set the state of the controls, show and hide them based on filters, and respond to their click events. I could probably handle the click response in the control itself, but the rest is going to be main-window stuff for sure (the controls will be affected by filtering and various save/load of settings).

CodePudding user response:

The FindName method uses x:Name values in Xaml to find children. If you need a dynamically added child to be available via FindName you need to explicitly register its name in C#:

FixersArea.RegisterName(key, fixerBoxes[key]);

Then you can use the following code to retrieve the child:

FixerBox f1box = (FixerBox)FixersArea.FindName("F1");

However, since you already maintain a dictionary of your fixerBoxes, you can do this:

FixerBox f1box = fixerBoxes["F1"];
  • Related