I am new to WPF and started learning it recently. I have a requirement and am trying to figure out what is the proper way to do it in WPF/MVVM.
Our application has 5 views (similar to pages/components in an Angular/web app). Before we can present the actual view model based on menu selection, we need to conduct a product search and have the product information stored somewhere in the context of the main window. Some views doesnt require product context
I can think of two options
somehow figure out that the actual selected view requires product context and present a product search view, conduct a search, and on success navigate/redirect to the corresponding view.
Include a search view as a control on each view, and hide the actual page view on load until the search is finished, then hide the search control.
Product Details <controls:ProductSearchView Grid.Row="1" DataContext="{Binding ProductSearchViewModel}"></controls:ProductSearchView>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
I just need some general guidelines or an overall concept so that i can research and implement. Any reference to articles or similar questions is much appreciated. thanks
You create a Grid with two elements:
When you click an option within a menu, a destination view is loaded (into ContentControl) but product search UserControl overlaps it when a view declared that product search needs to take place first.
Create a base class that defines a single view
abstract class ProductViewModel : INotifyPropertyChanged
{
public Views ViewName { get; set; }
private bool _productSearch;
public bool ProductSearch
{
get => _productSearch;
set
{
_productSearch = value;
OnPropertyChanged();
}
}
public RelayCommand SearchProductCommand { get; set; }
public ProductViewModel()
{
SearchProductCommand = new RelayCommand(() => ProductSearch = false);
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
}
and classes that implement base class
class PaymentsViewModel : ProductViewModel
{
public PaymentsViewModel()
{
ViewName = Views.Payments;
ProductSearch = true;
}
}
class OrdersViewModel : ProductViewModel
{
public OrdersViewModel()
{
ViewName = Views.Orders;
ProductSearch = false;
}
}
Then enum with view types (menu options)
enum Views
{
Orders,
Payments
}
MainWindow.xaml
<Window x:Class="WpfApp2.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"
xmlns:local="clr-namespace:WpfApp2"
xmlns:viewModel="clr-namespace:WpfApp2.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
d:DataContext="{d:DesignInstance viewModel:MainViewModel}">
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:OrdersViewModel}">
<TextBlock Text="Orders control to be implemented"/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:PaymentsViewModel}">
<TextBlock Text="Payments control to be implemented"/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="100"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding Views}" SelectedItem="{Binding SelectedView}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ViewName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Row="1">
<ContentControl Content="{Binding SelectedView}"/>
<local:Search DataContext="{Binding SelectedView}">
<local:Search.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ProductSearch}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</local:Search.Style>
</local:Search>
</Grid>
</Grid>
and its view-model
class MainViewModel : BaseViewModel
{
public ObservableCollection<ProductViewModel> Views { get; set; }
private ProductViewModel _selectedView;
public ProductViewModel SelectedView
{
get => _selectedView;
set
{
_selectedView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Views = new ObservableCollection<ProductViewModel>
{
new PaymentsViewModel(),
new OrdersViewModel()
};
}
}
set DataContext of MainWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
and user control of search bar
<UserControl x:Class="WpfApp2.Search"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp2"
xmlns:viewModel="clr-namespace:WpfApp2.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Background="White"
d:DataContext="{d:DesignInstance viewModel:ProductViewModel}">
<StackPanel>
<TextBlock Text="Product search"/>
<TextBox/>
<Button Content="Search" Command="{Binding SearchProductCommand}"/>
</StackPanel>
The application looks as follows:
When I click payments option, it opens up a search bar as PaymentsViewModel has ProductSearch set to true. Once search is done, I click a search button and search control is closed and destionation view is displayed, yet to be implemented by you.