I have custom classes that I currently instantiate within App.xaml as resources. I want to move these custom objects into a Merged ResourceDictionary, where they are used within styles, and keep them close to where they are used.
Here's an example of what I want, arbitrarily using fake converter objects, but they could be any custom object...
App.xaml (namespace declarations ommitted):
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Merged.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="SomeStyle" TargetType="SomeControl">
...
</Style>
...
</ResourceDictionary>
And then in Merged.xaml (namespace declarations omitted):
<ResourceDictionary>
<cvt:VisibilityToBoolConverter x:Key="BoolToVisibility" Inverted="True"/>
<cvt:VisibilityToBoolConverter x:Key="NotBoolToVisibility" Inverted="True" Not="True"/>
<Style x:Key="SomethingThatUsesVisibilityToBoolConvertersStyle" TargetType="SomeOtherControl">
....
</Style>
</ResourceDictionary>
The issue I'm seeing is this: when I create the BoolToVisibility and NotBoolToVisibility objects (as well instantiating other objects that are instances of custom classes I have created) just as part Application.Resources, they are created and all the references work as expected; however, when I move these into a Merged Resource Dictionary (as I have in the sample above), I get a malformed Application exception when the Silverlight application loads.
I belive this to be an issue with how objects are instantiated differently in Merged Resource Dictionaries (I belive it is more of a lazy-load approach), but I would expect it to work nonetheless.
I am very much able to add Style objects, DataTemplates, ControlTemplates, and the like. But when I want to create instances of custom classes that are named or keyed using Xaml, it works great inside of App.xaml's Application.Resources directly and Application.Resources/ResourceDictionary as well. As soon as they are moved into a merged dictionary, it throws the exception. Is this by design? Any other ideas that work? Thanks in advance...
I have FINALLY worked around this. I took a page out of how the App class gets instantiated and did the same with the Merged.xaml file. I created a class with "code-behind" for Merged.xaml, called Merged that inherits from ResourceDictionary. I then (borrowing from App.g.cs), I initialize the component by loading from the Merged.xaml file during construction.
Merged.xaml.cs:
public partial class Merged : ResourceDictionary
{
private bool _contentLoaded;
public Merged()
{
InitializeComponent();
}
public void InitializeComponent()
{
if (_contentLoaded)
{
return;
}
_contentLoaded = true;
System.Windows.Application.LoadComponent(this, new System.Uri("/MySilverlightApp;component/Merged.xaml", System.UriKind.Relative));
}
}
The Merged.xaml file looks exactly the same as in my original question, using ResourceDictionary as it's root element.
The App.xaml is SLIGHTLY different. Instead of referencing the Merged ResourceDictionary by using the ResourceDictionary element and the Source attribute, I simply referenced the Merged class:
<Application.Resources
xmlns:msa="clr-namespace:MySilverlightApplication">
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<msa:Merged />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="SomeStyle" TargetType="SomeControl">
...
</Style>
...
</ResourceDictionary>
</Application.Resources>
Viola! It works.
You can avoid the extra codebehind by setting the build action for your shared xaml to "Resource" and reference it as /AssemblyName;component/Shared.xaml. For a reason that mostly escapes me, referencing it in this way allows custom object instantiation to work properly.
Related
My problem is that I'm referencing a static resource, primary (as a color) in my MainPage, but when I have MainPage in the constructor of App() for Dependency Injection, primary isn't defined yet because its reference is loaded from App.xaml.
public partial class App : Application
{
//MainPage gets loaded first with a reference to
//StaticResource Primary which doesn't exist yet.
//An exception is thrown in mp.InitializeComponent()
//complaining about StaticResource Primary not existing
public App(MainPage mp, MainPageViewModel vm)
{
//StaticResource Primary is defined in here
InitializeComponent();
MainPage = mp;
MainPage.BindingContext= vm;
}
}
I can work around this by doing the following:
Adding Colors (and Styles) to the ResourceDictionary of MainPage:
<ContentPage.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/Styles/Colors.xaml" />
<ResourceDictionary Source="Resources/Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ContentPage.Resources>
But then I also have to add a ResourceDictionary in Styles.xaml referencing Colors.xaml:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colors.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This approach is fine for a small app, but I have many view pages utilizing Colors.xaml in the app I'm developing. Is there any way I can globally reference Colors.xaml and Styles.xaml in MauiProgram.cs? (So they're registered before DI takes place with MainPage)
You don't need to reference colors. What you actually ask is:
"How to resolve services with dependency injection".
You can add to any class IServiceProvider, and "ask" for a service to be provided to you. When you "ask" for your View, it will cascade (or to use your term: dominos down the line) and call the required constructors for ViewModel. And if your ViewModel uses something like ISettings, It will call the constructor of your MySettings class that implements the interface and so on.
The whole idea of using DI is to let it do this work for you.
You should not pass pages, in the constructor of you Application class. Why? Because when you construct it, LoadFromXaml is not called yet. But nothing is stopping you from having IServiceProvider in your constructor. And request services after Initialization is done.
This is good theory: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-7.0
This is good example: https://stackoverflow.com/a/32461714/6643940
I can't seem to get the #CssImport annotation working when the component class is in a separate jar. (Main web project is Vaadin 18)
I checked out the addon starter:
https://github.com/vaadin/addon-starter-flow
And adjusted the TheAddon class to add a css class name:
#CssImport("./theaddon.css")
public class TheAddon extends Div {
public TheAddon() {
setText("Hello");
addClassName("theaddon");
}
}
I then added the theaddon.css file to:
src\main\resources\META-INF\resources\frontend\theaddon.css
With the styles:
.theaddon {
color:Red;
}
However when I use the addon, I do not see the style applied. I do see the style if I extend the TheAddon class within my web project. So this leads me to believe there's some classpath magic that isn't happening correctly.
Argh - the issue was that the vaadin.whitelisted-packages property was set. Thus Vaadin was not scanning / finding the components when building the front-end. Correcting this property fixed it.
Lets say I have a main component that I want to initialize in a specific way and I have it's constructor take an Interface for this purpose. Is there a way to define the implementation I want for this interface in my xml and in turn inject that into the main component as a parameter? Like this:
public interface IComponent2 {
void DoStuff();
}
public class ConcreteCompImpl2 : IComponent2 {
IComponent1 _comp;
public ConcreteCompImpl2(IComponent1 comp) {
_comp = comp;
}
public void DoStuff(){
//do stuff
}
}
<component id="component1" service="ABC.IComponent1, ABC" type="ABC.ConcreteCompImpl1, ABC" />
<component id="component2" service="ABC.IComponent2, ABC" type="ABC.ConcreteCompImpl2, ABC" >
<parameters>
<component1>???</component1>
</parameters>
</component>
Or am I thinking about this all wrong and there is a much simpler way to accomplish this? The main thing I want to be able to do is configure what 'kind' of IComponent1 will get injected whenever an IComponent2 is created. Thanks
If you have only one concrete class implementing IComponent1, then it will automatically be injected when you resolve IComponent2.
If you have several classes implementing IComponent1 and want a specific one every time IComponent2 is resolved, you need to specific an inline dependency:
container.Register(
Component.For<IComponent2>()
.ImplementedBy<Component2>()
.DependsOn(Dependency.OnComponent<IComponent1, YourSpecialComponent1>())
);
I'm not completely sure you can specify this in the XML configuration, but honestly you should use the Fluent API instead of the XML configuration unless you have a really compelling reason to use it. As mentioned in the above link:
Ability to register components in XML is mostly a leftover from early days of Windsor before Fluent Registration API was created. It is much less powerful than registration in code and many tasks can be only accomplished from code.
I'm new to the world of WP7 and .net programming for that matter and i need help. I have a custom component that has a property that uses template binding.
<TextBlock Text="{TemplateBinding Info}" FontSize="20" Grid.Row="1" TextWrapping="{TemplateBinding TextWrap}"/>
I defined the dependency properties in the .cs file.
Now in my page.xaml i placed the custom component like so,
<rounded:RoundedImageView x:Name="pivotItem1" Info="Test bind" BorderBrush="White" ImageSrc="Images/default_service.png" TextWrap="Wrap"/>
Which works ok, now I want the Info and TextWrap properties to be changed dynamically based on some external variables so I did this
<rounded:RoundedImageView x:Name="pivotItem1" Info="{Binding sopInfo}" BorderBrush="White" ImageSrc="Images/default_service.png" TextWrap="{Binding wrap}"/>
where sopInfo and wrap are the external variables defined in the backing cs file of the page. But this doesn't work, the Info and TextWrap values do not change. How can i achieve it?
Thanks
Try to set the DataContext of your Page like this:
<phone:PhoneApplicationPage
DataContext="{Binding RelativeSource={RelativeSource Self}}" />
Then make sure that sopInfo and wrap are public DependancyProperties of your Page class.
public static readonly DependencyProperty sopInfoProperty =
DependencyProperty.Register(
"sopInfo", typeof(String),
);
public string sopInfo
{
get { return (string)GetValue(sopInfoProperty); }
set { SetValue(sopInfoProperty, value); }
}
When I access a JPA managed date value from JSF, it comes back with an javax.faces.component.UdateModelException saying
'Cannot convert 01.01.10 00:00 of type class java.util.Date to class org.apache.openjpa.util.java$util$Date$proxy
Using a JPA-managed date value (which means it is proxied) works fine when it is used directly from the EL likes this:
'<h:outputLabel value="MyDateValue" for="input"/>
'<h:inputText id="inputDate" value="#{bean.myDate}"/>
However, it causes trouble when trying to use it with composite components
and gives back the following converter exception and thus can't update the model...
The (simplified) JSF composite component inputDate.xhtml
<head>
<title>A date input field</title>
</head>
<composite:interface>
<composite:attribute name="dateValue"/>
</composite:interface>
<composite:implementation>
<h:outputLabel value="MyDateValue" for="input"/>
<h:inputText id="input" value="#{cc.attrs.dateValue}"/>
</composite:implementation>
Assumption:
It seems the proxy replacement in OpenJPA is handled differently when the value is being accessed from inside a composite. My guess is the EL-resolver handles calls to object values differently when it is passed to composites. Passing it to composites means it is first accessed within the composite, which is too late and the required replacement of the proxy is not accomplished (thus the converter exception)
So I tried to change the Expression Language for MyFaces, but it didn't work in Websphere, even though I changed the class loading to parent last and provided el-impl and el-api from glassfish in the lib folder and inserted the necessary context-param for MyFaces
How do you guys use JPA-managed dates (or other proxied entities) in composite components???
If you are using the sun EL implementation you might use the following ELResolver which works around this issue:
public class BugfixELResolver extends ELResolver {
//...
#Override
public Class<?> getType(ELContext anElContext, Object aBase, Object aProperty) {
if (aBase.getClass().getCanonicalName().equals("com.sun.faces.el.CompositeComponentAttributesELResolver.ExpressionEvalMap")){
Object tempProperty=((Map)aBase).get(aProperty);
if (tempProperty!=null&&tempProperty.getClass().getCanonicalName().equals("org.apache.openjpa.util.java.util.Date.proxy")) {
anElContext.setPropertyResolved(true);
return java.util.Date.class;
}
}
return null;
}
}
Add it to the faces-config this way:
<el-resolver>
xxx.BugfixELResolver
</el-resolver>
This workaround can also be used in environments where you can not change the EL implementation (like websphere etc.).
Here is the workaround. The problem seems to be WebSpheres' ExpressionLanguage Implementation or rather the order resolvers are executed. Registering the JBoss EL implementation works and resolves the date proxies before calling the composite component. I also tried the Glassfish EL, but it didn't work either...
Registering a alternative EL is quite strange: The setting in web.xml for MyFaces is
<context-param>
<param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
<param-value>org.jboss.el.ExpressionFactoryImpl</param-value>
</context-param>
Additionally under WebContent/META-INF/services/ a file named javax.el.expressionFactory is needed with this single line org.jboss.el.ExpressionFactoryImpl. The class comes from jboss-el-2.0.2.CR1.jar
(sorry, couldn't find the link to a maven repo)
I will keep you updated once I find a better solution...