มีวิธีการตรวจสอบว่า WPF กำลังทำงานในโหมดการออกแบบหรือไม่?


147

มีใครรู้บ้างเกี่ยวกับตัวแปรสถานะโกลบอลบางตัวที่มีอยู่เพื่อให้ฉันสามารถตรวจสอบว่ารหัสกำลังทำงานอยู่ในโหมดการออกแบบ (เช่นใน Blend หรือ Visual Studio) หรือไม่?

มันจะมีลักษณะเช่นนี้:

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

เหตุผลที่ฉันต้องการ: เมื่อแอปพลิเคชันของฉันถูกแสดงในโหมดการออกแบบใน Expression Blend ฉันต้องการให้ ViewModel ใช้ "Design Customer class" แทนซึ่งมีข้อมูลจำลองอยู่ในนั้นซึ่งนักออกแบบสามารถดูในโหมดการออกแบบ

อย่างไรก็ตามเมื่อแอปพลิเคชั่นกำลังดำเนินการจริง ๆ ฉันต้องการให้ ViewModel ใช้คลาสลูกค้าจริงซึ่งส่งคืนข้อมูลจริง

ขณะนี้ฉันแก้ปัญหานี้โดยมีผู้ออกแบบก่อนที่เขาจะทำงานให้ไปที่ 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บางครั้งสามารถส่งคืนค่าเท็จใน Visual Studio:

DesignerProperties.IsInDesignTool

แก้ไข:และสุดท้ายเพื่อประโยชน์ของความสมบูรณ์ความเทียบเท่าในแอปพลิเคชัน WinRT / Metro / Windows Store คือDesignModeEnabled:

Windows.ApplicationModel.DesignMode.DesignModeEnabled

3
ในฐานะที่เป็นบันทึกย่อด้าน IsInDesignMode เป็นคุณสมบัติที่แนบมาดังนั้นคุณสามารถใช้มันในการผูกจาก xaml เช่นกัน อาจจะไม่ใช่การใช้งานทั่วไปมากที่สุด :)
aL3891

3
ขอขอบคุณที่รักษาคำตอบล่าสุดด้วยแอปพลิเคชั่น XAML ล่าสุดเช่น WinRT และ WP
Sevenate

ในสวิตช์ VS2019 Enable project codeต้องเปิดใช้งาน (หรือเมนู -> ออกแบบ -> 🗹เรียกใช้รหัสโครงการ)
marbel82

115

คุณสามารถทำสิ่งนี้:

DesignerProperties.GetIsInDesignMode(new DependencyObject());

30
วิธีนี้ยังใช้ในการทำให้ผู้ออกแบบที่เป็นมิตรกับ ViewModels (เนื่องจากไม่ใช่ DependencyObjects เอง)
Pat

1
DependencyObject มี Constructor ที่ได้รับการปกป้อง - กำหนดinternal class MyDependencyObject : DependencyObject {}และใช้new MyDependencyObjectแทนDependencyObject
Rico Suter


ถ้าทำเช่นนี้ใน ViewModel คุณอาจต้องการที่จะเป็นนามธรรมมันไปในระดับคงที่และเก็บผลที่เป็นบูลีนแบบคงที่
Simon_Weaver

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

ทำงานได้จากทุกที่ ฉันใช้เพื่อหยุดวิดีโอ databound ไม่ให้เล่นในผู้ออกแบบ


รูปแบบข้างต้นApplication.Current.MainWindow == nullแม้ว่าฉันจะชอบการทดสอบประเภทดีกว่าตรงกว่า นอกจากนี้ยังปรากฏเป็นถ้าออกแบบเป็นเจ้าภาพใน Visual Studio เพิ่มทรัพยากรดังนั้นนี่คือวิธีที่จะทำ (ถ้าคุณไม่ได้มีการเข้าถึงที่เฉพาะเจาะจงอีกชนิดในห้องสมุดโฮสติ้งรหัสของคุณ)App ((bool)Application.Current.Resources["ExpressionUseLayoutRounding"])ต้องการตรวจสอบว่าทรัพยากรไม่มีอยู่หรือไม่ แต่ทำงานในบริบทนักออกแบบ
John Leidegren


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" 

7

และถ้าคุณใช้Caliburn.Microอย่างกว้างขวางสำหรับแอปพลิเคชันWPF / Silverlight / WP8 / WinRTขนาดใหญ่ของคุณคุณสามารถใช้คุณสมบัติคงที่ของคาลิเบอร์ที่มีประโยชน์และเป็นสากลExecute.InDesignModeในมุมมองแบบจำลองของคุณเช่นกัน (และทำงานใน 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"
        //};
    }
}

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กลับคืนมาเป็นเท็จ มีเหตุผลอะไรไหมที่จะไม่ทำอย่างนั้น?
Toby Smith

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>

ผลลัพธ์ของรหัสข้างต้น

นี้จะไม่ทำงานถ้าคุณจริงๆต้องนวกรรมิกว่างอย่างอื่น

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.