Solucionar problemas de errores de carga de WPF Designer

Actualización: noviembre 2007

Windows Presentation Foundation (WPF) Designer for Visual Studio incluye un diseñador visual complejo y extensible que representa XAML. Si un archivo XAML no se carga en el diseñador, existen varias formas de intentar comprender la causa del error. En este tema se describen algunas sugerencias y técnicas para ayudarle a solucionar problemas de errores de carga de WPF Designer.

Nota:

Muchas de las técnicas de este tema también se aplican a Expression Blend.

Pasos para la solución de problemas

Los pasos siguientes pueden ayudarle a solucionar problemas de errores de carga de WPF Designer.

  1. Lea todos los mensajes de excepción que reciba.

    Esto puede parecer obvio, pero si obtiene una excepción, lea el mensaje completamente. En algunos casos, puede ayudarle a diagnosticar el problema rápidamente. Para obtener más información, vea Depurar e interpretar errores en WPF Designer.

  2. Determine si el problema está en su implementación.

    Genere y ejecute su aplicación para determinar si el problema sólo es el resultado de su implementación o de una interacción con WPF Designer. Si la aplicación se genera y se ejecuta, es probable que el error en tiempo de diseño esté causado por su implementación.

  3. Utilice el depurador de Visual Studio para examinar el código en tiempo de diseño. Para obtener más información, vea Tutorial: Depurar controles personalizados de WPF en tiempo de diseño.

  4. Determine si el problema es un error de carga.

    Si la vista Diseño no se carga debido a una excepción, es probable que el problema sea un error de carga. Si tiene código personalizado que se carga en tiempo de diseño y experimenta excepciones o errores de carga en tiempo de diseño, vea la sección Escribir código para tiempo de diseño de este tema. Si está trabajando con recursos y parece que no se cargan, vea la sección UserControl y recursos del control personalizado de este tema.

  5. Revise el código que se carga en tiempo de diseño.

    Existen dos enfoques para escribir código que también se ejecuta en tiempo de diseño. El primer enfoque es escribir código estable comprobando los parámetros de entrada a las clases. El segundo enfoque es comprobar si el modo de diseño está activo llamando al método GetIsInDesignMode. Para obtener más información, vea la sección Escribir código para tiempo de diseño de este tema.

  6. Revise otras áreas del código.

    Revise la sección Sugerencias de programación de este tema para obtener sugerencias de programación cuando trabaje con WPF Designer. Revise la sección Procedimientos recomendados de programación de este tema para informarse sobre las técnicas para escribir un código más sólido.

  7. Si sigue teniendo problemas, puede utilizar WPF Designer forum on MSDN para conectarse con otros programadores que utilizan WPF Designer. Para informar sobre posibles problemas o para ofrecer sugerencias, use el sitio Visual Studio and .NET Framework Feedback.

Escribir código para tiempo de diseño

Asegúrese de que el código se ejecuta en tiempo de diseño, así como en tiempo de ejecución. Si el código se ejecuta en tiempo de diseño, no asuma que Application.Current es su aplicación. Por ejemplo, cuando use Expression Blend, Current es Expression Blend. En tiempo de diseño, MainWindow no es la ventana principal de su aplicación. Entre las operaciones normales que hacen que un control personalizado produzca errores en tiempo de diseño se incluyen las siguientes.

Existen dos enfoques para escribir código para tiempo de diseño. El primer enfoque es escribir código estable comprobando los parámetros de entrada a las clases, por ejemplo, convertidores de valores. El segundo enfoque es comprobar si el modo de diseño está activo llamando al método GetIsInDesignMode.

Es necesario comprobar los parámetros de entrada para algunas implementaciones porque el entorno de diseño proporciona diferentes tipos para algunas entradas que los que proporciona el entorno en tiempo de ejecución.

Los selectores de estilo y los convertidores de valores normalmente exigen que se ejecute correctamente uno de estos enfoques en tiempo de diseño.

Convertidores de valores

Las implementaciones personalizadas de IValueConverter deben comprobar si hay un valor null y si aparece el tipo esperado en el primer parámetro del método Convert. En el XAML siguiente se muestra un enlace a Application.Current que produce errores en tiempo de diseño si no se implementa el convertidor de valores correctamente.

<ComboBox.IsEnabled>
    <MultiBinding Converter="{StaticResource specialFeaturesConverter}">
        <Binding Path="CurrentUser.Rating" Source="{x:Static Application.Current}"/>
        <Binding Path="CurrentUser.MemberSince" Source="{x:Static Application.Current}"/>
    </MultiBinding>
</ComboBox.IsEnabled>

El enlace produce una excepción en tiempo de diseño porque Application.Current hace referencia a la aplicación del diseñador en lugar de a su aplicación. Para evitar la excepción, el convertidor de valores debe comprobar sus parámetros de entrada o comprobar el modo de diseño.

En el ejemplo de código siguiente se muestra cómo comprobar los parámetros de entrada en un convertidor de valores que devuelve true si dos parámetros de entrada satisfacen una lógica de negocios determinada.

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    // Check the values array for correct parameters.
    // Designers may send null or unexpected values.
    if (values == null || values.Length < 2) return false;
    if (!(values[0] is int)) return false;
    if (!(values[1] is DateTime)) return false;

    int rating = (int)values[0];
    DateTime date = (DateTime)values[1];

    // If the user has a good rating (10+) and has been a member for 
    // more than a year, special features are available.
    if((rating >= 10) && 
        (date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
    {
        return true;
    }
    return false;
}

El segundo enfoque para escribir código para el tiempo de diseño es comprobar si el modo de diseño está activo. En el ejemplo de código siguiente se muestra una comprobación del modo del diseño en lugar de la comprobación de parámetros que se ha mostrado anteriormente.

public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    // Check for design mode. 
    if ((bool)(DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) 
    {
        return false;
    }

    int rating = (int)values[0];
    DateTime date = (DateTime)values[1];

    // If the user has a good rating (10+) and has been a member for 
    // more than a year, special features are available.
    if((rating >= 10) && 
        (date.Date < (DateTime.Now.Date - new TimeSpan(365, 0, 0, 0))))
    {
        return true;
    }
    return false;
}

Selectores de estilo

Los selectores de estilo personalizados también se deben implementar para ejecutarse en modo de diseño. En el XAML siguiente se muestra un selector de plantilla personalizado que utiliza Application.MainWindow en tiempo de ejecución para determinar qué recurso se devuelve como DataTemplate. En tiempo de diseño, este recurso no puede estar disponible, de modo que la invalidación de SelectTemplate devuelve null en tiempo de diseño.

<local:TaskListDataTemplateSelector x:Key="myDataTemplateSelector"/>
<ListBox Width="400" Margin="10"
    ItemsSource="{Binding Source={StaticResource myTodoList}}"
    ItemTemplateSelector="{StaticResource myDataTemplateSelector}"
    HorizontalContentAlignment="Stretch" 
    IsSynchronizedWithCurrentItem="True"/>

En el siguiente código se muestra la implementación del selector de estilo.

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;

            // To run in design mode, either test for the correct window class
            // or test for design mode.
            if (window.GetType() == typeof(Window1))
            // Or check for design mode: 
            //if (!DesignerProperties.GetIsInDesignMode(window))
            {
                if (taskitem.Priority == 1)
                return window.FindResource("importantTaskTemplate") as DataTemplate;
                else
                return window.FindResource("myTaskTemplate") as DataTemplate;
            }
        }
        return null;
    }
}

UserControl y recursos del control personalizado

De forma predeterminada, UserControl y los recursos del control personalizados que están disponibles en tiempo de ejecución no pueden estar disponibles en tiempo de diseño. Cuando se agregan los controles personalizados y los controles de usuario a Page o Window en la superficie de diseño, se crea una instancia del control. Los recursos de App.xaml no están disponibles para UserControl y las instancias de control personalizadas se cargan en una página o ventana.

Para que los recursos estén disponibles en tiempo de diseño, descompóngalos en un diccionario de recursos independiente e incluya el diccionario en App.xaml y el XAML del control. Cambie todas las referencias StaticResource a las referencias DynamicResource. En el ejemplo de código siguiente se muestra cómo compartir un diccionario de recursos para que sus recursos estén disponibles en tiempo de diseño.

UserControl1.xaml

<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>

App.xaml

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Dictionary1.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Sintaxis de pack URI

No debe utilizar referencias de recurso relativas a las aplicaciones. En el ejemplo de código siguiente se muestra la sintaxis basada en aplicación, que podría dar lugar a un error en tiempo de diseño.

<Image Name="image1" Source="pack://application:,,,/Image1.bmp" />

Estas referencias son relativas a la aplicación, en lugar de a la DLL. Utilizar una referencia de recurso relativa a la aplicación en una DLL hace que esta DLL dependa de los recursos contenidos en la aplicación primaria. Este enfoque es frágil y no se garantiza que funcione en tiempo de diseño.

En lugar de utilizar referencias de recurso relativas a las aplicaciones, agregue los recursos a la propia DLL y utilice referencias de recurso basadas en componentes. Para obtener más información, vea URIs de paquete en Windows Presentation Foundation.

En los ejemplos de código siguientes se muestra la sintaxis basada en componentes, que es el enfoque recomendado.

<Image Name="image1" Source="/TestHostApp;component/Image1.bmp" />
<Image Name="image1" Source="pack://application:,,,/TestHostApp;component/Image1

Sugerencias de programación

A continuación se indican algunas sugerencias de programación para cuando trabaje con WPF Designer.

Procedimientos de programación recomendados

A continuación se indican algunos procedimientos de programación recomendados sobre cómo escribir un código más sólido para WPF Designer.

  • Incluya siempre los ámbitos de edición en instrucciones using o en bloques try/finally. Si se produce una excepción, el cambio se anula en la llamada Dispose. Para obtener más información, vea ModelEditingScope.

  • Utilice un objeto ModelEditingScope para mover un control de un contenedor a otro. En caso contrario, se produce una excepción.

  • En WPF y en WPF Designer, no establezca un valor de la propiedad en su valor predeterminado si su intención es borrarlo. Para los valores NaN, como Height, llame al método ClearValue en lugar de asignar NaN.

  • Cuando recupere valores de una propiedad, utilice el valor calculado de la propiedad. Esto significa que debe usar la propiedad ComputedValue en lugar del método GetCurrentValue de ModelItem. El método GetCurrentValue devolverá enlaces y otras expresiones si están almacenados en el XAML, para que pueda convertir las excepciones en algunos casos.

Vea también

Otros recursos

Depurar e interpretar errores en WPF Designer

Tutoriales para el uso de XAML y código

Conceptos de extensibilidad básica

Introducción a la extensibilidad de WPF Designer