MEF + SL4 question - silverlight-4.0

I'm working on an app in the Silverlight 4 RC and i'm taking the oppertunity to learn MEF for handling plugin controls. I've got it working in a pretty basic manor, but it's not exactly tidy and I know there is a better way of importing multiple xap's.
Essentially, in the App.xaml of my host app, I've got the following telling MEF to load my xap's:
AggregateCatalog catalog = new AggregateCatalog();
DeploymentCatalog c1 = new DeploymentCatalog(new Uri("TestPlugInA.xap", UriKind.Relative));
DeploymentCatalog c2 = new DeploymentCatalog(new Uri("TestPlugInB.xap", UriKind.Relative));
catalog.Catalogs.Add(c1);
catalog.Catalogs.Add(c2);
CompositionHost.Initialize(catalog);
c1.DownloadAsync();
c2.DownloadAsync();
I'm sure I'm not using the AggregateCatalog fully here and I need to be able to load any xap's that might be in the directory, not just hardcoding Uri's obviously....
Also, in the MainPage.xaml.cs in the host I have the following collection which MEF puts the plugin's into:
[ImportMany(AllowRecomposition = true)]
public ObservableCollection<IPlugInApp> PlugIns { get; set; }
Again, this works, but I'm pretty sure I'm using ImportMany incorrectly....
Finally, the MainPage.xaml.cs file implements IPartImportsSatisfiedNotification and I have the following for handling the plugin's once loaded:
public void OnImportsSatisfied()
{
sp.Children.Clear();
foreach (IPlugInApp plugIn in PlugIns)
{
if (plugIn != null)
sp.Children.Add(plugIn.GetUserControl());
}
}
This works, but it seems filthy that it runs n times (n being the number of xap's to load). I'm having to call sp.Children.Clear() as if I don't, when loading the 2 plugin's, my stack panel is populated as follows:
TestPlugIn A
TestPlugIn A
TestPlugIn B
I'm clearly missing something here. Can anyone point out what I should be doing?
Thanks!

I think most of what you are doing is fine. Although ObservableCollections do support notifications of individual elements being added and removed, MEF doesn't take advantage of this. In your case it will simply clear the collection and then add all the plugins. Since you are using OnImportsSatisfied for the change notification, you don't even need an ObservableCollection. You could just use an IEnumerable for your import.
To add flexibility in downloading different xaps, I would expose a service in your container that can be imported and that provides the functionality to download a xap given a url. Then any component in your container can trigger a download, and the url to download can come from whatever source you deem appropriate.

Related

ASP.NET Core 6 MVC - access a view (.cshtml) page from a different class library project

We have a scenario where we have to move couple of our view pages away from our Web API project to a separate class library. Where this class library will be be consumed by different Web API projects that needs to load these shared View pages as part of the functionality. I have been looking for a day now but cannot find a way how to do it.
The view pages work with no problems when accessed from within the Web API project but we have now moved these View pages into our existing common library (a class library) and added it as a reference to the Web API project. Basically when we build the application with the common class library containing the views (we changed the property to Content so it gets added on build time), it gets built and copied into the bin folder. So from this, we could say that the view files should be reachable as its just within the project assembly bin folder.
What happens now is that even if setting the web application builder to specify the Content directory to point to this, it still cannot see the View pages and I get an error
The view was not found
Code:
var builder = WebApplication.CreateBuilder(new WebApplicationOptions {
Args = args,
ContentRootPath = PlatformServices.Default.Application.ApplicationBasePath
});
What bugs me is that the same /Views folder is generated when we put back the View pages back to the Web API project. Same structure and files. Only when put to another project, it now cannot recognize it. Having the view pages on the Web API works while putting it to another project does not.
This is a required structure that we need to implement without the use of a RCL but would still work when referenced by different Web APIs. This may seem odd but this is what we need to do and if possible with only minimal changes.
Your help is very much appreciated!
After a few tries, we we're able to do this by setting the resource object to Embedded Resource and implementing the ManifestEmbeddedFileProvider in the common library to virtually map the location when it gets published as a NuGet. In this case, say like we have a folder named /StaticResources in our common lib. In the sample code below, Program refers to your program assembly or any class object within your application.
Code:
// Get embedded file assembly path to allow our static files to be read by the consuming apps
var manifestEmbeddedProvider = new ManifestEmbeddedFileProvider(
typeof(Program).Assembly,
"/StaticResources");
// Sets the `/StaticResources` folder to be servable like a wwwroot folder
app.UseStaticFiles(new StaticFileOptions {
FileProvider = manifestEmbeddedProvider,
RequestPath = "/Resources"
});
// Use it like this
<script src="/Resources/MyScrtipt.js"></script>
For the View() to work, assuming your views are in /StaticResources folder.
var viewsFileProvider = new ManifestEmbeddedFileProvider(
typeof(Program).Assembly,
"/StaticResources");
app.UseStaticFiles(new StaticFileOptions {
FileProvider = viewsFileProvider,
RequestPath = "/Views/Shared"
});
Hope this helps anyone who comes across this issue. Thank you for all of you who have shared their answers.

Mobile Specific Views / Device Detection

In the .NET Core docs there is a page titled "Building Mobile Specific Views" but is under construction: https://docs.asp.net/en/latest/mvc/views/mobile.html.
Does anyone have some insight on building mobile views or successfully doing device detection?
Serving specific views based on the browser's user-agent is an outdated concept as it do not sufficiently says much about the capabilities of the device. For example, iPhone and iPad come in different screen sizes and even mobile browsers allow to change the user-agent.
The new concept is called Responsive Design where one creates a single page that fits and show/hides certain element based on the available screen width. One popular responsive deisgn CSS Framework is Bootstrap, originally developed by Twitter and later open-sourced.
Here is an example of responsive design. When you go to the site and change the width of your browser, the design updates as well from 3 to 2 to 1 column design with browser or mobile like navigation (with the Hamburger menu).
This feature actually was not implemented by microsoft. There is couple open discussions for this question:
https://github.com/aspnet/Mvc/issues/4877
https://github.com/aspnet/Razor/issues/751
As a generic answer from them - use responsive web design and css media queries (which from my point of view is not perfect answer for team that claims himself for building general web framework).
There is a implementation for this feature exist as pull request - https://github.com/aspnet/Mvc/pull/4878.
Since this pull request seems to be forgotten, i extract this code into separate project which is available on
https://github.com/laskoviymishka/MvcDeviceDetector.
You may use this implementation (which is easy to add to exist MVC project) or implement this itself. This is pretty easy - you need just implement and reqister own IViewLocationExpander for that.
This can be handle in .Net Core using RazorViewEngineOptions
1) Create Service LocationExpanderService.cs
public class LocationExpanderService : IViewLocationExpander
{
public IEnumerable<string> ExpandViewLocations(ViewLocationExpanderContext context,
IEnumerable<string> viewLocations)
{
//replace the Views to MyViews..
var viewLocationsFinal = new List<string>();
if (!string.IsNullOrEmpty(context.Values["viewCustom"]))
{
foreach (var viewLocation in viewLocations)
{
viewLocationsFinal.Add(viewLocation.Replace(".cshtml", ".mobile.cshtml"));
}
}
viewLocationsFinal.AddRange(viewLocations);
return viewLocationsFinal;
}
public void PopulateValues(ViewLocationExpanderContext context)
{
var userAgent = context.ActionContext.HttpContext.Request.Headers["User-Agent"].ToString().ToLower();
var viewCustom = userAgent.Contains("android") || userAgent.Contains("iphone") ? "mobile" : "";
context.Values["viewCustom"] = viewCustom;
}
}
2) Configure services in startup.cs
services.Configure<RazorViewEngineOptions>(o =>
{
o.ViewLocationExpanders.Add(new LocationExpanderService());
});
3) Create view with .mobile extension
Index.mobile.cshtml

Remove version control from Eclipose RCP project

I have an Eclipse RCP application that uses EGit and JGit to implement the version control of projects.
In some cases, I want to make it so that a project is no longer under version control. This implies both deleting the project's .git directory on the file system, and making my application aware that the project is not under version control.
I tried looking through the JGit API for solutions to this, but could not find anything useful. Simply deleting the .git directory using a file manipulation API is not sufficient, as some files in it seem to be locked (very likely by JGit).
Sorry, I'm not 100% sure because your scenario is not clear enough to me, however you said that your application is based also on EGit, so I assume that you are using the standard eclipse APIs to manage the projects. If this is the case, what you are looking for is: org.eclipse.team.core.RepositoryProvider
This is an example:
public class UntrackProject extends AbstractHandler implements IHandler {
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
IProject project = getProject(); // get the project, for example using the selection service
try {
RepositoryProvider.unmap(project);
//TODO Refresh your viewer to show changes
} catch (TeamException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
For reference, you can check the handler of the context menu "Disconnect", that does exactly what you are looking for: org.eclipse.egit.ui.internal.actions.DisconnectActionHandler
https://git.eclipse.org/c/egit/egit.git/tree/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/actions/DisconnectActionHandler.java
It uses a lot of internal classes of egit, but in the end it uses the RepositoryProvider's API.
Note that the method RepositoryProvider.unmap(project) does NOT delete the repository, it just disconnects the project.
I hope this helps!

How do I include a custom field client control JS file?

I'm trying to develop a custom field using the steps outlined here: http://www.sitefinity.com/documentation/documentationarticles/developers-guide/sitefinity-essentials/controls/types-of-controls/field-controls/building-a-custom-field-control
Unfortunately, I don't know how/where to include the client control js file, and it doesn't seem to say in the documentation. Can anyone explain how I might do this?
I'm very new to Sitefinity dev (as in this is the first thing I've done) and web dev in general, so feel free to suggest things that "should be obvious"
I also asked this question on the Sitefinity forums - I'll be sure to copy the answer here if I get one there.
Ok, got it.
First you need to set the processing options for the js file to Embedded Resource
Then add it to the AssemblyInfo.cs:
[assembly: WebResource("namespace.filename.js", "text/javascript")]
Then override the GetScriptReferences method in the class that inherits from FieldControl:
public override IEnumerable<ScriptReference> GetScriptReferences()
{
var baseReferences = new List<ScriptReference>(base.GetScriptReferences());
var newRef = new ScriptReference(javascriptPath, this.GetType().Assembly.FullName);
baseReferences.Add(newRef);
return baseReferences;
}

Silverlight WCF Service Bound to Grid - Grid is Blank ... am I missing something?

Here's my situation - I am starting my first SL application coming from a Windows background where I have the MVC pattern in place. I am thinking I can take advantage of tha pattern, so I jump right in to creating a SL application (solution) with 3 projects --> 1 holding my Model (business objects from before), SL app (automatically generated when I picked SL project), and 1 SL.Web project (automatically generated when I picked SL project).
Here are the steps I did: I dropped a blank grid onto my xaml file, created a service and it is being recognized w/o any problems. Added a "Silverlight-enabled WCF Service" to my Web project and w/in my project I referenced my "Model" project. Within [OperationContract], I created the following method (service):
[OperationContract]
public EmployeeCollection GetEmployees()
{
EmployeeCollection employees = Model.EmployeeCollection.GetAllEmployees();
return employees;
}
When I debug though this and put a breakpoint, I DO see data on return of employees, so I KNOW data is being returned.
Now, on my page.xaml.cs file, it seems that that my grid is not being populated somehow, but don't why.
I have the following snippet:
void MyWebService_GetEmploteesCompleted(object sender, EmployeeWebService.GetEmployeesCompletedEventArgs e)
{
dataGrid.ItemsSource = e.Result;
}
Where else should I be looking? What can I be missing? Do I have to add other snippets of code to work w/ my BLL (Model)?
Thanks for any advice.
=============================
BTW, while debugging, in the "Autos" window, I notice the following values coming through on _GetEMployeesCompleted():
dataGrid.ItemSource ........... Count = 30
e.Result ......... Count = 30
Why aren't the values showing up on my DataGrid?
Did you set AutoGenerateColumns = true on the DataGrid? What interfaces does EmployeeCollection implement?
Pardon the silly question, but since we don't see how the Employee class is implemented, does your Employee class expose public properties (not just public members)? You can only bind to properties, that might explain why DataGrid is not auto generating columns.
This question is a bit old, but I've ran into a similar problem...
Are you using the theme engine? I've noticed that using the grid inside of another control (ChildWindow, Tab, etc) with the Theme Engine causes the children to not render. I've raised a bug with the toolkit people, so we'll see if it gets fixed.