Existe uma maneira de verificar se o WPF está atualmente em execução no modo de design ou não?

147

Alguém sabe de alguma variável de estado global que está disponível para que eu possa verificar se o código está atualmente em execução no modo de design (por exemplo, no Blend ou Visual Studio) ou não?

Seria algo como isto:

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

A razão pela qual eu preciso disso é: quando meu aplicativo estiver sendo mostrado no modo de design no Expression Blend, quero que o ViewModel use uma "classe Design Customer" que contenha dados simulados que o designer possa exibir no modo de design.

No entanto, quando o aplicativo está realmente em execução, é claro que desejo que o ViewModel use a classe Customer real, que retorna dados reais.

Atualmente, eu resolvo isso fazendo com que o designer, antes que ele trabalhe, vá para o ViewModel e altere "ApplicationDevelopmentMode.Executing" para "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
fonte

Respostas:

226

Eu acredito que você está procurando GetIsInDesignMode , que leva um DependencyObject.

Ou seja.

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

Editar: Ao usar o Silverlight / WP7, você deve usar, IsInDesignToolpois GetIsInDesignModeàs vezes pode retornar falso enquanto estiver no Visual Studio:

DesignerProperties.IsInDesignTool

Edit: E finalmente, no interesse da integridade, o equivalente nos aplicativos WinRT / Metro / Windows Store é DesignModeEnabled:

Windows.ApplicationModel.DesignMode.DesignModeEnabled
Richard Szalay
fonte
3
Como observação lateral, IsInDesignMode é na verdade uma propriedade anexada; portanto, você pode usá-lo também em uma ligação do xaml. Pode não ser o uso mais comum embora :)
aL3891
3
Obrigado por manter a resposta atualizada com os "aplicativos" XAML mais recentes, como WinRT e WP.
Sevenate
No VS2019, o switch Enable project codedeve estar ativado (ou Menu-> Design-> 🗹 Executar código do projeto).
marbel82 18/02
115

Você pode fazer algo assim:

DesignerProperties.GetIsInDesignMode(new DependencyObject());
Sacha Bruttin
fonte
30
Esse método também funciona para tornar os ViewModels compatíveis com o designer (já que eles não são os próprios DependencyObjects).
Pat
1
DependencyObject tem um construtor protegido - definir internal class MyDependencyObject : DependencyObject {}e utilização new MyDependencyObject, em vez deDependencyObject
Rico Suter
3
@RicoSuter: DependencyObjecto construtor é public.
Peter Duniho
se fazer isso em um viewmodel você provavelmente vai querer abstrato-lo em uma classe estática e armazenar o resultado como um boolean estática
Simon_Weaver
24
public static bool InDesignMode()
{
    return !(Application.Current is App);
}

Funciona de qualquer lugar. Eu o uso para impedir que vídeos do banco de dados sejam reproduzidos no designer.

Patrick
fonte
Uma variação do que foi Application.Current.MainWindow == nulldito acima, embora eu goste mais do teste de tipo, mais direto. Também parece que o designer hospedado no Visual Studio adiciona recursos, então aqui está outra maneira de fazer isso (se você não tiver acesso ao Apptipo específico na biblioteca que hospeda seu código) ((bool)Application.Current.Resources["ExpressionUseLayoutRounding"]). É necessário verificar se o recurso ainda não existe, mas funciona no contexto do designer.
John Leidegren
9

Quando o Visual Studio gerou automaticamente algum código para mim, ele foi usado

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

Existem outras maneiras (talvez mais recentes) de especificar dados em tempo de design no WPF, conforme mencionado nesta resposta relacionada .

Essencialmente, você pode especificar dados em tempo de design usando uma instância em tempo de design do seu ViewModel :

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

ou especificando dados de amostra em um arquivo XAML :

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

Você precisa definir as SamplePage.xamlpropriedades do arquivo para:

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

Coloco estes na minha UserControltag, assim:

<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="...">

No tempo de execução, todas as tags de tempo de design "d:" desaparecem; portanto, você só obtém seu contexto de dados em tempo de execução, mas escolhe defini-lo.

Editar Você também pode precisar dessas linhas (não tenho certeza, mas elas parecem relevantes):

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

E se você usa extensivamente o Caliburn.Micro para seu aplicativo WPF / Silverlight / WP8 / WinRT grande, também pode usar a propriedade estática do caliburn útil e universalExecute.InDesignMode em seus modelos de exibição (e funciona no Blend e no 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"
        //};
    }
}
Sevenate
fonte
2

Eu só testei isso com o Visual Studio 2013 e .NET 4.5, mas ele faz o truque.

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

É possível, no entanto, que alguma configuração no Visual Studio altere esse valor para false, se isso acontecer, podemos resultar apenas em verificar se esse nome de recurso existe. Foi nullquando eu corri meu código fora do designer.

A vantagem dessa abordagem é que ela não requer conhecimento explícito da Appclasse específica e que pode ser usada globalmente em todo o seu código. Especificamente para preencher modelos de exibição com dados fictícios.

John Leidegren
fonte
2

A resposta aceita não funcionou para mim (VS2019).

Depois de inspecionar o que estava acontecendo, eu vim com isso:

    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
fonte
Isso funcionou para mim, onde eu precisava saber se estava executando em tempo de design a partir de um viewModel e não podia usar as bibliotecas do Windows. Eu sei que é uma quantidade muito pequena de reflexão, mas não gostei da ideia de rodar em produção, então envolvi esse código em um #if DEBUGoutro retorno false. Existe alguma razão para não fazer isso?
Toby Smith
1

Tenho uma ideia para você, se sua classe não precisar de um construtor vazio.

A ideia é criar um construtor vazio e marque-o com ObsoleteAttribute. O designer ignora o atributo obsoleto, mas o compilador gera um erro se você tentar usá-lo, portanto não há risco de usá-lo acidentalmente.

( perdoe meu 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

E o 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 do código acima

Isso não funcionará se você realmente precisar do construtor vazio para outra coisa.

DonkeyMaster
fonte