Обновить сетку данных, используя привязку данных из базы данных, используя наблюдаемый объект - PullRequest
0 голосов
/ 02 сентября 2018

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

Вопрос: Как вызвать PropertyChangedEventArgs, когда другой класс обновляет базу данных SQL?

Я пробовал что-то следующим образом, однако, оно не работает (вероятно, потому что я уведомляю в новом случае). Если я снова открою окно, в котором отображается XAML, я вижу, что дополнительные записи были добавлены в базу данных, она просто не обновляется автоматически.

TableView.xaml.cs

private void AddNode()
{
    SQL.InsertNode(); //this fires a method to add a row to the database
    ViewModel viewmodel = new ViewModel(); //this fires the messagebox in my viewmodel the the notification is raised
    viewmodel.FillList(); // this doesn't seem to do anything. Cluthing at straws really
}

Надеюсь, я на правильном пути. Чтобы помочь ответить на вопрос, я предоставил все, что, я думаю, вам понадобится ниже.

Вот мой XAML - TableView.xaml

<DataGrid Grid.Row="2" 
          x:Name="NodeTableDataGrid" 
          ItemsSource="{Binding Nodes, Mode=TwoWay}" 
          Grid.ColumnSpan ="3" 
          CanUserAddRows="False" 
          AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Add Row" IsReadOnly="True" Visibility="Visible"/>
        <DataGridTextColumn Header="Id" Binding="{Binding Path='id'}" IsReadOnly="True" Visibility="Visible"/>
        <DataGridTextColumn Header="Node Name" Binding="{Binding Path='NodeName', Mode=TwoWay}" IsReadOnly="False" />
        <DataGridTextColumn Header="Easting (m)" Binding="{Binding Path='x'}" IsReadOnly="False" />
        <DataGridTextColumn Header="Northing (m)" Binding="{Binding Path='y'}" IsReadOnly="False" />
        <DataGridTextColumn Header="Cover Level (m)" Binding="{Binding Path='z_cover'}" IsReadOnly="False" />
    </DataGrid.Columns>
</DataGrid>

Вот код позади - TableView.xaml.cs

public TableView()
{
    InitializeComponent();
    co = new ViewModel();
    base.DataContext = co;
}

И мой Просмотр модели

public class ViewModel : INotifyPropertyChanged
{
    SQLiteCommand command;
    SQLiteDataAdapter adapter;
    DataSet ds;

    private ObservableCollection<MyModel> _nodes;
    public ObservableCollection<MyModel> Nodes
    {
        get
        {
            return this._nodes;
        }
        set
        {
            if (_nodes != value)
            {
                MessageBox.Show("Table changed");
                OnPropertyChanged("Nodes");
            }

            this._nodes = value;
        }
    }


    public ViewModel()
    {            
        FillList();
    }

    public void FillList()
    {
        try
        {
            string sql = "SELECT * FROM node_table";
            command = new SQLiteCommand(sql, SQL.m_dbConnection);
            adapter = new SQLiteDataAdapter(command);
            ds = new DataSet();
            adapter.Fill(ds, "node_table");

            if (Nodes == null)
                Nodes = new ObservableCollection<MyModel>();

            foreach (DataRow dr in ds.Tables[0].Rows)
            {
                Nodes.Add(new MyModel
                {
                    id = Convert.ToInt32(dr[0].ToString()),
                    NodeName = dr[1].ToString(),
                    x = Convert.ToInt32(dr[2].ToString()),
                    y = Convert.ToInt32(dr[3].ToString()),
                    z_cover = Convert.ToInt32(dr[4].ToString())

                });
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("The table view has failed to load from the database with the following error: " + ex.ToString());
        }
        finally
        {
            ds = null;
            adapter.Dispose();
        }
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

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


}

РЕДАКТИРОВАТЬ Чтобы захватить комментарии "от Хуана Карлоса Родригеса"

Я изменил TableView.xaml.cs следующим образом, что устраняет проблему для обновленных вызовов SQL из класса View, так что спасибо за ваш ответ. Очень полезно.

    ViewModel co;

    public TableView()
    {
        SQL.InsertNode();
        InitializeComponent();
        co = new ViewModel();
        base.DataContext = co;
    }

    public void AddNode()
    {
        SQL.InsertNode();
        co.Nodes.Clear();
        co.FillList();
    }

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

Какой-то случайный класс

    public void AddNodeFromRandomClass()
    {
        //Updates the database succesafully, however raises the change in the wrong instance of TableView.
        TableView tableView = new TableView();
        tableView.AddNode();
    }

1 Ответ

0 голосов
/ 03 сентября 2018

Он никогда не увеличит набор Nodes, потому что вы не создаете новый экземпляр ObservableCollection, вы добавляете в него элементы, которые не поднимают набор. Набор ObservableCollection повышается при изменении экземпляра.

Nodes.Add( new MyModel()) -> "Doesn't raise the set."

ObservableCollection<MyModel> myModelOC = new ObservableCollection<MyModel>() { "Declare 3 items" };
Nodes = myModelOC -> "This will raise the set".

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

public TableView()
{
  InitializeComponent();
  co = new ViewModel();
  base.DataContext = co;
}

Это ваш xaml.cs?

В любом случае, я бы сказал, что ваша проблема в том, что вы устанавливаете DataContext, а затем с помощью другого частного метода создаете новую ВМ, поэтому экземпляр вашей 101 * * и виртуальной машины, которую вы создаете в своей частной жизни, полностью иначе, поэтому вы не получаете обновленный DataGrid, вы обновляете виртуальную машину, которая не установлена ​​как DataContext в представлении.

Пожалуйста, отредактируйте свое сообщение и укажите, куда вы звоните, AddNode и как вы показываете свой вид, чтобы я мог рассказать вам, как это исправить.


Прежде всего, это не следует MVVM, так как вы помещаете код в свой код Viewbehind:

ViewModel co;

public TableView()
{
    SQL.InsertNode();
    InitializeComponent();
    co = new ViewModel();
    base.DataContext = co;
}

public void AddNode()
{
    SQL.InsertNode();
    co.Nodes.Clear();
    co.FillList();
}

Перед тем, как сделать TableView.Show(), вам нужно создать ViewModel и установить TableView DataContext. Было бы что-то вроде этого:

ViewModel yourViewModel = new ViewModel();
TableView tableView = new TableView();
tableView.DataContext = yourViewModel;
tableView.Show();

И ваш TableView.xaml.cs конструктор должен быть таким:

public TableView()
{
  InitializeComponent();
}

И ваш ViewModel конструктор должен быть таким:

public ViewModel()
{   
    FillList();
    Nodes.Clear();// You could do this if you need it to be cleared
}

Как говорится, для вашей реальной проблемы я предлагаю вам использовать события. Это действительно зависит от того, как строится ваша архитектура. Я приведу возможный подход:

Создайте новый класс, который будет содержать ваше событие, например:

public static class UpdaterEvent 
{
    public static event EventHandler DataUpdated;

    public static void PublishDataUpdated(EventArgs args) 
    {
        var updaterEvent = DataUpdated;
        if (updaterEvent != null)
            updaterEvent(null, args);
    }
}

Теперь в вашем конструкторе ViewModel добавьте:

public ViewModel()
{   
    FillList();
    Nodes.Clear();// You could do this if you need it to be cleared
    UpdaterEvent.DataUpdated += OnDataUpdated;        
}

private void OnDataUpdated(object sender, EventArgs e)
{
  FillList();
}

Всякий раз, когда вам нужно обновить ваши данные, просто сделайте следующее:

UpdaterEvent.PublishDataUpdated(new EventArgs());
...