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.
Related
I'm creating a WPF application (Visual Studio 2010, C#, MVVM) in which there is a ListBox. I also have a template for the ListBox item, and the template is the source of the issue.
The template is getting pretty big and I want to move it to a resource dictionary. What's stopping me is that this line exists within it:
PreviewMouseRightButtonDown="OnContainingListMouseRightButtonDown"
The method which is run (on a right click event) makes sure the SelectedItem of the ListBox is the same as the one I'm right clicking on: that is to say, it sets the SelectedItem of the ListBox to the one that I right click on.
Obviously if I move the template, this part won't work anymore, but the template is getting rather large.
Is there a way around this?
Edit: I should have mentioned that I reference the items (such as the ListBox by its name) in the code behind as it stands. If I create a new code behind for the resource dictionary, I will no longer have access to those things.
For example, your resource dictionary file name is ListBoxStyle.xaml. You should also create a C# file with name ListBoxStyle.xaml.cs. In your resource dictionary file you should add the following line:
<ResourceDictionary x:Class="CodeBehindNamespace.ListBoxStyle" ...>
It's the reference to your code behind. Your ListBoxStyle.xaml.cs content should be the following:
namespace CodeBehindNamespace
{
public partial class ListBoxStyle : ResourceDictionary
{
public WindowStyle()
{
InitializeComponent();
}
private void OnContainingListMouseRightButtonDown(object sender, RoutedEventArgs e)
{ ... }
}
}
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">
I am using .Net 4.5.2 with WPF and C# 5.0. I created a Window in a WPF project. I would like to copy paste this window in the solution explorer, making a second window, and rename it to a new name.
When I do this, the new (copied) window's InitializeComponent() method always gives me an error. How does one cleanly copy a window (and it's code, etc.) in the solution explorer?
This question was answered partially here: Copy pasting WPF window gives error however the answer did not solve my issue.
My approach (that does not work):
Create a window and title it WindowTest
In the solution explorer, select WindowTest and copy, then paste it into the same project
Rename the new copied Window to WindowTestCopy
In WindowTestCopy, change the x:class property in xaml to be WindowTestCopy instead of WindowTest
Open the code behind in WindowTestCopy, and change any references to WindowTest to WindowTestCopy
Compile
Expected: no errors, the copy (clone) operation is successful
Actual: compile error "Cannot access non-static method 'InitializeComponent' in static context".
I have only this one error. Obviously InitializeComponent() is becoming an ambiguous reference, but it isn't clear to me how to make manual edits to the code to fix this. I wish that VS or Resharper would automatically assist me with this.
UPDATE
WindowTest contains two userControls that I had not mentioned previously. After the copy/paste occurs, for some reason the following xaml elements became malformed within WindowTestCopy:
xmlns:userControls....(ellided)
xmlns:userControls....(ellided)
By deleting these, Resharper determined that the userControl objects were missing xmlns reference tags and asked me if I wanted to import them automatically. I selected yes. After Resharper added the missing xmlns reference tags I was able to compile (all errors disappeared).
I do not have an explanation for why this happened. In my steps to reproduce, I do not edit the xaml and it should therefore be identical to the originating xaml. This is curious behavior, but at least there is a workaround as stated.
When it happened to me, it went like this:
Copy the xaml in solution explorer.
Rename the xaml in solution explorer.
Rename the x:Class in the xaml.
Rename the class name in the xaml.cs.
Rename the constructor in the xaml.cs.
Why is everything still broken? <== (you are here probably)
Realize there is secret voodoo witchcraft underneath.
Copy the contents of the xaml and the xaml.cs to a couple of notepads.
Delete the xaml from solution explorer.
Create a new xaml in the solution explorer with the name you wanted.
Overwrite both the xaml and the xaml.cs with the exact contents of the two notepads.
Errors are gone.
Someone else actually posted and then deleted had the correct idea, but brief on why.
The issue you were running into is due to a duplication of a class within the same project... even though in the solution explorer you can easily copy/paste a WPF form or any other class, user control class, etc. When copied, it suffixes the file names to " - Copy", and if you compile right away will fail. Reason being? In the class itself the class name and window name is the same. So, in the project you have TWO instances of the class and it is choking on that hoping for you to resolve the duplicates. I ran into this exact same thing early on in my WPF development.
So, now that I've explained WHY it is failing, here is what you would need to do, to correct the issue. I created a window in my sample project called "FirstWindowSample", so two files are created FirstWindowSample.xaml and FirstWindowSample.xaml.cs. The .cs version is the code-behind of the window (or user control class, the principle is EXACTLY the same.
If you look in the "FirstWindowSample.xaml" code (the visual-design version), the code will look something like...
<Window x:Class="YourProject.FirstWindowSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="FirstWindowSample" Height="300" Width="300">
<Grid>
</Grid>
</Window>
Notice in the first line
x:Class="YourProject.FirstWindowSample"
You need to change the ".FirstWindowSample" to whatever you want for your new form such as
x:Class="YourProject.SecondWindowSample"
Next, change over to the CODE-BEHIND file "FirstWindowSample.xaml.cs".
namespace YourProject
{
/// <summary>
/// Interaction logic for FirstWindowSample.xaml
/// </summary>
public partial class FirstWindowSample : Window
{
public FirstWindowSample()
{
InitializeComponent();
}
}
}
This is the hard-reference of the class name and its corresponding constructor. Just change these to match the new window name you are expecting (such as sample SecondWindowSample)
namespace YourProject
{
/// <summary>
/// Interaction logic for SecondWindowSample.xaml
/// </summary>
public partial class SecondWindowSample : Window
{
public SecondWindowSample()
{
InitializeComponent();
}
}
}
At the end your project will now have a completely different class name that is not duplicated and cause compile errors. The FIRST time you run into this, yeah... a pain to find out. Once you get it, the next 2-3 times are the "oh-yeah" refresher... After that you sort of do it without thinking about it.
The issue was resolved by deleting all xlmns elements in the xaml and letting resharper automatically add them back in. I am not satisfied with this solution because it is not logical; however, it did resolve the compile error.
What I'm trying to do right now: Modify the Expression Blend UI / Visual Studio, to add a button on one of my dependency properties, and when I click on it, it creates a new trigger.
What is working: I created the button, it appears in the UI, that's fine.
What is not working: I cannot modify the Resource to add a trigger (if I step in, it works but it does not modify on the global resource, only on the instance I think).
I have my main project in Visual Studio, and a property with a button like this:
When the button is pressed this is what happens:
I get my Control that contains that dependency property (Ok).
I searched for the Resource file that contains the Resource I want to modify (Ok).
I update the Resource, but it does not replace the Resource on disk.
I think that it's because I only modify it on memory, so it's in the "air"
I don't know where to go now... I need help
The code behind where I modify the Resource is in an other DLL, the MyLibrary.Design.cs
I'm using Visual Studio 2010 / Blend 4 / .NET 4.0
That's one splution I can figure at the moment, but unfortunately I'm not able to test it now. Still you can check if it works for you.
You can have a class, say ResourceContainer.cs, and collect your control in it as a public static value, and let your control be a button:
public static Button MyButton;
Then you can use it in your code-behind:
If your window is MainWindow.xaml, and, say you need your particular control in a grid, then you probably have something like:
<Window x:Class=...
...(namespace stuff)...
Title="MainWindow" ...(size stuff)...>
<Grid x:Name="MyGrid">
...(your code here)...
</Grid>
...
</Window>
Then in MainWindow.xaml.cs you can use ResourceContainer.cs like this:
...
ResourceContainer rc;
...
MyGrid.Resources.Add("MyKey", rc.MyButton);
...
(when you need it)
rc.MyButton.Triggers.Add(TriggerBase item);
...
Trying to use the solution to this question gives me a weird problem. (Replicated here for convenience)
This is a solution for an issue with ListView swallowing the right-click event and preventing the AppBar from opening - A class which inherits ListView and overrides the OnRightTapped event of ListViewItem:
public class MyListView : ListView
{
protected override DependencyObject GetContainerForItemOverride()
{
return new MyListViewItem();
}
}
public class MyListViewItem : ListViewItem
{
protected override void OnRightTapped(Windows.UI.Xaml.Input.RightTappedRoutedEventArgs e)
{
base.OnRightTapped(e);
e.Handled = false; // Stop 'swallowing' the event
}
}
<CustomControls:MyListView
x:Name="ItemsList"
...
SelectionMode="Single"
RightTapped="MyListView_RightTapped">
</CustomControls:MyListView>
I have implemented a custom control as specified, in a new namespace called CustomControls, exactly as described. I've added that namespace to MainPage.xaml
xmlns:CustomControls="using:CustomControls"
When I then try and reference 'ItemsList' in code behind, I get a compile error
The name 'ItemsList' does not exist in the current context
I've tried building, rebuilding, cleaning the solution, closing and reopening the solution, putting the classes under the main project namespace, all to no avail.
To summarise, MainPage.cs cannot see a Custom Control on MainPage.xaml
UPDATE 2: Reworded the question to remove irrelevant issues. I've also changed the title to reflect the real issue.
I was using Name but should have been using x:Name. Apparently custom controls and user controls need to use x:Name rather than Name to be seen in code behind.
More info on the difference here:
In WPF, what are the differences between the x:Name and Name attributes?