Вопрос по архитектуре привязки данных WPF

#c# #wpf #xaml #dependency-properties #datacontext

#c# #wpf #xaml #зависимость-свойства #datacontext

Вопрос:

Я пытаюсь научиться использовать привязку WPF и архитектуру MVVM. У меня возникли некоторые проблемы со свойствами зависимостей. Я пытался управлять видимостью элемента в представлении, привязывая его к DependencyProperty в DataContext, но это не работает. Независимо от того, какое GridVisible значение я задаю в конструкторе модели представления ниже, оно всегда отображается как видимое при запуске кода.

Кто-нибудь может увидеть, где я ошибаюсь?

C # code (ViewModel):

 public class MyViewModel : DependencyObject
{
    public MyViewModel ()
    {
        GridVisible = false;
    }

    public static readonly DependencyProperty GridVisibleProperty =
    DependencyProperty.Register(
        "GridVisible",
        typeof(bool),
        typeof(MyViewModel),
        new PropertyMetadata(false,
                new PropertyChangedCallback(GridVisibleChangedCallback)));

    public bool GridVisible
    {
        get { return (bool)GetValue(GridVisibleProperty); }
        set { SetValue(GridVisibleProperty, value); }
    }

    protected static void GridVisibleChangedCallback(
        DependencyObject source,
        DependencyPropertyChangedEventArgs e)
    {
        // Do other stuff in response to the data change.
    }
}
  

Код XAML (Посмотреть):

 <UserControl ... >

    <UserControl.Resources>
        <BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
    </UserControl.Resources>

    <UserControl.DataContext>
        <local:MyViewModel x:Name="myViewModel" />
    </UserControl.DataContext>

    <Grid x:Name="_myGrid"
        Visibility="{Binding Path=GridVisible,
            ElementName=myViewModel,
            Converter={StaticResource BoolToVisConverter}}">

        <!-- Other elements in here -->

    </Grid>

</UserControl>
  

Я просмотрел несколько онлайн-руководств, и, похоже, я правильно следую тому, что я там нашел. Есть идеи? Спасибо!

Ответ №1:

Уберите ElementName из вашей привязки, это кажется неправильным. Измените его на:

 <Grid x:Name="_myGrid"
        Visibility="{Binding Path=GridVisible,
            Converter={StaticResource BoolToVisConverter}}">
  

Комментарии:

1. На самом деле я скопировал ваш код и обнаружил, что он работает с привязкой с включенным именем элемента и без него. Для чего вы используете пользовательский элемент управления (т. Е. window или другой пользовательский элемент управления)? Возможно, там что-то не так (код, который вы не включили).

2. Я удалил ElementName, и он по-прежнему не работает. Похоже, вы правы — это, вероятно, как-то связано с другой частью моего кода, а не с исправленной версией, опубликованной выше. К сожалению, я не могу опубликовать фактический код, поскольку он предназначен для работы.

3. В этом случае, когда вы запускаете свое приложение и открываете окно, содержащее этот пользовательский элемент управления, поищите в своем окне вывода в VisualStudio любые ошибки с текстом, подобным этому «Ошибка пути привязки выражения: свойство ‘GridVisible’ не найдено в ‘object ……» содержимое этого сообщения может помочь вам в дальнейшей отладке. извините, я не могу вам больше помочь. удачи.

4. Вы были правы, это была опечатка! Я проверил окно вывода при запуске в режиме отладки и обнаружил, что я неправильно написал одну из своих привязок данных. Теперь это работает отлично. Спасибо!

Ответ №2:

Пусть ваша ViewModel реализует INotifyPropertyChanged вместо наследования от DependencyObject. Реализуйте интерфейс и вызывайте PropertyChanged из вашего установщика для свойства.

     private bool gridVisible;

    public bool GridVisible
    {
        get { return gridVisible; }
        set 
        { 
            gridVisible = value; 
            OnPropertyChanged("GridVisible"); 
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

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

Комментарии:

1. Вам не нужно ссылаться на свою виртуальную машину в вашем XAML с помощью ElementName, и вам не нужно называть свою виртуальную машину. Когда вы назначаете его usercontrol. DataContext становится источником привязки по умолчанию для всех дочерних элементов пользовательского элемента управления.

Ответ №3:

Смысл установки ViewModel в качестве DataContext заключается в том, чтобы включить простые относительные привязки, все привязки, в которых вы указываете только Path , принимают DataContext в качестве источника, который наследуется во всем UserControl (если не установлено иное, например, в шаблонных элементах ItemsControl)

Итак, как только DataContext установлен в UserControl, вы обычно не указываете какой-либо источник при привязке к виртуальной машине. (Источниками являются ElementName , RelativeSource и Source )

Кроме того, я лично не стал бы наследовать ViewModels от DependencyObject , поскольку это вводит привязку к потоку, также точка DependencyProperties делает разреженные структуры данных более эффективными, не создавая ненужных полей во всех из них (ViewModels обычно являются полной противоположностью sparse).

Комментарии:

1. INotifyPropertyChanged является альтернативой DependencyObject — большинство viewmodels используют это вместо свойств зависимостей.