Вопрос

У меня есть эта разметка:

   <GroupBox BorderThickness="2">
    <GroupBox.BorderBrush>
        <SolidColorBrush x:Name="Border">
            <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
                    <Binding Path="IsConnected"/>
                    <Binding Path="IsLoggedIn"/>
                </MultiBinding>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.BorderBrush>

В коде у меня есть эта строка в методе window_loaded:

DataContext = uiManager;

uiManager имеет тип UIManager, который имеет два общедоступных свойства с именами IsConnected и IsLoggedIn.

Этот код завершается сбоем при запуске, поскольку массив значений в преобразователе, вызываемом мультипривязкой, не заполнен логическими значениями, а имеет значение DependencyProperty.UnsetValue.

Когда я использую разметку ниже (и меняю возвращаемый тип конвертера), это работает.

   <GroupBox BorderThickness="2">
    <GroupBox.BorderBrush>
         <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
              <Binding Path="IsConnected"/>
              <Binding Path="IsLoggedIn"/>
         </MultiBinding>
    </GroupBox.BorderBrush>

Кажется, что привязка, установленная через DataContext в коде, не работает в первом примере, но работает во втором.Почему?

Для полноты ниже класса UIManager:

public class UIManager:IUIManager
    {

        #region Implementation of IUIManager

        private const string IsLoggedInProperty = "IsLoggedIn";
        private bool loggedIn;
        private readonly object loggedInLock = new object();
        public bool IsLoggedIn
        {
            get
            {
                lock (loggedInLock)
                {
                    return loggedIn;
                }
            }
            set
            {
                lock (loggedInLock)
                {
                    if(value==loggedIn)return;
                    loggedIn = value;
                    OnPropertyChanged(IsLoggedInProperty);
                }
            }
        }

        private void OnPropertyChanged(string property)
        {
            if(PropertyChanged!=null)PropertyChanged(this,new PropertyChangedEventArgs(property));
        }

        private const string IsConnectedProperty = "IsConnected";
        private bool isConnected;
        private object isConnectedLock = new object();
        public bool IsConnected
        {
            get
            {
                lock (isConnectedLock)
                {
                    return isConnected;
                }
            }
            set
            {
                lock (isConnectedLock)
                {
                    if(value==isConnected)return;
                    isConnected = value;
                    OnPropertyChanged(IsConnectedProperty);
                }
            }
        }

        #endregion

        #region Implementation of INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

РЕДАКТИРОВАТЬ:Метод преобразования для неудачного XAML (он не работает при преобразовании в bool значений[0]):

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var is_connected = (bool) values[0];
            var is_loggedin = (bool) values[1];
            return is_loggedin
                       ? is_connected
                             ? Colors.YellowGreen
                             : Colors.Red
                       : Colors.Gray;
        }

для рабочего XAML:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            var is_connected = (bool) values[0];
            var is_loggedin = (bool) values[1];
            return is_loggedin
                       ? is_connected
                             ? Brushes.YellowGreen
                             : Brushes.Red
                       : Brushes.Gray;
        }
Это было полезно?

Решение

Проблема не имеет ничего общего с MultiBinding или ваш конвертер. DependencyProperty.UnsetValue означает, что привязка не получила значения.И действительно, если вы запустите в режиме отладки, вы можете увидеть ошибки привязки в Output окно:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsConnected; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color')
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsLoggedIn; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color')

Итак, давайте немного упростим разметку и применим некоторую диагностику:

<GroupBox>
    <GroupBox.BorderBrush>
        <SolidColorBrush>
            <SolidColorBrush.Color>
                <Binding Path="GroupColor" PresentationTraceSources.TraceLevel="High"/>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.BorderBrush>
</GroupBox>

Применение прикрепленного свойства зависимости PresentationTraceSources.TraceLevel дает еще несколько результатов:

System.Windows.Data Warning: 52 : Created BindingExpression (hash=17654054) for Binding (hash=44624228)
System.Windows.Data Warning: 54 :   Path: 'GroupColor'
System.Windows.Data Warning: 56 : BindingExpression (hash=17654054): Default mode resolved to OneWay
System.Windows.Data Warning: 57 : BindingExpression (hash=17654054): Default update trigger resolved to PropertyChanged
System.Windows.Data Warning: 58 : BindingExpression (hash=17654054): Attach to System.Windows.Media.SolidColorBrush.Color (hash=52727599)
System.Windows.Data Warning: 60 : BindingExpression (hash=17654054): Use Framework mentor <null>
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 61 : BindingExpression (hash=17654054): Resolve source deferred
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599)
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 66 : BindingExpression (hash=17654054): Found data context element: GroupBox (hash=51393439) (OK)
System.Windows.Data Warning: 67 : BindingExpression (hash=17654054): DataContext is null
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599)
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source  (last chance)
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=GroupColor; DataItem=null; target element is 'SolidColorBrush' (HashCode=52727599); target property is 'Color' (type 'Color')

Мы видим, что привязка не находит DataContext и привязка не удалась.Когда я меняю конструктор Windows так, чтобы DataContext устанавливается перед инициализацией содержимого, привязка работает:

public Window1()
{
    DataContext = ...;
    InitializeComponent();
}

Что странно, поскольку для привязок в других местах это не имеет значения.Не знаю, почему там это не работает, поэтому могу только предложить обходные пути.Например, работает создание кисти как ресурса с привязками (этот ресурс также может быть локальным для GroupBox):

<GroupBox BorderBrush="{DynamicResource resbrush}">
    <GroupBox.Resources>
        <SolidColorBrush x:Key="resbrush">
            <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
                    <Binding Path="IsConnected"/>
                    <Binding Path="IsLoggedIn"/>
                </MultiBinding>
            </SolidColorBrush.Color>
        </SolidColorBrush>
    </GroupBox.Resources>
</GroupBox>

Я бы предложил, однако, отказаться от MultiBinding и выполнить некоторую предварительную обработку в DataContext если ваш UIManager класс какой-то MVVM ViewModel.

Другие советы

Моя теория.Цвет имеет структуру (не может быть нулевым), поэтому SolidColorBrush.Color = null является неправильным.WPF не может создать SolidColorBrush, и вы получаете исключение.

 <GroupBox.BorderBrush>
     <SolidColorBrush x:Name="Border">
         <SolidColorBrush.Color>
             <MultiBinding Converter="{StaticResource 
                           ConnectionAndLoggedInToBorderBrush}">
                 <Binding Path="IsConnected"/>
                 <Binding Path="IsLoggedIn"/>
             </MultiBinding>
         </SolidColorBrush.Color>
     </SolidColorBrush>
 </GroupBox.BorderBrush>

BorderBrush — это объект (может иметь значение null), поэтому GroupBox.BorderBrush = null — это нормально.

 <GroupBox.BorderBrush>
      <MultiBinding Converter="{StaticResource 
                    ConnectionAndLoggedInToBorderBrush}">
           <Binding Path="IsConnected"/>
           <Binding Path="IsLoggedIn"/>
      </MultiBinding>
 </GroupBox.BorderBrush>

Эта SolidColorBrush — не объект, а ФАБРИКА.Его экземпляр создается только при необходимости, и на этом этапе вы уже подключили DataContext.

 <GroupBox.Resources>
      <SolidColorBrush x:Key="resbrush">
           <SolidColorBrush.Color>
                <MultiBinding Converter="{StaticResource 
                              ConnectionAndLoggedInToBorderBrush}">
                     <Binding Path="IsConnected"/>
                     <Binding Path="IsLoggedIn"/>
                </MultiBinding>
           </SolidColorBrush.Color>
      </SolidColorBrush>
 </GroupBox.Resources>

Просто мои 2 цента.

Прочтите мою статью, кстати, она может быть полезна, если вам нужны странные привязки или анимации со странными конвертерами. http://www.codeproject.com/KB/WPF/BindingHub.aspx

Именно по таким причинам вы можете рассмотреть возможность изучения MVVM.Этот шаблон помогает вам абстрагировать модель и привязки, чтобы вам не приходилось так сильно полагаться на DP - вместо этого вы можете просто привязаться к уведомляемому свойству в модели представления.

О MVVM есть несколько отличных статей, поэтому я бы посоветовал вам начать с чтения работ Карла Шиффлетта, Джоша Смита, Марлона Греча и Саши Барбера.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top