I'm making a windows universal 10 application with MVVM light.
But now I will, if I click on an item on the ShowWeatherPage be navigate to ShowWeatherDetailPage for more details about the clicked item. But I don't know how I can do this. Can you help me to do this?
Below you can find my code. I use IocContainers and for each page a viewmodel and only command bindings.
IocContainerpublic class IocContainer
{
static IocContainer()
{
SimpleIoc.Default.Register<ApplicationViewModel>(false);
SimpleIoc.Default.Register<ShowWeatherViewModel>(false);
SimpleIoc.Default.Register<ShowWeatherPage>(false);
SimpleIoc.Default.Register<ShowWeatherDetailPage>(false);
SimpleIoc.Default.Register<ShowWeatherDetailViewModel>(false);
}
public static ShowWeatherPage ShowWeatherPage
{
get { return SimpleIoc.Default.GetInstance<ShowWeatherPage>(); }
}
public static ShowWeatherViewModel ShowWeatherViewModel
{
get { return SimpleIoc.Default.GetInstance<ShowWeatherViewModel>(); }
}
public static ApplicationViewModel ApplicationViewModel
{
get { return SimpleIoc.Default.GetInstance<ApplicationViewModel>(); }
}
public static ShowWeatherDetailPage ShowWeatherDetailPage
{
get { return SimpleIoc.Default.GetInstance<ShowWeatherDetailPage>(); }
}
public static ShowWeatherDetailViewModel ShowWeatherDetailViewModel
{
get { return SimpleIoc.Default.GetInstance<ShowWeatherDetailViewModel>(); }
}
}
View modelsApplicationViewModelpublic class ApplicationViewModel: ViewModelBaseClass
{
private Page _currentPage = IocContainer.ShowWeatherPage;
public Page CurrentPage
{
get
{
return _currentPage;
}
set
{
if (_currentPage != value)
{
_currentPage = value;
OnPropertyChanged();
}
}
}
public void Navigate(Page page, object attribs)
{
CurrentPage = page;
}
}
ShowWeatherViewModelpublic class ShowWeatherViewModel: ViewModelBaseClass
{
#region variables
private Item _selectedVillage = null;
#endregion variables
#region properties
public Item SelectedVillage
{
get
{
return _selectedVillage;
}
set
{
if (_selectedVillage != value)
{
_selectedVillage = value;
ShowDetailPage();
}
}
}
#endregion properties
#region constructor
public ShowWeatherViewModel()
{ }
#endregion constructor
#region methodes
private void ShowDetailPage()
{
ApplicationViewModel appVm = new ApplicationViewModel();
appVm.Navigate(IocContainer.ShowWeatherPage, SelectedVillage);
}
#endregion methodes
}
ShowWeatherDetailViewModelpublic class ShowWeatherDetailViewModel: ViewModelBaseClass
{ }
ViewModelBaseClasspublic class ViewModelBaseClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
PagesMainPage<Page
x:Class="BALaboVoorbeeld.UWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BALaboVoorbeeld.UWP"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Source={StaticResource ioc}, Path=ApplicationViewModel}"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page Content="{Binding CurrentPage, Mode=TwoWay}" />
</Grid>
</Page>
ShowWeatherPage<Page
x:Class="BALaboVoorbeeld.UWP.Pages.ShowWeatherPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BALaboVoorbeeld.UWP.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
DataContext="{Binding Source={StaticResource ioc}, Path=ShowWeatherViewModel}"
mc:Ignorable="d" Width="450">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="240" />
<ColumnDefinition Width="60" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1" />
<RowDefinition Height="40" />
<RowDefinition Height="1*" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<TextBlock Text="Village:"
HorizontalAlignment="Right" Margin="4" VerticalAlignment="Center"
Grid.Row="1" Grid.Column="0" />
<Button HorizontalAlignment="Stretch" Margin="4" VerticalAlignment="Center"
Grid.Row="1" Grid.Column="2" Command="{Binding ShowWeahter}" >
<SymbolIcon Symbol="Find" />
</Button>
<ListBox Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"
ItemContainerStyle="{StaticResource lstidflt}"
SelectedItem="{Binding SelectedVillage, Mode=TwoWay}"
ItemTemplate="{StaticResource weatheritemdt}"
ItemsSource="{Binding VillageList}" />
</Grid>
</Page>
ShowWeatherDetailPage<Page
x:Class="BALaboVoorbeeld.UWP.Pages.ShowWeatherDetailPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:BALaboVoorbeeld.UWP.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<TextBlock Text="Yes we did it ☻"/>
</Grid>
</Page>
You can use MVVM Light's navigation service to navigate to another view.
http://www.mvvmlight.net/doc/nav1.cshtml
https://marcominerva.wordpress.com/2014/10/10/navigationservice-in-mvvm-light-v5/
Related
Greetings StackOverflow!
I'm trying to create a simple launcher that asks a set of questions. Six pages/views(that i'm yet to implement) - one question each. The idea is that user is able to switch between pages using two big side-buttons or to pick a specific page by pressing a corresponding radio button.
Template binding works, button calls correct method but UI does not reflect changes of ObservableCollection. I'm new to .NET so i'm certainly missing something. Please help. I've searched and applied all sorts of solutions proposed in similiar threads - nothing works.
Function in question is PageBarViewModel.NextPage() - it is called and excuted properly so it must be the UI that does not reflect changes. Only if called from constructor do the changes become visible.
Launcher.xaml
<Window x:Class="automeas_ui.Launcher"
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:automeas_ui"
xmlns:view="clr-namespace:automeas_ui.MWM.View"
xmlns:viewm="clr-namespace:automeas_ui.MWM.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="600" Width="920"
WindowStyle="None"
ResizeMode="NoResize"
WindowStartupLocation="CenterScreen"
Background="Transparent"
AllowsTransparency="True">
<Window.DataContext>
<viewm:PageBarViewModel/>
</Window.DataContext>
<Border Background="#272537"
CornerRadius="10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.25*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="0.25*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.45*"/>
<RowDefinition Height="3*"/>
<RowDefinition Height="0.25*"/>
</Grid.RowDefinitions>
<Button Grid.Row="0"
Grid.Column="2"
Width="50"
Height="50"
Content="❌"
FontSize="32"
Style="{StaticResource FOC_SquareButtonTheme}"
Click="Button_Clicked"/>
<Button Grid.Row="1"
Grid.Column="2"
Content="▷"
Foreground="#323232"
Background="Transparent"
BorderThickness="0"
FontSize="72" Command="{Binding Path=NpCommad}"/>
<Button Grid.Row="1"
Grid.Column="0"
Content="◁"
Foreground="#323232"
Background="Transparent"
BorderThickness="0"
FontSize="72"/>
<Label Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="44"
FontFamily="Verdana"
Content="Some text"
Foreground="White"/>
<view:PageBarView Grid.Column="1"
Grid.Row="2"/>
</Grid>
</Border>
How it looks like - Intended layout
PageBarViewModel.xaml
<UserControl x:Class="automeas_ui.MWM.View.PageBarView"
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:automeas_ui.MWM.View"
xmlns:model="clr-namespace:automeas_ui.MWM.ViewModel"
xmlns:viewm="clr-namespace:automeas_ui.MWM.ViewModel"
d:DataContext="{d:DesignInstance Type=model:PageBarViewModel}"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="800">
<UserControl.DataContext>
<viewm:PageBarViewModel/>
</UserControl.DataContext>
<Border CornerRadius="20"
Background="#323232"
Margin="150,0,150,0">
<ItemsControl ItemsSource="{Binding Pages}"
Grid.Column="1"
Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"
Margin="0,0,10,0"
HorizontalAlignment="Center"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Viewbox Height="40"
Margin="10,0,0,0">
<RadioButton
GroupName="pgs"
IsChecked="{Binding IsFocused, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
</Viewbox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Border>
</UserControl>
PageBarViewModel.xaml.cs
namespace automeas_ui.MWM.ViewModel
{
public class ViewedPage: INotifyPropertyChanged
{
private bool m_IsFocused;
public bool IsFocused
{
get { return m_IsFocused; }
set { m_IsFocused = value;
OnPropertyChanged("IsFocused");
}
}
public ViewedPage(bool focused = false)
{
IsFocused = focused;
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler? PropertyChanged;
}
public class PageBarViewModel
{
// internal interface
const int NumberOfPages = 5;
// eof
private TrulyObservableCollection<ViewedPage> _Pages;
public TrulyObservableCollection<ViewedPage> Pages
{
get { return _Pages; }
set
{
if (_Pages == value) return;
_Pages = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public PageBarViewModel()
{
Pages = new TrulyObservableCollection<ViewedPage>();
Pages.Add(new ViewedPage(true));
for (int i = 1; i < NumberOfPages; i++)
{
Pages.Add(new ViewedPage());
}
}
private ICommand? _npCommand;
public ICommand NpCommad
{
get
{
if (_npCommand == null)
{
_npCommand = new JSRelayCommand(
param => this.NextPage(),
param => this.CanSave()
);
}
return _npCommand;
}
}
private bool CanSave()
{
// Verify command can be executed here
return true;
}
private void NextPage()
{
int pgn = 1;
foreach (var item in Pages)
{
if (item.IsFocused)
{
item.IsFocused = false;
break;
}
pgn++;
}
Pages[pgn].IsFocused = true;
CollectionViewSource.GetDefaultView(Pages).Refresh();
}
}
}
----------FIXES THAT HAVE BEEN TRIED----------
Use ObservableCollection and implement INotifyChanged for T
Try to force update by adding/removing items from collection
CollectionViewSource.GetDefaultView(Pages).Refresh();
Implement TrulyObservableCollection
Grouped RadioButtons are janky - ungroup them
Any fix is deeply appreciated
you have two instances of PageBarViewModel:
in Window
<Window.DataContext>
<viewm:PageBarViewModel/>
</Window.DataContext>
in UserControl
<UserControl.DataContext>
<viewm:PageBarViewModel/>
</UserControl.DataContext>
remove UserControl.DataContext
Maybe I am missing something, but in NextPage() it looks like
Pages[pgn] = true;
should be
Pages[pgn].IsFocused = true;
When the View is initialized, their standard value "VM", definied in the ViewModel, is updated trough the Model and updated in the View. However, when the ICommand NavigationCommand is triggered, the code in the OnNavigationCommand method executes, and even the OnPropertyChanged (INotifyPropertyChanged) method is called in the Model. However, the textboxes in the UI still remains the same value: "VM". I have tried a lot, but can't seem to find the problem. Hope you can help.
View
<vw:View xmlns:cc="clr-namespace:HMI.CustomControl" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
x:Class="HMI.ZoneFView"
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:vw="http://inosoft.com/visiwin7"
xmlns:main="clr-namespace:HMI.Views.MainRegion"
xmlns:local="clr-namespace:HMI" xmlns:dialogregion="clr-namespace:HMI.Views.DialogRegion"
mc:Ignorable="d"
DataContext="{vw:AdapterBinding ViewModel}">
<Viewbox>
<Grid x:Name="LayoutRoot" Width="140" Height="220.5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3"/>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="3"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="5" />
<RowDefinition Height="5*" />
<RowDefinition Height="5" />
</Grid.RowDefinitions>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding theModel.VisibilityFFUView}" Margin="57,16,7,62" />
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding theModel.VisibilityFFUView}" Margin="57,48,7,30" />
</Grid>
</Viewbox>
</vw:View>
View.cs
using HMI.Views.MainRegion;
using VisiWin.ApplicationFramework;
namespace HMI
{
[ExportView("ZoneFView")]
public partial class ZoneFView : VisiWin.Controls.View
{
public ZoneFView()
{
this.InitializeComponent();
}
}
}
ViewModel
using System;
using System.ComponentModel.Composition;
using System.Windows.Input;
using VisiWin.ApplicationFramework;
using VisiWin.Commands;
namespace HMI.Views.MainRegion
{
[ExportAdapter("ViewModel")]
[PartCreationPolicy(CreationPolicy.NonShared)]
class ViewModel : AdapterBase
{
#region Constructor
public Model Model { get; set; }
public ViewModel()
{
// If the VisiWin system is not in the runtime, the VW7 functionalities cannot be accessed
if (ApplicationService.IsInDesignMode)
{
return;
}
// Create the Action Commands
this.NavigationCommand = new ActionCommand(OnNavigationCommand);
Model = new Model()
{
VisibilityFFUView = "VM"
};
}
#endregion
#region CordisAdapterBase implementation
// Called when the view on which this adapter is located as DataContext is loaded
public override void OnViewAttached(IView view)
{
base.OnViewAttached(view);
}
public override void OnViewDetached(IView view)
{
base.OnViewDetached(view);
}
#endregion
#region NavigationCommand - Command from the view into the ViewModel
public ICommand NavigationCommand { get; set; }
// NavigationCommand event call
// Will be called if one of the buttons in the "AppbarView" view is clicked.
// The command of the model is linked to the button via the Command property.
private void OnNavigationCommand(object commandParameter)
{
if (commandParameter != null)
{
if (ApplicationService.IsInDesignMode) return;
string strParameter = commandParameter.ToString();
switch (strParameter)
{
case "FFU":
Model.VisibilityFFUView = "Turn on";
break;
case "FFUswitch":
Model.VisibilityFFUView = "Turn off";
break;
default:
break;
}
}
else
{
throw new ArgumentNullException(nameof(commandParameter));
}
}
#endregion
}
}
Model
using System.ComponentModel.Composition;
using VisiWin.ApplicationFramework;
namespace HMI.Views.MainRegion
{
[ExportAdapter("Model")]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class Model : ObserverableObject
{
private string _visibilityFFUView;
public string VisibilityFFUView
{
get { return _visibilityFFUView; }
set
{
_visibilityFFUView = value;
OnPropertyChanged();
}
}
}
}
ObserverableObject
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace HMI.Views.MainRegion
{
public class ObserverableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
UPDATE
I have added the NavigationView, and shifted both the DataContext of NavigationView and ZoneFView to XAML to reduce some code..
NavigationView
<vw:View xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
x:Class="HMI.Views.Common.AppbarView"
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:vw="http://inosoft.com/visiwin7" xmlns:local="clr-namespace:HMI"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="638"
DataContext="{vw:AdapterBinding ViewModel}">
<Grid x:Name="LayoutRoot" Background="{DynamicResource AppbarBackgroundBrush}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="8" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Fill="{DynamicResource AccentBrush}" StrokeThickness="0" />
<StackPanel Grid.Column="0" Margin="10,10,0,0" VerticalAlignment="Top">
<vw:NavigationRadioButton HorizontalAlignment="Stretch" RegionName="MainRegion" ViewName="HomeView" IsChecked="True" Style="{DynamicResource AppbarNavigationRadioButtonStyle}" VerticalAlignment="Top" Height="52" Symbol="{DynamicResource appbar.tiles.nine}" LocalizableText="#Appbar.Dashboard" Margin="0,0,0,0" SymbolHorizontalAlignment="Left" />
<vw:NavigationRadioButton HorizontalAlignment="Stretch" RegionName="MainRegion" ViewName="FFUnitsView" IsChecked="false" Style="{DynamicResource AppbarNavigationRadioButtonStyle}" VerticalAlignment="Top" Height="52" Symbol="{DynamicResource appbar.interface.button}" LocalizableText="#Appbar.FFUnits" Margin="0,10,0,0" BorderThickness="1,1,0,1" CommandParameter="FFU" Command="{Binding NavigationCommand}">
</vw:NavigationRadioButton>
</StackPanel>
</Grid>
</vw:View>
Solution:
Like Clemens said I did use two instances of my DataContext. Therefore, when I did updated my property
I found this post on how to solve this problem: How can I create only one instance of a DataContext for multiple windows? Thanks for the input! Case closed.
I am adding a record from view 2 in the database;
1. The record is inserted successfully and is instantly shown in the usercontrol(DataGrid) on the same view(View2)
2. The changes are not shown in view 1 Unless I Close the the view or application and start it again.
// The INotifyProperty is implemented in ViewModelBase Class
I used the same view model on both views.
but I think it creates a new instance of the view model and datacontext on each view.
I want to resolve this issue.
---------------------------View 1-------------------------------------
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:UC="clr-namespace:HRSimplified.Controls"
x:Class="HRSimplified.MainWindow"
Title="MainWindow"
Height="628" Width="986">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<UC:EmployeeGridControl Grid.Column="1" />
</Grid>
</Window>
---------------------------View 2--------------------------------------
<Window
x:Class="HRSimplified.Windows.EmployeeDashboard"
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:local="clr-namespace:HRSimplified.View"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:VM="clr-namespace:HRSimplified.ViewModel"
xmlns:UC="clr-namespace:HRSimplified.Controls"
mc:Ignorable="d"
Title="EmployeeDashboard">
<Window.DataContext>
<VM:ViewModel_Employee />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<UC:EmployeeGridControl />
<ItemsControl Grid.Column="1" ItemsSource="{Binding Path=emp}">
<StackPanel>
<TextBox EditValue="{Binding emp.Name, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True}" Margin="5" />
<Button Command="{Binding AddCommand, UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True, ValidatesOnDataErrors=True}" Margin="5" Height="35" />
</StackPanel>
</ItemsControl>
</Window>
-------------------------UserControl----------------------------------
<UserControl
x:Class="HRSimplified.Controls.EmployeeGridControl"
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:HRSimplified.View"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:VM="clr-namespace:HRSimplified.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800">
<Grid>
<DataGrid x:Name="MasterData" MaxHeight="1080" ItemsSource="{Binding MasterData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True, NotifyOnSourceUpdated=True}">
<DataGridTextColumn x:Name="Name" Binding="{Binding Mode=TwoWay}"
/>
</DataGrid>
</Grid>
</UserControl>
--------------------ViewModel_Employe---------------------------------
public class ViewModel_Employee:ViewModelBase{
public HRSimplifiedEntities Model = new HRSimplifiedEntities();
private ObservableCollection<Model.Employee> _MasterData;
public ObservableCollection<Model.Employee> MasterData
{
get
{
return _MasterData;
}
set
{
SetProperty(ref this._MasterData, value);
}
}
private Employee _emp;
private ICommand _submitCommand;
public Employee emp
{
get { return _emp; }
set
{
_emp = value;
OnPropertyChanged("EmployeeCollection");
}
}
public ViewModel_Employee()
{
MasterData = new ObservableCollection<Model.Employee>
(Model.Employees.ToList() as IEnumerable<Employee>);
}
public ICommand AddCommand
{
get
{
if (_submitCommand== null)
{
_submitCommand = new RelayCommand(executeMethod, canExecuteMethod, false);
}
return _submitCommand;
}
}
private bool canExecuteMethod(object parameter)
{
if (string.IsNullOrEmpty(emp.Name) || string.IsNullOrEmpty(emp.Gender) ||
string.IsNullOrEmpty(emp.Salary.ToString()))
return false;
else
return true;
}
private void executeMethod(object parameter)
{
MasterData.Add(emp);
Model.Employees.Add(emp);
Model.SaveChanges();
System.Media.SystemSounds.Beep.Play();
}}
I think you can create a Singleton object of ViewModel_Employee and use as DataContext in all your views.
SingletonClass
public class ViewModel_Employee : INotifyPropertyChanged
{
private ViewModel_Employee()
{
}
private static ViewModel_Employee privateInstance = null;
public static ViewModel_Employee Instance
{
get
{
if (privateInstance == null)
privateInstance = new ViewModel_Employee();
return privateInstance;
}
// class is a singleton
}
DataContext in all Views:
<Window.DataContext >
<x:StaticExtension Member="VM:ViewModel_Employee.Instance" />
</Window.DataContext>
Hello stackoverflow people!
I consult you in deep desparation. I'm using Caliburn.Micro and am trying to implement a feature which shows Messages to the user.
I have a class MessageCenterViewModel which has an ObservableCollection of MessageViewModels and therefore holds all the current Messages to show up:
[Export(typeof(MessageCenterViewModel))]
public class MessageCenterViewModel : PropertyChangedBase, IShell
{
private ObservableCollection<MessageViewModel> messages;
public ObservableCollection<MessageViewModel> Messages
{
get{ return messages; }
set { messages = value; }
}
private string title;
public string Title
{
get { return title; }
set
{
title = value;
NotifyOfPropertyChange(title);
}
}
public MessageCenterViewModel()
{
Messages = new ObservableCollection<MessageViewModel>();
Title = "MessageCenter:";
}
public void AddMessage(MessageViewModel msg)
{
Messages.Add(msg);
msg.Expired += (o, e) => RemoveMessage(msg);
NotifyOfPropertyChange(() => Messages);
}
public void RemoveMessage(MessageViewModel msg)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => Messages.Remove(msg)));
}
}
The view looks like the following:
<UserControl x:Class="MessageCenter.Views.MessageCenterView"
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:views="clr-namespace:MessageCenter.Views"
xmlns:viewModels="clr-namespace:MessageCenter.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="#727F94">
<Grid>
<ItemsControl ItemsSource="{Binding Path=Messages}" Height="auto">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModels:MessageViewModel}">
<views:MessageView/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The MessageViewModel looks like this
[Export(typeof(MessageViewModel))]
public class MessageViewModel : PropertyChangedBase,IShell
{
public event EventHandler Expired;
protected virtual void OnExpired()
{
EventHandler handler = Expired;
if (handler != null) handler(this, EventArgs.Empty);
}
private string message;
public string Message
{
get { return message; }
set
{
message = value;
NotifyOfPropertyChange(()=>Message);
}
}
public MessageViewModel(string message,int expires = 0)
{
Message = message;
if (expires == 0) return;
var timer = new Timer(expires);
timer.Elapsed += (o, args) => OnExpired();
timer.AutoReset = false;
timer.Start();
}
}
and the corresponding view:
<UserControl x:Class="MessageCenter.Views.MessageView"
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="35" d:DesignWidth="500" Margin="3">
<Grid>
<Border BorderBrush="White" BorderThickness="1" Background="#34FFFFFF" CornerRadius="8">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="30" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" MinWidth="30"/>
</Grid.ColumnDefinitions>
<Image Width="16" Height="16" Grid.Column="0" Source="..\Icons\baloon_blue.png"/>
<Label x:Name="Message" Content="{Binding Path=Message}" Foreground="White" Grid.Column="1" HorizontalContentAlignment="Stretch" FontSize="12" FontFamily="/Fonts/#Roboto Medium" />
<Ellipse Width="14" Height="14" Stroke="Black" Grid.Column="2"/>
</Grid>
</Border>
</Grid>
When I add MessageViewModels in the ctor of MessageCenterViewModel they are displayed properly but when i try to add MessageViewModels after that they won't show up. What am I doing wrong? I appreciate your help!
Regards
Problem solved. Failure didn't lie within the code I posted rather than in multiple MessageCenterViewModels which weren't displayed.
I'm using MVVM in a windows phone 8 application. I would like to move from 1 view model to another inside my shell view model. I can't seem to get the ContentControl to bind to a template that is a usercontrol/phoneApplicationPage over the view model.
What am I missing?
I'm trying to avoid things like MVVM light. (I want my app to be as small a download as possible) And this should be possible to do.
P.S. I'm still pretty new to WPF/WP8
Here is a sample of what I have so far, Excuse the dumb functionality :)
/** The Shell view **/
<phone:PhoneApplicationPage
x:Class="PhoneAppWithDataContext.Navigation.ViewModelNavigation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
mc:Ignorable="d"
shell:SystemTray.IsVisible="True"
xmlns:vm="clr-namespace:PhoneAppWithDataContext.Navigation">
<phone:PhoneApplicationPage.DataContext>
<vm:AppViewModel/>
</phone:PhoneApplicationPage.DataContext>
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="MonthViewModel">
<vm:MonthViewControl/>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ContentControl Content="{Binding CurrentViewModel}"
ContentTemplate="{Binding ContentTemplate}"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"/>
</Grid>
<Button Content="Change VM" Command="{Binding ChangeViewModelCommand}"/>
</Grid>
</phone:PhoneApplicationPage>
/** Shell/Application View Model **/
public class AppViewModel : ViewModelBase
{
private ViewModelBase _currentViewModel;
private List<ViewModelBase> _viewModels = new List<ViewModelBase>();
private byte _month = 1;
public ViewModelBase CurrentViewModel
{
get
{
return _currentViewModel;
}
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
NotifyPropertyChanged("CurrentViewModel");
}
}
public DataTemplate SelectedTemplate
{
get
{
if (_currentViewModel == null)
return null;
return DataTemplateSelector.GetTemplate(_currentViewModel);
}
}
public List<ViewModelBase> ViewModels
{
get
{
return _viewModels;
}
}
public AppViewModel()
{
ViewModels.Add(new MonthViewModel(_month));
CurrentViewModel = ViewModels.FirstOrDefault();
}
private ICommand _changeViewModelCommand;
public ICommand ChangeViewModelCommand
{
get
{
return _changeViewModelCommand ?? (_changeViewModelCommand = new GenericCommand(() =>
{
_month++;
var newVM = new MonthViewModel(_month);
ViewModels.Add(newVM);
CurrentViewModel = newVM;
}, true));
}
}
private void ChangeViewModel(ViewModelBase viewModel)
{
if (!ViewModels.Contains(viewModel))
ViewModels.Add(viewModel);
CurrentViewModel = ViewModels.FirstOrDefault(vm => vm == viewModel);
}
}
/** DataTemplateSelector **/
public static class DataTemplateSelector
{
public static DataTemplate GetTemplate(ViewModelBase param)
{
Type t = param.GetType();
return App.Current.Resources[t.Name] as DataTemplate;
}
}
/** User Control **/
<UserControl x:Class="PhoneAppWithDataContext.Navigation.MonthViewControl"
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"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="480" d:DesignWidth="480"
xmlns:vm="clr-namespace:PhoneAppWithDataContext.Navigation">
<UserControl.DataContext>
<vm:MonthViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="{StaticResource PhoneChromeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Id" Width="100" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" />
<TextBlock Text="{Binding Id}" Width="100" VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" />
<TextBlock Text="Name" Width="100" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" />
<TextBlock Text="{Binding Name}" Width="100" VerticalAlignment="Center" Grid.Row="1" Grid.Column="1" />
</Grid>
</UserControl>
/** ViewModelBase **/
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
/** View model that the user control should bind to **/
public sealed class MonthViewModel : ViewModelBase
{
private byte _id;
private string _name;
public MonthViewModel()
{
}
public MonthViewModel(byte id)
{
_id = id;
_name = "Month " + id.ToString() + " of the year";
}
public override string ToString()
{
return _name;
}
public byte Id
{
get
{
return _id;
}
}
public string Name
{
get
{
return _name;
}
}
}
I believe the problem here is:
<UserControl.DataContext>
<vm:MonthViewModel/>
</UserControl.DataContext>
When your Content is changed from one MonthViewModel to the next, the DataContext of the returned DataTemplate is set to the object bound to Content. Well, once that DataContext is set, you should be good to go, but once the UserControl is loaded, it is resetting the DataContext to a new instace of an empty MonthViewModel (vm:MonthViewModel). Get rid of that explicit DataContext declaration--in other words delete the code that I posted above.
That way, when you first call CurrentViewModel and INPC is raised, it won't reset the DataContext. When you switch between CurrentViewModel's that are of MonthViewModel type, your UserControl won't call InitializeComponent again, instead the DataContext will change.
EDIT
In addition, if you still aren't seeing changes, then I would point to SelectedTemplate property. Instead of the null check in the property, just pass null to the GetTemplate. Inside of GetTemplate, check for null and return null there if it is null.