6

With WinForms programs I've become accustomed to marking the Modifiers property of a control as 'Private' to prevent external classes and whatever else have you from being able to see and mess with them.

Being still very green with WPF, I see no obvious equivalent in WPF that allows me to make it so external classes cannot see a control I drop onto a form or another user control or what not. I did notice something of x:FieldModifier = "Private" but I get the error "x:FieldModifier = "Private" is not valid for the language C#".

How do I mark a control as Private so it cannot be viewed or accessed by external class objects?

Will
  • 3,413
  • 7
  • 50
  • 107
  • 1
    Instead of `Private`, use `NonPublic`. See [x:FieldModifier Directive](https://msdn.microsoft.com/en-us/library/aa970905) on MSDN. – Wai Ha Lee Apr 08 '15 at 21:57
  • No problem. :) I was writing an answer about [`x:ClassModifier`](http://msdn.microsoft.com/en-us/library/ms754029) then I reread your question and realised it's not what you were asking. – Wai Ha Lee Apr 08 '15 at 22:09
  • Reading the link you furnished helped me understand that the C# equivalent for the NotPublic is Internal - still not for what I am looking... maybe it's just impossible with WPF? That wouldn't make sense and would be quite annoying... – Will Apr 08 '15 at 22:12
  • Yeah, it does seem that `internal` is the only alternative... – Wai Ha Lee Apr 08 '15 at 22:13
  • Or you could add controls in the code behind dynamically - then members won't get to see them *directly*. e.g. [this](http://stackoverflow.com/q/7884185/1364007) SO question. – Wai Ha Lee Apr 08 '15 at 22:18
  • 1
    Uhg - No thanks; it's not something so major that I would want to take that kind of approach to deal with it... – Will Apr 08 '15 at 22:23
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/74764/discussion-between-wai-ha-lee-and-will). – Wai Ha Lee Apr 08 '15 at 22:30

1 Answers1

22

TL;DR

Most of the time you don't need to worry about this in WPF. However:

  • If you name a XAML element using the x:Name attribute, then you can use the x:FieldModifier attribute to control the visibility of the auto-generated field representing that element. This attribute value is language- and case-specific.
  • If you don't name a XAML element, then don't bother using the x:FieldModifier attribute.

Read on for a more detailed explanation.


Explicit naming and generated fields

If you create a new WPF application project in Visual Studio, it will create a MainWindow class, the XAML for which looks something like this:

<Window x:Class="StackOverflow.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
    <Grid>

    </Grid>
</Window>

If you look at the code-behind class for this window, it will look like this:

// Several using statements...

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

Note the use of the partial keyword to denote this as a partial class. If you navigate to the project's obj\Debug folder using Windows Explorer, you will find a file called MainWindow.g.cs: it is this file that contains the code generated by the IDE from your XAML (it is basically the equivalent of the *.Designer.cs file from WinForms).

Your window has a Grid on it, but note that it is not surfaced directly anywhere in the code for MainWindow. Now edit your XAML to give the Grid a name:

<Grid x:Name="_myGrid">

Compile the application, and open the MainWindow.g.cs file again. You will see that the following line has been added:

internal System.Windows.Controls.Grid _myGrid;

Setting the x:Name property of the element in the XAML has caused the code generator to add a field with that name. The field is marked as internal which means it is accessible to all types in your project, but not to any other projects that reference your project.

So basically, if you do not explicitly name an element in the XAML using the x:Name attribute, the code generator will not create a named field for the element in the code-behind class, and your element will effectively be private (this means that the class itself cannot access the element directly either).


Nameless UI elements can still be accessed from code (if you have an instance)

An element without a name can still be accessed via code, by "walking" the visual tree of a Window instance. For example, because the window's content is set to a single Grid element, you can access that grid through code like so:

Grid grid = (Grid) this.Content;

this here refers to the MainWindow class instance.

WinForms has exactly the same "problem" as WPF in this regard: even controls that are not explicitly named can still be accessed through code. Imagine a WinForms Form with a single Button control on it. You can access that button like so:

Button button = (Button) this.Controls[0];

The fact that the button had a default Modifiers value of "Private" did not stop the code from being able to access it.


The FieldModifier attribute controls generated field visibility

Coming back to WPF, particularly if you're using the Model-View-ViewModel (MVVM) pattern, you will rarely need to explicitly name your elements in the XAML, hence the default behaviour will be fine. However, if you do find that you need to name your XAML elements, and you wish to "hide" these elements, then you can use the x:FieldModifier attribute to set the visibility of an element to private instead of the default internal. The value used for the attribute is language-dependent and case-sensitive, eg. for C#:

<Grid x:Name="_myGrid" x:FieldModifier="private">
Steven Rands
  • 5,160
  • 3
  • 27
  • 56
  • 2
    Good comprehensive answer. It could be improved by moving the `x:FieldModifier="private"` to the beginning as that is the actual answer. – Metalogic Mar 12 '18 at 05:19