Как реализовать выборку категории при которой показывается нужные элементы? WPF+MVVM+MSSQL
В базе 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<Category>();
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<Item>();
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 = "")
{
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("ID");
}
}
public string Creator
{
get { return creator; }
set
{
creator = value;
OnPropertyChanged("Creator");
}
}
public string Year
{
get { return year; }
set
{
year = value;
OnPropertyChanged("Year");
}
}
public string Materials
{
get { return materials; }
set
{
materials = value;
OnPropertyChanged("Materials");
}
}
public byte[] Image
{
get { return image; }
set
{
image = value;
OnPropertyChanged("Image");
}
}
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
public int Quantity
{
get { return quantity; }
set
{
quantity = value;
OnPropertyChanged("Quantity");
}
}
public string Price
{
get { return price; }
set
{
price = value;
OnPropertyChanged("Price");
}
}
public string ShortText
{
get { return shorttext; }
set
{
shorttext = value;
OnPropertyChanged("ShortText");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
Так вот, не доходит до ума чтобы передать id таблицы categories в таблицу products, видимо чего-то очень важного не понимаю, чтобы сделать это.
Подскажите, что нужно лучше сделать, чтобы достичь выборки категории?
Ответы (1 шт):
Используйте команду
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<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);
}
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:
- команда не будет выполнена
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 потока, в противном случае он просто не сработает.