MVC5 DisplayModeProvider registration problems - mobile

so I have an mvc 5 application with 3 display modes, desktop (default), mobile, and tablet. I'm using WURFL to figure out devices. Here's the code called from global.cs to register:
public static void RegisterDisplayModes(IList<IDisplayMode> displayModes){
var datafile = HttpContext.Current.Server.MapPath(WurflDataFilePath);
var configurer = new InMemoryConfigurer().MainFile(datafile);
var manager = WURFLManagerBuilder.Build(configurer);
HttpContext.Current.Cache[WURFLMANAGER_CACHE_KEY] = manager;
bool mobileEnabled = ConfigurationManager.AppSettings["EnableMobileSite"] == "true";
bool tabletEnabled = ConfigurationManager.AppSettings["EnableTabletSite"] == "true";
var modeDesktop = new DefaultDisplayMode("") {
ContextCondition = (c => c.Request.IsDesktop())
};
var modeMobile = new DefaultDisplayMode("mobile"){
ContextCondition = (c => c.Request.IsMobile())
};
var modeTablet = new DefaultDisplayMode("tablet"){
ContextCondition = (c => c.Request.IsTablet())
};
displayModes.Clear();
if (mobileEnabled) displayModes.Add(modeMobile);
if (tabletEnabled) displayModes.Add(modeTablet);
displayModes.Add(modeDesktop);
}
I'm using some extension methods to HttpRequestBase, as discussed in http://msdn.microsoft.com/en-us/magazine/dn296507.aspx:
public static bool IsDesktop(this HttpRequestBase request){
return true;
}
public static bool IsMobile(this HttpRequestBase request) {
return IsMobileInternal(request.UserAgent) && !IsForcedDesktop(request);
}
public static bool IsTablet(this HttpRequestBase request) {
return IsTabletInternal(request.UserAgent) && !IsForcedDesktop(request);
}
public static void OverrideBrowser(this HttpRequestBase request, bool forceDesktop){
request.RequestContext.HttpContext.Cache[OVERRIDE_BROWSER_CACHE_KEY] = forceDesktop;
}
public static bool IsForcedDesktop(this HttpRequestBase request){
var isForced = request.RequestContext.HttpContext.Cache[OVERRIDE_BROWSER_CACHE_KEY];
return isForced != null ? isForced.ToString().ToBool() : false;
}
private static bool IsMobileInternal(string userAgent) {
var device = WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
if (device.IsTablet() == true) {
return false;
} else {
return device.IsMobile();
}
}
private static bool IsTabletInternal(string userAgent) {
var device = WURFLManagerBuilder.Instance.GetDeviceForRequest(userAgent);
return device.IsTablet();
}
It all works fine for a while, but then after an hour or so, mobile and tablet devices start displaying the desktop views, and the desktop view starts showing the ViewSwitcher shared view (I assume most people are familiar with it, it just allows you to show the desktop view from a mobile device). It's almost like that caching bug in mvc4. I have tried removing my code to register the display modes, and just went with the default mvc mobile support, and it works fine it has the same issue! So clearly there's a problem in here somewhere... can anyone see anything obvious? Almost impossible to debug cause problems only start coming up after a long time, and even then only on the live system really! Any ideas?
Thanks heaps... been on this issue for way too long now...
Cheers
Andy
EDIT: even stripping it right back to the default implementations creates the issue. I added some debugging code to make sure I'm actually running mvc5, but it appears I am. I've also tried the initially recommended workaround for the issue on mvc4 by disabling the cache, still no joy. Is there really no one with info on this?

So I finally figured it out. Very simple as usual. For some reason I used RequestContext.HttpContext.Cache to save the status when someone wants the full view as opposed to the mobile view. I've never used HttpContext.Cache, I'm pretty sure I would have taken that from a blog somewhere - can't find it anymore though. So all that happened was that it would switch the view for everyone, not just the one person. Can't believe it took weeks to figure that out. Hope it helps someone else at some point.

Related

Proper way to upgrade WPF program settings on program update (Desktop Bridge/Project Centennial)

When it was a clickonce program it worked, but then I made an appxpackage and exported it as a centennial app for windows store and the upgrade does not work any more.
Right now I have in App.xaml.cs
protected override void OnStartup(StartupEventArgs e) {
if (myprog.Properties.Settings.Default.UpgradeRequired)
{
myprog.Properties.Settings.Default.Upgrade();
myprog.Properties.Settings.Default.UpgradeRequired = false;
myprog.Properties.Settings.Default.Save();
}
With UpgradeRequired as a bool in user settings. Is that the right place?
I am getting settings reset on each version update. Now I have several of these directories
C:\Users\me\AppData\Local\progname\prog.exe_Url_randomChars
each with several different version of program settings. So after the upgrade another one of those is created, instead a subfolder with x.x.x.x of the current version.
As before, on each version release I increase version in Assembly Information the Assembly Version, File Version, and now I have the same numbers in AppxManifest.xml. I keep the last number group of the version to 0 as advised, and just increase the 3rd number group.
Is there something I am missing?
UWP and Desktop Bridge apps need to store their settings in ApplicationData.LocalSettings:
https://learn.microsoft.com/en-us/windows/uwp/design/app-settings/store-and-retrieve-app-data#local-app-data
You could load the previous user.config file into current settings. This is just a workaround, and can be used to transition to ApplicationData.LocalSettings.
public static void Init()
{
if (myprog.Properties.Settings.Default.UpgradeRequired)
{
LoadPreviousSettings(myprog.Properties.Settings.Default);
myprog.Properties.Settings.Default.UpgradeRequired = false;
myprog.Properties.Settings.Default.Save();
}
}
private static void LoadPreviousSettings(params ApplicationSettingsBase[] applicationSettings)
{
const string companyName = "YOUR_COMPANY_NAME_HERE";
var userConfigXml = GetUserConfigXml(companyName);
Configuration config = ConfigurationManager.OpenExeConfiguration(
ConfigurationUserLevel.PerUserRoamingAndLocal);
foreach (ApplicationSettingsBase setting in applicationSettings)
{
try
{
// loads settings from user.config into configuration
LoadSettingSection(setting, config, userConfigXml);
config.Save(ConfigurationSaveMode.Modified);
ConfigurationManager.RefreshSection("userSettings");
}
catch (FileNotFoundException)
{
// Could not import settings.
// Perhaps user has no previous version installed
}
setting.Reload();
}
}
private static void LoadSettingSection(ApplicationSettingsBase setting, Configuration config, XDocument userConfigXml)
{
string appSettingsXmlName = setting.ToString();
var settings = userConfigXml.XPathSelectElements("//" + appSettingsXmlName);
config.GetSectionGroup("userSettings")
.Sections[appSettingsXmlName]
.SectionInformation
.SetRawXml(settings.Single().ToString());
}
private static XDocument GetUserConfigXml(string companyName)
{
var localPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + $#"\{companyName}";
// previous package folder
var previousLocal = GetDirectoryByWriteTime(localPath, 1);
// previous version, e.g. 1.2.0
var prevousVersion = GetDirectoryByWriteTime(previousLocal, 0);
// application settings for previous version
return XDocument.Load(prevousVersion + #"\user.config");
}
private static string GetDirectoryByWriteTime(string path, int order)
{
var direcotires = new DirectoryInfo(path).EnumerateDirectories()
.OrderBy(d => d.LastWriteTime)
.Reverse()
.ToList();
if (direcotires.Count > order)
{
var previous = direcotires[order];
return previous.FullName;
}
throw new FileNotFoundException("Previous config file not found.");
}
There is a working answer here.
Basically you need to create a duplicate version using UWP's ApplicationData.Settings and then loading it at the beginning of the app. It is very straightforward when your settings are strings, bools, etc. But not so if you have unique settings.
To elaborate more from the answer in the link, when you have settings consisting of custom types/classes, when creating UWP's duplicate version, you can use Newtonsoft.Json to serialise the custom setting:
try
{
ApplicationData.Current.LocalSettings.Values[value.Name] = value.PropertyValue;
}
catch
{
string serialised = JsonConvert.SerializeObject(value.PropertyValue);
ApplicationData.Current.LocalSettings.Values[value.Name] = serialised;
}
Then when loading your custom setting:
if (s.Name == "MyCustomSetting")
{
var deserialised = JsonConvert.DeserializeObject<MyCustomClass>((string)setting.Value);
s.PropertyValue = deserialised;
}

Network check works in android but same doesn't work in iOS

I checked NetworkManager.getInstance().getCurrentAccessPoint() equals to null or not to determine if there is a network connection or not. It's simple & works great in android but in iOS, it always shows false. How can i make it work in iOS. Before I had used a cn1 library for this purpose. I think its by steve but using the library, I felt it makes the app a bit slower. So i used the code below.
public static boolean check_online() {
boolean online = false;
String net = NetworkManager.getInstance().getCurrentAccessPoint();
if (net == null || "".equals(net) || net.equals(null)) {
online = false;
} else {
online = true;
}
return online;
}
#Override
protected void postExample(Form f) {
if (check_online()) {
ecc = new CategoryConnection();
ecc.egCategoryConnectionMethod(this);
- - - - - -- -
//codes
- - - - - -- -
}
}else {
noConnection = new Label("No connection");
f.add(noConnection);
}
That feature is implemented only for Android/RIM as it's very device specific. iOS doesn't provide access point control.
There is a cn1lib in the extensions section that allows you to detect if there is a network connection or isn't which I guess is what you are really looking for.

Windows Phone - MediaElement not working anymore after navigation

I have a problem with the MediaElement in my windows phone (8) application. It's working fine until I navigate to some other page and come back on the page containing the MediaElement.
I have the following code to play a stream coming from Bing Translator APIs:
private void TranslationService_SpeakComplete(object sender, SpeakCompleteEventArgs e)
{
var stream = e.Stream;
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
MediaElement.Stop();
MediaElement.Source = null;
string filename = "FlipNLearnItAudio";
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
bool fileExists = isf.FileExists(filename);
if (fileExists)
{
isf.DeleteFile(filename);
}
var isfs = isf.CreateFile(filename);
using (isfs)
{
Helpers.SaveFile(stream, isfs);
isfs.Position = 0;
MediaElement.AutoPlay = true;
MediaElement.SetSource(isfs);
}
}
}
catch (Exception ex)
{
//TODO: log exception
}
});
}
The biggest problem is that there's no exception.... the "MediaElement.SetSource(isfs);" is called normally without any exception!
Do you have any idea of what can happen?
Thanks for any help!
Bastien
Ok, I find a solution to my problem. I put the MediaElement in the App.xaml resources and now it's working fine...
But it doesn't explain why it was not working with the MediaElement in the MainPage.xaml.
If somebody has the answer, I would be very interested to hear it ;-).

Isolated Storage Application Settings Not Persisting after Application exit

I'm having a big problem using WP7 isolated storage and applicationsettings.
I have been using code from Adam Nathan's 101 Windows Phone 7 apps volume 1 as a basis.
I have a settings page where values can be altered and whilst the application is still running these remain active and it all works perfectly. However, as soon as the app exits on my developer phone these are lost and the app restarts with the default settings.
I have no idea why these values are not persisting. Any help would be much appreciated.
Here is the code i've got, its from adam nathan's new book. I sent him a message on twitter and he said its to do with a data type that isn't serializable. I looked into this but i'm only using double and bool values.
public class Setting<T>
{
string name;
T value;
T defaultValue;
bool hasValue;
public Setting(string name, T defaultValue)
{
this.name = name;
this.defaultValue = defaultValue;
}
public T Value
{
get
{
//checked for cached value
if (!this.hasValue)
{
//try to get value from isolated storage
if (IsolatedStorageSettings.ApplicationSettings.TryGetValue(this.name, out this.value))
{
//not set yet
this.value = this.defaultValue;
IsolatedStorageSettings.ApplicationSettings[this.name] = this.value;
}
this.hasValue = true;
}
return this.value;
}
set
{
//save value to isolated storage
IsolatedStorageSettings.ApplicationSettings[this.name] = value;
this.value = value;
this.hasValue = true;
}
}
public T DefaultValue
{
get { return this.defaultValue; }
}
//clear cached value;
public void ForceRefresh()
{
this.hasValue = false;
}
}
Further development:
I receive this error on exiting the application:
A first chance exception of type 'System.IO.IsolatedStorage.IsolatedStorageException' occurred in mscorlib.dll
ERROR FOUND: I'm an idiot and left out one exclamation mark! from the trygetvalue part.
Could you please post your storage code so we could see exactly what's going on? In absense of that code, here's the code I use to save setting to local storage:
IsolatedStorageSettings isoStoreSettings = IsolatedStorageSettings.ApplicationSettings;
if (isoStoreSettings.Contains(key))
{
isoStoreSettings[key] = value;
}
else
{
isoStoreSettings.Add(key, value);
}
isoStoreSettings.Save();
My guess is that you're missing that last line that commits the changes to isolated storage settings to the materialized isolated store instead of just leaving them in memory. If that's not the case, please edit your post with the code so that we can help.

Silverlight 4: Detect browser F5 / refresh and X / Close

I want to determine how to filter F5, refresh button, X and close in browser via silverlight 4.0 or even in server side.
thank you
EDITED:
I added bounty to my question just today, July 28 2011. My previous solution / answer is no longer working in IE 9.
window.onunload = function (e) {
// Firefox || IE
e = e || window.event;
var y = e.pageY || e.clientY;
if (y < 0) {
alert("close");
}
else {
alert("refresh");
}
}
When the user hit F5, refresh, X and close button, message box should NOT appear. Just in case the solution is onbeforeunload.
Thanks for you help!
It is not possible client-side to determine whether an application startup is the result of a refresh operation performed by the user.
However you can determine at serverside that a page is being refreshed. You can add the following property to the code-behind of the ASPX page hosting the Silverlight application.
public bool IsRefresh
{
get { Request.Headers["pragma"] ?? "").Contains("no-cache"); }
}
Now you use this property to conditionally include a value in the silverlight plugin initParams.
<object ...>
<param name="initParams" value="IsRefresh=<%=IsRefresh.ToString()%>" />
</object>
Then in silverlight code you can determine if the application was last loaded as a result of a refresh with:-
if (Application.Current.Host.InitParams["IsRefresh"] == "True")
since it is not possible in client side, i did it in server side.
I solve my problem using this code:
window.onunload = function (e) {
// Firefox || IE
e = e || window.event;
var y = e.pageY || e.clientY;
if (y < 0) {
alert("close");
}
else {
alert("refresh");
}
}
There is no property to check if your application is loaded by pressing the F5-button but you could handle the application startup event and set a variable with a datetime. The moment your page gets loaded you can check if the timespan is just a couple of seconds ago. So now you know that the application is loaded the first time or the F5-button is pressed when that time it is only a couple of seconds ago.
I don't know if this is sufficient for you but you can give it a try:
App.xaml.cs
public class App : Application
{
private DateTime appStartupTime {get; set};
public App()
{
Startup += new EventHandler(Application_Startup);
}
void Application_Startup(object sender, StartupEventArgs e)
{
//initialize the startupTime
appStartupTime = DateTime.Now;
}
public bool IsApplicationReLoaded
{
get
{
//return true if your app is started less 10 seconds ago
return DateTime.Now.AddSeconds(-10) < appStartupTime;
}
}
}
Now you can start using the code below from everywhere
(Application.Current as App).IsApplicationReloaded

Resources