开发者

WPF - DataTemplate binding to static member

I have a class with a boolean static property called CanSeePhotos and this should control the visibility of pictures in my DataTemplate. For debugging purposes I am binding "CanSeePhotos" to a text block in the DataTemplate.

What I would like t开发者_Python百科o do is:

  1. InitializeComponent()
  2. Set CanSeePhotos based on the logged-in user
  3. Load data and show it appropriately

My problem is that if I set CanSeePhotos = true after InitializeComponent(), the data is still shown with CanSeePhotos as false (if I do it before it works ok). Why is that? How can I fix it so that I can set the value at any point before loading the data?

Here's how I am binding to the static variable in my DataTemplate:

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

And here's the LoggedInUser class:

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

EDIT: If I bind the visibility of a control straight to the static property it will show/collapse according to the value of the property:

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

But I need to use a DataTrigger like so:

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

In the case above the setter never gets set if the property is true.

What gives?


There are three considerations here:

Consideration 1: The property has no change notification

Some data bindings may be evaluated during the InitializeComponent() call, and others are evaluated later. You are requesting the ability to set CanSeePhotos after InitializeComponent() has already returned. If don't have any change notification, any binding evaluated during InitializeComponent() will have the original value and won't be update. Any binding evaluated afterwards (such as at DataBind priority) will have the new value. To make this work in all cases you need some kind of change notification.

Using a NET Framework property declared with "{ get; set; }" won't work because the property has no mechanism to notify anyone if its value is changed. There are actually two very sneaky ways to get notification from a standard NET Framework property (MarshalByRefObject and IL rewriting) but they are much too complex for your situation.

Consideration 2: The property is static

NET Framework has several property change notification mechanisms (DependencyProperty, INotifyPropertyChanged, etc) but none of the built-in mechanisms support change notification on static properties. So you can't use a static property for this without creating a new mechanism for signaling changes (for example, you could have an object that wraps the property).

Consideration 3: DataTriggers share a single Binding

When setting Visibility you are constructing a new Binding each time, so it gets the latest value of LoggedInUser.CanSeePhotos.

When creating the DataTrigger, WPF constructs a single Binding when the trigger is loaded and uses it for every object. This Binding is constructed when the resource dictionary containing the DataTrigger is loaded, which is probably at app startup, so it will always get the default value for CanSeePhotos. This is because Source= assigns an actual object into the binding (its computation is not deferred). So every Binding is getting constructed with either Source=true or Source=false.

Recommended Solution

Use a DependencyObject with a DependencyProperty and reference it from a static property, like this:

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());

}

This class will always have one instance and that instance will be available as LoggedInUser.Instance. So it is somewhat like a static class. The difference is, LoggedInUser.Instance has a DependencyProperty, so when you modify the property it can notify any interested parties. WPF's Binding will register for this notification, so your UI will be updated.

The code above would be used like this in XAML:

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

In your code-behind if you need to access CanSeePhotos it would be, for example:

LoggedInUser.Instance.CanSeePhotos = true;
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜