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"
Related
I have an issue, and I don't know if I can do that, or if there is another way to do that. I have an abstract class called "BasePage", it is my .NET Standard library, so I can't access to System.Windows namespace. Its declaration is:
public abstract class BasePage<VR> where VR : new()
EDIT: The class above has a public property to access to VR
So now, for each platform (WPF, Xamarin, etc) I have to create its own implementation of the class begin VR the content control of each platform (UserControl for WPF, for example). I already did it and this is:
public class WindowsBasePage : BasePage<UserControl>
Now, every time I want to create a page to add content to a window, I have to create something like this:
public partial class UserPassPage : WindowsBasePage
{
public UserPassPage()
{
InitializeComponent();
}
}
The problem becomes when I have to set it in the XAML file (the View). I can create it and it builds:
<local:WindowsBasePage x:Class="Bitture.AppManager.Manager.UserPassPage"
[...]
xmlns:local="clr-namespace:Bitture.AppManager.Manager"
mc:Ignorable="d" >
</local:WindowsBasePage>
but I can't add components (like buttons, text, grids, stackpanels, etc). I want to know If I can do it with my current code or there is something I have to change. Because I have to access to the generic type WindowsBasePage that inherits from
class WindowsBasePage : BasePage<UserControl>
I am not sure what you are trying to do here but you can't add UIElements to a custom class of yours that doesn't inherit from any of the common WPF base classes such as for example Panel or ContentControl, and expect it to be rendered as a UserControl or some other WPF control.
This won't work. WPF knows about how to render WPF controls but it doesn't know how to render a custom BasePage<UserControl>.
I am currently trying to add functionality to a ResourceDictionary, by declaring it's x:Class and linking an On Click event to a function within said class. Here is an example:
Where I link the x:Class
Where I link an x:Class function to an On Click event:
And the source for my x:Class:
using System;
using System.Windows;
namespace VoidwalkerEngine.Editor.Resources.Themes.Styles
{
public partial class VoidwalkerCellBrowserTreeView : ResourceDictionary
{
public VoidwalkerCellBrowserTreeView()
{
InitializeComponent();
}
private void BaseTreeView_NewFolder_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Test"); // This should be fired when I click on "New Folder"
}
}
}
A picture of the menu item just before I click it:
After I click the menu item, it should print "Test" to the console. However, nothing happens. Clearly, I must be doing something wrong. I also found a similar question to mine located here: Control event not firing from class linked to resource dictionary wpf
And their suggestion was to add an extra line to the .csproj file, which I did:
However, this still is not working. Obviously I'm still not linking something correctly, I'm just at a loss of how to proceed from here. Does anyone know how to properly link a ResourceDictionary with it's x:Class? My project is throwing no errors, and Visual Studio even auto-completed the BaseTreeView_NewFolder_Click function into the x:Class file, so I know the source file itself is attached just fine.
EDIT 1:
Here is the full XAML ResourceDictionary: https://pastebin.com/8UepKGTa
EDIT 2:
After testing a few things, I noticed something very peculiar. Apparently, any console command I place in the default constructor will be fired, but no methods will be fired. Here is an image:
I'm seriously at a loss right now. The class IS linking just fine, but for whatever reason, the functions won't link to it.
You don't need to manually add that stuff in csprroj.
It works rather like a window.
You need the class to be referred to in the resource dictionary:
The code file must inherit from resourcedictionary and be a partial class:
namespace MapEditor
{
public partial class TerrainResources : ResourceDictionary
And you need the initializecomponent stuff:
public TerrainResources()
{
InitializeComponent();
}
The class properties build action should be compile compile
And of course the resource dictionary must be build action page.
Looking at your code, at first glance it looks like it ought to work.
You have quite a long namespace.
VS doesn't get on well with deep folder structures and very long namespaces.
Where is your menuitem?
I'm using my code there for a loaded event, and that's used from datatemplates inside that resource dictionary:
<DataTemplate DataType="{x:Type local:SwampVM}">
<Polygon Points="{Binding Points}"
Fill="YellowGreen"
local:TerrainProp.TerrainCanvas="{Binding RelativeSource={RelativeSource AncestorType={x:Type Canvas}}}"
FrameworkElement.Loaded="Terrain_Loaded"
Your contextmenu would have to be a resource in the resource dictionary to work with that event.
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">
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}");
}
is there a way to have a WPF UserControl Class to be a class with a Template type?
e.g.
public partial class MyControl : UserControl
should be:
public partial class MyControl<MyData> : UserControl
as I always get compile errors that MyControl than has no reference to InitializeComponents which is in the automatic generated part of the class.
The problem is, that I can't tell in the xaml part of the class that the usercontrol is of type MyControl<MyData>. I even tried MyControl<MyData> ...
No, you can't declare a generic type in XAML. From http://social.msdn.microsoft.com/forums/en-US/wpf/thread/02ca0499-80af-4c56-bb80-f1185a619a9e:
Hello, you can use generic as long as
you don’t use XAML. But unfortunately,
if you want to use XAML to define your
control, you can’t use generic…
You can create a control in XAML that inherits from a generic type by putting a x:TypeArguments attribute on the root tag, but the control itself must be concrete.