Новичок WPF: как привязать экземпляр класса и ссылаться на него в обработчиках кода с выделенным кодом - PullRequest
0 голосов
/ 20 марта 2019

Попытка выучить WPF. Борьба с настройкой привязки данных. В моем основном пользовательском интерфейсе у меня есть два комбинированных списка, связанных со свойствами класса экземпляра. Я хочу «повторно использовать» этот экземпляр класса в обработчиках событий.

Моя борьба заключается не в том, чтобы заставить код работать, а в основе передовых методов проектирования.

Мой класс (упрощенно)

public class SomeClass : INotifyPropertyChanged
{
    public SomeClass() {}

    public IList<string> Categories
    {
        get
        {
            return _categories;
        }
        set
        {
            _categories = value;
            OnPropertyChanged(nameof(Categories));
        }
    }

    public IList<string> Forms
    {
        get
        {
            return _forms;
        }
        set
        {
            _forms = value;
            OnPropertyChanged(nameof(Forms));
        }
    }

    public void OnPropertyChanged(string propertyName)
    {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Моим первым инстинктом было объявить все привязки в xaml. Все в одном месте. Что-то вроде

<Window.Resources>
    <local:SomeClass x:key="MyClass"/>
</Window.Resources>

Затем на панели инструментов:

<ToolBarPanel x:Name="myToolStripName" DataContext="{StaticResource MyClass}">
    <ToolBarTray>
        <ToolBar Height="25px">
            <ComboBox x:Name="cboCategories"
                      ItemsSource="{Binding Path=Categories}"
                      SelectionChanged="CboCategories_SelectionChanged" />
            <ComboBox x:Name="cboForms"
                      ItemsSource="{Binding Path=Forms}" />
        </ToolBar>
    </ToolBarTray>
</ToolBarPanel>

В коде MainWindow есть обработчик событий

private void CboCategories_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    string categoryName = e.AddedItems[0].ToString();
    if (!string.IsNullOrEmpty(categoryName))
    {
        [problem: MyClass/SomeClass].GetFormNames(categoryName);
    }
}

У меня тут проблемы. Мне нужно вызвать метод в моем классе экземпляра, но я не думаю, что смогу получить его из обработчика событий. Могу я? Я мог бы просто обновить его, но я бы потерял привязку.

Так что этот подход не сработает. Я мог бы изменить свой SomeClass на Singleton, но я читаю, что это считается анти-паттерном. Другим подходом было бы сделать его static, но тогда я не смогу (легко) реализовать INotifyPropertyChanged.

В конце концов, я сделал SomeClass a private readonly поле MainWindow.xaml.cs и установил DataContext оттуда. Это прекрасно работает, но теперь приложение полагается на привязки, определяемые как в xaml, так и в программном коде, что кажется нелогичным. Конечно, я мог бы перенести все вещи, связанные с данными, в кодовый пакет. Опять нелогично.

Учитывая описанную настройку, которая, на мой взгляд, должна быть довольно распространенной, не могли бы вы дать мне несколько советов о том, что считается наилучшей практикой, и предложениях по улучшению?

1 Ответ

1 голос
/ 20 марта 2019

Лучшим подходом было бы назначить модель представления SomeClass для Window.DataContext вместо Window.Resources и использовать функцию наследования свойств DataContext:

<Window.DataContext>
    <local:SomeClass x:Name="ViewModel"/>
</Window.DataContext>

Удалите объявление свойства DataContext из ToolBarPanel, поскольку DataContext наследуется от родительского элемента (Window). Все дочерние элементы FrameworkElement в визуальном дереве будут наследовать DataContext своих родителей:

<ToolBarPanel x:Name="MyToolStripName">
    <ToolBarTray>
        <ToolBar Height="25px">
            <ComboBox x:Name="CboCategories"
                      ItemsSource="{Binding Categories}"
                      SelectionChanged="CboCategories_SelectionChanged" />
            <ComboBox x:Name="CboForms"
                      ItemsSource="{Binding Forms}" />
        </ToolBar>
    </ToolBarTray>
</ToolBarPanel>

В вашем обработчике событий просто приведите DataContext из Object к вашей модели представления SomeClass:

private void CboCategories_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
    var categoryName = e.AddedItems[0].ToString();
    if (!string.IsNullOrEmpty(categoryName)
        && this.DataContext is SomeClass viewModel)
    {
        viewModel.GetFormNames(categoryName);
    }
}

Если вы хотите избавиться от обработчика событий в файле с выделенным кодом, учитывая, что он не выполняет операций, связанных с пользовательским интерфейсом, вы можете добавить свойство в качестве цели привязки для SelectedItem к модели представления:

public class SomeClass : INotifyPropertyChanged
{
    private string selectedCategory;
    public string SelectedCategory
    {
        get => this.selectedCategory;
        set
        {
            this.selectedCategory = value;
            OnPropertyChanged();
            GetFormNames(this.SelectedCategory);
        }
    }     

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }  

    ...
}

Замените обработчик событий привязкой SelectedItem к модели представления. Binding.Mode должен быть установлен либо OneWayToSource или TwoWay для отправки данных в источник привязки:

<ToolBarPanel x:Name="MyToolStripName">
    <ToolBarTray>
        <ToolBar Height="25px">
            <ComboBox x:Name="CboCategories"
                      ItemsSource="{Binding Categories}"
                      SelectedItem="{Binding SelectedCategory, Mode=TwoWay}" />
            <ComboBox x:Name="CboForms"
                      ItemsSource="{Binding Forms}" />
        </ToolBar>
    </ToolBarTray>
</ToolBarPanel>

Применение атрибута [CallerMemberName] к параметру вашего метода-вызова PropertyChanged, как в примере, делает вызовы более удобными, поскольку вам больше не нужно передавать имена свойств.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...