WPF - MVVM - Как обновить выпадающий список уведомлений об изменении свойств - PullRequest
0 голосов
/ 02 февраля 2020

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

Моя (частичная) цель, как следует :

  1. Открыть главное окно, содержащее два комбинированных списка с пометкой «Система и документы»

  2. При загрузке этого окна я подключаюсь к файлу .accdb, который содержит таблица со столбцами «Система» и «Документ».

  3. Создает список всех отдельных имен System, хранящихся в файле базы данных, и сохраняет их в виде arrayList, к которому привязан мой выпадающий список "System". Он успешно заполняет комбинированный список и выбирает первый элемент списка в качестве выбранного элемента.

  4. Выбранный элемент комбинированного списка «Система» - OneWayToSource, связанный со строкой с именем SystemFilter. Каждый раз, когда я меняю выбор, эта строка успешно обновляется.

  5. ПРОБЛЕМА C ЧАСТЬ: Я также хочу отфильтровать документы, принадлежащие выбранной системе, и заполнить поле со списком «Документ» , Вспомогательный метод, который я успешно написал, делает это с помощью строки SystemFilter и создает arrayList со списком документов. Однако комбинированный список «Document» не обновляется, даже если он связан с этим arrayList. Интересно, что иногда, если я запускаю этот вспомогательный метод с аппаратным аргументом String, он корректно обновляет комбинированный список.

My MainWindow.xaml (соответствующая часть):

            <!--System-->
            <TextBlock Margin="5 0 0 0" Text="System" FontWeight="Bold" HorizontalAlignment="Left"/>
            <ComboBox SelectionChanged="Combobox_Doc_Sys_SelectionChanged" x:Name="Combobox_Doc_Sys" 
                      ItemsSource="{Binding mSystemList}" SelectedItem="{Binding mSystemFilter,Mode=OneWayToSource}"
                      SelectedIndex="0" Padding="2" Margin="5 0 5 0" >
            </ComboBox>

            <!--Document-->
            <TextBlock Margin="5 10 0 0" Text="Document" FontWeight="Bold" HorizontalAlignment="Left"/>
            <ComboBox x:Name="Combobox_Doc_Doc" ItemsSource="{Binding mTitleList}" SelectedIndex="0" 
                      Padding="2" Margin="5 0 5 0">
            </ComboBox>

Код позади:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new MainWindowViewModel();
     }

    private void Combobox_Doc_Sys_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        MainWindowViewModel.GetListOfTitle();
    }
}

MainWindowViewModel:

class MainWindowViewModel : BaseViewModel
{

    static ArrayList SystemList = new ArrayList();
    public static ArrayList mSystemList { get { return SystemList; } set { } }
    //--------------------------------------------------------------------------------------------//
    static ArrayList TitleList = new ArrayList();
    public static ArrayList mTitleList { get { return TitleList; } set { } }
    //--------------------------------------------------------------------------------------------//
    static ArrayList RevisionList = new ArrayList();
    public static ArrayList mRevisionList { get { return RevisionList; } set{ } }
    //--------------------------------------------------------------------------------------------//

    public static ObservableCollection<DocumentModel> DocumentList = new ObservableCollection<DocumentModel>();
    //--------------------------------------------------------------------------------------------//
    static string SystemFilter = String.Empty;
    public static string mSystemFilter{get { return SystemFilter; } set { SystemFilter = value; } }
    //--------------------------------------------------------------------------------------------//

    static readonly string ConnectStringDocList = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=|DataDirectory|\\Model\\Data\\DocumentList.mdb";

    public MainWindowViewModel()
    {
        GetListofSystems();
        GetListOfTitle();

    }
    public static void GetListofSystems()
    {


        //SQL for gettint all the distinctive values from the system column
        string listSQL = "SELECT DISTINCT System FROM DocList";

        SystemList.Clear();

        //Creating new connection
        using (OleDbConnection MyConnection = new OleDbConnection(ConnectStringDocList))
        {
            //Create command
            OleDbCommand command = new OleDbCommand(listSQL, MyConnection);
            command.Connection = MyConnection;
            MyConnection.Open();
            //Create reader
            OleDbDataReader reader = command.ExecuteReader();

            //With given parameter "listSQL" we iterate through the "system" column of DocumentList.mdb
            // and wee fill up "SystemList" with all the distinctive values

            while (reader.Read())
            {
                string item = reader.GetString(0);
                SystemList.Add(item);
            }

            //Close reader
            reader.Close();
            //Close connection
            MyConnection.Close();

        }
    }

    public static void GetListOfTitle()
    {
        //List that will store the list of the title

        //filtering  SQL
        string sysFilterSQL = $"SELECT * FROM DocList WHERE System='{SystemFilter}'";

        //Clear List for safety sake
        TitleList.Clear();

        using (OleDbConnection MyConnection = new OleDbConnection(ConnectStringDocList))
        {
            OleDbCommand command = new OleDbCommand(sysFilterSQL, MyConnection);
            command.Connection = MyConnection;
            MyConnection.Open();

            OleDbDataReader reader = command.ExecuteReader();

            //We fill up TitleList with the list of the titles filtered by "system"
            while (reader.Read())
            {
                var item = reader.GetString(2);
                TitleList.Add(item);
            }
                        }

    }

И BaseViewModel:

    public class BaseViewModel : INotifyPropertyChanged
{
    /// <summary>
    /// The event that is fired when any child poperty changes its value
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged = ( sender, e) =>{ };

}

Спасибо за ответы заранее. Я пытался найти четкое и понятное руководство по этому вопросу, но безуспешно. Как правильно уведомить мой пользовательский интерфейс о том, что источник изменился ...

Ответы [ 2 ]

0 голосов
/ 02 февраля 2020

Спасибо за быстрый ответ, Blechdose, это очень помогло.

Для соглашения об именах: Это не для моей работы, это просто проект хобби, так что никаких изменений в том, как вы его предложили, нет. *

Я понял, что неправильно применил INotifyPropertyChanged. Итак, я переписал его, после небольшого поиска я понял, что если я использую пакет FodyPropertyChanged NuGet для плетения своей сборки, мой код может быть немного чище.

Я также изменил свои arrayLists на ObservableCollections. (Я использовал только аргумент arrayList, так как он был новичком в программировании, он мне показался подходящим)

    class MainWindowViewModel : BaseViewModel
{

    public static ObservableCollection<string> SystemList { get; set; } = new ObservableCollection<string>();
    //--------------------------------------------------------------------------------------------//
    public static ObservableCollection<string> TitleList { get; set; } = new ObservableCollection<string>();
    //--------------------------------------------------------------------------------------------//
    public static ObservableCollection<string> RevisionList { get; set; } = new ObservableCollection<string>();
    //--------------------------------------------------------------------------------------------//
    public static ObservableCollection<DocumentModel> DocumentList { get; set; } = new ObservableCollection<DocumentModel>();
    //--------------------------------------------------------------------------------------------//
    public static string SystemFilter { get; set; } = String.Empty;
    //--------------------------------------------------------------------------------------------//

А что касается BaseViewModel, я сохранил:

    public class BaseViewModel : INotifyPropertyChanged
{
    /// <summary>
    /// The event that is fired when any child poperty changes its value
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;
}

И я установил Fody PropertyChanged, чтобы я Не нужно каждый раз устанавливать что-то вроде этого:

private string mTest;
    public string Test
    {
        get
        {
            return mTest;
        }

        set
        {
            if (mTest == value)
                return;
            mTest = value;
            PropertyChanged(this, new PropertyChangedEventArgs(nameof(Test)));
        }

    }

Спасибо за отзыв. Теперь это работает правильно. (Пока хотя бы :))

0 голосов
/ 02 февраля 2020

Вы хотите что-то изменить в ViewModel, и это должно обновить представление. Это работает только при использовании уведомлений Свойство Изменено для свойств и INotify Коллекция Изменено для коллекций. В вашем случае просто используйте ObservableCollection<string>, он уже реализован.

public ObservableCollection<string> TitleList { get; set; } = new ObservableCollection<string>();

Используйте TitleList в качестве ItemSource в представлении вместо mTitleList. И добавьте материал прямо к нему во ViewModel, как это (я не уверен на 100% с синтаксисом, вы должны попробовать или Google это)

TitleList.Add("my stuff");

Некоторые дополнительные заметки

  1. для меня довольно сложно прочитать ваш код, потому что я не использую соглашение об именах, которое вы используете. Если вам разрешено работать, переключитесь на Microsoft c# соглашение об именах: имена свойств в PascalCase (TitleList) и имена полей в Camelcase с _-префиксом (_titleList).
  2. В первый раз я вижу ArrayList используется , Если у вас есть определенная причина для этого, сохраните ее. Но обычно мы используем List<string> для всего, что может быть массивом строк в других языках.
  3. вашего BaseViewModel недостаточно. В будущем вы можете захотеть вызвать событие PropertyChanged в вашей модели представления с именем свойства в EventArgs. Важное примечание: вы запускаете событие PropertyChanged в вашей ViewModel. Никто другой не делает это. Представление (или, скорее, класс привязки) подписывается на событие PropertyChange вашей ViewModel с помощью eventHandler, так что оно уведомляется, когда вы вызываете событие PropertyChanged в вашей ViewModel Дополнительная информация о нем
  4. должны ли эти поля быть полями для свойств? если да, то вы сделали их неправильно. Вот как это делается:
private string _myText;

public bool MyText
{
   get => _myText;
   set { _myText = value; // raise notify property changed here, if view should be updated } 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...