WPF - DataTemplate绑定到静态成员

时间:2009-11-06 19:41:21

标签: wpf datatemplate

我有一个带有名为CanSeePhotos的布尔静态属性的类,这应该控制我的DataTemplate中图片的可见性。 出于调试目的,我将“CanSeePhotos”绑定到DataTemplate中的文本块。

我想做的是:

  1. 的InitializeComponent()
  2. 根据登录用户设置CanSeePhotos
  3. 加载数据并正确显示
  4. 我的问题是,如果我在InitializeComponent()之后设置CanSeePhotos = true,那么数据仍显示为CanSeePhotos为false(如果我在它工作正常之前这样做)。这是为什么?如何修复它以便我可以在加载数据之前随时设置值?

    以下是我在DataTemplate中绑定静态变量的方法:

    <TextBlock Text="{Binding Source={x:Static DAL:LoggedInUser.CanSeePhotos}, Mode=OneWay}"/>
    

    这是LoggedInUser类:

    public class LoggedInUser
    {
        public static bool CanSeePhotos { get; set; }
    }
    

    编辑: 如果我将控件的可见性直接绑定到静态属性,它将根据属性的值显示/折叠:

    Visibility="{Binding Source={x:Static DAL:LoggedInUser.CanSeePhotos}, Converter={StaticResource BooleanToVisibilityConverter}}"
    

    但我需要像这样使用DataTrigger:

    <DataTrigger Binding="{Binding Source={x:Static DAL:LoggedInUser.CanSeePhotos}}" Value="true">
       <Setter TargetName="icon" Property="Source" Value="{Binding Photo}"/>
    </DataTrigger>
    

    在上面的情况中,如果属性为true,则setter永远不会被设置。

    是什么给出了?

1 个答案:

答案 0 :(得分:7)

这里有三个注意事项:

考虑事项1:该属性没有变更通知

可以在InitializeComponent()调用期间评估某些数据绑定,稍后再评估其他数据绑定。您正在请求在InitializeComponent()已经返回后设置CanSeePhotos的功能。如果没有任何更改通知,则在InitializeComponent()期间评估的任何绑定都将具有原始值,并且不会更新。之后评估的任何绑定(例如在DataBind优先级)将具有新值。要在所有情况下都能使用此功能,您需要进行某种更改通知。

使用以“{get; set;}”声明的.NET Framework属性将不起作用,因为该属性没有机制在其值发生更改时通知任何人。实际上有两种非常偷偷摸摸的方法可以从标准的.NET Framework属性(MarshalByRefObject和IL重写)获取通知,但它们对于您的情况来说太复杂了。

考虑2:属性是静态的

.NET Framework具有多个属性更改通知机制(DependencyProperty,INotifyPropertyChanged等),但没有任何内置机制支持静态属性的更改通知。因此,如果不创建用于发送更改信号的新机制,则不能使用静态属性(例如,您可以拥有一个包装属性的对象)。

考虑3:DataTriggers共享一个Binding

设置Visibility时,每次都在构建一个新的Binding,因此它获取LoggedInUser.CanSeePhotos的最新值。

创建DataTrigger时,WPF在加载触发器时构造单个Binding并将其用于每个对象。当加载包含DataTrigger的资源字典时,可以在应用程序启动时构建此Binding,因此它将始终获取CanSeePhotos的默认值。这是因为Source =将一个实际对象分配到绑定中(它的计算不是延迟的)。所以每个Binding都是用Source = true或Source = false构建的。

推荐的解决方案

将DependencyObject与DependencyProperty一起使用,并从静态属性引用它,如下所示:

public class LoggedInUser : DependencyObject
{
   // Singleton pattern (Expose a single shared instance, prevent creating additional instances)
   public static readonly LoggedInUser Instance = new LoggedInUser();
   private LoggedInUser() { }

   // Create a DependencyProperty 'CanSeePhotos'
   public bool CanSeePhotos { get { return (bool)GetValue(CanSeePhotosProperty); } set { SetValue(CanSeePhotosProperty, value); } }
   public static readonly DependencyProperty CanSeePhotosProperty = DependencyProperty.Register("CanSeePhotos", typeof(bool), typeof(LoggedInUser), new UIPropertyMetadata());

}

此类将始终具有一个实例,该实例将可用作LoggedInUser.Instance。所以它有点像静态类。区别在于,LoggedInUser.Instance具有DependencyProperty,因此当您修改属性时,它可以通知任何感兴趣的各方。 WPF的Binding将注册此通知,因此您的UI将会更新。

上面的代码将在XAML中使用:

Visibility="{Binding CanSeePhotos, Source={x:Static LoggedInUser.Instance}, Converter=...

如果你需要访问CanSeePhotos,你的代码隐藏就可以了,例如:

LoggedInUser.Instance.CanSeePhotos = true;