次の方法で共有


Dealing with Application.Current in Design Mode

There are a ton of WPF samples on MSDN which are really useful if you are developing in or learning WPF.  If you aren't aware of these samples, please check them out.

One of the samples illustrates a point I want to make in my post today -- which is that as much as we try to avoid making you do this, sometimes you have to tweak your XAML/code in order to make it work within the Cider designer.

The sample I am going to use is from a DataTemplate Sample located here:  https://msdn2.microsoft.com/en-us/library/aa972119.aspx

If you load up this sample in VS 2008 beta 2 or later and (after conversion) you'll see that you can build and run the application no problem.  However, if you open up Window1.xaml, you'll notice that it fails to load in the designer and you'll get an error:

 'myTaskTemplate' resource not found.
   at MS.Internal.Helper.FindResourceHelper.DoTryCatchWhen(Object arg)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler) 

The problem occurs when we try to instantiate the TaskListDataTemplateSelector which is in the Window.Resources:

    <local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>

This class does the following:

    public class TaskListDataTemplateSelector : DataTemplateSelector
    {
        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            if (item != null && item is Task)
            {
                Task taskitem = item as Task;
                Window window = Application.Current.MainWindow;

                if (taskitem.Priority == 1)
                    return
                        window.FindResource("importantTaskTemplate") as DataTemplate;
                else
                    return
                        window.FindResource("myTaskTemplate") as DataTemplate;
            }

            return null;
        }
    }

Do you see the problem? What do you suppose Application.Current.MainWindow is in the designer?

Since Visual Studio isn't a WPF application, for a long time during the development of Cider Application.Current returned null.  It no longer returns null but it is setup by Cider and has to do with Cider and not what you will get when you run your application. 

In other words, calls to window.FindResource() will fail.

So what can you do to make this work in the designer?  Well you have a few options, but if you've been following this blog for a long time you'll remember this post on detecting design mode.

Since this situation nicely demonstrates the use of design mode, I'll solve the problem that way by changing the above code to check for Design Mode before accessing Application.Current.

        public override DataTemplate
            SelectTemplate(object item, DependencyObject container)
        {
            if (!DesignerProperties.GetIsInDesignMode(container) && item != null && item is Task)
            {
                Task taskitem = item as Task;
                Window window = Application.Current.MainWindow;

           (. . .)
        }

The parameter you pass to GetIsInDesignMode doesn't really matter here since I'm counting on the default value which has been overriden to true in the context of a designer, the DependencyObject passed in will look for the IsInDesignMode attached property up it's tree.

Now -- lo and behold!  The designer loads in Cider.

Comments

  • Anonymous
    September 24, 2007
    PingBack from http://www.artofbam.com/wordpress/?p=2646

  • Anonymous
    October 11, 2007
    Recently I made a post about how to deal with Application.Current at design time . I thought I&#39;d

  • Anonymous
    April 07, 2008
    Lots of great posts that Jim has been blogging about in the past 6 months. Application Authors Using