Search code examples
wpfxamlmvvm

WPF MVVM - Search view functionality before presenting actual view model


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 

  1. 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.

  2. 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


Solution

  • You create a Grid with two elements:

    • ContentControl
    • Product search user control

    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: enter image description here
    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.