【问题标题】:Is there a way to check if WPF is currently executing in design mode or not?有没有办法检查 WPF 当前是否在设计模式下执行?
【发布时间】:2010-10-24 11:20:51
【问题描述】:

有谁知道一些可用的全局状态变量,以便我可以检查代码当前是否在设计模式下(例如在 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);
        }
    }
}

【问题讨论】:

    标签: c# wpf expression-blend


    【解决方案1】:

    我相信您正在寻找 GetIsInDesignMode,它接受一个 DependencyObject。

    即。

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

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

    DesignerProperties.IsInDesignTool
    

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

    Windows.ApplicationModel.DesignMode.DesignModeEnabled
    

    【讨论】:

    • 附带说明,IsInDesignMode 实际上是一个附加属性,因此您也可以在 xaml 的绑定中使用它。虽然可能不是最常见的用途:)
    • 感谢您使用最新的 XAML“应用程序”(如 WinRT 和 WP)及时更新答案。
    • 在 VS2019 中开关 Enable project code 必须启用(或菜单->设计->?运行项目代码)。
    【解决方案2】:

    你可以这样做:

    DesignerProperties.GetIsInDesignMode(new DependencyObject());
    

    【讨论】:

    • 此方法也适用于使 ViewModel 设计者友好(因为它们本身不是 DependencyObjects)。
    • DependencyObject 有一个受保护的构造函数 - 定义 internal class MyDependencyObject : DependencyObject {} 并使用 new MyDependencyObject 而不是 DependencyObject
    • 如果在视图模型中执行此操作,您可能希望将其抽象为静态类并将结果存储为静态布尔值
    【解决方案3】:
    public static bool InDesignMode()
    {
        return !(Application.Current is App);
    }
    

    在任何地方工作。我用它来阻止数据绑定视频在设计器中播放。

    【讨论】:

    • 上述Application.Current.MainWindow == null 的变体,虽然我更喜欢类型测试,更直接。看起来好像 Visual Studio 中托管的设计器添加了资源,所以这是另一种方法(如果您无权访问托管代码的库中的特定 App 类型)((bool)Application.Current.Resources["ExpressionUseLayoutRounding"])。需要检查资源是否不存在,但它确实在设计器上下文中工作。
    • 这个答案在任何地方都不起作用。例如,在一个库中,您没有 App 类。 ;)
    • @GregorMohorko 如果您需要在App 的静态构造函数中检查,这是正确的答案。
    • @MartinBraun 是的,它会起作用的。我正在回复他的Works from anywhere. 声明。
    • @GregorMohorko 对不起,是的,将您的话误读为“无处不在”。你是对的,你需要访问App,我什至认为从App 本身以外的任何地方访问App 以确保模块化是不好的。
    【解决方案4】:

    还有其他(可能是较新的)方法可以在 WPF 中指定设计时数据,例如 mentioned in this related answer

    基本上,您可以使用design-time instance of your ViewModel 指定设计时数据:

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

    specifying sample data in a XAML file:

    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" 
    

    【讨论】:

      【解决方案5】:

      当 Visual Studio 自动为我生成一些代码时,它使用了

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

      【讨论】:

        【解决方案6】:

        如果您在大型 WPF / Silverlight / WP8 / WinRT 应用程序中广泛使用 Caliburn.Micro,您可以在您的视图模型也是如此(它在 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"
                //};
            }
        }
        

        【讨论】:

          【解决方案7】:

          接受的答案对我不起作用(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 中。有什么理由不这样做吗?
          • 我在 VS2022 和 .NET 6 中尝试过这种技术,但我得到的 appname 是 WpfSurface
          【解决方案8】:

          我只使用 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 类,并且可以在整个代码中全局使用。专门用虚拟数据填充视图模型。

          【讨论】:

            【解决方案9】:

            如果你的类不需要空的构造函数,我有一个想法。

            想法是创建一个空的构造函数,然后用 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>
            

            如果您真的需要空的构造函数来做其他事情,这将不起作用。

            【讨论】:

            • 优秀而简单的解决方案。
            猜你喜欢
            • 1970-01-01
            • 2010-09-06
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-02-05
            • 2022-01-17
            • 2021-12-21
            相关资源
            最近更新 更多