Есть ли способ проверить, выполняется ли в настоящее время WPF в режиме разработки или нет?

147

Кто-нибудь знает какую-нибудь глобальную переменную состояния, которая доступна, чтобы я мог проверить, выполняется ли код в данный момент в режиме разработки (например, в Blend или Visual Studio) или нет?

Это будет выглядеть примерно так:

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

Причина, по которой мне это нужно, заключается в том, что когда мое приложение отображается в режиме разработки в Expression Blend, я хочу, чтобы ViewModel вместо этого использовал «класс Design Designer», в котором есть фиктивные данные, которые дизайнер может просматривать в режиме конструктора.

Однако, когда приложение фактически выполняется, я, конечно, хочу, чтобы ViewModel использовал реальный класс Customer, который возвращает реальные данные.

В настоящее время я решаю эту проблему с помощью конструктора, прежде чем он работает над ним, перейдите в ViewModel и измените «ApplicationDevelopmentMode.Executing» на «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);
        }
    }
}
Эдвард Тангей
источник

Ответы:

226

Я полагаю, что вы ищете GetIsInDesignMode , который принимает DependencyObject.

То есть.

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

Изменить: при использовании Silverlight / WP7, вы должны использовать, IsInDesignToolтак как GetIsInDesignModeиногда может возвращать false, когда в Visual Studio:

DesignerProperties.IsInDesignTool

Изменить: И, наконец, в интересах полноты, эквивалент в приложениях WinRT / Metro / Windows Store DesignModeEnabled:

Windows.ApplicationModel.DesignMode.DesignModeEnabled
Ричард Сзалай
источник
3
В качестве примечания, IsInDesignMode на самом деле является вложенным свойством, поэтому вы можете использовать его также в привязке из xaml. Возможно, это не самое распространенное использование :)
aL3891
3
Спасибо за актуальность ответа с последними XAML-приложениями, такими как WinRT и WP.
Семнадцатое
В VS2019 переключатель Enable project codeдолжен быть включен (или Menu-> Design-> 🗹 Run Project Code).
marbel82
115

Вы можете сделать что-то вроде этого:

DesignerProperties.GetIsInDesignMode(new DependencyObject());
Саша Бруттин
источник
30
Этот метод также работает для того, чтобы сделать ViewModels дружественным по отношению к дизайну (поскольку они сами не являются объектами DependencyObject).
Пэт
1
DependencyObject имеет защищенный конструктор - определите internal class MyDependencyObject : DependencyObject {}и используйте new MyDependencyObjectвместо негоDependencyObject
Rico Suter
3
@RicoSuter: DependencyObjectконструктор public.
Петер Дунихо
если вы делаете это в модели представления, вы, вероятно, захотите абстрагировать ее в статический класс и сохранить результат в виде статического логического значения
Simon_Weaver
24
public static bool InDesignMode()
{
    return !(Application.Current is App);
}

Работает откуда угодно. Я использую его, чтобы остановить воспроизведение видео с привязкой к данным в дизайнере.

Патрик
источник
Разновидность вышеописанного, Application.Current.MainWindow == nullхотя мне больше нравится типовой тест, более прямой. Также кажется, что дизайнер, размещенный в Visual Studio, добавляет ресурсы, так что вот еще один способ сделать это (если у вас нет доступа к определенному Appтипу в библиотеке, содержащей ваш код) ((bool)Application.Current.Resources["ExpressionUseLayoutRounding"]). Нужно проверить, если ресурса нет, хотя он работает в контексте дизайнера.
Джон Лейдгрен
9

Когда Visual Studio автоматически генерировал какой-то код для меня, он использовал

if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) 
{
    ...
}
Даррен
источник
9

Есть другие (возможно, более новые) способы указания данных времени разработки в WPF, как упомянуто в этом связанном ответе .

По сути, вы можете указать данные времени разработки, используя экземпляр времени разработки вашей ViewModel :

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

или указав пример данных в файле XAML :

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

Вы должны установить SamplePage.xamlсвойства файла:

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

Я помещаю их в свой UserControlтег, вот так:

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

Во время выполнения все теги времени разработки "d:" исчезают, поэтому вы получите только контекст данных времени выполнения, однако вы решите его установить.

Редактировать Вам также могут понадобиться эти строки (я не уверен, но они кажутся актуальными):

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
mc:Ignorable="d" 
cod3monk3y
источник
7

И если вы широко используете Caliburn.Micro для своего большого приложения WPF / Silverlight / WP8 / WinRT, вы также можете использовать удобное и универсальноеExecute.InDesignMode статическое свойство caliburn в своих моделях представления (и оно работает в Blend так же хорошо, как в 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
источник
2

Я тестировал это только с Visual Studio 2013 и .NET 4.5, но это помогает.

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

Возможно, однако, что некоторые настройки в Visual Studio изменят это значение на false, если это когда-либо произойдет, мы можем просто проверить, существует ли это имя ресурса. Это было, nullкогда я запускал свой код за пределами дизайнера.

Преимущество этого подхода в том, что он не требует явных знаний о конкретном Appклассе и что он может использоваться глобально во всем вашем коде. Специально для заполнения моделей представления фиктивными данными.

Джон Лейдегрен
источник
2

Принятый ответ не работал для меня (VS2019).

Изучив, что происходит, я пришел к следующему:

    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");
        }
    }
Гер Хоббелт
источник
Это сработало для меня, когда мне нужно было знать, работаю ли я во время разработки из viewModel и не могу использовать библиотеки Windows. Я знаю, что это очень мало отражений, но мне не понравилась мысль о том, что он работает в производстве, поэтому я обернул этот код в #if DEBUGвозвращаемое значение false. Есть ли причина не делать этого?
Тоби Смит
1

У меня есть идея, если вашему классу не нужен пустой конструктор.

Идея состоит в том, чтобы создать пустой конструктор, а затем пометить его ObsoleteAttribute. Дизайнер игнорирует устаревший атрибут, но компилятор выдаст ошибку, если вы попытаетесь его использовать, поэтому нет риска случайного использования его самостоятельно.

( простите мой визуальный основы )

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

И 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>

результат приведенного выше кода

Это не сработает, если вам действительно нужен пустой конструктор для чего-то другого.

DonkeyMaster
источник