¿Hay alguna manera de verificar si WPF se está ejecutando actualmente en modo de diseño o no?

147

¿Alguien sabe de alguna variable de estado global que esté disponible para que pueda verificar si el código se está ejecutando actualmente en modo de diseño (por ejemplo, en Blend o Visual Studio) o no?

Se vería algo así:

//pseudo code:
if (Application.Current.ExecutingStatus == ExecutingStatus.DesignMode) 
{
    ...
}

La razón por la que necesito esto es: cuando mi aplicación se muestra en modo de diseño en Expression Blend, quiero que ViewModel utilice en su lugar una "Clase de diseño de cliente" que tiene datos simulados que el diseñador puede ver en modo de diseño.

Sin embargo, cuando la aplicación se está ejecutando, por supuesto, quiero que ViewModel use la clase de cliente real que devuelve datos reales.

Actualmente resuelvo esto haciendo que el diseñador, antes de trabajar en él, vaya al ViewModel y cambie "ApplicationDevelopmentMode.Executing" a "ApplicationDevelopmentMode.Designing":

public CustomersViewModel()
{
    _currentApplicationDevelopmentMode = ApplicationDevelopmentMode.Designing;
}

public ObservableCollection<Customer> GetAll
{
    get
    {
        try
        {
            if (_currentApplicationDevelopmentMode == ApplicationDevelopmentMode.Developing)
            {
                return Customer.GetAll;
            }
            else
            {
                return CustomerDesign.GetAll;
            }
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message);
        }
    }
}
Edward Tanguay
fuente

Respuestas:

226

Creo que está buscando GetIsInDesignMode , que toma un DependencyObject.

Es decir.

// 'this' is your UI element
DesignerProperties.GetIsInDesignMode(this);

Editar: cuando use Silverlight / WP7, debe usar IsInDesignToolya GetIsInDesignModeque a veces puede devolver falso mientras está en Visual Studio:

DesignerProperties.IsInDesignTool

Editar: Y finalmente, en aras de la integridad, el equivalente en las aplicaciones de WinRT / Metro / Windows Store es DesignModeEnabled:

Windows.ApplicationModel.DesignMode.DesignModeEnabled
Richard Szalay
fuente
3
Como nota al margen, IsInDesignMode es en realidad una propiedad adjunta, por lo que también puede usarlo en un enlace desde xaml. Sin embargo, podría no ser el uso más común :)
aL3891
3
Gracias por mantener la respuesta actualizada con las últimas "aplicaciones" de XAML como WinRT y WP.
Sevenate el
En VS2019 el interruptor Enable project codedebe estar habilitado (o Menú-> Diseño-> 🗹 Ejecutar código de proyecto).
marbel82
115

Puedes hacer algo como esto:

DesignerProperties.GetIsInDesignMode(new DependencyObject());
Sacha Bruttin
fuente
30
Este método también funciona para hacer que ViewModels sea amigable para el diseñador (ya que no son DependencyObjects).
Pat
1
DependencyObject tiene un constructor protegido - definir internal class MyDependencyObject : DependencyObject {}y usar en new MyDependencyObjectlugar deDependencyObject
Rico Suter
3
@RicoSuter: DependencyObjectel constructor es public.
Peter Duniho
si hace esto en un modelo de vista, probablemente quiera abstraerlo en una clase estática y almacenar el resultado como un booleano estático
Simon_Weaver
24
public static bool InDesignMode()
{
    return !(Application.Current is App);
}

Funciona desde cualquier lugar. Lo uso para evitar que los videos enlazados a datos se reproduzcan en el diseñador.

Patricio
fuente
Una variación de lo anterior, Application.Current.MainWindow == nullaunque me gusta más la prueba de tipo, más directa. También parece que el diseñador alojado en Visual Studio agrega recursos, así que aquí hay otra forma de hacerlo (si no tiene acceso al Apptipo específico en la biblioteca que aloja su código) ((bool)Application.Current.Resources["ExpressionUseLayoutRounding"]). Sin embargo, es necesario verificar si el recurso no está allí, pero funciona en el contexto del diseñador.
John Leidegren
9

Cuando Visual Studio generó automáticamente un código para mí, lo usó

if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) 
{
    ...
}
Darren
fuente
9

Hay otras formas (tal vez más nuevas) de especificar datos en tiempo de diseño en WPF, como se menciona en esta respuesta relacionada .

Esencialmente, puede especificar datos de tiempo de diseño utilizando una instancia de tiempo de diseño de su ViewModel :

d:DataContext="{d:DesignInstance Type=v:MySampleData, IsDesignTimeCreatable=True}"

o especificando datos de muestra en un archivo XAML :

d:DataContext="{d:DesignData Source=../DesignData/SamplePage.xaml}">

Debe establecer las SamplePage.xamlpropiedades del archivo en:

BuildAction:               DesignData
Copy to Output Directory:  Do not copy
Custom Tool:               [DELETE ANYTHING HERE SO THE FIELD IS EMPTY]

Coloco estos en mi UserControletiqueta, así:

<UserControl
    ...
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 

    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    ...
    d:DesignWidth="640" d:DesignHeight="480"
    d:DataContext="...">

En tiempo de ejecución, todas las etiquetas de tiempo de diseño "d:" desaparecen, por lo que solo obtendrá su contexto de datos de tiempo de ejecución, sin embargo, si elige configurarlo.

Editar También puede necesitar estas líneas (no estoy seguro, pero parecen relevantes):

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="d" 
cod3monk3y
fuente
7

Y si usa ampliamente Caliburn.Micro para su gran aplicación WPF / Silverlight / WP8 / WinRT , también podría usar la propiedad estática de caliburn práctica y universalExecute.InDesignMode en sus modelos de vista (y funciona en Blend tan bien como en Visual Studio):

using Caliburn.Micro;

// ...

/// <summary>
/// Default view-model's ctor without parameters.
/// </summary>
public SomeViewModel()
{
    if(Execute.InDesignMode)
    {
        //Add fake data for design-time only here:

        //SomeStringItems = new List<string>
        //{
        //  "Item 1",
        //  "Item 2",
        //  "Item 3"
        //};
    }
}
Severo
fuente
2

Solo probé esto con Visual Studio 2013 y .NET 4.5, pero funciona.

public static bool IsDesignerContext()
{
  var maybeExpressionUseLayoutRounding =
    Application.Current.Resources["ExpressionUseLayoutRounding"] as bool?;
  return maybeExpressionUseLayoutRounding ?? false;
}

Sin embargo, es posible que alguna configuración en Visual Studio cambie este valor a falso, si eso sucede, podemos simplemente verificar si este nombre de recurso existe. Eranull cuando ejecuté mi código fuera del diseñador.

La ventaja de este enfoque es que no requiere un conocimiento explícito de la Appclase específica y que puede usarse globalmente en todo el código. Específicamente para llenar modelos de vista con datos ficticios.

John Leidegren
fuente
2

La respuesta aceptada no funcionó para mí (VS2019).

Después de inspeccionar lo que estaba pasando, se me ocurrió esto:

    public static bool IsRunningInVisualStudioDesigner
    {
        get
        {
            // Are we looking at this dialog in the Visual Studio Designer or Blend?
            string appname = System.Reflection.Assembly.GetEntryAssembly().FullName;
            return appname.Contains("XDesProc");
        }
    }
Ger Hobbelt
fuente
Esto funcionó para mí donde necesitaba saber si estaba ejecutándose en tiempo de diseño desde viewModel y no podía usar las bibliotecas de Windows. Sé que es una cantidad muy pequeña de reflexión, pero no me gustó la idea de que se ejecute en producción, por lo que envolví este código en #if DEBUGotra respuesta falso. ¿Hay alguna razón para no hacer eso?
Toby Smith el
1

Tengo una idea para ti si tu clase no necesita un constructor vacío.

La idea es crear un constructor vacío, luego marcarlo con ObsoleteAttribute. El diseñador ignora el atributo obsoleto, pero el compilador generará un error si intenta usarlo, por lo que no hay riesgo de usarlo accidentalmente usted mismo.

( perdón mi visual básico )

Public Class SomeClass

    <Obsolete("Constructor intended for design mode only", True)>
    Public Sub New()
        DesignMode = True
        If DesignMode Then
            Name = "Paula is Brillant"
        End If
    End Sub

    Public Property DesignMode As Boolean
    Public Property Name As String = "FileNotFound"
End Class

Y el xaml:

<UserControl x:Class="TestDesignMode"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:vm="clr-namespace:AssemblyWithViewModels;assembly=AssemblyWithViewModels"
             mc:Ignorable="d" 
             >
  <UserControl.Resources>
    <vm:SomeClass x:Key="myDataContext" />
  </UserControl.Resources>
  <StackPanel>
    <TextBlock d:DataContext="{StaticResource myDataContext}" Text="{Binding DesignMode}" Margin="20"/>
    <TextBlock d:DataContext="{StaticResource myDataContext}" Text="{Binding Name}" Margin="20"/>
  </StackPanel>
</UserControl>

resultado del código anterior

Esto no funcionará si realmente necesita el constructor vacío para otra cosa.

DonkeyMaster
fuente