Нужна помощь в понимании MVVM модели WPF DataGrid - PullRequest
0 голосов
/ 27 февраля 2020

Я сделал небольшое C# приложение для отображения базы данных в WPF DataGrid. Все работает нормально, но так как у меня есть два windows в приложении, мне нужно загрузить базу данных отдельно для них обоих. Я хотел бы загрузить его один раз и иметь возможность отфильтровать данные по-разному в двух разных windows. Я понял, что правильный путь - это создать модель MVVM?

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


Вот мое рабочее решение (не MVVM):

C#

using System.Data.Odbc;
using System.Windows;
using System.Data;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System;

namespace DB_inspector_FilterTest
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }

        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            try
            {
                ProgressBar.IsIndeterminate = true;

                DataGrid1.ItemsSource = await GetDataAsync();

                ProgressBar.IsIndeterminate = false;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private Task<DataView> GetDataAsync()
        {
            return Task.Run(() =>
            {

                string connectionStringDE = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSSE;Uid=ADMIN;Pwd=123;";

                string queryStringDE = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                string connectionStringFR = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSFI;Uid=ADMIN;Pwd=123;";

                string queryStringFR = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                DataTable dataTable = new DataTable("COMPANY");
                // using-statement will cleanly close and dispose unmanaged resources i.e. IDisposable instances
                using (OdbcConnection dbConnectionDE = new OdbcConnection(connectionStringDE))
                {
                    dbConnectionDE.Open();
                    OdbcDataAdapter dadapterDE = new OdbcDataAdapter();
                    dadapterDE.SelectCommand = new OdbcCommand(queryStringDE, dbConnectionDE);

                    dadapterDE.Fill(dataTable);

                }
                using (OdbcConnection dbConnectionFR = new OdbcConnection(connectionStringFR))
                {
                    dbConnectionFR.Open();
                    OdbcDataAdapter dadapterFR = new OdbcDataAdapter();
                    dadapterFR.SelectCommand = new OdbcCommand(queryStringFR, dbConnectionFR);

                    var newTable = new DataTable("COMPANY");
                    dadapterFR.Fill(newTable);

                    dataTable.Merge(newTable);

                }

                return dataTable.DefaultView;

            });
        }

        private Dictionary<string, string> _conditions = new Dictionary<string, string>();

        private void UpdateFilter()
        {
            try
            {
                var activeConditions = _conditions.Where(c => c.Value != null).Select(c => "(" + c.Value + ")");
                DataView dv = DataGrid1.ItemsSource as DataView;
                dv.RowFilter = string.Join(" AND ", activeConditions);
            }
            catch (Exception)
            {
                //MessageBox.Show(ex.Message);
            }
        }

        private void NameSearch_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
        {
            string filter = NameSearch.Text;
            if (string.IsNullOrEmpty(filter))
                _conditions["name"] = null;
            else
                _conditions["name"] = string.Format("NAME Like '%{0}%'", filter);
            UpdateFilter();
        }

        private void ActiveCustomer_Click_1(object sender, RoutedEventArgs e)
        {
            if (ActiveCustomer.IsChecked == true)
            {
                _conditions["active"] = string.Format("ACTIVE Like '%{0}%'", "1");
                UpdateFilter();
            }
            else
            {
                _conditions["active"] = null;
                UpdateFilter();
            }
        }

        private void CheckBox_Click(object sender, RoutedEventArgs e)
        {
            if (OnlyFIandSE.IsChecked == true)
            {
                _conditions["onlyfrandde"] = string.Format("NRO Like '2%' OR NRO Like '3%'");
                UpdateFilter();
            }
            else
            {
                _conditions["onlyfrandde"] = null;
                UpdateFilter();
            }
        }
    }
}

XAML

<Window x:Class="DB_inspector_FilterTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="DB database inspector v.0.0.01" Height="600" Width="1000" Icon="logo_icon-small.jpg" Background="White">
    <Grid Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <DataGrid x:Name="DataGrid1" Margin="0,103,0,0" Background="White" BorderBrush="#FF38853F"/>
        <TextBox x:Name="NameSearch" HorizontalAlignment="Left" Height="20" Margin="22,41,0,0" TextWrapping="Wrap" Text="" VerticalAlignment="Top" Width="437" TextChanged="NameSearch_TextChanged"/>
        <Button Content="Load" Margin="640,41,0,0" Click="Button_Click_1" BorderBrush="{x:Null}" Foreground="White" Background="#FF55B432" Width="66" Height="29" HorizontalAlignment="Left" VerticalAlignment="Top"/>
        <ProgressBar x:Name="ProgressBar" HorizontalAlignment="Left" Height="11" VerticalAlignment="Top" Width="992" BorderBrush="{x:Null}" Background="{x:Null}"/>
        <Label Content="Customer name" HorizontalAlignment="Left" Height="25" Margin="22,11,0,0" VerticalAlignment="Top" Width="154"/>
        <CheckBox x:Name="ActiveCustomer" Content="Active" HorizontalAlignment="Left" Height="24" Margin="486,63,0,0" VerticalAlignment="Top" Width="86" Click="ActiveCustomer_Click_1"/>
        <CheckBox x:Name="Only" Content="Leave only good" HorizontalAlignment="Left" Height="17" Margin="486,41,0,0" VerticalAlignment="Top" Width="115" Click="CheckBox_Click"/>
        <Image Margin="856,0,22,520" VerticalAlignment="Bottom" Source="logo_small.jpg" Height="27"/>

    </Grid>
</Window>

MVVM:

C#

using System;
using System.ComponentModel;
using System.Data;
using System.Data.Odbc;
using System.Windows;
using System.Windows.Input;

namespace DB_inspector
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {

    }

    public class ViewModel : INotifyPropertyChanged
    {
        public ICommand myCommand => new RelayCommand(obj =>
        {
            try
            {
                string connectionStringDE = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSSE;Uid=ADMIN;Pwd=123;";

                string queryStringDE = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                string connectionStringFR = "Driver={Pervasive ODBC Client Interface};ServerName=DB123;dbq=@DBFSFI;Uid=ADMIN;Pwd=123;";

                string queryStringFR = "select NRO,NAME,NAMEA,NAMEB,ADDRESS,POSTA,POSTN,POSTB,CORPORATION,COUNTRY,ID,ACTIVE from COMPANY";

                DataTable dataTable = new DataTable("COMPANY");
                // using-statement will cleanly close and dispose unmanaged resources i.e. IDisposable instances
                using (OdbcConnection dbConnectionDE = new OdbcConnection(connectionStringDE))
                {
                    dbConnectionDE.Open();
                    OdbcDataAdapter dadapterDE = new OdbcDataAdapter();
                    dadapterDE.SelectCommand = new OdbcCommand(queryStringDE, dbConnectionDE);

                    dadapterDE.Fill(dataTable);

                }
                using (OdbcConnection dbConnectionFR = new OdbcConnection(connectionStringFR))
                {
                    dbConnectionFR.Open();
                    OdbcDataAdapter dadapterFR = new OdbcDataAdapter();
                    dadapterFR.SelectCommand = new OdbcCommand(queryStringFR, dbConnectionFR);

                    var newTable = new DataTable("COMPANY");
                    dadapterFR.Fill(newTable);

                    dataTable.Merge(newTable);

                }

                _ = dataTable.DefaultView;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        });

        private bool _allowUIChanges = true;
        public bool AllowUIChanges
        {
            get => _allowUIChanges;
            set
            {
                _allowUIChanges = value;
                OnPropertyChanged(nameof(AllowUIChanges));
                OnPropertyChanged(nameof(IsReadOnlyDataGrid));
            }
        }

        private void OnPropertyChanged(string v)
        {
            throw new NotImplementedException();
        }

        public bool IsReadOnlyDataGrid
        {
            get => !_allowUIChanges;
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Func<object, bool> _canExecute;

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
        public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);

        public void Execute(object parameter) => _execute(parameter);
    }
}

XAML

<Window x:Class="DB_inspector.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="DB database inspector" Height="595.404" Width="1005.571">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <DataGrid IsReadOnly="{Binding IsReadOnlyDataGrid}" AutoGenerateColumns="False" ItemsSource="{Binding MyCollection}" Width="998" Margin="0,98,0,0" >
        </DataGrid>
        <Image Height="41" Margin="0,21,10,0" Width="141" Source="logo_small.jpg" HorizontalAlignment="Right" VerticalAlignment="Top"/>
        <Button Content="Go" Command="{Binding myCommand}" Width="80" Height="30" Margin="48,42,870,492"/>

    </Grid>
</Window>

1 Ответ

1 голос
/ 27 февраля 2020

Мне кажется, вы пришли из фона WinForms. У меня были такие же проблемы при переходе на WPF. Я приведу пример следования вашему коду, чтобы вы могли лучше понять, что происходит.

Вы были на правильном пути при создании экземпляра модели, но забыли назначить модель на DataContext главного окна. , DataContext это именно то, что он говорит. Контекст данных окна и в вашем случае это ViewModel класс. Назначьте его в конструкторе MainWindow.

public MainWindow()
{
    InitializeComponent();

    DataContext = new ViewModel();
}

Отныне вы можете использовать обычные привязки в XAML, о чем в основном и говорит WPF.

Теперь вам нужно работать над своим модели немного. Прежде всего, вы должны изменить команду, чтобы она была свойством, а не полем. Достаточно определить пустое свойство и присвоить ему Command в конструкторе ViewModel.

public ICommand Load { get; }

Для простоты я добавил простейшую коллекцию в качестве свойства.

private List<string> m_items;
public List<string> Items
{
    get => m_items;
    set
    {
        m_items = value;
        OnPropertyChanged(nameof(Items));
    }
}

Кроме того, я вижу, что вы начали реализовывать интерфейс INotifyPropertyChanged. Это наиболее важный интерфейс в WPF, и я предлагаю немного ознакомиться с ним. Правильная реализация в вашем случае должна выглядеть следующим образом:

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

Наконец, в конструкторе ViewModel присвойте свойство Command Load следующим образом:

public ViewModel()
{
    Load = new RelayCommand(obj =>
    {
        try
        {
            // Load your stuff here
            Items = new List<string>() { "Item1", "Item2", "Item3", "Item4" };
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    });
}

Your XAML очень прост в этом случае. Я добавил только кнопку и сетку.

<Grid Background="White">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Content="Load" Command="{Binding Load}" HorizontalAlignment="Center"/>
    <DataGrid Grid.Row="1" ItemsSource="{Binding Items}"/>
</Grid>

Как вы можете видеть, сетка данных связана со свойством Items (наша коллекция), а свойство Buttons Command - со свойством Load ( наша команда). Когда пользователь нажимает кнопку, команда будет выполнена. Команда устанавливает свойство Items с новой коллекцией и, в свою очередь, вызывает метод OnPropertyChanged, на который подписан DataGrid. Это заставляет Grid читать свойство Items и заполнять себя.

Это примерно так, как это делается. При этом я предлагаю вам немного прочитать о WPF и о том, как он работает. Я думаю, что это гораздо лучше, чем сразу же попробовать что-то такое большое, а затем попросить о помощи. Обычно эти большие примеры не привлекают большого внимания.

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