WPF当前是否在设计模式下执行有检查的方法吗?

163

有没有人知道是否有一些全局状态变量可用,以便我可以检查代码当前是否在设计模式下执行(例如在 Blend 或 Visual Studio 中)?

它将类似于:

//pseudo code:
if (Application.Current.ExecutingStatus == ExecutingStatus.DesignMode) 
{
    ...
}
我需要这样做的原因是,当我的应用在Expression Blend的设计模式下显示时,我希望ViewModel使用“设计客户类”,该类中包含设计师可以在设计模式下查看的模拟数据。
然而,当应用程序实际执行时,我当然希望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);
        }
    }
}
9个回答

255

我相信你正在寻找GetIsInDesignMode,它需要一个DependencyObject作为参数。

例如:

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

编辑:如果使用 Silverlight / WP7,应该使用IsInDesignTool,因为在 Visual Studio 中,GetIsInDesignMode有时会返回 false:

DesignerProperties.IsInDesignTool

编辑: 最后,为了完整起见,在 WinRT / Metro / Windows Store 应用程序中的等效选项是DesignModeEnabled:

Windows.ApplicationModel.DesignMode.DesignModeEnabled

4
顺带一提,IsInDesignMode 实际上是一个附加属性,因此您也可以在 xaml 中使用它进行绑定。虽然这可能不是最常见的用途 :) - aL3891
4
感谢您将答案与最新的XAML“应用程序”(如WinRT和WP)保持更新。 - Sevenate
1
在VS2019中,必须启用“启用项目代码”开关(或菜单->设计->运行项目代码)。 - marbel82

137

你可以像这样做:

DesignerProperties.GetIsInDesignMode(new DependencyObject());

39
这种方法同样适用于使ViewModels更方便设计(因为它们本身不是DependencyObjects)。 - Pat
1
DependencyObject有一个受保护的构造函数 - 定义internal class MyDependencyObject : DependencyObject {}并使用new MyDependencyObject代替DependencyObject - Rico Suter
3
DependencyObject 的构造函数是 public - Peter Duniho
1
如果您在ViewModel中执行此操作,可能希望将其抽象成一个静态类,并将结果存储为静态布尔值。 - Simon_Weaver

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

随时随地工作。我使用它来停止在设计师中播放受数据绑定的视频。


尽管我更喜欢类型测试,因为更直接,但这是上面 Application.Current.MainWindow == null 的一种变化。另外,似乎在 Visual Studio 中托管的设计器会添加资源,因此这里有另一种方法(如果您无法访问库中特定的 App 类型),即 ((bool)Application.Current.Resources["ExpressionUseLayoutRounding"])。需要检查资源是否不存在,但它确实可以在设计器上下文中工作。 - John Leidegren
3
这个答案在任何地方都不起作用。例如,在库中你没有App类。;) - GregorMohorko
如果您需要在“App”的静态构造函数中检查此内容,则这是正确的答案。 - Martin Braun
@MartinBraun 是的,在那里它会工作。我是在回复他的“可以从任何地方工作”的说法。 - GregorMohorko
@GregorMohorko 对不起,我误读了你的话,以为是“到处”。你是对的,你需要访问App,我甚至认为从除了App本身之外的任何地方访问App都是不好的,以确保模块化。 - Martin Braun
显示剩余2条评论

12

相关答案中提到的,WPF 中还有其他(可能是更新的)指定设计时数据的方法。

本质上,您可以使用视图模型的设计时实例来指定设计时数据:

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" 

9
Visual Studio自动生成了一些代码,但它使用了某些标签。
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this)) 
{
    ...
}

7

如果你在大型的WPF / Silverlight / WP8 / WinRT应用中广泛使用Caliburn.Micro,那么你可以在视图模型中使用方便且通用的caliburn的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"
        //};
    }
}

4

接受的答案对我没用(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 else return false中。有没有任何理由不这样做? - Toby Smith
我在VS2022和.NET 6中尝试了这种技术,但我得到的应用程序名称是WpfSurface - Arturo Torres Sánchez
如果在JetBrains Rider或Visual Studio Code中运行会怎样呢? - undefined

2
如果你的类不需要一个空构造函数,我有一个建议给你。
这个想法是创建一个空构造函数,然后使用ObsoleteAttribute标记它。设计师会忽略过时的属性,但是编译器会在你尝试使用它时引发错误,因此你不会意外地使用它自己。
(请见pardon my 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

而且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>

result of the above code

如果你真的需要空构造函数做其他事情,那么这个方法就不起作用。

优秀且简单的解决方案。 - IFink

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类,而且它可以全局在你的代码中使用。特别是用于将视图模型填充为虚拟数据。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接