0

Confused embedded programmer here

I am making a WPF application that will have multiple views/pages. I want the views to be full screen and be able to click through via buttons.

View 1: Landing Welcome page, with a next button on

View 2: Select Comport Page, User selects Comport then click through

View 3: Main Comport Application Page

I don't want to make 3 separate view models as view 3 will require the comport selection from View 2. The only way I have found to switch the views requires you to switch the data context. However, then I cannot access data in the other view models.

If anyone knows how this could be achieved please let me know!

ali king
  • 17
  • 2
  • 1
    Is this a single window app or are you thinking in terms of multiple windows? A single window is quite common. That has menu options and your different views would be switched out in a contentcontrol that fills most of the window. – Andy Oct 13 '20 at 14:19
  • Hello this would be a single window App! I would like the view to occupy the entire window. However if this not possible i will have to settle for something similar to you have suggested. – ali king Oct 13 '20 at 14:36
  • Let's call this container and content. So we navigate by changing content. How do you do that if the content fills the entire window? This is why many web pages will have some sort of a masthead or navigation list. SO has both. There are options on the left, right and in the masthead. And you don't usually want to replicate all that for every view so you would usually have those as part of the container. – Andy Oct 13 '20 at 14:56
  • The recommended approach is to create a view model for each page/view and use a `DataTemplate` to render the corresponding layout e.g., a `UserControl`. You can use the following pattern to accomplish your goal: [C# WPF Page Navigation](https://stackoverflow.com/a/61323201/3141792). – BionicCode Oct 13 '20 at 15:01
  • Note that it is just that, a *recommended* approach. I have used a single VM for all pages/views and shared as needed in commercial apps. If your app is small enough, I would (IMHO) advise to stick with one VM. A VM in MVVM is just a separation of concerns from the View to the Business logic, to the database.... – ΩmegaMan Oct 13 '20 at 15:15
  • There is no reason why your view models cannot share data. You would use the main view model that handles the page navigation to provide the shared data to the corresponding page models or bind the relevant controls to a common parent DataContext. There are different design choices available. Having a view model class for each page/view is not a handicap. It just makes navigation very very easy and convenient. – BionicCode Oct 13 '20 at 15:41

2 Answers2

1

If you don't want three different view models, you could add a property to the main view model that decides which view to display:

public class MainViewModel : INotifyPropertyChanged
{
    public string SharedSampleProperty { get; set; } = "Some value that is shared across all views...";

    private View _currentView = View.First;
    public View CurrentView
    {
        get => _currentView;
        set { _currentView = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged; 
    private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

}

public enum View 
{
    First,
    Second,
    Third
}

In the view, you could then use a ContentControl and a Style that sets the ContentTemplate based on the value of this property:

<ContentControl Content="{Binding}">
    <ContentControl.Resources>
        <DataTemplate x:Key="{x:Static local:View.First}">
            <TextBlock>1...</TextBlock>
        </DataTemplate>
        <DataTemplate x:Key="{x:Static local:View.Second}">
            <TextBlock>2...</TextBlock>
        </DataTemplate>
        <DataTemplate x:Key="{x:Static local:View.Third}">
            <TextBlock>3...</TextBlock>
        </DataTemplate>
    </ContentControl.Resources>
    <ContentControl.Style>
        <Style TargetType="ContentControl">
            <Style.Triggers>
                <DataTrigger Binding="{Binding CurrentView}" Value="First">
                    <Setter Property="ContentTemplate" Value="{StaticResource {x:Static local:View.First}}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding CurrentView}" Value="Second">
                    <Setter Property="ContentTemplate" Value="{StaticResource {x:Static local:View.Second}}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding CurrentView}" Value="Third">
                    <Setter Property="ContentTemplate" Value="{StaticResource {x:Static local:View.Third}}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

You may obviously replace the TextBlock elements with UserControls in the DataTemplates.

mm8
  • 163,881
  • 10
  • 57
  • 88
-1

I don't want to make 3 separate view models as view 3 will require the comport selection from View

One way to use a single VM is to create a page where the navigation, such as button clicks actions, is accomplished by just hiding/showing specific controls based on a bound state variable. Meaning the the Visiblity of all controls on the screen is tied to that bound state value; some are visible and some are hidden based on the value found.

If you have an application which is not too large, hiding and showing what looks like to the user a new page view by turning off the old items and turning on the new can be done.

Avoid this design if the app will have more than roughly 25 controls which need such orchestration.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
  • I've recently worked on a web page did this. We started with just two alternative sub views but then requirements changed and... then there were more. It became very clunky. It's ok for very simple apps where you know the requirements aren't going to change. – Andy Oct 13 '20 at 15:53