Czy istnieje sposób sprawdzenia, czy WPF jest obecnie wykonywany w trybie projektowania, czy nie?


147

Czy ktoś wie o jakiejś globalnej zmiennej stanu, która jest dostępna, abym mógł sprawdzić, czy kod jest obecnie wykonywany w trybie projektowania (np. W Blend lub Visual Studio), czy nie?

Wyglądałoby to mniej więcej tak:

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

Powód, dla którego tego potrzebuję, jest następujący: kiedy moja aplikacja jest wyświetlana w trybie projektowania w programie Expression Blend, chcę, aby ViewModel zamiast tego używał „klasy Design Customer”, która zawiera makiety danych, które projektant może wyświetlać w trybie projektowania.

Jednak gdy aplikacja jest faktycznie wykonywana, oczywiście chcę, aby ViewModel używał prawdziwej klasy Customer, która zwraca rzeczywiste dane.

Obecnie rozwiązuję ten problem, prosząc projektanta, zanim zacznie nad tym pracować, wchodząc do ViewModel i zmieniając „ApplicationDevelopmentMode.Executing” na „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);
        }
    }
}

Odpowiedzi:


226

Wierzę, że szukasz GetIsInDesignMode , który przyjmuje DependencyObject.

To znaczy.

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

Edycja: w przypadku korzystania z Silverlight / WP7 należy użyć, IsInDesignToolponieważ GetIsInDesignModeczasami może zwrócić false w programie Visual Studio:

DesignerProperties.IsInDesignTool

Edycja: I wreszcie, aby uzyskać kompletność, odpowiednikiem w aplikacjach WinRT / Metro / Windows Store jest DesignModeEnabled:

Windows.ApplicationModel.DesignMode.DesignModeEnabled

3
Na marginesie, IsInDesignMode jest w rzeczywistości dołączoną właściwością, więc można jej również użyć w powiązaniu z XAML. Może nie jest to jednak najczęstsze zastosowanie :)
aL3891

3
Dziękujemy za aktualizowanie odpowiedzi o najnowsze „aplikacje” XAML, takie jak WinRT i WP.
Siódmy

W VS2019 przełącznik Enable project codemusi być włączony (lub Menu-> Projekt-> 🗹 Uruchom kod projektu).
marbel82

115

Możesz zrobić coś takiego:

DesignerProperties.GetIsInDesignMode(new DependencyObject());

30
Ta metoda działa również w celu uczynienia ViewModels przyjaznymi dla projektanta (ponieważ same nie są DependencyObjects).
Pat

1
DependencyObject ma chronionego konstruktora - definiuj internal class MyDependencyObject : DependencyObject {}i używaj new MyDependencyObjectzamiastDependencyObject
Rico Suter

3
Konstruktorem @RicoSuter: DependencyObjectjest public.
Peter Duniho

jeśli robisz to w modelu widoku, prawdopodobnie chcesz wyodrębnić go do klasy statycznej i zapisać wynik jako statyczną wartość logiczną
Simon_Weaver

24
public static bool InDesignMode()
{
    return !(Application.Current is App);
}

Działa z dowolnego miejsca. Używam go, aby zatrzymać odtwarzanie wideo z danymi w projektancie.


Odmiana powyższego, Application.Current.MainWindow == nullchociaż bardziej mi się podoba test typu, bardziej bezpośredni. Wygląda również na to, że projektant hostowany w programie Visual Studio dodaje zasoby, więc oto inny sposób, aby to zrobić (jeśli nie masz dostępu do określonego Apptypu w bibliotece obsługującej kod) ((bool)Application.Current.Resources["ExpressionUseLayoutRounding"]). Konieczne jest sprawdzenie, czy zasobu nie ma, ale działa w kontekście projektanta.
John Leidegren

9

Kiedy Visual Studio automatycznie wygenerował dla mnie jakiś kod, użył

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

9

Istnieją inne (być może nowsze) sposoby określania danych czasu projektowania w WPF, jak wspomniano w tej powiązanej odpowiedzi .

Zasadniczo można określić dane czasu projektowania przy użyciu wystąpienia modelu ViewModel w czasie projektowania :

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

lub określając przykładowe dane w pliku XAML :

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

Musisz ustawić SamplePage.xamlwłaściwości pliku na:

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

Umieszczam je w swoim UserControltagu, w ten sposób:

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

W czasie wykonywania wszystkie znaczniki czasu projektowania „d:” znikają, więc będziesz mieć dostęp tylko do kontekstu danych czasu wykonywania, niezależnie od tego, jak go ustawisz.

Edytuj Możesz również potrzebować tych wierszy (nie jestem pewien, ale wydają się istotne):

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

7

A jeśli intensywnie używasz Caliburn.Micro dla swojej dużej aplikacji WPF / Silverlight / WP8 / WinRT , możesz użyć poręcznej i uniwersalnej właściwości Execute.InDesignModestatycznej Caliburn również w swoich modelach widoku (i działa w Blend tak dobrze, jak w 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"
        //};
    }
}

2

Testowałem to tylko w Visual Studio 2013 i .NET 4.5, ale to załatwia sprawę.

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

Jest jednak możliwe, że niektóre ustawienia w programie Visual Studio zmienią tę wartość na false, jeśli tak się stanie, możemy po prostu sprawdzić, czy ta nazwa zasobu istnieje. To było, nullgdy uruchomiłem kod poza projektantem.

Zaletą tego podejścia jest to, że nie wymaga ono jawnej znajomości konkretnej Appklasy i może być używane globalnie w całym kodzie. W szczególności w celu wypełnienia modeli widoków danymi fikcyjnymi.


2

Zaakceptowana odpowiedź nie działa dla mnie (VS2019).

Po sprawdzeniu, co się dzieje, wymyśliłem to:

    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");
        }
    }

To zadziałało dla mnie, gdy musiałem wiedzieć, czy pracowałem w czasie projektowania z poziomu ViewModel i nie mogłem korzystać z bibliotek systemu Windows. Wiem, że jest to bardzo mała ilość refleksji, ale nie podobała mi się myśl, że działa w środowisku produkcyjnym, więc zawinąłem ten kod w #if DEBUGinny zwrócony fałsz. Czy jest jakiś powód, żeby tego nie robić?
Toby Smith

1

Mam dla Ciebie pomysł, jeśli Twoja klasa nie potrzebuje pustego konstruktora.

Chodzi o to, aby utworzyć pusty konstruktor, a następnie oznaczyć go ObsoleteAttribute. Projektant ignoruje przestarzały atrybut, ale kompilator zgłosi błąd, jeśli spróbujesz go użyć, więc nie ma ryzyka przypadkowego użycia go samodzielnie.

( przepraszam za mój Visual Basic )

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

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

wynik powyższego kodu

To nie zadziała, jeśli naprawdę potrzebujesz pustego konstruktora do czegoś innego.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.