Visual Studio设计器

时间:2017-11-15 12:57:07

标签: c# wpf visual-studio xaml dependency-properties

如何为UserControl的XAML中的绑定提供默认值,以便Visual Studio能够正确地接收它?

我试图实现可重用的WPF UserControl,它使用DependencyProperties来自定义其行为,并且我在Visual Studio中遇到有关属性默认值的奇怪行为。

采取以下控制措施:

Widget.designer.cs

namespace WpfPropertiesTest
{
    public partial class Widget : UserControl
    {
        public Widget()
        {
            InitializeComponent();
        }

        public static readonly DependencyProperty TestProperty =
            DependencyProperty.Register(nameof(Test), typeof(string), typeof(Widget),
                new PropertyMetadata("Hello World"));

        public string Test
        {
            get { return (string)GetValue(TestProperty); }
            set { SetValue(TestProperty, value); }
        }
    }
}

Widget.xaml:

<UserControl x:Class="WpfPropertiesTest.Widget"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         x:Name="_this">
    <TextBlock Text="{Binding ElementName=_this, Path=Test, FallbackValue='Fallback', TargetNullValue='Null', Mode=OneWay}" />
</UserControl>

当我看到Widget.xaml的设计者时,我看到了回退值,这是合理的:

Visual Studio WPF designer shows 'Fallback value'.

但是,我们现在可以在窗口中添加一个Widget:

// TestWindow.xaml
<Window x:Class="WpfPropertiesTest.TestWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfPropertiesTest"
    Title="TestWindow" Height="100" Width="200">
   <Grid>
       <local:Widget />
   </Grid>
</Window>

我希望看到其中一个&#34; Hello World&#34;,&#34; Fallback&#34;或&#34; Null&#34;,但实际得到的是:

Visual Studio designer now shows 'Test' in the window.

......我绑定的属性名称是什么。

在运行时,使用相同的代码,我可以看到默认值:

Window shows 'Hello World' at runtime.

现在,如果我明确设置属性的值:

<local:Widget Test="A value that I put in." />

设计师现在正确显示:

Visual Studio designer now shows 'A value that I put in.'

所有这些都告诉我,我的绑定是正确的,但Visual Studio(合理地)并没有实际查看代码隐藏中的依赖属性。当我实际对属性做某事时,例如绑定对它的可见性,这就成了一个问题。

那么,我错过了什么?有没有办法为Visual Studio提供在设计器中使用的值?我不希望每次使用它时都必须显式设置我的所有UserControl属性,并且我希望控件在设计器中的行为尽可能接近运行时。

我应该完全放弃绑定,只需在属性回调中设置代码隐藏值吗?

从更一般的意义上讲,这是否是制作可由消费者配置的可重用UserControl的正确方法?

1 个答案:

答案 0 :(得分:0)

解决方案

如果我没有弄错,那么这种行为的原因就是您在设计器中禁用了项目代码。要启用项目代码,请在设计器的底部切换最右边的按钮,从滚动条向左移动。

诊断

让我们做一个简单的实验。首先,让我们创建简单的测试类:

namespace Test
{
    public class EmptyClass { }
}

然后让我们将这个 XAML 代码放入一些设计器的 XAML 控件中:

<Grid xmlns:test="clr-namespace:Test">
    <Grid.Resources>
        <ObjectDataProvider x:Key="Provider"
                            ObjectType="test:EmptyClass"
                            MethodName="GetType" />
    </Grid.Resources>
    <TextBlock Text="{Binding Source={StaticResource Provider}}" />
</Grid>

现在在设计器视图中,当启用项目代码时,我们会看到Test.EmptyClass。但是,当禁用项目代码时,我们会看到Mocks.Test_EmptyClass_0_96752088的内容。这得出结论,设计师使用生成的模拟类而不是实际的类(这是我所期望的 - 设计者不使用我的项目中的代码)。

我不知道生成模拟类的确切机制。也许没有调用mocked类的静态构造函数,或者mock类甚至不是从原始类派生的。无论哪种方式,分配默认属性元数据的代码都不会(也不应该)执行。