-2

For better or worse, I am building a WPF UI in code behind only. I have a button and a Text block within a Stack Panel. The button, upon clicking, updates the TextBlock with some information. If I make the TextBlock a member of the class, it will work successfully. However, just declaring global UI elements all over the place feels like the easy way out. Is there a way to instantiate the TextBlock in the MainWindow() and still have access to it in the button click Event?

The following pseudo code works:

public partial class MainWindow : Window
{
    public TextBlock myTextBlock;

    public MainWindow()
    {

        StackPanel mainStackPanel = new StackPanel();

        Button myButton = new Button();

        myButton.Click += NewButton_Click();

        mainStackPanel.Children.Add(myTextBlock);

        mainStackPanel.Children.Add(myButton);

        Content = mainStackPanel;

    }

    private void NewGridButton_Click(object sender, RoutedEventArgs e)
    {
        myTextBlock.Text = "Some Text"
    }

}

However, I am curious if something along these lines is also possible:


public partial class MainWindow : Window```
{
    public MainWindow()
    {

        StackPanel mainStackPanel = new StackPanel();

        Button myButton = new Button();

        TextBlock myTextBlock = New TextBlock();

        myButton.Click += NewButton_Click();

        mainStackPanel.Children.Add(myTextBlock);

        mainStackPanel.Children.Add(myButton);

        Content = mainStackPanel;

    }

    private void NewGridButton_Click(object sender, RoutedEventArgs e)
    {
        //Somehow Access the myTextBlock from MainWindow here in order to set the text?

        myTextBlock.Text = "Some Text" // This would not compile.
    }

}

For brevity, the code here is just for example and not fully functional. Thank you in advance for any information you may be able to share on this topic.


EDIT - Follow up. The consensus from the answers here seem to indicate I was overthinking this issue. A private class variable that represents the UI element I wish to use seems to be a universally accepted (and not necessarily a lazy) way to declare and access your UI elements. Thanks to everyone who took the time to review and comment.

mherr
  • 348
  • 1
  • 7
  • 25
  • I have never worked this way. Be aware that all XAML code is translated into C# code by the compiler. What you can do, is create a demo application and study the generated code. This may be a source of inspiration. In general, why do you not use XAML? I do not like XAML but it is the easiest way to create the UI layer. – RudolfJan Jun 04 '20 at 06:05
  • "declaring global UI elements all over the place" - `public TextBlock myTextBlock;` is not global, and not all over the place. It is a field in MainWindow class, with different value for each Window instance. better make it `private` though. `private TextBlock myTextBlock;` will be fine. `` gives the same private field, only in hidden generated code – ASh Jun 04 '20 at 08:14
  • @RudolfJan That is an interesting suggestion, and kind of what I am looking for overall with WPF. I try to create the layout with code-behind in an attempt to understand better what's actually happening. Seems like the general consensus doesn't agree with my sentiment though, but thanks for taking the time to comment. – mherr Jun 04 '20 at 22:18
  • @ASh You're correct, it's not global, I misspoke there. What I meant by "all over the place", was if you had to do this for every element you wanted to work with in a real Application, it may start to look like too many class variables that are just accessible from any function. Setting to private is a great suggestion though. Thanks for taking the time to comment. – mherr Jun 04 '20 at 22:19

4 Answers4

1

The use of a locally scoped variable isn't a bad thing in this case (as per your original example). It may feel ugly but it is essentially what gets created in the background by defining things in a XAML file.

As an alternative, see this question/answer that contains code to locate a control as well as a reference to a VisualTreeHelper that could assist. In my opinion, though, both these options are less elegant than the original idea.

ZombieSheep
  • 29,603
  • 12
  • 67
  • 114
  • I advise you to use the MVVM architecture. – Filipe Piletti Plucenio Jun 03 '20 at 23:55
  • Thanks ZombieSheep for taking the time to comment. I am ultimately seeking WPF best practices and it appears that I have severely overthought this one. I figured by declaring class variables for every element I wanted to manipulate elsewhere, I was just being lazy with the code. It looks like the alternatives are needlessly more complicated. Guess I over thought this one. Thanks again. – mherr Jun 04 '20 at 22:36
1

This is a diabolically bad misuse of WPF, and I suspect you already know that. But if you insist, you can give your TextBlock a name when you create it:

myTextBlock.Name = "MyTextBlock";

You can then traverse the visual tree at any time to find it. Add the code for FindChild in this answer to your class, then do this in your click handler:

private void MyButton_Click(object sender, RoutedEventArgs e)
{
    var textBlock = FindChild<TextBlock>(this, "MyTextBlock");
    textBlock.Text = "Hello World!";
}

If it's just the dynamic creation of child controls you need to support then a marginally better solution would be to create a dictionary for them:

private Dictionary<string, FrameworkElement> Children = new Dictionary<string, FrameworkElement>();

...to which you would then add each of your controls to, using unique identifiers:

TextBlock myTextBlock = new TextBlock();
this.Children["MyTextBlock"] = myTextBlock;

While still not the "proper" way of doing this in WPF, at least you'd no longer have to traverse the entire tree looking for the element you're after:

var textBlock = this.Children["MyTextBlock"] as TextBlock;
textBlock.Text = "Hello World!";
Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • 1
    Thanks Mark for taking the time to comment. I am not a WPF expert and just wishing to learn best practices. Is it a bad use of WPF because the XAML is not being utilized? My thought behind wanting to make the UI with code-behind is so that I can better understand what's actually happening with it. It doesn't seem like people agree with this, however. I think overall the traversing of the visual tree was what I was curious if was possible, and you've answered that. Thank you. – mherr Jun 04 '20 at 22:22
  • Glad to help! If it's just for hands-on learning then go for it, it will certainly help teach what the XAML is doing behind the scenes. For real-world applications, though, WPF was designed to use data binding. Even things like event handlers (such as the one for your button click) were supported to facilitate the upgrading of legacy code (MS knew they'd have a fight on their hands trying to enforce such a major change in one hit). In a "pure" MVVM app you can actually leave all of your window/control .cs files empty (or delete them), leaving only the XAML ones, and it'll still build fine. – Mark Feldman Jun 04 '20 at 22:42
1

If you don't store a reference to the TextBlock in a field, you need to get a reference to it dynamically somehow.

There are various ways to do this. You may for example get in from the Children collection of the root Panel:

private void NewGridButton_Click(object sender, RoutedEventArgs e)
{
    TextBlock myTextBlock = (Content as Panel).Children.OfType<TextBlock>().FirstOrDefault();
    if (myTextBlock != null)
        myTextBlock.Text = "Some Text";
}

Or by name if you register it:

public MainWindow()
{
    ...

    TextBlock myTextBlock = new TextBlock();
    RegisterName("myTextBlock", myTextBlock);

    ...
}

private void NewGridButton_Click(object sender, RoutedEventArgs e)
{
    TextBlock myTextBlock = FindName("myTextBlock") as TextBlock;
    if (myTextBlock != null)
        myTextBlock.Text = "...";
}

Neither of these approaches are "better" than storing the reference in a field though. Using a field, you don't have to perform any dynamic lookups.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thanks mm8 for taking the time to comment. Ultimately I was looking for dynamic ways to find elements without having to store them in a class field, and you've provided ways to do that. I think you're right, just creating the class field appears fine. For some reason, I was making it too complicated. Thanks again. – mherr Jun 04 '20 at 22:25
-2

i think that the best way to to move the event code to a method

private void NewGridButton_Click(object sender, RoutedEventArgs e)
{

    NewGridButtonClick();

}

 private void NewGridButtonClick()
 {
    myTextBlock.Text = "Some Text" // This would not compile.
 }

then from any place in your app you may call it

 private void SomeButton_Click(object sender, RoutedEventArgs e)
{

    MainWindow.NewGridButtonClick();

}
Amr.Alaa
  • 31
  • 5
  • I think you missed the point of the question. This is about elements that were added to the view programmatically from other methods without creating variables to hold them. – ZombieSheep Jun 03 '20 at 23:37
  • if you are using MVVM then you made is more easier, just register MainWindow as a singleton , then get it in any needed vie and run the method within – Amr.Alaa Jun 03 '20 at 23:47