Wednesday @ 2:44 pm
- from Hood River, Oregon
I’m happy to announce that my WPF Localization paper went live a couple of days ago on CodePlex. You can find it here:
http://wpflocalization.codeplex.com/ (click on Downloads – it’s published only as PDF <shrug>)
I’ve been working on this document for quite some time and those that follow this blog and twitter probably already know that it’s been an interesting journey. It’s a very long document as it covers a whole range of topics from some localization overview, the basic .NET localization infrastructure to the actual localization approaches using LocBaml and Resx resources as well as couple of custom approaches using custom markup extensions and attached properties. There are a lot of topics covered, and for once though it was actually supposed to be this long based on the article specs on the contract.
The reason for the length is primarily that WPF localiztion doesn’t just take a single path. Unlike previous Windows desktop technologies that preceded it, WPF does not have one clear path to localization and no direct tool support in Visual Studio or Blend. This means the choices for localization are left up to the developer to explore and figure out.
The two major choices available are using LocBaml for localizing XAML resources or using the more traditional Resx approach. LocBaml focuses on using the XAML document itself as a resource store and allows for exporting of these XAML resources for localizations after development is complete. The rigid LocBaml approach lends itself to applications that are completed and won’t change much (yeah right – is there really such a thing?) but is not a good choice for applications that need to be incrementally localized. Resx provides better tool integration in Visual Studio with support for resource editing right in Visual Studio as well as the ability to create strongly typed resource classes from Resx resources. However, there’s no direct designer support for the locailzation content which means you manually have to hook up resource keys to properties of elements or at the very least assign some sort of marker property so a custom extension can map resources to properties. For WPF accessing Resx resources involves the manual process of mapping resource ids to XAML elements for binding which is more work up front compared to LocBaml but allows for more flexibility when updating applications at a later point. The Resx approach also has number of options available. The simple x:Static binding markup extension makes for easy bindings of strongly typed resources to properties using simple, built-in binding syntax. For more control custom markup extensions or attached properties can be used to provide custom binding to Resx resources.
The LocBaml approach is interesting in that it defers localization until the end of a project, but in its current form with the limited tool support in LocBaml itself and lack of integration the process of localization is very fragile – it’s very easy to break the CSV localization files and have to start over or roll back to a previously saved version. Using LocBaml it’s definitely worthwhile to back up constantly! Resx offers a more traditional approach and while it’s definitely more work during development I feel that this is the more stable and consistent approach to localization at this time. There are lots of choices available for Resx localization as well beyond what has been discussed in this white paper, many of them interesting and innovative. Lots of innovation around Resx extensions, but nobody seems to be extending LocBaml.
The bottom line is that as a WPF developer you need to spend a little time with each of these approaches to get a feel for what works for you and what works for your localization staff. Choices are good, but arriving at the right one unfortunately can take a little extra time. I’m hoping that the white paper provides a good background on some of the tools available and a better understanding what to look for in any solution you use for WPF localization. I doubt that this is the final word in WPF localization – I suspect at some point there will be some proper tooling provided, but as of the current version of .NET (3.5SP1) and as far as I can see of .NET 4.0 Localization for WPF hasn’t seen a lot of love from Microsoft. <shrug>
I’ve also had the pleasure of working with Michele Leroux Bustamante on this project who did a bang up job on final editing and providing feedback in the course of writing. Michele of course is one of the THE experts in Localization in WPF and I felt honored being asked to help out by writing this white paper and take on the WPF Localization topic.
Anyway, I hope this article will prove useful to developers if for nothing else than putting a lot of the information that is out on the Web for WPF Localization into one comprehensive document that can act as a starter before going off looking at the wide variety of custom solutions available. It’ll be a good place to start and hopefully give you a good idea on what approach is closest to the way you like to work.
Check it out.
Sunday @ 10:28 pm
- from Hood River, Oregon
Got a question in response to a Localization article today that asked how to store requested culture settings. In the article I recommend that easiest and most reliable way to switch cultures is to assign the culture when the application runs and allow the user to change cultures somewhere in the UI. When the culture is changed it’s up to the application to decide how to handle the actual culture change. One of the easiest things to do is write the change to a configuration setting and then exit and immediately restart the application.
.NET 2.0 gained the ability to write configuration settings in addition to easily reading them. Although this isn’t something you need frequently it’s quite handy to be able to update values easily and then write the changes back out into the configuration store. Here’s an example of a sample application that allows changing the active culture writing a new culture selection into a configuration file (in WPF):
private void lstLanguageSelections_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string origCulture = CultureInfo.CurrentUICulture.IetfLanguageTag;
string culture = ((ComboBoxItem)this.lstLanguageSelections.SelectedValue).Tag as string;
// static app method that acutally sets the culture
App.SetCulture(culture, true); // force windows to close
if (this.IsInitialized && culture != origCulture)
{
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Add an Application Setting.
config.AppSettings.Settings.Remove("Culture");
config.AppSettings.Settings.Add("Culture", culture);
// Save the configuration file.
config.Save(ConfigurationSaveMode.Modified);
// Force a reload of a changed section.
ConfigurationManager.RefreshSection("appSettings");
Process.Start(
new ProcessStartInfo(Environment.CommandLine)
{
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Normal
});
Application.Current.Shutdown();
}
}
Notice that you have to remove the setting before adding it back in when updating the value. The code checks to see if the culture has indeed changed (a little odd since this IS a SelectionChanged event handler after all but this has to do with how WPF initially assigns values) and removes the key and adds it back in. Finally the whole file is written back out and the configuration values are refreshed so the next time you read the changed configuration key(s) they will reflect the new values.
All of this assumes the that you have appropriate permissions – you need to have read/write access in the folder where your .config file is located – most likely the applications’s startup folder, which by no means is guaranteed. Under restricted profiles (or with UAC enabled in Vista) writing into the installation folder is not allowed. So use this approach with some awareness of the security environment it runs in.
June 18, 2009 @ 9:03 pm
- from Hood River, Oregon
I got an interesting question via email today that asked the question:
“How do ASP.NET Application_ Event Handlers get hooked so that they are automatically fired?”
If you’ve worked with ASP.NET for any amount of time you probably know about the global.asax file and its backing global class that holds event handlers for several common HttpApplication Event Handlers:
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
}
protected void Application_BeginRequest(object sender, EventArgs e)
{
}
protected void Application_EndRequest(Object sender, EventArgs e)
{
}
public override string GetVaryByCustomString(HttpContext context, string custom)
{
}
protected void Application_Error(object sender, EventArgs e)
{
}
protected void Application_End(object sender, EventArgs e)
{
}
}
Notice that the global implementation inherits from HttpApplication which is the top level exposed ASP.NET component that manages pipeline of events and its processing (among other things). There are actually multiple instances of HttpApplication active at any given time depending on the load on the application and each instance processes requests on its own separate thread. If you’re interested in how the pipeline works and how requests and HttpApplicaiton objects map you can take a look at an oldish article of mine A low-level Look at the ASP.NET Architecture which explains this and a few other low level topics. It’s based on IIS 5/6 so there are some changes in the way the low level hooks happen in IIS 7, but the pipeline related topics still apply. Logically the ASP.NET pipeline hasn’t changed drastically with IIS 7 although physically there are siginificant processing changes.
HttpApplication is also responsible for hooking up and executing HttpHandlers and HttpModules which typically is done declaratively in web.config. Modules can also be added dynamically in code very early in the pipeline process. I don’t think HttpHandlers can be added in code. ASP.NET internally uses some very complex logic to find modules and handlers from various locations and hooks them up to new HttpApplication instances and finally manages the execution via StepManager.
Application Events are HttpModule ShortCuts
The Application_ events that you see in global.asax are effectively shortcuts for HttpModules as they map the events in the HttpApplication object. The Application_ level events are easier to deal with than a module, since you can simply implement them without having to register a module in web.config. For relatively simple application level logic that doesn’t need to be reused elsewhere this is perfect. For example, in my apps I tend to put generic logging and error handler code here. Full HttpModules are useful for more complex operations that require isolation of code or for anything that needs to be reused in other ASP.NET applications.
Ok, so much for HttpApplication 101. So what about those auto Application_ methods, how the heck do they get hooked up? So we know that Application_ handlers are hooked to HttpApplication events. It’s also clear that the bindings are not static, but rather have to be figured out by the compiler at runtime. Static binding is definitely not the way this is done because you can easily add ANY HttpApplication event handler using the Application_EventName syntax. So if you want to handle the HttpApplication::PreRequestHandlerExecute event you can simple add:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
LogManager.Current.WriteEntry(new WebLogEntry()
{
ErrorLevel = ErrorLevels.Message,
Message = "PreRequestHandlerExecute"
});
}
and the code gets automagically fired at the appropriate time in the ASP.NET pipeline event processing. You can effectively implement any HttpApplication event this way in global.asax. Application_Start/_End/_EndSession are specical cases. The Start and Stop methods are manually configured by ASP.NET because there are no matching events (they basically get fired off Init and Dispose) and the Session related events require a special event signature. They are forwarded as Application_ events because they are obviously quite useful and also correspond to Classic ASP events that were available.
Clearly there’s a lot more going on behind the scenes than just statically binding events when Application_ events are bound. The ASP.NET parser has to actively parse global class figure out which events are being referenced with this specialized magic string syntax and bind the events for you. So how does this happen? ASP.NET is definitely using Reflection on the global class to determine available Application_ methods and matching them to HttpApplication events. The answer to this took a bit of spelunking with Red Gate’s Reflector and the help of a few folks on Twitter (particularily Scott Allen, Peter Bromberg and Bryan Cooke).
The key behavior is located in HttpApplicationFactor.ReflectOnApplicationType and specifically ReflectOnMethodInfoIfItLooksLikeEventHandler (say that a couple of times in a row :-}). The HttpApplicationFactory – as the name suggests – is responsible for feeding the HttpRuntime instance new instances of HttpApplication objects. It effectively instantiates the new instance and gets it ready for processing. The ReflectOnApplicationType() method is called from the HttpContext.CompileApplication() which in turn is part of the initial instance retrieval process of HttpContextFactory. If you look at the call tree in Reflector (using the Analyze feature) you can trace back the call sequence all the way to HttpRuntime.ProcessRequest:
Inside of ReflectOnApplicationType loops through all instance methods of your global class and calls ReflectOnMethodInfoIfItLooksLikeEventHandler to check and see if the method has the right signature (Event Handler signature basically). If it does the method is added to the list of event handlers:
private void ReflectOnApplicationType()
{
ArrayList list = new ArrayList();
foreach (MethodInfo info in this._theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public |
BindingFlags.Static | BindingFlags.Instance))
{
if (this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info))
{
list.Add(info);
}
}
Type baseType = this._theApplicationType.BaseType;
if ((baseType != null) && (baseType != typeof(HttpApplication)))
{
foreach (MethodInfo info2 in baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance))
{
if (info2.IsPrivate && this.ReflectOnMethodInfoIfItLooksLikeEventHandler(info2))
{
list.Add(info2);
}
}
}
this._eventHandlerMethods = new MethodInfo[list.Count];
for (int i = 0; i < this._eventHandlerMethods.Length; i++)
{
this._eventHandlerMethods[i] = (MethodInfo)list[i];
}
}
private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m)
{
ParameterInfo[] parameters;
string str;
if (m.ReturnType == typeof(void))
{
parameters = m.GetParameters();
switch (parameters.Length)
{
case 0:
goto Label_007A;
case 2:
if (parameters[0].ParameterType == typeof(object))
{
if ((parameters[1].ParameterType != typeof(EventArgs)) && !parameters[1].ParameterType.IsSubclassOf(typeof(EventArgs)))
{
return false;
}
goto Label_007A;
}
return false;
}
}
return false;
Label_007A:
str = m.Name;
int index = str.IndexOf('_');
if ((index <= 0) || (index > (str.Length - 1)))
{
return false;
}
if (StringUtil.EqualsIgnoreCase(str, "Application_OnStart") || StringUtil.EqualsIgnoreCase(str, "Application_Start"))
{
this._onStartMethod = m;
this._onStartParamCount = parameters.Length;
}
else if (StringUtil.EqualsIgnoreCase(str, "Application_OnEnd") || StringUtil.EqualsIgnoreCase(str, "Application_End"))
{
this._onEndMethod = m;
this._onEndParamCount = parameters.Length;
}
else if (StringUtil.EqualsIgnoreCase(str, "Session_OnEnd") || StringUtil.EqualsIgnoreCase(str, "Session_End"))
{
this._sessionOnEndMethod = m;
this._sessionOnEndParamCount = parameters.Length;
}
return true;
}
ah you gotta love the goto statements in this code. Surprising how often that crops up in the ASP.NET runtime code. You can see the Reflection code retrieving anything that contains in underscore in the name. Notice also that several events like Application_Start/End Session_End are special cased since there are no specific matching events.
This code is actually callled by HttpApplicationFactory.CompileApplication():
private void CompileApplication()
{
this._theApplicationType = BuildManager.GetGlobalAsaxType();
BuildResultCompiledGlobalAsaxType globalAsaxBuildResult = BuildManager.GetGlobalAsaxBuildResult();
if (globalAsaxBuildResult != null)
{
if (globalAsaxBuildResult.HasAppOrSessionObjects)
{
this.GetAppStateByParsingGlobalAsax();
}
this._fileDependencies = globalAsaxBuildResult.VirtualPathDependencies;
}
if (this._state == null)
{
this._state = new HttpApplicationState();
}
this.ReflectOnApplicationType();
}
The actual hookup of events occurs in HttpApplication.HookupEventHandlersForApplicationAndModules which is called during HttpApplication Initialization and there’s some special processing in that method that checks for the “Application” prefix and if so points the handler at itself via this and hooks the event processing appropriately.
private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers)
{
this._currentModuleCollectionKey = "global.asax";
if (this._pipelineEventMasks == null)
{
Dictionary<string, RequestNotification> eventMask = new Dictionary<string, RequestNotification>();
this.BuildEventMaskDictionary(eventMask);
if (this._pipelineEventMasks == null)
{
this._pipelineEventMasks = eventMask;
}
}
for (int i = 0; i < handlers.Length; i++)
{
MethodInfo arglessMethod = handlers[i];
string name = arglessMethod.Name;
int index = name.IndexOf('_');
string str2 = name.Substring(0, index);
object obj2 = null;
if (StringUtil.EqualsIgnoreCase(str2, "Application"))
{
obj2 = this;
}
else if (this._moduleCollection != null)
{
obj2 = this._moduleCollection[str2];
}
if (obj2 != null)
{
Type componentType = obj2.GetType();
EventDescriptorCollection events = TypeDescriptor.GetEvents(componentType);
string str3 = name.Substring(index + 1);
EventDescriptor descriptor = events.Find(str3, true);
if ((descriptor == null) && StringUtil.EqualsIgnoreCase(str3.Substring(0, 2), "on"))
{
str3 = str3.Substring(2);
descriptor = events.Find(str3, true);
}
MethodInfo addMethod = null;
if (descriptor != null)
{
EventInfo info3 = componentType.GetEvent(descriptor.Name);
if (info3 != null)
{
addMethod = info3.GetAddMethod();
}
}
if (addMethod != null)
{
ParameterInfo[] parameters = addMethod.GetParameters();
if (parameters.Length == 1)
{
Delegate handler = null;
if (arglessMethod.GetParameters().Length == 0)
{
if (parameters[0].ParameterType != typeof(EventHandler))
{
goto Label_01F4;
}
ArglessEventHandlerProxy proxy = new ArglessEventHandlerProxy(this, arglessMethod);
handler = proxy.Handler;
}
else
{
try
{
handler = Delegate.CreateDelegate(parameters[0].ParameterType, this, name);
}
catch
{
goto Label_01F4;
}
}
try
{
addMethod.Invoke(obj2, new object[] { handler });
}
catch
{
if (HttpRuntime.UseIntegratedPipeline)
{
throw;
}
}
if ((str3 != null) && this._pipelineEventMasks.ContainsKey(str3))
{
if (!StringUtil.StringStartsWith(str3, "Post"))
{
this._appRequestNotifications |= this._pipelineEventMasks[str3];
}
else
{
this._appPostNotifications |= this._pipelineEventMasks[str3];
}
}
Label_01F4: ;
}
}
}
}
}
This code is pretty cryptic – basically it’s all based on naming conventions with the name of methods matching up to the name of the event they are binding to. If a match is found the event is bound. If you follow some the intermediate methods of this code through in Reflector (or in the Symbol source if you’re more adventurous) it makes your head spin, as the code goes through some wicked gyrations to hook up the event handlers and then handle execution.
No need to know, but good to know
This is one of those things that is not really necessary to understand – knowing how this works is not likely to ever be an issue in your day to day development tasks. But as I got that message today I was intrigued enough by the realization that I didn’t know how this works although at first blush I thought I did. Then there’s this nagging feeling, well, you know how that goes… anyway, I’m doing a low level ASP.NET session in November at ASP.NET Connections and it turns out this was a good way to jog my memory about the low level gymnastics that ASP.NET goes through in request pipeline setup. So, not a complete waste of time of an hour or so, eh? :-}
June 14, 2009 @ 11:55 am
- from Hood River, Oregon
I was surprised to find today that while putting together some localization samples that by default WPF bindings do not respect the current culture. For example check out this localized form that has been localized to German and is running with both Culture and UICulture in the de-DE locale:
If you look at the highlighted formatting text you’ll notice that the form is localized (ie.the UICulture is applied) and that the active culture which in this case displays the CultureInfo.CurrentCulture is also set to German. But as you can see the date and number format is clearly using the default en-US formatting.
Apparently this is by design in WPF, although I can’t think of a single reason why this behavior should be so. Every other UI environment correctly assumes that if you have your culture set to a specific locale you’ll want your formatting and conversions to apply using this culture. But no that would be, uh too easy and nothing in WPF is easy after all.
Well, maybe it is, since the solution is relatively simple. All WPF elements include a Language property that can be assigned and determines the Culture that is used for formatting. The property is applied down the container hierarchy so setting the language at the top level container like the on the Window in the above form is all that’s needed.
Here’s the one liner:
this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
Easy enough but not something that search turned up real easily. Just another one of those issues I fretted over for a couple of hours experimenting that makes WPF such an incredible time sink.
Note that it’s important that the language assignment happens before InitializeComponent in the constructor because the language is applied at load time:
public LocalizationInfo()
{
// MAKE SURE you set the language of the page explicitly or else
// all number and date formatting occurs using the neutral culture
this.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag);
InitializeComponent();
this.Loaded += LocalizationInfo_Loaded;
}
You can also set the language globally for your entire application in the App class:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Set application startup culture based on config settings
string culture = ConfigurationManager.AppSettings["Culture"];
SetCulture(culture);
Theme = ConfigurationManager.AppSettings["Theme"];
SetTheme(Theme);
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement),
new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
}
This will switch the default language for the entire application. You’ll want to use this only in startup code as this setting can be applied only once per application. You can still override individual forms when necessary using the explicit language override shown above.
Hopefully this will help somebody out by making this issue more readily findable.
And no jokes about my funky German localization. Even though I speak German fluently localizing a computer form is definitely not something I feel qualified for :-}.
June 09, 2009 @ 11:04 am
- from Maui, Hawaii
Ok, once again I’m soliciting some audience participation as I’ve hit a dead end with a markup extension I’m working with in WPF.
I’m working on a localization markup extension that provides localized resource content using a simple markup extension with some additional features beyond what x:Static bindings provide. Specifically the ability to use a different resource manager, provide default values and the ability to use string based types for automatic type conversion. Yes, I know this has been done before and in fact my extension is based on some previous code found here from Christian Moser. I basically simplified this code and added a few additional features that I need for one of my projects.
The markup extension works great at runtime. It also works great in the designer as long as I host the extension class in the same project as the resources I binding against because I can easily find the ExecutingAssembly that I assume to hold application’s resources that will be used for binding.
The problem is that in order to create a ResourceManager I need to know which assembly the resources live in. A ResourceManager always requires an assembly reference (and resourceSet name) in its constructor to find the appropriate resources. Sounds easy enough, except I have been utterly stumped to find the Startup assembly (the WPF EXE) when running in the WPF designer.
The problem comes in when using the designer AND when trying to isolate the MarkupExtension into its own assembly/project. By moving the extension into another project I can no longer rely on Assembly.GetExecutingAssembly() to figure out the assembly where resources are coming from. Now at runtime it’s fairly easy to work around this by using Assembly.GetEntryAssembly() or even walking the control tree to find the main document and it’s underlying type and assembly.
However, in the WPF designer the actual Document is not walkable (all Parent refs are null), and GetEntryAssembly() throws an exception. Looking in the debugger while inside of the markup extension, reveals that the callstack goes straight presentation design time classes – the main WPF Exe doesn’t show up in the callstack (although the assembly IS loaded in the AppDomain.GetAssemblies()).
When running the designer too there’s no user code executing as far as I can tell, so even explicit type assignment in app.xaml.cs’s startup constructor doesn’t work.
I’m stumped. Right now the only way I can figure this out is to force the markup extension to run in the same assembly that contains the resources.
To give you an idea on what I’m doing, here’s a quick overview. The markup extension implements the required ProvideValue() method which delegates to ProvideValueInternal(). At the top of this method I try to retrieve the requested resource manager (which is caches internally) for the requested (or default) resource set.
/// <summary>
/// Internal value retrieval
/// </summary>
/// <returns></returns>
private object ProvideValueInternal()
{
// Get a cached resource manager for this resource set
ResourceManager resMan = this.GetResourceManager(this.ResourceSet);
object localized = null;
…
}
GetResourceManager() then tries to retrieve a cached resource manager or create a new one. And this where the problem starts:
/// <summary>
/// Retrieves a resource manager for the appropriate ResourceSet
/// By default the 'global' Resource
/// </summary>
/// <param name="resourceSet"></param>
/// <returns></returns>
private ResourceManager GetResourceManager(string resourceSet)
{
// At design time try to get the default assembly
if (ResExtension.Settings.DefaultResourceAssembly == null)
ResExtension.Settings.FindDefaultResourceAssembly();
if ( string.IsNullOrEmpty(resourceSet) )
return DefaultResourceManager ?? null;
if (ResExtension.Settings.ResourceManagers.ContainsKey(resourceSet))
return ResExtension.Settings.ResourceManagers[resourceSet];
ResourceManager man = new ResourceManager(resourceSet, ResExtension.Settings.DefaultResourceAssembly);
ResExtension.Settings.ResourceManagers.Add(resourceSet, man);
return man;
}
The Settings object is static and holds all the ‘global’ configuration settings like the default assembly and default resource manager. The code basically checks whether the static Settings.DefaultResourceAssembly property is set and if it isn’t it will try to get it (once).
Currently this implementation is rigged to look at the current executing assembly:
/// <summary>
/// This method tries to find the strongly typed Resource class in a project
/// so the designer works properly.
///
/// THIS CODE ASSUMES THE MAKKUP EXTENSION RUNS IN THE SAME ASSEMBLY THAT
/// ALSO CONTAINS RESOURCES. THIS IS DONE FOR THE DESIGNER ONLY. I HAVE
/// NOT BEEN ABLE TO FIGURE OUT A WAY TO GET A REFERENCE TO THE STARTUP
/// ASSEMBLY IN THE DESIGNER. IF YOU FIGURE OUT A WAY PLEASE CONTACT ME.
/// </summary>
/// <returns></returns>
internal bool FindDefaultResourceAssembly()
{
// Assume the Document we're called is in the same assembly as resources
Assembly ass = Assembly.GetExecutingAssembly();
// Search for Properties.Resources in the Exported Types (has to be public!)
Type ResType = ass.GetExportedTypes().Where(type => type.FullName.Contains(".Properties.Resources")).FirstOrDefault();
if (ResType == null)
return false;
ResourceManager resMan = ResType.GetProperty("ResourceManager").GetValue(ResType, null) as ResourceManager;
this.DefaultResourceAssembly = ass;
this.DefaultResourceManager = resMan;
}
which works both at runtime and inside of the WPF designer as long as both the extension and the resources live in the same assembly.
The question is how to replace the GetExecutingAssembly call with something that will get me a reference to the main WPF EXE’s assembly. It works as is as long as I compile the markup extension into the same assembly as the resources. But otherwise no luck.
I’ve tried a bunch of different things here:
- Explicitly setting the Static Properties in Code
Doesn’t work because WPF doesn’t run any code. So app.xaml.cs doesn’t fire – there’s no global ‘entry point’ that
the designer runs AFAIK.
- Experimented around with GetCallingAssembly, GetExecutingAssembly, GetStartupAssembly
None of these worked as in the designer all but GetExecutingAssembly throw.
- Used Configuration Value to hold the assembly name
Didn’t work because WPF Designer doesn’t read AppSettings
- Tried walking the Control Tree up to the Document and grab type
Failed as in the WPF Designer all Parent references are null. No walking for you!
Anyway, I’m out of ideas so I’m throwing it out here.
Does anybody see a way to get at the default assembly in the WPF designer? Or am I approaching this in the wrong manner and there’s an easier way to assign the assembly generically?
If attached the extension’s .cs file in case anybody’s interested or wanting to experiment.
Source: ResExtension.zip
June 04, 2009 @ 4:19 am
- from Maui, Hawaii
If there’s one thing that’s confusing as heck about Localization in WPF it’s how resources are loaded. The two major approaches for localization in WPF – using LocBaml BAML resource localization or standard Resx Localization with MarkupExtensions – stand at odds in what they expect for their resource loading requirements.
BAML Resources
When using the LocBaml approach that creates localized BAML resources it’s necessary to specify the <UICulture> key in the project file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<UICulture>en-US</UICulture>
…
You also need to specify at the assembly level what the neutral culture is and that ALL resources need to be stored in external satellite resources including the neutral culture:
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
This setting tells the runtime to look for the final fallback not in the MainAssembly, but in a satellite assembly.For BAML localization both of these settings must be made or localized BAML content won’t load. Effectively these two settings result in no resources being stored in the main assembly.
When both settings are made the compiler emits the default BAML resources – basically those XAML documents you’ve created – into an en-US folder in the target output folder. When enabled WPF will expect resources to be loaded from these resource directories including the default ‘neutral’ resources.
If localization is enabled and you try to set the UltimateFallbackLocation.MainAssembly you’ll run into the unfortunate result that your BAML Resources could not be found even for the neutral/default language. MainAssembly is the default setting for the flag, but it will only work if localization is not enabled and the <UICulture> flag is not set in the project file.
Bottom line: If you are localizing BAML resources both settings are required and you have to use satellite assemblies for everything including the neutral culture.
Resx Resources and BAML Resources in the same Project: Resource Hell
LocBaml is a pretty unstable solution that has lots of issues, but fortunately Resx resources also still work in WPF. You can use Resx resources through code, by binding static resource values ( Content=”{x:static res:Resources.HelloWorld}”) to strongly typed resources or by using one of the many custom localization markup extensions that are available.
Here’s where things get confusing though – if you are mixing approaches with BAML and Resx localization you need to be real careful how to set up your Resx resources because of the required UltimateResourceFallbackLocation.Satellite setting used.
Specifically at design time you need to create a non-culture specific .resx file like Resources.resx. You can then create your culture specific versions as well (Resources.de.resx). Normally you’d expect that’s enough, but because of the Satellite export of resources even the neutral culture gets exported to an external resource assembly in the en-US folder. This means in addition to Resources.resx you also need to have Resources.en-US.resx in your project! At runtime .NET only looks for the en-US version for that particular culture as well as the fallback culture when a specific or non-specific culture can’t be mapped.
Assuming my neutral culture is en-US here’s what this looks like in the VS Project:
Notice both ResxResources.resx and ResxResource.en-US.resx. The former is never used at runtime, but required in order to provide the strongly typed class.
You might think that why can’t we just skip the ResxResource.resx file and JUST create the the ResxResource.en-US.resx file. While that works just fine for compilation at runtime, if you want strongly typed resources the non-culture version of the resources is the only one that generates the strongly typed resource class.
Bottom Line: You’ll want to work with Resource.resx and then also copy that file to Resources.en-US.resx when keys change or use a build task to automate this process.
That’s pretty annoying, but it gets even worse: Microsoft Blend doesn’t load resources from satellite assemblies and since this approach effectively requires that resources are loaded from satellite assemblies any binding against resources or use of markup extensions that retrieve even neutral resources in Blend will fail with an error that Resources could not be loaded. Now that’s a big bummer especially after building a markup extension and having it work in the Visual Studio Cider designer.
NOTE: This tedious copy process is required only if you use both localized BAML and Resx resources in the same project and you have the <UICulture> key set in your project file.
Running only Resx Resources
Things are much easier if you don’t localize BAML resources and only work with Resx resources. In this scenario don’t specify the <UICulture> key in your project file or remove it if it was there before. Then set your assembly NeutralResourcesLanguage attribute to compile the neutral resources into the main assembly:
[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
In this set up you can work with resources the way you always have in WinForms and classlibrary projects where neutral resources are retrieved from the main assembly and any localized resources are retrieved from satellite assemblies. This means you just create your neutral resources in Resources.resx (or whatever other file) and create only your translated cultures in Resources.de.resx and so on. No copying for the explicitly fallback culture. Nice and logical the way you would expect it to work.
With the neutral fallback resources stored in the main assembly Expression Blend is also happy and now loads static resources and markup extension bound resources without issues.
Confused?
If you think all of this sounds confusing as hell you’re not alone. While working on this I found dozens of questions regarding resource load failures. This stuff isn’t real obvious and worst of all the behavior that BAML resources introduces are partially incompatible with the standard Resx resources. It also doesn’t help that Microsoft has practically no guidance on this matter. Oh wait – I’m supposed to be writing that :-}
Hopefully this will help some of you out. I know I wish had seen something along these lines a couple of weeks back when I was ramming my head against the wall wondering why BAML resources were working and Resx Resources continued to fail. The more I see the more I wonder who should be strapped to a wheel for throwing LocBaml into the localization mix for WPF. I’ll have more on that in my next post.
May 25, 2009 @ 11:20 pm
- from Maui, Hawaii
Creating a full featured editable component that handles a number of scenarios is quite complex. Some time ago I started in on this process with a wwEditable plug-in which I’ve used in a number of applications. It works, but it’s fairly large and even so has a few rough edges for full generic use.
However, if you’re just after a quick and dirty mechanism for editing some text for the purpose of updating it there’s actually an easier way using the contentEditable attribute on DOM elements. The nice thing about contentEditable is that it maintains original markup and is in effect live editing of text in the currently formatted text. So if unlike wwEditable which used a textbox to copy content and had to figure out what CSS attributes to copy to make the text look right and then handle markup conversion issues, the text you type into a contentEditable block is live HTML.
Some time ago I created this behavior as a way to quickly edit my comments posted here on the WebLog. When I reply, more on a few occasions I’ve been rather sloppy and need to fix a couple of things. Unlike you – dear readers – I can remove posts, but even so that process required copying the old comment, then adding a new comment and hoping the text works. Occasionally there’s also been a request by users to make a small change to a comment – and before I added the ability to edit there was no easy way for me to do this.
With the editable ability I can just tweak the text briefly and in-place. Here’s the regular comment display you see:
and here is what I see when I’m editing a comment:
Notice that the text here becomes just editable inside of the formatting. You can see it where I added the new line code and it inherits the current <pre> formatting (but not the auto C# formatting – that’s another story :-}). There’s also a Save button that pops underneath the content.
Let’s see how this works. The process is pretty simple. I’m using jQuery to select the editable area, make it editable using the contentEditable attribute on the DOM element and then dynamically add the button beneath the text. Then when the save button is clicked an AJAX service callback is made to send the HTML of the edited text back to the server. Finally the button is removed and the display formatting reset. To the user the experience is very smooth and nearly instant because the text is updated in place and the Web Service call to update the text happens in the background.
My first shot at this was to just create a simple non-generic function which is actually fairly short. To give you some context, here’s what the HTML layout of a comment looks like here on the Weblog:
<div class="comment commentauthor" id="cmt_635331">
<a href="#635331" name="635331">#</a>
<img hspace="5" align="right" style="opacity: 0.75;" src="http://www.gravatar.com/avatar.php?gravatar_id=xxx"/>
<b>re: Using Enums in List Controls</b>
<img style="border-width: 0px;" src="../images/EditWhite.gif" class="hoverbutton commentedit"/>
<br/>
<small> by
<a target="_WebLog" href="http://www.west-wind.com/">Rick Strahl</a>
<span class="commenttime">February 22, 2009 @ 2:21 pm</span>
<div class="commentbody">
@Mark - you caught me :-}… rest of content here
</div>
<a onclick="DeleteComment( 635331 );return false;" href="javascript:{}">Remove Comment</a>
</small>
</div>
You can see there’s an image icon with a .commentedit style attached to it which triggers the editing operation. It’s hooked up in $().ready() with a click handler assignment which in turn calls the actual editing routine (note the button behavior and activation click isn’t included in the behavior I’ll describe – maybe in the future):
$().ready(function() {
$(".commentedit").click(commentEdit);
});
function commentEdit(evt) {
var jComment = $(this).parents(".comment").find(".commentbody");
if (jComment.length < 1)
return;
jComment.get(0).contentEditable = true;
jComment
.css( {background:"azure",padding: 10} );
// create button and hookup click handler
var jButton = $("<input type='button' value='Save' />")
.click(function() {
// find the id on the .comment item and strip off cmt_ prefix
// id="cmt_111"
var id = jComment.parents(".comment").get(0).id.replace("cmt_", "");
// Call Web Service with numeric id and updated html
Proxy.UpdateCommentText(+id, jComment.html());
jComment.get(0).contentEditable = false;
// remove button and reset content display
jComment
.css({ background: "transparent", padding: "20px 0 0" });
jButton.remove();
});
jComment.after(jButton);
jButton.after("<br />").css("margin", 5);
}
Not a lot of code here for inline editing. The code starts by checking if the .comment item was found. Next the actual comment item is made editable with the simple jComment.get(0).contentEditable = true. contentEditable is a DOM property (not a jQuery property) and so .get(0) is used to get the first element and assign the property. Voila that’s really all you need to make something editable. BTW, this works on all modern browsers in recent versions which actually surprised me when I first looked at this: IE 6+, FireFox, Safari, Chrome and Opera all work.
The rest of the code deals with adding the save button and it’s action when clicked. When clicked the code finds the parent Comment element and extracts the comment ID out of it. I generate IDs on the server when the comments are created in a ListView with a cmt_ prefix plus the actual comment id so I have a way to get a unique comment id to link to on the page as well as for this update editing.
In this case the save action calls a Web Service (Proxy.UpdateCommentText) which takes the HTML entered and posts it to the server. Then the edit field is returned to its original display state (non-editable) and the save button is removed.
The server is an AJAX service saves the HTML and thus the server is updated. I’m using the AjaxMethodCallback control from the West Wind Web Toolkit here:
<ww:AjaxMethodCallback runat="server" ID="Proxy" ServerUrl="~/WebLogCallbacks.ashx" ></ww:AjaxMethodCallback>
and server method that handles the callback looks like this:
[CallbackMethod]
public bool UpdateCommentText(int id, string html)
{
if (!this.IsAdmin())
throw new AccessViolationException("Access denied - must be logged in");
busComment comment = new busComment();
if (!comment.Load(id))
throw new ArgumentException("Invalid comment Id");
// Content area includes a couple of leading line breaks that we
// don't want in our HTML markup
html = StringUtils.TrimStart(html.Trim(),"<br>",true).Trim();
comment.Entity.Body = html;
return comment.Save();
}
private bool IsAdmin()
{
if (HttpContext.Current.User.Identity != null && HttpContext.Current.User.Identity.IsAuthenticated)
return true;
return false;
}
This is using the West Wind Web Toolkit, but the code would be very similar if you ASP.NET AJAX and an ASMX/WCF Service.
Note that I simply accept the HTML and allow it to be saved. In my scenario here this is acceptable since this is an administrative function. Only administrative users have access, everybody else is bounced. If this was an open connection I’d have to be very, very careful and worry about script injection. While the input typed will be safe since the HTML will be encoded by the Web Browser itself (it creates properly encoded HTML text), there’s always direct HTTP access by a malicious user or script kiddie. Make sure when you update strings as raw HTML over the wire you think about the possible security implications for script injection. Of course you don’t have to send HTML – you can return the .text() value and treat the content entered as text rather than HTML which for many applications will be perfectly valid (think FaceBook’s text editing for example – it’s only text). You still have to worry about script injection though either way you look at it.
Pretty cool though how easy the base process is, right? contentEditable sure is a lot easier to work in a plug-in than having to add a textbox and try to match the overall text formatting. Here all you can do is apply contentEditable = true and you get live editing in the current format. Sweet!
Take Two: Creating a more generic contentEditable Plug-in
The code for doing this sort of inplace editing is not terribly complex, but it does take a little bit of tweaking to remember the right properties to access and add a button, so almost as soon as I had this working I figured this needs to be a jQuery plug-in. As simple as the code above is, creating a plug-in ends up being a little more involved as you start looking at things from a more generic usage perspective.
The generic version is a bit more code, but it’s also a bit more flexible:
$.fn.contentEditable = function(opt) {
if (this.length < 1)
return;
var oldPadding = "0px";
var def = { editClass: null,
saveText: "Save",
saveHandler: null
};
$.extend(def, opt);
return this.each(function() {
var jContent = $(this);
if (this.contentEditable == "true")
return this; // already editing
var jButton = $("<input type='button' value='" + def.saveText + "' class='editablebutton' style='display: block;'/>");
var cleanupEditor = function() {
if (def.editClass)
jContent.removeClass(def.editClass);
else
jContent.css({ background: "transparent", padding: oldPadding });
jContent.get(0).contentEditable = false;
jButton.remove();
};
jButton.click(function(e) {
if (def.saveHandler.call(jContent.get(0), e))
cleanupEditor();
});
jContent.keypress(function(e) {
if (e.keyCode == 27)
cleanupEditor();
else if(e.keyCode == 9)
});
jContent
.after(jButton)
.css("margin", 2);
this.contentEditable = true;
if (def.editClass)
jContent.addClass(def.editClass);
else {
oldPadding = jContent.css("padding");
jContent.css({ background: "lavender", padding: 10 });
}
return this;
});
}
jQuery plug-ins are very easy to create by extending the jQuery.fn object with a custom function that receives the a jQuery object of all the selected elements in the current chain. Typically a plug-in needs to run through each of the elements (typically with a .foreach() ) and then apply the appropriate functionality inside of the foreach() operation. If the plug-in is chainable it should return the jQuery object (this or in this case the result from this.foreach() which is the jQuery object).
So this version adds a few options since it’s generic:
editClass
The CSS class applied to the edited element. If not specified an azure background and 10px padding is applied.
saveText
The text for the save button. The button also has a CSS class of editablebutton applied to it in case you need to override the button display format.
saveHandler
The handler that is called when the save button is clicked. The handler is passed the click handler’s jQuery Event object and the call is made in the context of the edited content element (ie this=content not the button). This is a little unusual – but it makes sense in this circumstance as you want to have access to the content element not the button (which you can still access with $(e.target) if necessary).
In addition you can also press ESC to abort editing without calling the save handler.
The rest of the code should be fairly familiar from the non-generic version. The base operation is similar but there are a few extra checks like whether the item is already being edited and making sure that the display is reset when done editing.
Using this plug-in the application JavaScript code gets a little simpler:
function commentEdit(evt) {
var jComment = $(this).parents(".comment").find(".commentbody");
jComment.contentEditable(
{ editClass: "contenteditable",
saveHandler: function(e) {
// grab id from parent .comment element and strip cmt_ prefix
var id = jComment.parents(".comment").get(0).id.replace("cmt_", "");
// call service to update comment with numeric id and updated html
Proxy.UpdateCommentText(+id, jComment.html());
// return true to close editor (false leaves open)
return true;
}
});
}
I’ve created a sample page so you can check this out:
http://www.west-wind.com/WestwindWebToolkit/samples/Ajax/plugins.aspx
it’s kinda silly, but you get the idea how it works.
Storage Formatting for Text
This is a fairly simple plug-in and it’s not meant as an end all editor and certainly not as a rich text editor. It simply allows you to edit inline content smoothly. Personally I prefer this sort of inline editing to slapping a text box into place as I did with the wwEditable plug-in mentioned earlier.
But because the content you’re entering is HTML you need to think about how you want to manage the data entered: Do you want to treat it as HTML as I did in my blog comment editing or do you treat it as raw text and lose all formatting? It’s really up to you or rather up to the way you store text in your application.
There are really two major approaches that can be taken by applications to store text: You store the raw text the user entered and format the text on the fly as the page is rendered. Here on the WebLog for example, the text you enter in comments is post processed a bit to handle URL expansion and code formatting. I can store the data in the original entered formated or I can choose to turn the text into HTML and store the HTML in the database instead.
I actually chose the original path because originally comments where one-way only. They were never to be edited. Obviously that’s changed now though and I’m wondering if that was the right choice now. The reason to store HTML is that you don’t want to have to re-format the data all the time. I have a few posts that a couple hundred comments and if those all had to be re-formatted it’d be somewhat resource intensive. (it also seems a good idea to start thinking about limiting comments :-})
So in my case the raw HTML editing actually works well as long as it’s an admin only operation via AJAX to avoid malicious data input via scripting.
Note that my example returns the .html() to the server:
Proxy.UpdateCommentText(+id, jComment.html());
but you can just as easily just return the text (.text() instead of .html()). In fact, in many text scenarios that is all that’s really needed.
Since I created this component I’ve been using it in a number of admin interfaces and have added it to the ww.jquery.js client library so it’s always there in my base lib. You can grab the latest code for the plug-in from repository (there are no dependencies for this plug-in), so if you just want this plugin, you can copy it. Documentation can be found here and if you want to get the sample (or any other of the samples) it’s part of the Web Toolkit download and in the full Subversion repository.
May 21, 2009 @ 12:31 pm
- from Maui, Hawaii
This might be one of those “Duh” moment posts that seems real obvious now, but this particular issue – trying to get OutputCache to work in an HttpHandler and having it not work because of an errand Response.End() - has caused me grief on a few occasions. Some time ago I posted a JavaScript Resource handler that serves server side ASP.NET resources to client side JavaScript. Bertrand commented on that post that I should be using OutputCache instead of writing values to the ASP.NET Element Cache as I had been in the code posted in the blog entry. I agree – OutputCache is probably the most efficient way to cache full requests. However, in the past I’ve had a number of issues with OutputCache not working in custom handlers and so I’ve been wary of using it in handler code falling back on using the ASP.NET Element cache instead. The Element Cache works well enough, but there’s additional processing required on each hit that OutputCache handles transparently especially if you need to do special encoding like GZip, so OutputCaching is much preferred. You can see the original code I used that uses the element cache in the post. It certainly would be nice to use OutputCache.
Output Caching
To review, OutputCaching is part of the ASP.NET Pipeline and is the same mechanism that you can use in ASP.NET Pages using the @OutputCache directive. Using OutputCache with a handler rather than a Page is a little less discoverable than the page directive, but it provides the same functionality for any code running in the pipeline. OutputChacing is implemented as a Module in the pipeline and depending on the type of request that is being processed (headers, length, whether it’s a POST operation etc.) the caching can get elevated to the IIS Kernel Cache (although information on when and how that actually occurs is very scarce). OutputCaching is probably the most efficient way to cache request because it is handled internally and doesn’t have to hit custom code if cached content is served. The content of a cached entry is fully held in the cache including headers, encoding and the content so there’s no requirement to re-encode or re-send headers etc. as you would when caching content on your own using the element cache as I did originally in the blog post code.
OutputCache is applied in its own step in the ASP.NET Event Pipeline via the ResolveRequestCache event which fires after request authorization (because you still want to authenticate/authorize cached requests) and before the request state is retrieved and a handler is loaded. For new entries the cache is then updated towards the end of the request in the UpdateRequestCache Event. You can see where in the pipeline these events fire on this old slide:

All this adds up to a very efficient mechanism of serving cached content because it fires very early in the pipeline processing and is handled internally by ASP.NET or (in IIS7) by IIS itself.
Writing to OutputCache is pretty straight forward and looks something like this:
HttpResponse Response = HttpContext.Current.Response;
// client cache
if (!HttpContext.Current.IsDebuggingEnabled)
{
Response.ExpiresAbsolute = DateTime.UtcNow.AddDays(30);
Response.Cache.SetLastModified(DateTime.UtcNow);
Response.AppendHeader("Accept-Ranges", "bytes");
Response.AppendHeader("Vary", "Accept-Encoding");
//Response.Cache.SetETag("\"" + javaScript.GetHashCode().ToString("x") + "\"");
}
// OutputCache settings
HttpCachePolicy cache = Response.Cache;
cache.VaryByParams["LocaleId"] = true;
cache.VaryByParams["ResoureType"] = true;
cache.VaryByParams["IncludeControls"] = true;
cache.VaryByParams["VarName"] = true;
cache.VaryByParams["IsGlobal"] = true;
cache.SetOmitVaryStar(true);
DateTime now = DateTime.Now;
cache.SetCacheability(HttpCacheability.Public);
cache.SetExpires(now + TimeSpan.FromDays(365.0));
cache.SetValidUntilExpires(true);
cache.SetLastModified(now);
Response.ContentType = contentType;
Response.Charset = "utf-8";
Response.Write(text);
Response.End(); // No caching if specified
The updated version of the JavaScriptResource handler that this code block is pulled from can be found in the West Wind Web Toolkit Repository here so you can compare with the original code from the original post.
Watch out for Response.End()
Now as it turns out the code as I have it written here DOES NOT work – as you can surmise from the highlighting the Response.End() actually makes this code run, but the output never makes it into the OutputCache. The reason for the Response.End() in the code here is that the code in question is a method in the handler rather than directly defined in ProcessRequest. Originally there was a bit of error checking code and cleanup that followed the output generation code in case there was a problem. There are also a number of error generation routines that create 404 requests on missing resources and errors on invalid syntax and so on – all of which also fire a Response.End(). So to be consistent I thought that using Response.End() everwhere would be the right thing to do.
Au contraire – using Response.End() in this fashion causes the processing of the pipeline to abort complete after output generation. In other words by specifying the Response.End() code at the end of the request causes UpdateRequestCache() to never fire and put the content into the cache. Response.End() (and HttpApplication.CompleteRequest()) causes the request to stop right at the point of the Response.End() call and jump to the EndRequest() handler, bypassing any events in between including UpdateRequestCache() which is needed to write the cache content into the OutputCache.
So, after a quick refactoring of the method and how post processing is handle I could remove the Response.End() call and voila the caching started working. The error method still do a Response.End() to exit early, but that’s acceptable because they should not cache anyway – they never actually hit the cache code (which is obviously important – you wouldn’t want to cache an error :-}).
This is pretty obvious once you know what’s happening, but I can tell you I looked at the code for quite some time agonizing over WHY the content was not getting into OutputCache even though it was staring me straight into the face. The Response.End() looked innocent enough.
Hopefully this post will help me – and maybe even one or two of you – jog my memory and avoid this problem in the future.
May 18, 2009 @ 3:12 am
- from Maui, Hawaii
I’ve tried an interesting experiment today trying to see if I can get LINQ to SQL to run with a custom ADO.NET Data Provider I’ve created some time ago. Basically I implemented a provider that is a proxy to a remote Web ‘service’ that can marshal SQL commands via Web Requests on a remote server. I implemented a custom provider for this interface that ships XML across the wire and marshals raw SQL commands to the Web server to process. It works well using plain ADO.NET and my own internal business layer.
The provider uses SQL Server syntax since all it’s really doing is marshalling commands to the server via XML, but the raw syntax of commands is still full SQL Server T-SQL dialect. So I started thinking why shouldn’t I be able to use this provider with LINQ to SQL since the SQL it generates should still work with this provider – no dialect problems here.
The dialect is important though since LINQ to SQL is SQL Server specific. I’m not sure what logic it actually uses to figure out which provider it’s dealing with (it officially supports SQL 2005, Sql 2008 and Sql Compact), but I suspect the default is SQL 2005, which is likely what’s being used with my provider. Either way – the following will only work if you have a provider that’s highly SQL Server T-SQL compatible . If the provider is some other database that isn’t closely TSQL compatible (like Oracle, MySql etc.) this approach won’t work, so this is not a general purpose solution.
After an evening of mucking around with this I was able to get LINQ to SQL to work with my custom provider. The process involved is actually quite simple, although it’s not real obvious to discover. LINQ to SQL doesn’t have any explicit support for plugging in new providers (unfortunately) however, you CAN pass it an instance of a Connection object like this:
WebRequestConnection conn = new WebRequestConnection(); // my custom provider’s Connection class
conn.ConnectionString = "Data Source=http://rasnote/PraWeb/DataService.ashx;uid=ricks;pwd=secret"; // this.connectionString
TimeTrakkerDataContext context = new TimeTrakkerDataContext(conn);
Here I create an instance of my custom provider’s WebRequestConnection() class, set its connection string and pass it to the constructor of the DataContext. And lo and behold – assuming your provider is compatible with SQL syntax and implements the DbProvider classes properly LINQ to SQL works using the custom provider.
I haven’t done any extensive checking, but running through a number of my small apps I’ve been able to simply plug in the Web ADO.NET provider and the apps work so far. My ADO.NET provider implementation is pretty bare bones. Because it’s disconnected for example it doesn’t support Transactions, but for the applications that I will be using this for this won’t be a problem since the app deals with simple atomic updates.
This post probably falls into the stupid pet tricks category since there’s not much need to use an alternate provider that uses SQL Server dialect, but I’m pretty stoked because this solves a very specific problem I’ve been agonizing over. I could not LINQ to SQL for this particular app because of the remote provider aspect that should be switchable with local and SQL operation. Now it looks I will be able to. This gives me Sql Server, Sql Compact (local data) and remote Web access.
I haven’t had enough time to try this out with other providers, but I wonder how compatible the SQL driver actually has to be to work with LINQ to SQL. For example, there are several other tools out there that supposedly are very compatible SQL Server syntax. VistaDb springs to mind since they claim a very high level of SQL Server compatibility. I’d be curious if something like VistaDb could actually be made to work with LINQ to SQL using this approach.
May 12, 2009 @ 11:52 am
- from Maui, Hawaii
Here’s a problem I’ve hit on a few occasions with the very cool jQuery.ui.sortable plug-in. When you’re sorting elements that are clickable it often happens that when you drop your sortable item in a new sort location that the click event of the item fires.
For example check out this page:
http://www.west-wind.com/WestwindWebToolkit/samples/Ajax/AmazonBooks/BooksAdmin.aspx
Notice that each of the items are clickable to bring up in-place editing for each item via a popup window. Now click on the Sort List button. Once you do the list becomes sortable and you can drag items up and down the list based on the sortable plug-in:

(Note: it works correctly now, but just *imagine* that when you drop the item the item display pops up. :-})
The sortable plug-in is super easy to use. In this case the sortable is applied to a <div> container element that contains additional <div> tags for each of the items. Here’s the .sortable applied against the outer container:
$("#divBookListWrapper").sortable(
{
opacity: 0.7,
revert: true,
scroll: true,
containment: "parent",
stop: function(e) {
$(ctl).data("Sort", "1");
$(ctl).html($(ctl).html().replace("Sort List", "Update Sort"));
}
});
The code as written would cause a problem however, because when sort items are dropped into place when a single sort operation is done the mouse up event from the drop also triggers a click event on the item dropped which produces rather unexpected results. In the example above it would show the detail window at the end of each drop operation – hardly the desired result. (note the sample doesn’t exhibit this behavior)
Unfortunately there’s no native switch for the .sortable plug-in that allows you to override this behavior although I think there definitely should be because I’ve had this particular behavior kick in for me in a variety of situations.
I experimented around with a few generic solutions trying to find a way to surpress the click event from firing by attempting to preventDefault, stopPropagation on the original source events fired, but unfortunately this had no effect. In the end the only thing that did work was to remove the click events in the start event handler for .sortable and then hook the events back up in the stop handler. The code that actually works correctly looks like this:
[Note: Updated based on comments – problem with e.originalTarget in WebKit and Opera requires unbinding all sortable elements]
$("#divBookListWrapper").sortable(
{
opacity: 0.7,
revert: true,
scroll: true,
containment: "parent",
start: function(e) {
// have to remvoe click handler off item so drop doesn't click
//$(e.originalTarget).unbind("click");
$(".bookitem").unbind("click");
},
stop: function(e) {
$(ctl).data("Sort", "1");
$(ctl).html($(ctl).html().replace("Sort List", "Update Sort"));
// reattach the item click handler
//$(e.originalTarget).click(itemClickHandler);
$(".bookitem").click(itemClickHandler);
}
});
The start and stop events fire when you start and stop sorting a single item and so is perfect for this situation. Start and stop are called in the context of the parent element, not the actual item, so in order to get the current dragged item e.originalTarget can be used which is the element the mouseDown event fires on. In start, the click handler is unbound and then hooked back up in stop. I’m using an explicit function itemClickHandler here so the event code is only defined in one place for loading/updating of items and reattaching here. If you have more than one click handler in the particular item you might also want to look into hooking up your events using event namespaces (ie. .bind("click.bookitem") and .unbind("click.bookitem")).
[Note: Updated based on comments – problem with e.originalTarget in WebKit and Opera requires unbinding all sortable elements]
The original code is commented out because it caused problems with Safari, Chrome and Opera. The issue is the e.originalTarget apparently is not getting set by the start/stop handlers so the click handler is actually NOT unset. The workaround here is to remove the click handler from ALL child items (since we don’t know which one is being dragged) and the reattaching them all in the stop handler. It appears that this is a bug in sortable as e.originalTarget certainly should be set as it is in FireFox and IE. <sigh>
Thanks to claya for the nudge in the right direction via Twitter.
FWIW, I’ve run into situations like this on a few occasions with various plug-ins including my own where overlapping events can cause errant behavior like this.The idea of unhooking events at the start of an operation and then re-attaching them is not an uncommon task and if I wouldn’t have been thinking that “there should be an easier way” from the start I would have solved this problem much quicker :-}. jQuery’s event management makes it very easy to unattach and reattach events so as long as you have your event logic isolated it’s just a couple of lines of code.
May 11, 2009 @ 2:47 am
- from Maui, Hawaii
A few weeks ago I posted a closable plug-in for jQuery and I’ve been using it in a number of places for list based close operations. It works well in most but I ran into an apparently known problem with Internet Explorer (pre IE8 Standards Mode). The closable plug-in works by injecting either an image or a styled div tag into the specified element and absolutely positioning it – typically in the upper right corner. The relevant code does something like this:
var el = $(this);
var pos = el.css("position");
if (!pos || pos == "static")
el.css("position", "relative");
var h = opt.handle ? $(opt.handle).css({ position: "relative" }) : el;
var div = opt.imageUrl ? $("<img />").attr("src", opt.imageUrl).css("cursor", "pointer") : $("<div></div>");
div.addClass(opt.cssClass)
This works great with all browsers and in most situations even with old versions of IE. But there’s a problem with pre IE 8 Standard Mode rendering of position: relative elements. Once position: relative is applied apparently these elements lose their ability to be contained in the parent container. As soon as elements become relative they spill out of the bottom the container regardless of the overflow or overflow-y settings.
You can see this first hand here with Internet Explorer in compatibility mode:
http://www.west-wind.com/WestwindWebToolkit/samples/Ajax/AmazonBooks/BooksAdmin.aspx
(it might be fixed by the time you look so here’s a screen shot)
as you can see the content is simply spilling beyond the containing list. Basically there is an outer div (divBookListWrapper) that has a fixed height:600px and overflow-y: scroll set so the list should scroll whenever the content doesn’t fit. Inside of this element there are then a number of .bookitem <div> tags which render the detail content. The closeable plug-in then adds the position:relative as part of its processing.
This renders correctly in every browser but IE regardless of whether the .closable plug-in has been applied or not. In IE this also renders correctly if position: relative is not applied. Add position relative as part of the .closable plug-in (or just in markup) and the mess above occurs.
I’ve been mucking with this for a while, but I don’t see a workaround for this. The link mentioned at the top of this post mentions that quirks mode in IE is the only way around this. Oh joy. This is fixed in IE 8 Standards mode, but that’s of little solace given that IE 8 is still lightly used and that compatibility mode can easily be accessed. Also – annoyingly – the IE Tab and other IE plug-ins all use pre IE 8 rendering so the problem also shows up in IETab mode in FireFox.
In this case the solution is easy enough: Don’t use the plug-in and manually connect the delete button using a <div style=”float: right”> but this is not nearly as clean as the .cloasable plug-in which can be applied unobtrusively. Here’s the original template that top aligns the remove buttons:
<script type="text/html" id="item_template">
<div id="divBookItem" class="bookitem">
<div style="float: right;" id="divBookOptions">
<img src="../../css/images/remove.gif" onclick="removeBook(this,event )" class="hoverbutton" />
<br />
<small><#= SortOrder #></small>
</div>
<img src="<#= AmazonSmallImage #>" id="imgAmazon"/>
<b><a href="<#= AmazonUrl #>" target="_blank" ><#= Title#></a></b>
<br/>
<small><#= Author #></small>
<br/>
<# if (Highlight) { #>
<small><i>Highlighted</i></small>
<# } #>
</div>
</script>
If anybody happens to know of a work around that can keep the position:relative in place I’d love to hear it.
Things like this make me yearn for desktop development again. Nah, just kidding, but still I wonder when we can realistically start ignoring old versions of IE. What can we do to accelerate getting old versions of IE out of the Web? Purposely break sites with stuff like the above oughta be a good start – now if we could only get some of the big sites to do that so the turnover is quicker :-}.
May 04, 2009 @ 6:27 pm
- from Maui, Hawaii
The fact that you can assign namespace and assemblies in your web.config file is one of the most underused features of ASP.NET I think. I was working with a customer today and when I demonstrated an example he noted that I got my custom controls to show up in page markup without having an explicit @Register tag in the page. Usually when you embed a custom control into a page you need to add a @Register tag like so:
<%@ Page language="c#" Inherits="Westwind.WebToolkit.MessageDisplay"
CodeBehind="MessageDisplay.aspx.cs"
enableViewState="false" AutoEventWireup="True"
MasterPageFile="~/WestWindWebToolkit.master"
%>
<%@ Register Assembly="Westwind.Web" Namespace="Westwind.Web.Controls" TagPrefix="ww" %>
in order to get a control to work in the page and show up with Intellisense. If you’re using the visual designer to drop controls you probably won’t notice this requirement because the designer automatically adds the assembly and namespace dependency for you into the page. However if you work in markup only as I do mostly, it’s often annoying to first have to register the control on the top of the page and then go back to actually embedding the control into the page to get Intellisense.
An easier and application global way to do this is to declare your namespaces and control tags directly in web.config and have them apply globally:
<system.web>
<pages>
<namespaces>
<add namespace="System.IO" />
<add namespace="System.Text" />
<add namespace="Westwind.Utilities" />
<add namespace="Westwind.Web.Controls" />
</namespaces>
<controls>
<add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add tagPrefix="ww" namespace="Westwind.Web.Controls" assembly="Westwind.Web" />
</controls>
</pages>
<compilation debug="true">
<assemblies>
<add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
</system.web>
The controls section is what provides effectively the equivalence of the @Register tag in pages and once you’ve defined the tag prefix there the @Register tag is no longer required in the page.
The Namespaces section does something similar the the @Import tag in the page header by allowing you to define global namespace definitions that replace things like:
<%@ Import Namespace="Westwind.Web.Controls" %>
but on a global scale. The @Import is typically used to make namespace references available in your page so that page server code doesn’t need to use fully qualified types for everything. This helps keeps your code shorter
<%= StringUtils.DisplayMemo( Item.Description ) %>
instead of
<%= Westwind.Utilities.StringUtils.DisplayMemo( Item.Description ) %>
You do want to keep your namespace lists reasonable just like you should in all code to keep the compiler from having to do name resolution on its own. Remember ASPX pages compile once at runtime (unless you use the ASP.NET compiler) during startup and lots of extra namespaces do add some overhead during compilation.
Behind the scenes the ASP.NET compiler injects the namespace references into all generated pages and the assemblies as references to any of the dynamic assemblies it creates. By default ASP.NET includes all assemblies in the bin path, plus any GAC based assembly references in the <compilation> section of web.config. Note that you don’t need to add any private assemblies in this section – it’s only needed to add GAC references. All private assemblies in BIN folder are automatically referenced.
This isn’t exactly news – this feature has been there since ASP.NET 2.0, but it does make life easier especially if you have markup pages that utilize external utility libraries frequently.