Modify Resource in Design Time, with code behind in WPF project - c#

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);
...

Related

WPF ResourceDictionary x:Class Function Not Firing

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.

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

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

how to copy paste a WPF Window (clone) and not have errors

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.

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.

Expression Blend 4 accidently deleted App.xaml - now new problems with static main method

I accidently deleted by App.xaml and App.xaml.cs files in my blend 4 project.
So I made a new one successfully - however now when I go to run the app it says :
ok done that thanks, now it says Program " does not have a static main method suitable for an entry point.
How do I make its main method - where should it exist , and what should it look like!?
How do I make its main method - where should it exist , and what should it look like!?
In a WPF application, you typically don't create the Main method yourself. It is generated based on App.xaml. You need to make sure that the build action for App.xaml is set to ApplicationDefinition and the custom tool is set to MSBuild:Compile, otherwise the Main method won't be generated (this is for VS, no Blend, but I assume Blend has something similar)
in app.xaml add StartupUri to Main Window/Page etc.
<Application ...
StartupUri="Menu.xaml"

Categories

Resources