I've got a Resource Dictionary called EditorResources.xaml and it contains the following code:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:editors="clr-namespace:MyCompany.Editors">
<DataTemplate x:Key="PasswordEditorDataTemplate">
</DataTemplate>
</ResourceDictionary>
But when I include this line of code in a class:
private EditorResources res = new EditorResources();
It doesn't compile my project. I doesn't display any Errors List view in the .NET IDE but when I look at my Output view, it displays the following error:
error CS0246: The type or namespace name 'EditorResources' could not be found (are you missing a using directive or an assembly reference?)
But, I don't believe the reference is missing as both the resource dictionary and class are in the same project (same level) and I can clearly see the EditorResources type highlighted as Type and IntelliSense kicks in as expected when I hit my '.' key and it displays properties relevant to a Resource Dictionary i.e. .MergeDictionaries, etc...
UPDATE:
if like me, you've stored your resource dictionary (editorresources.xaml) in a sub-folder (Editors), and your assembly is called 'MyCompany.Activity.Shared' for example, don't forget to include the sub-folder in the component's part so that when you call the code provided by #DrewNoakes, you can call the following without any problems:
var res = new ResourceDictionary
{
Source = new Uri("/MyCompany.Activity.Shared;component/editors/editorresources.xaml",
UriKind.RelativeOrAbsolute)
};
Once you have your object, you can access your DataTemplates or other via code.
Seems like you have a EditorResources.xaml file but not a corresponding EditorResources.xaml.cs file.
In this case, the type your XAML file defines is ResourceDictionary. If you wish to have a subclass of that, you need to define the partial backing class and add the relevant attributes in the XAML to link them together.
If you don't need any C# code behind the scenes, just load the ResourceDictionary directly.
Some code like this might help:
var dict = new ResourceDictionary
{
Source = new Uri("/YourAssemblyName;component/EditorResources.xaml",
UriKind.RelativeOrAbsolute)
}
Related
I have a very simple wpf custom control that defines two constructors:
public class SomeControl : System.Windows.Controls.Button
{
public SomeControl()
{
}
public SomeControl(ISomeService service)
{
}
}
This control is defined in a class library called ControlLib. The ISomeService interface is defined in another class library project called ServiceContracts and ControlLib has a reference to it.
The third project in the solution (called FrontEnd) is a simple WPF-project and i place the custom control on the MainWindow like this:
<Window x:Class="FrontEnd.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:ControlLib;assembly=ControlLib"
Height="450"
Width="800">
<Grid>
<controls:SomeControl />
</Grid>
Until now, everything works fine and as intended. The project structure looks roughly like this:
The problem occurs when i give the costum control a name. When i set the Name attribute like this <controls:SomeControl x:Name="OhWhy" /> the project does not longer compile. I get the following error:
Unknown build error, 'Cannot resolve dependency to assembly 'ServiceContracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it has not been preloaded. When using the ReflectionOnly APIs, dependent assemblies must be pre-loaded or loaded on demand through the ReflectionOnlyAssemblyResolve event. Line 8 Position 31.' FrontEnd C:\01_Data\Tmp\SomeSolution\FrontEnd\MainWindow.xaml 8
My question is: Why does it break when i add the Name-attribute and why does it work in the first place?
I know that setting the Name-attribute will add a field to the designer generated *.g.i.cs file to access the control from code behind, but compilation also breaks when i do the same in a template in some resource dictionary without any designer generated files.
The following things solved the problem but im not exactly sure why:
Adding a reference in FrontEnd to ServiceContracts
Making the parametrized constructor internal
This is caused by the XAML compiler. Please refer to the following question for more information.
Cannot resolve dependency to assembly 'PostSharp' because it has not been preloaded
The solution is to add a reference to ServiceContracts.dll from the WPF application project.
I think what is happening with giving it a name is that you get a local member variable of type SomeControl in FrontEnd. This pulls in the dependency. Before that, you just have baml in a resource and when the baml is deserialized at runtime, the SomeControl type is already loaded in the AddDomain and can be dynamically instantiated using reflection.
I want my user control to display data when I am viewing it in the WPF designer in Visual Studio.
The ViewModel does not have a default constructor, so I wrote my own static TestData class to construct the model and all of its dependencies.
public static class TestData
{
public static ELabelViewModel ELabelViewModel
{
get
{
return new ELabelViewModel
(
new ControlPanelGridLine(TestData.ELabel),
new SerialPortFactoryImpl(),
new Repository(),
new PriceLabelGenerator(TestData.IPriceLabelViewModelFactory)
);
}
}
// Other static getter methods
This all compiles with no problems. However, problems start when I add this in the XAML:
d:DataContext="{x:Static local:TestData.ELabelViewModel}"
The XAML editor puts a curly blue line under my d:DataContext attribute, and in the error list I see:
Error 7 Method not found: 'Void
ELabel.Manager.ViewModels.ELabelViewModel..ctor(ELabel.Manager.ViewModels.ControlPanelGridLine,
ELabel.Control.ISerialPortFactory, ELabel.Data.IRepository,
ELabel.ImageGeneration.IPriceLabelGenerator)'.
My interpretation of this is that it is finding the TestData class, and also finding the TestData.ELabelViewModel property. It just cannot resolve the constructor that is being called inside the getter.
Why can it not find the ELabelViewModel constructor? To confirm my code was OK, I made this test view model the actual data context by using DataContext= instead of d:DataContext=. In this case I opened the application and confirmed that, at runtime, all works as expected: TestData.ELabelViewModel was invoked, the code insider the getter function ran, and it used this view model. It's just the designer that is failing to run the code.
The ELabelViewModel class is in a separate assembly called ELabel.Manager.ViewModels. Is the editor failing to fully load this assembly?
Later Edit
I tried moving this TestData class to the ELabel.Manager.ViewModels assembly (the same assembly that the constructor resides in). Sure enough, it now works fine, and I can see test data when viewing the control in the editor. Curious.
I've double-checked that the ELabelViewModel class and the constructor are public (Which of course it is, otherwise I never would have been able to build the application).
I implement all my viewmodel classes like this:
<UserControl x:Class="MyApp.Views.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:vm="clr-namespace:MyApp.ViewModel"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Height="607" Width="616">
<UserControl.DataContext>
<vm:TestData/>
</UserControl.DataContext>
I have really strange problem. I've created WPF project in 2012 or 2013 VS it doesn't matter. I use .NET 4.5.
I add a new Activity (Workflow class) to that project. Its name is CustomActivity.
Then I add a new class that has got an attached property, example below:
public class AttachedObject : DependencyObject
{
public static readonly DependencyProperty NameProperty = DependencyProperty.RegisterAttached(
"Name",
typeof(string),
typeof(AttachedObject),
new FrameworkPropertyMetadata(
string.Empty,frameworkPropertyMetadataOptions.AffectsRender));
public static void SetName(ContentControl element, string value)
{
element.SetValue(NameProperty, value);
}
public static string GetName(ContentControl element)
{
return (string)element.GetValue(NameProperty);
}
}
The last step is to change MainWindow class that way:
public MainWindow()
{
InitializeComponent();
var activity = new CustomActivity();
}
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1;assembly=WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ContentControl wpfApplication1:AttachedObject.Name="MainArea"/>
</Grid>
</Window>
The problem is it doesn't compile because of below error:
Error 1 The type or namespace name 'CustomActivity' could not be found (are you missing a using directive or an assembly reference?) WpfApplication1\MainWindow.xaml.cs 13 32 WpfApplication1
CustomActivity has a default namespace. In obj folder there is CustomActivity.g.cs generated, so I have no idea what's going on.
It's 100% reproducible. When I remove using of CustomActivity or using of AttachedObject from xaml then the problem disappear.
Try replacing this:
xmlns:wpfApplication1="clr-namespace:WpfApplication1;assembly=WpfApplication1"
with this
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
The error you're seeing is due to a "known" issue in WPF applications that xaml namespaces that reference clr namespace from current assembly your in don't require the full assembly qualified name. If you were to declare a xaml namespace that references a clr namespace from another assembly, than you would have to specify the full name (with the ;[assemblyname] syntax).
Workflow Foundation has nothing to do with it.
EDIT:
Didn't realize it was a xaml activity.
But still, you can make it work, maybe, with a few hacks, but I wouldn't recommend it.
The reason you get that error is due to the different code generation and build action VS uses when creating xaml artifacts for WPF (Page):
System.Windows.Application.LoadComponent(this, resourceLocater);
and when creating xaml activities (XamlAppDef):
typeof(CustomActivity).Assembly.GetManifestResourceStream(resourceName);
If you turn your CustomActivity xaml build action to Page, the whole thing will compile - but i'm guessing something else might be broken someplace else...or not, who knows. My guess is that these two kinds of xaml were not meant to live together in a VS WPF application project template. But you can still define activities in a WF activity library, that way your activities will also be more easily reusable for other projects, WPF, console or even services.
I have the same issue under Visual Studio 2017.
The problem in my case is that Visual Studio is not compiling the Workflow activities before the code that use them.
To fix it, what I did is to move all workflows to other project dll, so visual Studio is forced to compile the workflows before the classes that make use of them.
I am currently saving my .NET FX 4.0.1 StateMachine activity like this:
var sb = new StringBuilder();
var xamlWriter = ActivityXamlServices.CreateBuilderWriter(
new XamlXmlWriter(new StringWriter(sb),
new XamlSchemaContext()));
XamlServices.Save(xamlWriter, activityBuilder);
return sb.ToString();
This works fine and the generated XAML looks good. Unfortunately, it is invalid. I can read it back in using ActivityXamlServices.Load but when I execute it, it says that it doesn't know the properties defined in the workflow. Opening it in the Visual Studio designer yields the same errors:
Compiler error(s) encountered processing expression "ActiveCall". "ActiveCall" is not declared. It may be inaccessible due to its protection level.
Through comparing the original XAML with the XAML produced by my code, I found out how to fix this problem. I have to have this tag before the StateMachine tag:
<mva:VisualBasic.Settings>
Assembly references and imported namespaces for internal implementation
</mva:VisualBasic.Settings>
By the way:
The text inside the tag must be exactly like this, otherwise there will be an error when opening the WF in VS:
Failed to create a 'Settings' from the text 'FooBar'
Question:
What do I have to change in my code to have this tag in the generated XAML?
I think I found the answer.
I have to use the following code before calling Save:
VisualBasic.SetSettings(activityBuilder, new VisualBasicSettings());
If the activityBuilder has been created from a DynamicActivity it is even better to use the following code:
VisualBasic.SetSettings(activityBuilder,
VisualBasic.GetSettings(dynamicActivity));
If this is not used, namespaces that are only needed for extension methods are not written to the XAML and will lead to an error when loading and executing the XAML.
I summarized my findings on code project.
How do I access the contents in a resource dictionary using C#?
For example, here is my code in XAML:
<system:String x:Key="NewGroup">New Group Name</system:String>
And I want to access it here in C#:
private void OnAddGroup(object sender, ExecutedRoutedEventArgs e)
{
BooksGroupInfo group = new BooksGroupInfo();
group.GroupName = "New Group" + TheTabControl.Items.Count;
TabItem tab = AddGroup(group);
_currentLibrary.addGroup(group);
_currentLibrary.CurrentGroup = group;
}
Instead of typing "New Group" in C#, I would like to replace that and have access in the resource dictionary in XAML. So the command will automatically get the Name that is in the resource dictionary.
I've tried a couple of solutions like:
(System.String)this.FindResource("NewGroup");
Application.Current.Resources[typeof(System.String)];
and so on...
but they do not seem to work.
I am doing a Localization using locbaml and it doesn't parse the Text/Name on C# (or I don't know how to) and that was the only solution I thought was possible.
Usually using FrameworkElement.FindResource like this: string s = this.FindResource("NewGroup") as string; works. It is more likely that the resource with the key "NewGroup" does not exist in the scope of your control or window (whatever this is). You must make sure that the resource is there. E.g. if your resource comes from another file you have to use MergedDictionaries. You can test if the resource is actually acessible try to acess it from XAML that belongs to your codebehind where OnAddGroup is definde.
I hope that makes sense.