Click here to Skip to main content
15,885,278 members
Please Sign up or sign in to vote.
3.00/5 (1 vote)
See more:
I need a help in my project.Could not get any proper solution as I want. I explain you my scenario here.

I am working on WPF project with MVVM pattern, where I have Main Windows screen, on load of Main Windows screen it should be load blank, and only one label is there. On Click of that Label I have to open new form that is Widget Screen and Widget Screen have multiple buttons(in number of 12) with Images.

Now, on click of any button, I want that particular button name or parameter, so accordingly that name or parameter I have to find User Controls from views and render User Controls on Main Windows screen with collection of 20 records. If again I click on Main Windows label and select another button from widget screen then it should load another selected User Controls will be bind ob Main Windows screen.

So, in short as per selected design from widget screen all records should be show in Main Windows using User Controls.
This is my Main Windows screen code, currently it is binding static User Control that I mentioned directly into controls from that 12 user controls one of them is UserControlColumn1XL, How it should be dynamically changed with whole scenario?

What I have tried:

C#
<Window x:Class="TechMedic.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:uc ="clr-namespace:TechMedic.View.UserControls"
    xmlns:local="clr-namespace:TechMedic"
    xmlns:controls="clr-namespace:TechMedic.View.UserControls"
    xmlns:data="clr-namespace:TechMedic.Data"
    mc:Ignorable="d"
    WindowStartupLocation="CenterScreen" Background="Black"
    Title="Main Window}" WindowState="Maximized" MinWidth="750" >

<StackPanel>    
    <StackPanel Grid.Column="0" Background="Black" VerticalAlignment="Top" >
        <Border BorderThickness="1" Background="Black" BorderBrush="White">
            <Button Style="{StaticResource TransparentStyle}" Command="{Binding OnClickNormalCommand}" CommandParameter="Normal" Foreground="LightSkyBlue" HorizontalAlignment="Center" FontSize="12pt" >
                <TextBlock Text="{Binding NormalCollection.Count, StringFormat='Normal: {0}'}" Padding="0,5, 0, 5" Foreground="{Binding IsNormalColor}" />
            </Button>
            </Border>
            <Border Name="UserControl1BorderNormal" BorderBrush="White" BorderThickness="0" >
                <ItemsControl ItemsSource="{Binding NormalCollection}" Name="ListNormal" Margin="4,0" >
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel HorizontalAlignment="Left" />
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" />
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Border>
        </StackPanel>
    </StackPanel>
</Window>
Posted
Updated 8-Aug-18 17:08pm
Comments
Dirk Bahle 3-Aug-18 3:31am    
Not sure what your question is - you are showing only controls but no model or viewmodel. To hard to read your mind :-(
Rahul96553 3-Aug-18 6:27am    
That is too lengthy code to place here. so explain you in easiest way.

Actually ,The Project includes a MainWindow which has a Label. I want to open a Widget form with Multiple Image Button on Label's Click command. Each Image button will return List of Parameters on MainWindowViewModel. The list will bind with UserControl on MainWindow Screen. If I repeat the Same thing by choosing different Image Button. The MainWindowViewModel will Replace with new List. But in this case I want change UserControl for Example in this line : <controls:UserControlColumn1XL HorizontalAlignment="Left" Margin="2" /> . Here, UserControlColumn1XL is Static for now. But I want to change it dynamically on Selected Widget Button. I am not getting how to made it possible. So I am waiting for your valuable Suggestions.
Nathan Minier 3-Aug-18 8:12am    
Two things:
Is the selected control represented by a property in the ViewModel.

Is the class representing the selected control polymorphic, or does it only vary based on specific properties?

There are multiple questions above. To keep it simple, I will try and answer the part about navigation in the MainWindow using the MVVM Design Pattern.

I'm sure that there are a lot of people have different ideas on what is the best solution and there are many libraries that implement a variety of solutions based on the MVVM Design Pattern using Dependency Injection (DI) and Inversion of Control (IOC).

For solutions that use navigation, WPF has the Frame[^] control and the Page[^] view.

If you are looking for something more lightweight, you can use ContentPresenter[^] control to host UserControl views. This is the solution that I'll use below.

Because we are using the MVVM Design Pattern, we cannot directly reference controls from the ViewModel. So we're going to use ViewModel navigation and rely on WPF's Data Binding and Implicit Templates do load the UserControl views.

First, we need to add some core mechanisms for DataBinding for properties and Commanding for the Button events. These are core to the MVVM Design Pattern.

1. INotiftyPropertyChanged
C#
public abstract class ObservableBase : INotifyPropertyChanged
{
    public void Set<TValue>(ref TValue field, 
                            TValue newValue, 
                            [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<TValue>.Default.Equals(field, default(TValue))
            || !field.Equals(newValue))
        {
            field = newValue;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;
}

2. ViewModelBase
C#
public abstract class ViewModelBase : ObservableBase
{
    public bool IsInDesignMode()
    {
        return Application.Current.MainWindow == null
            ? true
            : DesignerProperties.GetIsInDesignMode(Application.Current.MainWindow);
    }
}

3. ICommand
C#
public class RelayCommand<T> : ICommand
{
    private readonly Action<T> execute;
    private readonly Predicate<T> canExecute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        this.execute = execute ?? throw new ArgumentNullException("execute");
        this.canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
        => canExecute == null || canExecute((T)parameter);

    public event EventHandler CanExecuteChanged
    {
        add => CommandManager.RequerySuggested += value;
        remove => CommandManager.RequerySuggested -= value;
    }

    public void Execute(object parameter)
        => execute(parameter == null
            ? default(T)
            : (T)Convert.ChangeType(parameter, typeof(T)));
}

Next, we need to set up our pages. There are two parts: ViewModel and the View (UserControl). IT is always good form to keep the ViewModel and the View with the same prefix.

4. [Page x]ViewModels
C#
public interface IPage
{
    string Title { get; }
}

public abstract class PqgeViewModelBase : ViewModelBase, IPage
{
    public PqgeViewModelBase(string title)
    {
        Title = title;
        if (!IsInDesignMode())
        {
            // runtime only code here...
            Title += " is alive!";
        }
    }

    public string Title { get; }
}

public class Page1ViewModel : PqgeViewModelBase
{
    // Add page specific code here....
    public Page1ViewModel() : base(nameof(Page1ViewModel))
    {
    }
}

public class Page2ViewModel : PqgeViewModelBase
{
    // Add page specific code here....
    public Page2ViewModel() : base(nameof(Page2ViewModel))
    {
    }
}

5. [Page x]Views
XML
<UserControl x:Class="WpfCPNav.Page1View"
             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" 
             mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
    <Viewbox>
        <TextBlock Text="{Binding Title}"/>
    </Viewbox>
</UserControl>

XML
<UserControl x:Class="WpfCPNav.Page2View"
             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" 
             mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800">
    <Viewbox>
        <TextBlock Text="{Binding Title}"/>
    </Viewbox>
</UserControl>

Now that we have the pages, we are ready to host them in the MainWindow of the app. The MainViewModel will handle the switching of the views.

6. MainViewModel
C#
public class MainViewModel : ViewModelBase
{
    private IPage content;
    public IPage Content { get => content; private set => Set(ref content, value); }

    public RelayCommand<int> NavigateCommand => new RelayCommand<int>(Navigate);

    private readonly Dictionary<int, Lazy<IPage>> pages
        = new Dictionary<int, Lazy<IPage>>
    {
        [1] = new Lazy<IPage>(() => new Page1ViewModel()),
        [2] = new Lazy<IPage>(() => new Page2ViewModel())
    };

    public MainViewModel() => Navigate(1);
    private void Navigate(int value) => Content = pages[value].Value;
}

Lastly, we need to do the User interface. As we are using Implicit templates, we define them in the MainWindow.

7. MainWindow
XML
<Window x:Class="WpfCPNav.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:v="clr-namespace:WpfCPNav"
        xmlns:vm="clr-namespace:WpfCPNav"

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" WindowStartupLocation="CenterScreen"
        Title="MainWindow" Height="450" Width="800">

    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>

    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:Page1ViewModel}">
            <v:Page1View />
        </DataTemplate>

        <DataTemplate DataType="{x:Type vm:Page2ViewModel}">
            <v:Page2View />
        </DataTemplate>

        <Style TargetType="Button">
            <Setter Property="Margin" Value="10"/>
            <Setter Property="Padding" Value="10 5"/>
        </Style>

        <Style TargetType="StackPanel">
            <Setter Property="Orientation" Value="Horizontal"/>
            <Setter Property="HorizontalAlignment" Value="Center"/>
        </Style>

    </Window.Resources>


    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
        </Grid.RowDefinitions>

        <StackPanel>
            <Button Content="PAGE 1" Command="{Binding NavigateCommand}" CommandParameter="1" />
            <Button Content="PAGE 2" Command="{Binding NavigateCommand}" CommandParameter="2" />
        </StackPanel>

        <ContentPresenter Content="{Binding Content}" Grid.Row="1"/>
    </Grid>

</Window>

How it works:
When a Button is clicked, the MainViewModel will pass the selected MainViewModel to the ContentPresenter control, the ContentPresenter control will them look for a template and loads it diplaying the associated UserControl and the ViewModel is is automatically is automatically set to the UserControl DataContext property and DataBinding is applied.
 
Share this answer
 
Your "view" is buried in a data template of an items control stuck in a stack panel with a bunch of second-fiddle controls.

You need to rethink your "view architecture" so that it is more "manageable" in terms of dynamic loading.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900