0

введите сюда описание изображенияВ базе SQL server есть таблицы categories и products, также есть внешний ключ в products от categories.

В MainWindow.xaml есть два Listbox

Этот для категорий

 <ListBox ScrollViewer.VerticalScrollBarVisibility="Hidden" ItemsSource="{Binding Categories}" Background="{x:Null}" BorderBrush="{x:Null}" Padding="0">
      <ListBox.ItemsPanel>
          <ItemsPanelTemplate>
             <WrapPanel Orientation="Horizontal" Margin="40,0"/>
           </ItemsPanelTemplate>
           </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
                     <DataTemplate>
                        <Grid Margin="0,20">
                             <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                              </Grid.ColumnDefinitions>
                              <TextBlock FontSize="18" Text="{Binding Path=Title}" Foreground="#fff" Margin="0,0,0,0" Grid.Column="0"/>
                              <Button Margin="5" DataContext="{Binding ID_Category}" Click="select_category" Background="Transparent" Grid.Column="0"/>
                        </Grid>
                     </DataTemplate>
           </ListBox.ItemTemplate>
  </ListBox>

А этот для продуктов

<ListBox ItemsSource="{Binding Items}" Background="{x:Null}" BorderBrush="{x:Null}" SelectionMode="Multiple" Margin="0,50">
                <ListBox.ItemsPanel>
                    <ItemsPanelTemplate>
                        <WrapPanel Orientation="Horizontal"/>
                    </ItemsPanelTemplate>
                </ListBox.ItemsPanel>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Width="250" Height="370" Margin="40,0,0,0" Background="#121212">
                            <Image Source="{Binding Path=Image}" Width="250" Height="200" Stretch="Fill"/>
                            <TextBlock FontSize="18" Text="{Binding Path=Name}" Foreground="#fff" Margin="15,15,0,0"/>
                            <TextBlock FontSize="16" Text="{Binding Path=ShortText}" Foreground="#9C9999" Margin="15,8,15,0" TextWrapping="Wrap" Height="40"/>
                            <Grid Margin="15,8,15,0">
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
                                <TextBlock FontSize="14" Text="{Binding Path=Quantity}" Foreground="#fff" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
                                <TextBlock FontSize="18" Text="{Binding Path=Price}" Foreground="#fff" Grid.Column="1" Margin="15,0,0,0" HorizontalAlignment="Right"/>
                            </Grid>
                            <Button DataContext="{Binding ID}" Content="To basket" FontSize="16" Background="#046902" Width="100" Height="30" Foreground="#fff" Margin="0,10,0,0" HorizontalAlignment="Center" Click="ToBasket"/>
                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
        </ListBox>

А в ViewModel я прописал так

public ApplicationViewModel()
    {
        string sqlConnection = @"server=HOME-PC\SQLEXPRESS;Trusted_Connection=Yes;DataBase=nanoEnergy;";
        string sqlCategory = "SELECT id_category,name FROM categories";
        string sqlItems = "SELECT img,name,discription,quantity,price,creator,year_of_issue,materials,id_product, id_category FROM products";
        string sqlPriceList = "SELECT * FROM price_list_view";
    using (SqlConnection Connection = new SqlConnection(sqlConnection))
    {
        Connection.Open();
        SqlCommand command = new SqlCommand(sqlCategory, Connection);
        SqlDataReader reader = command.ExecuteReader();

        Categories = new ObservableCollection&lt;Category&gt;();
        if (reader.HasRows)
        {
            while (reader.Read())
            {

                Categories.Add(new Category
                {
                    Title = reader.GetString(1),
                    ID_Category = reader.GetInt32(0)
                });
            }
        }

        reader.Close();
    };
    using (SqlConnection Connection = new SqlConnection(sqlConnection))
    {
        Connection.Open();
        SqlCommand command = new SqlCommand(sqlItems, Connection);
        SqlDataReader reader = command.ExecuteReader();
        Items = new ObservableCollection&lt;Item&gt;();
        if (reader.HasRows)
        {
            while (reader.Read())
            {
                byte[] image = (byte[])(reader.GetValue(0));
                object price = reader.GetValue(4);
                string price_item = Convert.ToString(price); 
                object year = reader.GetValue(6);
                string year_item = Convert.ToString(year);

                Items.Add(new Item
                {
                    Image = image,
                    Name = reader.GetString(1),
                    ShortText = reader.GetString(2),
                    Quantity = reader.GetInt32(3),
                    Price = price_item,
                    Creator = reader.GetString(5),
                    Year = year_item,
                    Materials = reader.GetString(7),
                    ID = reader.GetInt32(8)
                });
            }
        }
        reader.Close();
    };

}

public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = &quot;&quot;)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(prop));
}

}

Здесь Item:

class Item : INotifyPropertyChanged
    {
        private byte[] image;
        private string name;
        private int quantity;
        private string price;
        private string shorttext;
        private string creator;
        private string year;
        private string materials;
        private int i_id;
    public int ID
    {
        get { return i_id; }
        set
        {
            i_id = value;
            OnPropertyChanged(&quot;ID&quot;);
        }
    }
    public string Creator
    {
        get { return creator; }
        set
        {
            creator = value;
            OnPropertyChanged(&quot;Creator&quot;);
        }
    }
    public string Year
    {
        get { return year; }
        set
        {
            year = value;
            OnPropertyChanged(&quot;Year&quot;);
        }
    }
    public string Materials
    {
        get { return materials; }
        set
        {
            materials = value;
            OnPropertyChanged(&quot;Materials&quot;);
        }
    }

    public byte[] Image
    {
        get { return image; }
        set
        {
            image = value;
            OnPropertyChanged(&quot;Image&quot;);
        }
    }
    public string Name
    {
        get { return name; }
        set
        {
            name = value;
            OnPropertyChanged(&quot;Name&quot;);
        }
    }

    public int Quantity
    {
        get { return quantity; }
        set
        {
            quantity = value;
            OnPropertyChanged(&quot;Quantity&quot;);
        }
    }

    public string Price
    {
        get { return price; }
        set
        {
            price = value;
            OnPropertyChanged(&quot;Price&quot;);
        }
    }
    public string ShortText
    {
        get { return shorttext; }
        set
        {
            shorttext = value;
            OnPropertyChanged(&quot;ShortText&quot;);
        }
    }


    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged([CallerMemberName] string prop = &quot;&quot;)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(prop));
    }
}

Так вот, не доходит до ума чтобы передать id таблицы categories в таблицу products, видимо чего-то очень важного не понимаю, чтобы сделать это.

Подскажите, что нужно лучше сделать, чтобы достичь выборки категории?

  • кажется, вы совсем не в ту степь забрели, какую задачу вы решаете? сейчас ответ звучит так: что бы вы не написали в этих скобках, работать не будет. Начните с того, что надо понять, что именно делает new MainWindow(), как оно работает. Если вы думаете, что дает вам доступ к основному окну, то это не так. Оно создает второе основное окно, и если вы вызовете mainWindow.Show(), то своими глазами в этом убедитесь. – aepot Feb 18 '21 at 10:51
  • @aepot Решаю задачу по сути с коллекциями, у меня есть в базе sql servel таблица category, а также есть таблица products в которой есть id соответственно, мне нужно при выборе category показывать по ней продукцию. Сейчас я изменю свою вопрос и код внутри. Предупреждаю, код у меня ужасен:) – Astronaut Pavel Feb 18 '21 at 10:58
  • XAML разметку не забудьте показать. – aepot Feb 18 '21 at 10:58
  • @aepot Да, mainWindow.Show(); будет вызывать новое окно, именно его в своей недопрограмме использую его частенько. – Astronaut Pavel Feb 18 '21 at 11:16
  • Click="ToBasket" - использование обработчиков событий в данном ключе - это не MVVM, в MVVM с контролами не работают в C# (почти никогда), а работают с данными. Что вы хотите добиться то? Что должен делать метод select_category, и зачем вы ему добавили аргумкенты как обработчику события, если он не обработчик события? Откуда он будет вызван с этими аргументами? Нужно больше кода или рассказ поподробнее. В курсе ли вы что вместо обработчиков Click можно использовать команды ICommand? – aepot Feb 18 '21 at 11:29
  • Еще вместо Convert.ToString(reader.GetValue(N)) можно использовать reader.GetString(N), и там много еще разных методов для разных типов данных, напишите reader. и вам вывалится целый список, в котором много всего интересного. – aepot Feb 18 '21 at 11:36
  • Используете ли вы MVVM фреймворк? Покажите модель данных Item. Реализует ли модель данных INotifyPropertyChanged? Если да, то как именно? – aepot Feb 18 '21 at 11:39
  • @aepot 1) Насчёт события Click стало ясно, что это не MVVM. 2) Хочу добиться чтобы при выборе категории, показывались элементы(Products), которые находятся именно в этой категории. 3) select_category, Вообще я думал при событии он обработает, но видимо что нет. 4) Как раз откуда бы был вызван, так и не дошло:(. 5) Насчёт ICommand только сегодня узнал о нём. Если больше объяснений, то загружу фото программы, там будет нужно. Насчёт больше кода, тоже загружу, но не особо уверен, что он весь будет нужен – Astronaut Pavel Feb 18 '21 at 11:41
  • @aepot Сейчас поправлю у себя на reader.GetString(N). – Astronaut Pavel Feb 18 '21 at 11:43
  • @aepot Нет, я не использую фраймворки. Код Item тоже сейчас загружу:) – Astronaut Pavel Feb 18 '21 at 11:44
  • Сразу обновите тогда код в вопросе, он станет заметно короче. Вы можете прямо внтури Items.Add(new Item {...}) вызывать reader.Get...(..). – aepot Feb 18 '21 at 11:44
  • @aepot изменил reader, но не везде, так как дата и цену не знаю как лучше записать, попробовал но пока не вышло, потихоньку буду разбираться. За сокращение спасибо:) – Astronaut Pavel Feb 18 '21 at 12:20
  • С ценой все просто, она у вас в базе как число? Вот и храните как число, а если хотите баксы пририсовать, сделайте это например в интерфейсе. <TextBlock Text="{Binding Price, StringFormat={0}$}" ... - ссылка, или в свойстве внутри Item - public string PriceText => Price + " $"; + Text="{Binding PriceText}. Как вам удобнее, а модель данных в приложении должна быть в совместимых с базой форматах. – aepot Feb 18 '21 at 15:29
  • @aepot В базе цена float, поначалу ставил money, но вот только 4 нуля после запятой покою не давали, поэтому и поменял его, но да:), мне нужны баксы – Astronaut Pavel Feb 18 '21 at 15:35
  • public string PriceText => Price.ToString("F2") + " $";, где public float Price или double, не важно. ссылка или соовтетвтенно StringFormat={0:F2} $ - два нуля после запятой. P.S. значок бакса ставится перед числом по стандарту :) $1,000 – aepot Feb 18 '21 at 15:36
  • @aepot посмеялся с себя, когда прочитал P.S. Ну а так, "F2" для чего? – Astronaut Pavel Feb 18 '21 at 15:43
  • F2 - https://docs.microsoft.com/ru-ru/dotnet/standard/base-types/standard-numeric-format-strings – aepot Feb 18 '21 at 15:45

1 Answers1

0

Используйте команду

RelayCommand

Добавьте вот такой класс себе в проект

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&lt;object&gt; execute, Func&lt;object, bool&gt; canExecute = null)
{
    _execute = execute;
    _canExecute = canExecute;
}

public bool CanExecute(object parameter) =&gt; _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) =&gt; _execute(parameter);

}

UI

Тогда кнопки должны у вас такие

<Button Margin="5"
        Background="Transparent"
        Grid.Column="0"
        Command="{Binding DataContext.SelectCategoryCommand, RelativeSource={RelativeSource AncestorType=Window}}"
        CommandParameter="{Binding}"/>
<Button Content="To basket"
        FontSize="16"
        Background="#046902"
        Width="100" Height="30"
        Foreground="#fff"
        Margin="0,10,0,0"
        HorizontalAlignment="Center"
        Command="{Binding DataContext.ToBasketCommand, RelativeSource={RelativeSource AncestorType=Window}}"
        CommandParameter="{Binding}"/>

Команды

При таких привязках команды должны быть расположены во вьюмодели окна и выглядеть так

private ICommand _selectCategoryCommand;

public ICommand SelectCategoryCommand => _selectCategoryCommand ??= new RelayCommand(parameter => { if (parameter is Category category) { // здесь выполняйте код, category - это объект категории, в который юзер ткнул } });

private ICommand _toBasketCommand;

public ICommand ToBasketCommand => _toBasketCommand ??= new RelayCommand(parameter => { if (parameter is Item item) { // здесь выполняйте код, item - это объект Item, в который юзер ткнул } });

CanExecute

Есть еще одна фича, CanExecute называется, вы можете добавить условие, которое будет делать элемент интефейса неактивным, прямо в команду, и если оно вернет false:

  1. команда не будет выполнена
  2. IsEnabled элемента станет false автоматически

пользоваться так:

public ICommand ToBasketCommand=> _toBasketCommand ??= new RelayCommand(parameter =>
{
    Item item = (Item)parameter; // так как проверка на is Item уже выполнена в CanExecute, 
    // можно выполнять сразу каст, потому что CanExecute дает гарантию,
    // что команда не будет выполнена, если CanExecute == false
// здесь выполняйте код, item - это объект Item, в который юзер ткнул

}, parameter => parameter is Item item && item.Quantity > 0);

Все кнопки у элементов с Quantity == 0 будут вырублены и кнопка To Basket не будет работать. Сами придумайте, как это можно использовать. Условие перепроверяется автоматически каждый раз, когда юзер ткнул в интерфейс или нажал на клавиатуру, то есть когда произошло событие любого ввода.

Вызвать перепроверку всех CanExecute вручную можно вот так:

CommandManager.InvalidateRequerySuggested();

Только если вы будете использовать многопоточность, этот метод можно вызывать только из UI потока, в противном случае он просто не сработает.

aepot
  • 49,560
  • 1
    Ого, теперь думаю что когда стану хорошо понимать программирование, буду также помогать как и вы. Хороший пример. Ещё раз большое спасибо:) – Astronaut Pavel Feb 18 '21 at 14:58
  • @AstronautPavel работает? отлично! вслепую на коленке писал. :) – aepot Feb 18 '21 at 15:03
  • 1
    Ахахах:), Да, только ещё ищу про команды ICommand, так как ещё слабовато разбираюсь:) – Astronaut Pavel Feb 18 '21 at 15:13
  • @AstronautPavel важно RelativeSource={RelativeSource AncestorType=Window} - это перенаправление привязки в окно, если это убрать и написать просто Command="{Binding SelectCategoryCommand}", то команда будет искаться в текущем DataContext, то есть внутри класса Item или Category соответственно, а не во вьюмодели окна. Соответственно, если вы делаете команды для кнопок за пределами коллекции, а просто в интерфейсе, то вам не нужен RelativeSource, потому что DataContext этих кнопок будет и так указывать на нужную вьюмодель. – aepot Feb 18 '21 at 15:15
  • @AstronautPavel в смысле работает? о_О не должно, сейчас баг исправлю. Исправил. Command="{Binding DataContext.SelectCategoryCommand,... Если у вас команды в классе Window - унесите их в вьюмодель окна ApplicationViewModel, они там должны быть. В идеале, при правильном MVVM в классе Window ничего не должно быть, вообще пусто, только одинокий InitializeComponent(); DataContext = new ApplicationViewModel(); в конструкторе. – aepot Feb 18 '21 at 15:18
  • 1
    Ах да, не написал, да DataContext не хватало, он у меня ругался, но я его добавил. Просто наугад написал, даже не знал и вот, больше не ругался на него:) – Astronaut Pavel Feb 18 '21 at 15:28
  • Вот то что важно в копилочку знаний!!! – Astronaut Pavel Feb 18 '21 at 15:29
  • @AstronautPavel ну теперь у вас код должен собраться в одну кучу, а то события в одном классе, свойства в другом, куча перекрестных ссылок. Чем меньше класс связан с внешним миром, тем легче его допиливать/перепиливать. Суть MVVM в максимально возможном ослаблении связей между компонентами приложения. – aepot Feb 18 '21 at 15:35
  • суть как я понимаю, точнее аналогия меньше материального недосчастья, больше свободы внутри себя. Ну это так, лирика. А вообще мне интересно, вот думал на web перейти, так вот можно ли в WPF использовать как web, то есть исполнитель визуалки за место браузерв? – Astronaut Pavel Feb 18 '21 at 15:40
  • @AstronautPavel не понял вопроса, поэтому ответ "нет". Есть специальный ASP.NET Core для веба. – aepot Feb 18 '21 at 15:44
  • А что насчет десктопных приложений, например социальные сети какие-нибудь ну или просто проги, которые с API связываются и используют только его. – Astronaut Pavel Feb 18 '21 at 15:49
  • @AstronautPavel десктопные приложения не так популярны, если это только что-то реально крупное или игра. Вся мелочь уехала в мобильный рынок, там вас встретит Xamarin.Forms, и там кстати XAML как в WPF. – aepot Feb 18 '21 at 15:50
  • 1
    о да, Xamarin.Forms ноут туго тянет, а вот в коллеже того года сделал на нём откровенное д****, но оно было лучшим. API у nasa спёр. Ох, хорошо с вами знания получать, но нужно дальше пилить прогу, а то курсач по времени к концу подходит. Все мои одногруппники через datagrid, а я без него:) При многом благодарен^-^ – Astronaut Pavel Feb 18 '21 at 15:57