如何让VS WPF/XAML设计器显示FallbackValue为Hidden的控件?
我太懂这个两难的处境了——运行时要确保未初始化的控件(比如详情页UserControl)不显示空白,用FallbackValue=Hidden很合理,但VS设计器直接把它藏起来,连编辑布局都找不到,简直头疼!下面给你几个实用的解决方案:
方案1:给设计器单独绑定设计时数据上下文
这是最直接的办法,让设计器拿到一个有效的ViewModel实例,这样Visibility绑定就不会触发FallbackValue了。你需要在控件的XAML里添加d:DataContext属性(记得先引入xmlns:d="http://schemas.microsoft.com/expression/blend/2008"命名空间),指向一个设计时用的ViewModel(可以是模拟数据的实例,甚至直接实例化真实VM):
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:YourProjectNamespace" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=local:DetailViewModel, IsDesignTimeCreatable=True}"> <!-- 你的详情页布局 --> <Grid Visibility="{Binding IsInitialized, FallbackValue=Hidden}"> <!-- ... --> </Grid> </UserControl>
IsDesignTimeCreatable=True会让设计器直接创建ViewModel实例,只要你的VM有无参构造函数就行。如果真实VM需要依赖注入,你可以写一个专门的设计时VM(继承自真实VM或者模拟接口),专门给设计器用。
方案2:用触发器判断设计模式,强制显示控件
通过DesignerProperties.IsInDesignMode附加属性,在设计时强制把Visibility设为Visible,运行时保持原来的绑定逻辑。可以写一个Style或者直接在控件里加触发器:
<Grid> <Grid.Style> <Style TargetType="Grid"> <Setter Property="Visibility" Value="{Binding IsInitialized, FallbackValue=Hidden}" /> <Style.Triggers> <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(DesignerProperties.IsInDesignMode)}" Value="True"> <Setter Property="Visibility" Value="Visible" /> </DataTrigger> </Style.Triggers> </Style> </Grid.Style> <!-- 你的布局内容 --> </Grid>
这个方法的好处是不用修改数据上下文,直接通过样式触发器覆盖设计时的显示状态,代码改动很小。
方案3:自定义MarkupExtension,区分设计时和运行时的FallbackValue
如果上面的方法都不够灵活,可以写一个自定义的MarkupExtension,让它在设计时返回Visible,运行时返回Hidden作为FallbackValue:
using System; using System.ComponentModel; using System.Windows; using System.Windows.Markup; namespace YourProjectNamespace { public class DesignTimeFallbackExtension : MarkupExtension { public object RuntimeValue { get; set; } public object DesignTimeValue { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { var designerService = serviceProvider.GetService(typeof(IDesignerPropertiesService)) as IDesignerPropertiesService; if (designerService?.IsInDesignMode == true) { return DesignTimeValue ?? DependencyProperty.UnsetValue; } return RuntimeValue ?? DependencyProperty.UnsetValue; } } }
然后在XAML里这样用:
<Grid Visibility="{Binding IsInitialized, FallbackValue={local:DesignTimeFallback RuntimeValue=Hidden, DesignTimeValue=Visible}}"> <!-- 布局内容 --> </Grid>
这个方法最灵活,适合需要针对不同控件设置不同设计时/运行时 fallback 的场景。
这三个方案都能解决你的问题,我个人推荐方案1或者方案2,简单直接,不用写太多额外代码。
内容的提问来源于stack exchange,提问作者Steffen Winkler

