How do I mark a control as 'Private' in WPF? - c#

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?

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">

Related

Why is the root element of MainWindow Window instead of MainWindow?

I notice that the root element in any XAML file (in WPF) seems to be one of:
Window
Page
UserControl
ResourceDictionary
Application
I tried to change the root element to local:MainWindow, but then the project cannot compile, saying the base class of a partial class should be the same. Then I guess the root element is the base class of the actual class? What is the reason for it? Since the root element cannot be changed to the actual class, I cannot access the dependency properties written in MainWindow.xaml.cs. How can those DPs be referenced in XAML?
Besides, I also notice that some third-party themes also provide special window classes, and in that case, the root element is often changed. How is this being achieved? E.g. GlowWindow from HandyControl
I tried to change the root element to local:MainWindow, and then the project cannot compile, saying the base class of a partial class should be the same. Then I guess the root element is the base class of the actual class?
Yes, this is correct, the root element is the base class that can, but does not necessarily have to be specified in code-behind. The connection between the partial classes (compiled from XAML markup and the code-behind file) is specified using the x:Class attribute in XAML, see Code-Behind and XAML in WPF.
The XAML language includes language-level features that make it possible to associate code files with markup files, from the markup file side. Specifically, the XAML language defines the language features x:Class Directive, x:Subclass Directive, and x:ClassModifier Directive. [...]
The partial class must derive from the type that backs the root element.
What is the reason for it?
A code-behind file is not mandatory if there is no custom code, see x:Class.
In existing programming models that use x:Class, x:Class is optional in the sense that it is entirely valid to have a XAML page that has no code-behind. However, that capability interacts with the build actions as implemented by frameworks that use XAML.
Even if there is one, the base class can be omitted, but then again the base class must be determined somehow and that is done through the root element type, see Code-behind, Event Handler, and Partial Class Requirements in WPF.
Note that under the default behavior of the markup compile build actions, you can leave the derivation blank in the partial class definition on the code-behind side. The compiled result will assume the page root's backing type to be the basis for the partial class, even if it not specified.
How can those DPs be referenced in xaml?
Simply use a Binding with a RelativeSource that specifies the MainWindow as AncestorType.
{Binding YourDependencyProperty, RelativeSource={RelativeSource AncestorType={x:Type local:MainWindow}}}
If you assign an x:Name to your window, you could alternatively use ElementName in the binding.
<Window ...
x:Name="MyMainWindow">
{Binding YourDependencyProperty, ElementName=MyMainWindow}
Besides, I also notice that some third-party themes also provide special Window classes, and in that case, the root element is often changed. How is this being achieved?
No it is not. For instance, the GlowWindow is exactly defined like your MainWindow with code-behind and x:Class to refer to it and Window as root element. What you see in the link is that a new derivative of the GlowWindow is created, just like you create MainWindow from Window, they just happen to use the same name GlowWindow, unfortunately. Notice the namespaces.
Try it yourself and create a new window by specifying your MainWindow as root element. It is exactly the same scenario.
I will supplement the answer from #thatguy.
In WPF, it is customary to separate the logic part of the control (which is written in Sharpe) from the visual part (which is written in XAML in the theme template).
For your example, creating a template is redundant. But it could be done like this:
public class MainWindowBase : Window
{
public int SomeProperty
{
get { return (int)GetValue(SomePropertyProperty); }
set { SetValue(SomePropertyProperty, value); }
}
public static readonly DependencyProperty SomePropertyProperty =
DependencyProperty.Register("SomeProperty", typeof(int), typeof(MainWindowBase), new PropertyMetadata(0));
}
public partial class MainWindow : MainWindowBase
{
public MainWindow()
{
InitializeComponent();
<local:MainWindowBase x:Class="****.MainWindow"

How to create White CustomUIItem with DockPanel root?

I have custom controls set in my window with a complicated structure.
I'd like to create a custom control to incapsulate all logic of this control (with inner grids, buttons, etc.).
In a XAML i've added:
<DockPanel AutomationProperties.AutomationId="WidgetName">
In a source code i've added:
[ControlTypeMapping(CustomUIItemType.Custom)]
public class MyWidget : CustomUIItem{}
Add now i'm trying to find the item:
_window.Get<MyWidget>("WidgetName");
It throws with error that couldn't find control with Custom type and 'WidgetName' name.
Also there will be a set of such controls in a window. Is there something like
_window.GetAll<> instead of .Get<>?
Try to use Window.Get(SearchCriteria.All);

Binding singleton property to XAML markup

First of all, let me apologize for the super-noob question about WPF and binding. I have started, a few days ago, to get interested in WPF and its implementation with XAML markup and C# code-behind in Visual Studio Express 2013, and I'm trying to bind the contents of a button to a property that is part of a singleton class. And I can't, apparently.
What I want to do is this:
I have a button saying "Start" and I want, on click, to have the button contents change to a timer + stop, something like "00:00:02 -- Stop" and, on click again, to have it change back to "Start".
I have this class that I have designed as a singleton to prevent it from being instantiated more than once, which contains an instance stopwatch of System.Diagnostics.Stopwatch, and a string property which I change either to "Start" or stopwatch.Elapsed.TotalSeconds.ToString () + "Stop" back and forth.
The problem is that when I try to refer to my singleton class in the XAML markup, like so (to be honest, I don't really know what I'm doing, I'm just inspiring myself from diverse examples I've found over the web):
<Window
...
xmlns:local="clr-namespace:myNameSpace">
<Grid>
<local:mySingleton x:Key="myStringProperty" />
...
</Grid>
</Window>
I get a slew of compiler errors (6 total) of which some say: "The type mySingleton does not include any accessible constructors." and "The "Key" attribute can only be used on an element that is contained in "IDictionary"."
I'm clueless as to what to do, knowing that I don't want to make the constructor public (and therefore get rid of the singleton thing).
Any pointers towards the right direction ?
Thank you guys for your help.
At first you have problem in displaying your string property. You have to use text box and then use property for text binding:
<TextBox Text="{Binding Path=myStringProperty}"/>
Also you have to set your class (view model) to the Window.DataContext and your property have to call OnPropertyChanged event (see: WPF: simple TextBox data binding).
Now about singleton. The singleton shouldn't be used for this. You should have window (view) and that window have to work with some class (view model, you have one instance per one window) and if you want to run more windows together and you always want same result, then inside that view model you should have some static object (it can be that timer, or some class which will handle requests about get result from timer which will inside that class what can be singleton). So it could looks like Window (view) -> view model class -> static timer / class which will works with timer inside itself.

How to use a User Control XAML within a Class Library

I have a sub project wich is a class and contains DataLib.cs and an user controll MediumTile.xaml. This user control will be the generated to an image to use it as tile background. But before I have to change a few thing dynamicly. So how can I get controll above the LayoutRoot inside MediumTile.xaml for example to set the background color?
Something like this:
MediumTile.LayoutRoot.Background = new SolidColorBrush(Color.FromArgb(255, 206, 23, 23);
MediumTile.xaml probably exists in some kind of namespace.
You can find the namespace of the UserControl in the top of the file beside the x:Class declaration.
Typically it'll look something like
x:Class="MyProject.UserControls.MediumTile"
if your project is set up normally.
If you look at the MediumTile.xaml.cs, you should see a namespace like so
namespace MyProject.UserControls
{
public partial class MediumTile : UserControl
...
First off, you will need to reference your subproject.
Assuming you have a project structure like so...
CurrentProject/
-MyPage.xaml
SubProject/
-MediumTile.xaml
Right-click your Solution in Visual Studio and click Properties.
Under Properties select Project Dependencies.
Choose the CurrentProject in the dropdown.
In the Depends On checkbox field, choose the SubProject.
Click on StartUp Project in the side bar.
Make sure Single StartUp Project points to CurrentProject. If not, set it.
Now you're done setting up, you'll need to actually use MediumTile.xaml now.
To use the MediumTile UserControl in another XAML file, you will need to declare
xmlns:customControls="clr-namespace:MyProject.UserControls"
inside the page header, and call
<ListBox.ItemTemplate>
<DataTemplate>
<customControls:MediumTile/>
...
To use this UserControl in another CS file, you will need to import the namespace
using MyProject.UserControls;
at the top of the page, and reference your control like so (depending on your usercontrol's constructor),
MediumTile mediumTile = new MediumTile()
About your LayoutRoot problem, you can simply set the Background color directly on the UserControl. UserControl inherits from Control, which has the Background property already.
I've never done it for windows phone 8, but for normal desktop applications, you can do it by adding the following references:
PresentationCore
PresentationFramework
WindowsBase
Then you can create and access a Control in a normal way.

Is there any unique identifier for wpf UIElement?

For logging user actions in my WPF forms, I added some global event handlers
I want to log exactly which control fire the event, is there some unique identifier for a wpf UIElement like ClientId in ASP.Net?
Why don't you use the Hash Code.
You can compare the values to make sure they are the same object, and its easy to get them with .GetHashCode()
Edit
Obviously this is different every time you run the program, so actually this is prolly a bad idea, unless you want to update the log each time the process is logged. Still possible though
I mean you could store a hash value for each object at the time the log is created, but i don't know if I like that
Seems I found answer to my question, the answer is No, now way to do that, As noted in MSDN here (http://msdn.microsoft.com/en-us/magazine/dd483216.aspx)
Notice that the top-level Window control definition does not contain a
Name attribute. This is significant because, as we'll see shortly,
when you write test automation, an easy way to get a reference to a
control using the MUIA library is to access the AutomationId property,
which is generated by the compiler from the control's Name attribute.
Controls without a XAML Name attribute will not receive an
AutomationId property. This idea is a specific, low-level example of
the importance of considering application design issues for things
such as security, extensibility, and test automation.
One way you can do this is with a custom attribute. Like so...
The UIElement you want to log (UserControl for example):
[UserInterfaceID(ID = "{F436E9B3-C2F6-4CF8-8C75-0A2A756F1C74}")]
public partial class MyUserControl : UserControl
{
InitializeComponent();
// or whatever...
}
Then you need the custom attribute class
[System.AttributeUsage(AttributeTargets.Class)]
public class UserInterfaceIDAttribute : Attribute
{
public Guid ID { get; set; }
}
Now in your code, you can do something like this:
MyUserControl control = new MyUserControl();
foreach(object att in control.GetCustomAttributes(typeof(UserInterfaceAttribute),false))
{
UserInterfaceAttribute uiAtt = (UserInterfaceAttribute)att;
Guid theID = uiAtt.ID;
}
Because you are tagging the control with an attribute in the code, the unique identifier never changes no matter how many times you kill / launch the application.
Of course this is a basic example that shows how to access the ID but you will probably want to use some type of Aspect Oriented Programming. I do exactly this kind of thing using Castle Windsor Interceptors, but that is out of the scope of this post.
Ideally, you will be accessing this ID when there is some kind of event that gets fired. Using interceptors allows you go capture method calls before they are invoked wherein you can look up the ID as shown above and log the action. Alternatively, you can just use
this.GetCustomAttributes(...)
in some method when an event is fired on the control and embed your Logging code there. This pattern is not the best because you're sprinkling cross-cutting concerns all over making some type of Aspect Oriented Programming approach better...but again I digress and it is out of the scope of this post...but you get the idea.
Hope this helps.
You're just looking at adding a Name or x:Name so that the Window/UserControl/Page exposes the control to the rest of the class with the specified name.
<Window ...>
<Grid>
...
<!-- These controls are named, so you can access them directly by name -->
<Button x:Name="btnMyNamedButton" ... />
<Button Name="btnMyOtherNamedButton" ... />
<!-- This control is not named, so you can not directly access it by name -->
<Button ... />
<Grid>
</Window>
public partial class MyWindow : Window
{
public MyWindow()
{
InitializeComponent();
//btnMyNamedButton can be accessed
//btnMyOtherNamedbutton can also be accessed
//The third button can be accessed, but not directly by name.
}
}
Also, you can always use the FrameworkElement.Tag object. It's meant to store arbitrary information, so you can use this as a unique identifier if you want to.
I believe, for logging user actions, you can use the UIAutomation tree and the AutomationElement.AutomationId property, because this approach is supported in all standard UI controls by default. Many third-party controls are also supports the AutomationId for their elements (e.g. grid cells). An AutomationId is useful for creating test automation scripts.
see FrameworkElement.Tag Property
The Tag property can be used. It is of type object and can be set to anything.
I think UIElement.Uid should work. Assign it in XAML and access it in code.
<Label Uid="FirstName"/>
OR in a style property
<Setter Property="Uid" Value="SecondName"/>
Then refer in C# code:
if (Keyboard.FocusedElement is UIElement uiElement)
{
Debug.WriteLine(this, $"{uiElement.Uid}");
}

Categories

Resources