several small view in one big view mvvm light - c#

I'm new in c# but i try to have several small view that i had to one big view
but my databinding doesn't work.
I have use mvvm light.
I have add a datacontex in each small view.`
it is the code of the mainviewwindows:
<Window xmlns:View1="clr-namespace:BBS.CaseDetails.Operator.View" xmlns:View="clr-namespace:BBS.CaseDetails.Suspect.View" x:Class="BBS.CaseDetails.CaseDetailsWindow"
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:BBS.CaseDetails"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
Title="CaseDetailsWindow" Height="450" Width="800">
<Grid d:DataContext="{Binding Source={StaticResource Locator}}">
<View1:CaseInformationView />
</Grid>
</Window>
the code of a small view :
<UserControl x:Class="BBS.CaseDetails.Operator.View.CaseInformationView"
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:BBS.CaseDetails.Operator.View"
xmlns:resx="clr-namespace:BBS.Resource.Properties;assembly=BBS.Resource"
mc:Ignorable="d"
d:DataContext="{Binding CaseInformationViewModel, Source={StaticResource Locator}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="4*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{x:Static resx:CaseDetails.CaseInformation}" FontWeight="Bold"/>
<Label Grid.Row="1" Grid.Column="0" Content="{x:Static resx:CaseDetails.SteriaFitPlusOperator}"/>
<Label Grid.Row="2" Grid.Column="0" Content="{x:Static resx:CaseDetails.IncidentNumber}"/>
<Label Grid.Row="3" Grid.Column="0" Content="{x:Static resx:CaseDetails.IncidentDate}"/>
<TextBox Grid.Column="1" Grid.Row="1" Text="{Binding SteriaFitPlusOperator}" />
<TextBox Grid.Column="1" Grid.Row="2" Text="{Binding CaseInformationModel.IncidentNumber}" />
<DatePicker Grid.Column="1" Grid.Row="3" Text="{Binding CaseInformationModel.IncidentDate.Date, Mode=OneWay}" />
<Image Grid.Column="2" HorizontalAlignment="Center" Height="auto" Grid.Row="1" Width="100" />
</Grid>
</UserControl>
my viewmodel of my small view :
namespace BBS.CaseDetails.Operator.ViewModel
{
public class CaseInformationViewModel: ViewModelBase
{
private CaseInformationModel _caseInformationModel;
public CaseInformationModel CaseInformationModel
{
get
{
return _caseInformationModel;
}
set
{
Set(() => this.CaseInformationModel, ref _caseInformationModel, value);
}
}
}
}

Your problem might be here:
...<Grid d:DataContext="{Binding Source={StaticResource Locator}}">...
...mc:Ignorable="d"
d:DataContext="{Binding CaseInformationViewModel, Source={StaticResource Locator}}">...
Check this out: Commenting properties in xaml

Related

WPF Navigation Header MVVM

I need tips from you guys, I hope someone can help me.
I want to make a WPF application which has a navigation header.
By navigation header I mean: I want to have a grid on top that contains buttons and when you click on the buttons, the bottom grid should show a completely different view. These views can also contain buttons and when clicking on these buttons only the lower grid should be updated and the top should remain as it is.
Also i want to use MVVM in my application.
below in the code you could better understand what I mean
`
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow"
Height="450"
Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Red"> <!--this should be the header for the application-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="View 1" Margin="4"/>
<Button Grid.Column="1" Content="View 2" Margin="4"/>
</Grid>
<Grid Grid.Row="1" Background="LightBlue">
<Label Content="View 1/ View 2 Content" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</Window>
`
enter image description here
In addition to what Yannick pointed out for the visibility converter, I would do the following.
Create two additional "UserControl" classes (similar xaml markup as your window, but have each user control draw just what IT is to represent. This way, if you need to move the stuff around, you fix that one control. It still resides within your outer main window. For example.
<Window x:Class="NavigationHeader.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:NavigationHeader"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConv" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Red">
<!--this should be the header for the application-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="View 1" Margin="4" Command="{Binding View1Command}"/>
<Button Grid.Column="1" Content="View 2" Margin="4" Command="{Binding View2Command}"/>
</Grid>
<local:MyFirstControl
Grid.Row="1"
Background="LightCoral"
Visibility="{Binding View1Visibility,
Converter={StaticResource BoolToVisConv}}" />
<local:MySecondControl
Grid.Row="1"
Background="LightBlue"
Visibility="{Binding View2Visibility,
Converter={StaticResource BoolToVisConv}}" />
</Grid>
</Window>
<UserControl x:Class="NavigationHeader.MyFirstControl"
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:NavigationHeader"
mc:Ignorable="d" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0"
Content="View 1" FontSize="24"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="my content" Width="100" Margin="10"/>
<Button Grid.Row="2" Grid.Column="2"
Content="Ok" Width="50" />
</Grid>
</UserControl>
<UserControl x:Class="NavigationHeader.MySecondControl"
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:NavigationHeader" >
<StackPanel>
<Label Content="View 1"
FontSize="24"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Text="my content" Width="100" Margin="10"/>
<Button Content="Ok" Width="50" Margin="10"/>
</StackPanel>
</UserControl>
Notice the main window is referencing two more classes you would create as a user control MyFirstControl and MySecondControl. This can help keep your clutter in each respective control vs bloating the main control up and worrying about different formats such as one using a grid, and the other using a stack panel, docking panel, or whatever other type control you want to display.
I've used the community toolkit nuget package for the following. This implements inpc for me and generates some code using attributes.
The basic principle of what follows is called viewmodel first navigation. You should be able to find examples out there on the web explain more fully if you like.
To navigate this switches out a viewmodel instance which is then datatemplated into the view, with the viewmodel as datacontext.
Mainwindow
<Window.Resources>
<DataTemplate DataType="{x:Type local:AlphaViewModel}">
<local:AlphaView/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:BetaViewModel}">
<local:BetaView/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:GammaViewModel}">
<local:GammaView/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Red">
<!--this should be the header for the application-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Command="{Binding NavigateViewModelCommand}"
CommandParameter="{x:Type local:AlphaViewModel}"
Content="Alpha" Grid.Column="0" Margin="4"/>
<Button Command="{Binding NavigateViewModelCommand}"
CommandParameter="{x:Type local:BetaViewModel}"
Content="Beta" Grid.Column="1" Margin="4"/>
</Grid>
<Grid Grid.Row="1" Background="LightBlue">
<ContentPresenter Content="{Binding CurrentViewModel}"/>
</Grid>
</Grid>
Notice how viewmodel types are associated with usercontrols. Where an alphaviewmodel is presented to the view, it will be datatemplates with an alphaview.
Buttons bind to a command and pass a type as a parameter.
The contentpresenter is where that viewmodel will be presented to the view.
That's bound to CurrentViewModel.
MainWindowViewModel
using CommunityToolkit.Mvvm.Input;
namespace WpfVMFirst
{
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
private object? currentViewModel = new GammaViewModel();
[RelayCommand]
private void NavigateViewModel(Type type)
{
var vm = Activator.CreateInstance(type);
CurrentViewModel = null;
CurrentViewModel = vm;
}
}
}
NavigateViewModel will become that NavigateViewModelCommand due to code generation.
It takes the type I mentioned above and is going to generically instantiate a viewmodel from that.
If you wanted to retain state between navigations, you could cache in a dictionary and re-use a viewmodel if it was already there.
Seting the currentviewmodel to null forces re templating. If you navigate to the same viewmodel it will force a new instance of the view usercontrol.
One of my usercontrols has a button of it's own:
Beta:
<Grid Background="Blue">
<TextBlock Text="Beta"/>
<Button HorizontalAlignment="Right"
Content="Gamma"
VerticalAlignment="Top"
Command="{Binding DataContext.NavigateViewModelCommand, RelativeSource={RelativeSource AncestorType=Window}}"
CommandParameter="{x:Type local:GammaViewModel}"/>
</Grid>
</UserControl>
Click that button and the view is navigated to gamma.
That command works because it's using relativesource to look up the visual tree to the window, then use the command out the window's datacontext
Other viewmodels and usercontrols are pretty minimal. I have nothing in the alpha, beta and gamma viewmodels since this is just for demo purposes.
AlphaView
<Grid Background="Red">
<TextBlock Text="Alpha"/>
</Grid>
That wiring up connecting viewmodel to view type can be put into a resource dictionary merged in app.xaml.
In "real" apps the viewmodels are likely to have all sorts of functionality in them and it's usual to dependency inject those. The instantiation would be a bit more sophisticated.
As is reading any data such viewmodels require.
It's common to have an IGetData interface they implement with a GetData asynchronous task so they can be instantiated using a minimal CTOR and the data then retrieved using that abstracted method.
This is a mainstream commercial pattern which I've used and seen used successfully in real world single window apps.
Here is a begining :
Encapsulate each view in a Grid and manage their visibility in the View Model. A simple solution if you are starting with MVVM.
THE MAIN VIEW
<Window x:Class="NavigationHeader.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:NavigationHeader"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConv" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="Red">
<!--this should be the header for the application-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Content="View 1" Margin="4" Command="{Binding View1Command}"/>
<Button Grid.Column="1" Content="View 2" Margin="4" Command="{Binding View2Command}"/>
</Grid>
<Grid Grid.Row="1" Background="LightCoral" Visibility="{Binding View1Visibility, Converter={StaticResource BoolToVisConv}}">
<StackPanel>
<Label Content="View 1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Text="my content" Width="100" Margin="10"/>
<Button Content="Ok" Width="50" Margin="10"/>
</StackPanel>
</Grid>
<Grid Grid.Row="1" Background="LightBlue" Visibility="{Binding View2Visibility, Converter={StaticResource BoolToVisConv}}">
<Label Content="View 2" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</Window>
THE MAIN VM
For the View Model you need to install the Nuget's Package CommunityToolkit.Mvvm as the Main VM needs these namespaces:
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
public class MainVM : ObservableObject
{
private bool myView1Visibility;
private bool myView2Visibility;
public MainVM()
{
myView1Visibility = false;
myView2Visibility = false;
}
public bool View1Visibility
{
get { return myView1Visibility; }
set
{
if (value == myView1Visibility) return;
myView1Visibility = value;
OnPropertyChanged(nameof(View1Visibility));
}
}
public bool View2Visibility
{
get { return myView2Visibility; }
set
{
if (value == myView2Visibility) return;
myView2Visibility = value;
OnPropertyChanged(nameof(View2Visibility));
}
}
RelayCommand myView1Command;
RelayCommand myView2Command;
public RelayCommand View1Command
{
get
{
if (myView1Command == null)
myView1Command = new RelayCommand(View1CommandAction);
return myView1Command;
}
}
public RelayCommand View2Command
{
get
{
if (myView2Command == null)
myView2Command = new RelayCommand(View2CommandAction);
return myView2Command;
}
}
private void View1CommandAction()
{
View2Visibility = false;
View1Visibility = true;
}
private void View2CommandAction()
{
View1Visibility = false;
View2Visibility = true;
}
}
Instanciate the VM in the Main's View code behind:
public partial class MainWindow : Window
{
private readonly MainVM myMainVM;
public MainWindow()
{
InitializeComponent();
myMainVM = new MainVM();
DataContext = myMainVM;
}
}

How can I improve the layout for this Input Screen?

Right now I have this XAML layout for my WPF application:
<Window x:Class="Cabrillo_Editor.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:Cabrillo_Editor"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_Exit" Click="ExitApplication"/>
<MenuItem Header="_New"/>
<MenuItem Header="_Save" Click="SaveCabrilloFile"/>
</Menu>
<StackPanel>
<GroupBox Height="Auto" Header="General">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="185"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="10"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0" Margin="0,0,0,5">
<Label>My Call:</Label>
<TextBox Width="120" Name="CallsignTextBox" HorizontalAlignment="Right" CharacterCasing="Upper"/>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="0">
<Label>My Grid:</Label>
<TextBox Width="120" Name="GridTextBox" HorizontalAlignment="Right"/>
</DockPanel>
<DockPanel Grid.Row="0" Grid.Column="2">
<Label>Contest:</Label>
<ComboBox Width="150" Name="ContestComboBox" Margin="5,0,0,0"/>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="2">
<Label>Assisted:</Label>
<CheckBox VerticalAlignment="Center" Name="AssistedCheckBox" Margin="5,0,0,0"/>
</DockPanel>
<DockPanel Grid.Row="0" Grid.Column="4">
<Label>Band:</Label>
<ComboBox Width="150" Name="BandComboBox" Margin="5,0,0,0"/>
</DockPanel>
</Grid>
</GroupBox>
</StackPanel>
</DockPanel>
</Window>
And it looks like this:
Why, for example, is are the ComboBoxes so streched if I set the row height to "Auto" (same for the TextBoxes)?
Is there a better way to make consistent horizontal space between the columns?
This is occurring because those controls will stretch to fill their parent container by default. To override this, simply set the VeriticalAlignment property and Height property (if needed).
<ComboBox Width="150" Name="ContestComboBox" Margin="5,0,0,0" VerticalAlignment=Top/>

Keep getting error on x:DataType

I have been getting "the attribute 'DataType' from the XAML namespace is not Defined.
[the attribute 'DataType' from the XAML namespace is not Defined][1]
<Page
x:Class="VisitorApp.sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:VisitorApp"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:VisitorApp.Common"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="GuestListDataTemplate" x:DataType="data:GuestGlobal">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left">
<StackPanel Margin="20,20,0,0">
<TextBlock Text="{Binding GuestName}" HorizontalAlignment="Left" FontSize="16" />
<TextBlock Text="{x:Bind HostName}" HorizontalAlignment="Left" FontSize="10" />
</StackPanel>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Margin="0,20,20,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="1"
Name="ResultTextBlock"
FontSize="24"
Foreground="Red"
FontWeight="Bold"
Margin="20,20,0,0" />
<GridView ItemsSource ="{x:Bind Guests}"
ItemClick="ListView_ItemClick"
IsItemClickEnabled="True"
ItemTemplate="{StaticResource GuestListDataTemplate}">
</GridView>
</Grid>
</Page>
strong text
This helped me https://msdn.microsoft.com/en-us/magazine/mt683802.
Thanks All for your contribution.

WPF - Binding in ListView's ItemsSource cannot be a List

I have a WPF application that I try to code using MVVM.The goal is to make something like a notification center, that lists different types of data.
To do this, I want to fill a ListView on the main page with different ViewModels. One ViewModel for each type of data.
The problem is:
When I put a (not a list) ViewModel in my ListView, it works fine. But if I put a list in my ListView, the program crashes on startup. I need the ListView to take a list (ObservableCollection perhaps) of mixed ViewModels.
I receive the error “Items collection must be empty before using ItemsSource.” at a seemingly random location in my code. Completely removing the code where the exception appears just causes it to show somewhere else.
I have the following:
C#:
public class MainViewModel : ObservableObject
{
private List<IPageViewModel> _items;
public MainViewModel()
{
_items = new List<IPageViewModel>
{
new StatusViewModel(),
new SettingsViewModel(),
new OverviewViewModel()
};
}
public List<IPageViewModel> Items => _items ?? (_items = newList<IPageViewModel>());
}
XAML:
<Window x:Class="InfoCenter.Views.Main.MainView"
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:test="clr-namespace:InfoCenter.Views.Test"
xmlns:main="clr-namespace:InfoCenter.Views.Main"
xmlns:views="clr-namespace:InfoCenter.Views"
xmlns:settings="clr-namespace:InfoCenter.Views.Settings"
xmlns:status="clr-namespace:InfoCenter.Views.Status"
xmlns:overview="clr-namespace:InfoCenter.Views.Overview"
mc:Ignorable="d"
Title="MainView" Height="450" Width="500"
MaxWidth="1920"
WindowStyle="None" Loaded="MainViewLoaded"
SizeChanged="MainViewSizeChanged"
PreviewKeyDown="OnPreviewKeyDown"
GotFocus="OnGotFocus"
Closing="OnClosing"
ResizeMode="NoResize"
d:DataContext="{d:DesignInstance main:MainViewModel}">
<Window.Resources>
<DataTemplate DataType="{x:Type overview:OverviewViewModel}">
<overview:OverviewView />
</DataTemplate>
<DataTemplate DataType="{x:Type status:StatusViewModel}">
<status:StatusView />
</DataTemplate>
<DataTemplate DataType="{x:Type settings:SettingsViewModel}">
<settings:SettingsView />
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="24" />
<RowDefinition Height="*" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="24" />
</Grid.ColumnDefinitions>
<Button Command="{Binding ButtonClickCommand}" CommandParameter="Minimize" Grid.Row="0" Grid.Column="1">
<Image Source="/Resources/arrow-down-1.png"></Image>
</Button>
<ListView ItemsSource="{Binding Items}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ScrollViewer.HorizontalScrollBarVisibility="Hidden" PreviewMouseWheel="OnPreviewMouseWheel" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{Binding Path=Items.Header}" />
<ContentControl Grid.Row="1" Grid.Column="0" Content="{Binding}" />
</Grid>
</ListView>
<StatusBar FlowDirection="RightToLeft" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
<Image Width="24" Height="24" Source="{Binding ConnectionIcon}" />
</StatusBar>
</Grid>
</Window>
I really hope that you can help. I have been trying to fix it the whole day!
You have put one thing in the ListView contents here: A Grid control. Don't do that. There are two ways to populate a ListView in XAML: Bind ItemsSource or put items inside the content of the ListView element. You can't do both, and you did both, so it threw an exception.
Here's the ItemsSource version.
<ListView
ItemsSource="{Binding Items}"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
PreviewMouseWheel="OnPreviewMouseWheel"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
/>
I'm not certain what that Grid is for; you may have to find somewhere else to put it.
But my guess is that want it to be used to display the items. We can do that by making it into an ItemTemplate. I'm a little confused by it, though: What is Items.Header? Items is a List, which has no Header property. Does IPageViewModel have a Header property? For the moment I'll assume that's the case; let me know if I'm mistaken.
<ListView
ItemsSource="{Binding Items}"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
PreviewMouseWheel="OnPreviewMouseWheel"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
Content="{Binding Header}"
/>
<ContentControl
Grid.Row="1"
Grid.Column="0"
Content="{Binding}"
/>
</Grid>
<DataTemplate>
</ListView.ItemTemplate>
</ListView>
Based on the error I would recommend to change your XAML as was mentioned earlier in "Items collection must be empty before using ItemsSource."
Try this XAML:
<ListView ItemsSource="{Binding Items}" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" ScrollViewer.HorizontalScrollBarVisibility="Hidden" PreviewMouseWheel="OnPreviewMouseWheel" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.View>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="{Binding Path=Items.Header}" />
<ContentControl Grid.Row="1" Grid.Column="0" Content="{Binding}" />
</Grid>
</ListView.View>
</ListView>
Hope it will be helpful!

How do you load a frame into the HamburgerMenu?

I'm trying to create a simple app using the MahApps.Metro HamburgerMenu control. So far I've set up my Main Window as described in the examples:
<Controls:MetroWindow x:Class="HamburgerDemo.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:HamburgerDemo"
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Controls:MetroWindow.Resources>
<DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type Controls:HamburgerMenuGlyphItem}">
<Grid Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Glyph}" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}" />
</Grid>
</DataTemplate>
</Controls:MetroWindow.Resources>
<Grid>
<Controls:HamburgerMenu ItemTemplate="{StaticResource HamburgerMenuItem}">
<Controls:HamburgerMenu.ItemsSource>
<Controls:HamburgerMenuItemCollection>
<Controls:HamburgerMenuGlyphItem Glyph="1" Label="Page 1"/>
<Controls:HamburgerMenuGlyphItem Glyph="2" Label="Page 2"/>
</Controls:HamburgerMenuItemCollection>
</Controls:HamburgerMenu.ItemsSource>
</Controls:HamburgerMenu>
</Grid>
</Controls:MetroWindow>
I'd like each menu button to link to a specific Page which will be loaded into a Frame when clicked, however I can't figure out how to do this.
I noticed that the HamburgerMenuGlyphItem does have a property TargetPageType, and I imagine that this might be the key but I have no idea how to use this.
If there are better methods to loading content using this control or different design patterns I would love to hear them.
I've done it like this, in code-behind, if I understood you right. If it's not what you meant to just tell me!
MainWindow.xaml
<Window x:Class="Stackoverflow.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:Stackoverflow"
mc:Ignorable="d"
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type Controls:HamburgerMenuGlyphItem}">
<Grid Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Glyph}" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}" />
<Button Grid.Column="2" Click="Button_Click" Tag="{Binding Tag}">Go</Button>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Controls:HamburgerMenu ItemTemplate="{StaticResource HamburgerMenuItem}">
<Controls:HamburgerMenu.ItemsSource>
<Controls:HamburgerMenuItemCollection>
<Controls:HamburgerMenuGlyphItem Glyph="1" Label="Page 1" Tag="Page1.xaml"/>
<Controls:HamburgerMenuGlyphItem Glyph="2" Label="Page 2" Tag="Page2.xaml"/>
</Controls:HamburgerMenuItemCollection>
</Controls:HamburgerMenu.ItemsSource>
</Controls:HamburgerMenu>
<Frame x:Name="_TheFrame" Grid.Column="2"/>
</Grid>
</Window>
MainWindow.xaml.cs (code-behind)
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
string targetView = ((Button)sender).Tag.ToString();
_TheFrame.Source = new Uri(targetView, UriKind.Relative);
}
}
Page1.xaml
<Page x:Class="Stackoverflow.Page1"
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:Stackoverflow"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page1">
<Grid>
<TextBlock Text="PAGE 1" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
Page2.xaml
<Page x:Class="Stackoverflow.Page2"
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:Stackoverflow"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Title="Page2">
<Grid>
<TextBlock Text="PAGE 2" FontSize="24" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
I answer in another one to make it easier to read.
Another approach would be using MVVM. For this I've come up with this. If it works for you and you'd like some explanations tell me. I'm kind of lazy now, so I've also used the Prism framework to take the DelegateCommand and the PropertyChanged base object.
MainWindow.xaml
<Controls:MetroWindow x:Class="Stackoverflow.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:Stackoverflow"
mc:Ignorable="d"
BorderThickness="2"
BorderBrush="DarkGray"
xmlns:Controls="http://metro.mahapps.com/winfx/xaml/controls"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type Controls:HamburgerMenuGlyphItem}">
<Grid Height="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" />
<ColumnDefinition />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Glyph}" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}" />
<Button Grid.Column="2"
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType=Controls:MetroWindow}}"
CommandParameter="{Binding Tag}">Go</Button>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Controls:HamburgerMenu ItemTemplate="{StaticResource HamburgerMenuItem}">
<Controls:HamburgerMenu.ItemsSource>
<Controls:HamburgerMenuItemCollection>
<Controls:HamburgerMenuGlyphItem Glyph="1" Label="Page 1" Tag="Page1.xaml"/>
<Controls:HamburgerMenuGlyphItem Glyph="2" Label="Page 2" Tag="Page2.xaml"/>
</Controls:HamburgerMenuItemCollection>
</Controls:HamburgerMenu.ItemsSource>
</Controls:HamburgerMenu>
<Frame Grid.Column="2" Source="{Binding FrameSource}"/>
</Grid>
</Controls:MetroWindow>
MainWindowViewModel.cs
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Stackoverflow
{
public class MainWindowViewModel : BindableBase
{
private string _FrameSource;
public MainWindowViewModel()
{
NavigateCommand = new DelegateCommand<string>(OnNavigateCommandExecute);
}
public string FrameSource
{
get { return _FrameSource; }
set { SetProperty(ref _FrameSource, value); }
}
public ICommand NavigateCommand { get; private set; }
private void OnNavigateCommandExecute(string frameSource)
{
FrameSource = frameSource;
}
}
}

Categories

Resources