2009-11-06 3 views
4

У меня есть класс с булевым статическим свойством CanSeePhotos, и это должно контролировать видимость изображений в моем DataTemplate. Для целей отладки я привязываю «CanSeePhotos» к текстовому блоку в DataTemplate.WPF - привязка DataTemplate к статическому члену

Что я хотел бы сделать, это:

  1. InitializeComponent()
  2. Набор CanSeePhotos на основе зарегистрированного пользователя
  3. загрузки данных и показать его соответствующим образом

Моя проблема заключается в том, что если я устанавливаю CanSeePhotos = true после InitializeComponent(), данные по-прежнему отображаются с CanSeePhotos как false (если я делаю это до того, как он будет работать нормально). Почему это? Как я могу исправить это, чтобы установить значение в любой момент перед загрузкой данных?

Вот как я привязка к статической переменной в моей DataTemplate:

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

А вот класс LoggedInUser:

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

EDIT: Если я связываю видимость контроля прямо статическое свойство, которое оно будет показывать/сжиматься в соответствии со значением свойства:

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> 

В случае выше сеттер никогда не будет установлен, если свойство верно.

Что дает?

ответ

7

Есть три соображения здесь:

Рассмотрения 1: Свойство не имеет уведомления об изменении

Некоторые привязки данных могут быть оценены во время вызова InitializeComponent(), и другие оцениваются позже. Вы запрашиваете возможность установки CanSeePhotos после того, как InitializeComponent() уже возвращен. Если у вас нет уведомления об изменении, любое связывание, полученное в процессе InitializeComponent(), будет иметь исходное значение и не будет обновляться. После каждого последующего связывания (например, при приоритете DataBind) появится новое значение. Чтобы сделать эту работу во всех случаях, вам нужно какое-то уведомление об изменении.

Использование свойства NET Framework, объявленного с помощью функции "{get; set;}", не будет работать, потому что свойство не имеет механизма, чтобы уведомить кого-либо, если его значение изменилось. На самом деле существует два очень простых способа получить уведомление от стандартного свойства NET Framework (MarshalByRefObject и перезаписи IL), но они слишком сложны для вашей ситуации.

Рассмотрение 2: Свойство статичен

NET Framework имеет несколько механизмов уведомления об изменении свойств (DependencyProperty, INotifyPropertyChanged и т.д.), но ни один из встроенных механизмов не поддерживает уведомление об изменении на статических свойствах.Таким образом, вы не можете использовать статическое свойство для этого без создания нового механизма для сигнализации изменений (например, у вас может быть объект, который обертывает свойство).

Рассмотрение 3: DataTriggers разделяют один Binding

При настройке видимости вы строите новый Binding каждый раз, поэтому он получает последнюю ценность LoggedInUser.CanSeePhotos.

При создании DataTrigger WPF создает единственное связывание при загрузке триггера и использует его для каждого объекта. Это привязка создается, когда загружается ресурсный словарь, содержащий DataTrigger, который, вероятно, при запуске приложения, поэтому он всегда будет получать значение по умолчанию для CanSeePhotos. Это связано с тем, что Source = назначает фактический объект в привязку (его вычисление не откладывается). Поэтому каждое связывание создается с помощью 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 будет регистрироваться для этого уведомления, поэтому ваш пользовательский интерфейс будет обновлен.

Код выше будет использоваться, как это в XAML:

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

В коде-за, если вам нужно получить доступ к CanSeePhotos было бы, например:

LoggedInUser.Instance.CanSeePhotos = true; 
+0

Hi Ray, я понимают свойства зависимостей, но не хотят их использовать, потому что CanSeehotos никогда не будет изменен после получения данных в первый раз. Что меня отталкивает, так это то, что привязка видимости объекта к статическому свойству в DataTemplate отлично работает, но он не работает в DataTrigger DataTemplate. Спасибо за Ваш ответ. Я попробую. –

+0

Спасибо за разъяснение вашего вопроса. Я добавил новую «Проблему 3» к моему ответу, чтобы выяснить, почему DataTrigger не работает, а также некоторое объяснение в «Задаче 1», чтобы объяснить, почему вам требуется уведомление об изменении в вашем сценарии. Надеюсь это поможет. –

+0

+1, но вам действительно не нужен DependencyProperty, IMHO, использующий INotifyPropertyChanged, лучше –

Смежные вопросы