Problem Statement
I have two assemblies, one with primarily data contracts and other containing the UI layer.
The data contracts are public and are used in DataTemplate tags in the XAML in UI assembly.
<DataTemplate DataType="{x:Type contracts:MyContract}">
///other stuff
</DataTemplate>
The data contract is assembly is referred like this:
<xmlns:contracts="clr-namespace:Contracts.DataContracts,assembly=Contracts.DataContracts"
Now I would like to make everything internal DataContract assembly to keep low API surface. I have added UI assembly as friend assembly in the AssemblyInfo class of DataContracts assembly. However, I get the following error in the XAML when I compile the UI assembly:
Only public or internal classes can be used within markup. 'MyContract' type is not public or internal.
I have checked that the InternalVisibleTo is working as I can access other internal stuff in code behind. But in XAML it is not possible.
What are the possible ways to get around this?
Not too clear on what stuff you are trying to extend from an assembly into your application, but when I have XAML in a library, I tend to reference it this way in an application.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/YourAssembly;component/PathInAssemblyIfNeeded/YourXAMLReferences.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
If this does not help, give more information on what you are a trying to accomplish.
Related
I converted an application to a DLL library where my main window became a user control. I was using the method of creating a System.Windows.Application object manually to store my resources but I want move away from that and have my user control be self sufficient, so I can simply do something like:
CustomUserControl control = new CustomUserControl ( object_to_pass);
It will then take care of everything else internally. The basic layout of the control is a frame that hosts multiple pages, like a wizard style app.
I am having two main issues:
Setting up references to the view models
I thought that instead of using System.Windows.Application.FindResource which I extensively used, I will use a similar function on the user control class and pass a reference to my user control around via a singleton.
To do this I use mvvm-light's SimpleIoc container in a class called 'ViewModelLocator' to keep track of all the view models. Problem is, this was a resource in App.xaml loaded from the datacontext binding of the user control.
This cannot be done anymore as the user control itself has to instantiate the resources containing it further along in its own xaml:
<UserControl x:Class="WUP.Views.WarmUpPluginUserControl"
mc:Ignorable="d"
...
...
<!--This will not work-->
DataContext="{Binding Source={StaticResource Locator}, Path=MainWindowLogic}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Path/To/ViewModelLocator/Resource.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Now I have to instantiate the ViewModel locator in the user control code behind and set it up as a resource with Resources.Add. This forces me to switch to dynamic resource for all references to the ViewModel locator from all other views (Pages). Not only does this cause issues described further on, its ugly as I can no longer access member function with the path like I used to:
DataContext="{Binding Source={StaticResource Locator}, Path=MainWindowLogic}">
Referencing resources from xaml, Dynamic vs Static
The resources I use are brushes, colours, templates and converters, each in their own resource dictionary, and I add them in the right order to avoid dependency issues.
The method in the first part works ok for accessing resources from the ViewModel via the reference to the user control in the singleton. The problem now is how to have the resources loaded in each view of the app. I tried the brute force method of sticking them all in Page.Resources or UseControl.Resources but that gave me resource not found errors in some pages despite them existing there. I am looking into why this happens but I am not sure
I then tried Dr.WPF's method of creating a singleton class that you can use to create a single instance of resources and expose them as a dependent property. This forces me to use dynamic resources again for all my views.
This is fine for all my resources except the converters, and I get errors for all converters originally referenced in this way:
Visibility="{Binding Functions.DictatesActions, Converter={StaticResource BooleanToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}"
So I don't know how to deal with this is the dynamic scenario.
I am seriously thinking to abandon this approach and just use System.Windows.Application to store all my resources, despite it potentially causing issues with other user controls in the hosting application (winforms). Please let me know if there is a better way!
I finally managed to fix my issues:
Setting up references to the view models
Here I just had to do it all from the code behind. As I mentioned I used a ViewModelLocator to keep track of all my VMs, so I set up the references as resources in the actual user control constructor:
Resources["Start"] = view_model_locator.Start;
Resources["SelectUnit"] = view_model_locator.SelectUnit;
Resources["HardwareChecks"] = view_model_locator.HardwareChecks;
Resources["ConfigurationChecks"] = view_model_locator.ConfigurationChecks;
...
I then included the reference to the user control in the ViewModel locator as static property:
ViewModelLocator.WarmUpPluginUserControl = this;
Then I could access it from the other views in their code behind like this:
DataContext = ViewModelLocator.WarmUpPluginUserControl.FindResource("Start");
I could also use it in the VMs in the same way that I used the Application.Current.FindResource(). It's not the most elegant solution, but it worked
Referencing resources from xaml, Dynamic vs Static
Here I stuck with the brute force method of including all the resources at the top of every page:
<WUP:WUPPage.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/WarmUpPlugin;component/Resources/Colors.xaml"/>
<ResourceDictionary Source="pack://application:,,,/WarmUpPlugin;component/Resources/Styles.xaml"/>
...
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This did cause issues for me in the beginning where I had resource not found errors, but this was due to some of my dictionaries having dependencies other ones so I had to add them in to the relevant dictionaries via MergedDictionaries.
I did not notice this dependency issue when the application was standalone as all the resources required were already loaded in the application scope.
I intend to replace this with the Dr WPF method, but I would still have to change all my XAML references to dynamic from static then deal with the converters not being able be accessed via dynamic resource references.
I have a grid that I am trying to hook up a visibility converter to. I have a class set up in the HelperObjects namespace, but for some reason the xaml is not picking up the reference. The error message is "The resource BoolToVisConverter could not be resolved" Why won't the xaml pull in this resource? I am sure I am doing something ridiculous here...
Here is the xaml:
xmlns:HelperObjects="clr-namespace:foo.HelperObjects"
...
<Grid Visibility="{Binding isZoneTwoVisible, Converter={StaticResource BoolToVisConverter}}">
The error message is indicating that the resource identified by the key BoolToVisConverter, to which you're trying to bind, cannot be found.
The most likely explanation is that you haven't declared that resource within a scope that can be accessed by your xaml. You'll want to create a StaticResource with a key matching the name you're referencing, within a Resources section of your xaml, the exact location may vary depending on your needs/structure.
Assuming you're doing this within a Window, you could do something like:
<Window>
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVis"/>
</Window.Resources>
<Grid Visibility="{Binding isZoneTwoVisible, Converter={StaticResource BoolToVis}}">
<--...Content...-->
</Grid>
</Window>
Note: I haven't included your namespace in front of the BooleanToVisibilityConverter because this is a class which already exists within the framework.
It may be the case you require slightly different behaviour, or don't have access to that class, in which case you may need to add your namespace when defining the resource, e.g. <HelperObjects:BooleanToVisibilityConverter x:Key="BoolToVis"/>
Potentially useful further info about static resources: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/staticresource-markup-extension
Try to specify the converter in UserControlResources or even parent level Grid Resources. Assuming you are using a UserControl.
And your code is unclear as of where you are defining the StaticResource BoolToVisConverter. But generally heres the process.
Look at this sample code below to define your StaticResorce in the UserControl Resources section. Make sure you set the DataContext where isZoneTwoVisible is residing. I am hoping you implemented ImplementINotifyPropertyChanged and said PropertyChanged on your isZoneTwoVisible or isZoneTwoVisible is a DependencyProperty. Note: BooleanToVisibilityConverter is a class that implements an IValueConverter or if your property is a bool you don't even need that class.
<UserControl.Resources>
<BooleanToVisibilityConverter
x:Key="boolToVisibility"></BooleanToVisibilityConverter>
</UserControl.Resources>
You did all the things said above and it still dosen't work, sometimes I specify the relative Source hoping its residing in a UserControl.
Visibility="{Binding isZoneTwoVisible,Converter={StaticResource boolToVisibility},RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}">
I have a c# wpf-based class library with models, view models, and views, which I am using in multiple add-ins for dektop applications that have APIs which support hosted dockable WPF user controls. From here I can use different controls and launch dialogs to interact with various data structures in the 3rd-party application. My class library doesn't have an App.xaml file.
I have created a ResourceDictionary xaml file that I am using as a main style sheet throughout my views. In my user controls and windows, I reference it:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../Resources/styleMain.xaml"/>
</ResourceDictionary.MergedDictionaries
Inside of this style sheet, I manage my various controls to simplify and unify my views. At the lowest level, I have settings for things like font size, font family, and color. For example, I have several brushes that I use throughout my controls. One looks like this:
<Color x:Key="ColorBase_Primary" R="255" G="0" B="115" A="255"/>
<SolidColorBrush x:Key="Color_Primary" Color="{StaticResource ColorBase_Primary}"/>
This all works great for design and testing. However, I am referencing this class library in multiple other projects. In these other projects, I am hosting the main user control from my class library:
<UserControl x:Class="MyAppHost.ucDockingHost"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
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"
xmlns:local="clr-namespace:MyAppHost"
xmlns:viewModels="clr-namespace:MyClassLibrary.ViewModels;assembly=MyClassLibrary"
xmlns:views="clr-namespace:MyClassLibrary.Views;assembly=MyClassLibrary"
mc:Ignorable="d">
<UserControl.DataContext>
<viewModels:vmMain/>
</UserControl.DataContext>
<Grid>
<views:ucMainGrid ViewModelMain="{Binding}" Property1="SomeValue" Property2="SomeOtherValue"/>
</Grid>
</UserControl>
Here I am setting DependencyProperties from the add-in application that references my class library without any problems, and all of my bindings and control behaviors are working great.
What I am now hoping to do is be able to specify from my add-in (and not the main class library) the basic resource values that are used throughout my style sheet, especially color (as in being able to change the value associated with x:Key="ColorBase_Primary"). This way, I can adapt different implementations to the branding of different clients, etc. I have been searching for a long time for any examples or insight and keep coming up short.
It would be easy enough to simply deploy the entire class library independently with each application and adjust the style sheet directly, but that doesn't seem right at all. Is there some way for me to create a ResourceDictionary in my add-in applications that could supply, modify or and/override the resource values specified in my class library? Or should I do it through a DependencyProperty in my view model? That doesn't seem right either.
Do not use the MergedDictionaries in your library, i.e. remove this:
In your application, you then merge all resource dictionaries from your library in the correct order:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/WpfControlLibrary1;component/Brushes.xaml"/>
<ResourceDictionary Source="pack://application:,,,/WpfControlLibrary1;component/Controls.xaml"/>
</ResourceDictionary.MergedDictionaries>
<SolidColorBrush x:Key="brush">Red</SolidColorBrush>
</ResourceDictionary>
</Application.Resources>
</Application>
You can then simply override any resource you want by defining another resource with the same x:Key like I have done with the SolidColorBrush above. This resource overrides any resource with an x:Key of "brush" defined in either Brushes.xaml or Controls.xaml.
In FluentRibbon project, the XAML file contains:
<Fluent:RibbonWindow xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
I know that RibbonWindow is a class (derived from WPF Window class). It's the Fluent: prefix that I don't understand.
The same type of construct is also used by MahMetro like so:
<Metro:MetroWindow x:Class="FluentTest.MahMetroWindow"
What does the prefix signify in the above cases?
That's the namespace those controls/types live in. Some namespaces are included automatically when Xaml is converted to C#, but anything new that you bring in (either from 3rd party libraries or your own application) has to be explicit.
<UserControl xmlns:customControls="using:MyNamespace.CustomControls"
... more declarations ...
<customControls:MySpecialPanel />
</UserControl>
I have Content directory in main solution.
In content catalog i have two catalogs: ViewModels and Views
In XAML, i have declared:
xmlns:vm ="clr-namespace:AppName.Content"
now, i want get reference to some class in ViewModel catalog:
<DataTemplate DataType="{x:Type vm:LaserPathViewModel}">
I know thats wrong because the namespace of LaserPathViewModel is AppName.Content.ViewModels.
But how get this reference without add next one namespace declaration?
You don't. You have to declare that other namespace. One way to do that is by adding another namespace declaration:
xmlns:vm2 ="clr-namespace:AppName.Content.ViewModel"
And then you can use it like this:
<DataTemplate DataType="{x:Type vm2:LaserPathViewModel}">
But there is another way to declare the namespaces. You can use the XmlnsAttribute which allows you to map multiple .NET namespaces to one X(A)ML namespace. You can find some nice explanation here.