WPF MVVM Switching Between Usercontrols - c#

I am new to MVVM. I found this artcle and it resolve my haf of problems.
https://social.technet.microsoft.com/wiki/contents/articles/30898.simple-navigation-technique-in-wpf-using-mvvm.aspx
However, I need to navigate from one usercontrol to another from the button click event of one usercontrol not from the button on the main window.
Usercontrol 1:
Usercontrol 2:
This is what I have tried sofar;
class Usercontrol1ViewModel : INotifyPropertyChanged
{
public ICommand navCommand { get; set; }
public Usercontrol1ViewModel()
{
navCommand = new BaseCommand(navigate);
}
private void navigate(object obj)
{
NavigationViewModel mainViewModel = new NavigationViewModel();
mainViewModel.SelectedViewModel = new Usercontrol2ViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
NavigationView model class
Class NavigationViewModel : INotifyPropertyChanged
{
public ICommand btn1Command { get; set; }
public ICommand btn2Command { get; set; }
private object selectedViewModel;
public object SelectedViewModel
{
get { return selectedViewModel; }
set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
}
public NavigationViewModel()
{
btn1Command = new BaseCommand(Opencontrl1);
btn2Command = new BaseCommand(Opencontrl2);
}
private void Opencontrl1(object obj)
{
SelectedViewModel = new Usercontrol1ViewModel();
}
private void Opencontrl2(object obj)
{
SelectedViewModel = new Usercontrol2ViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Main window code behind;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new NavigationViewModel();
}
}
Can somebody guide me how to achieve this with MVVM?

Try the below code. I used the sample code in the article and I am the author of the MSDN article :)
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type local:EmployeeViewModel}">
<local:EmployeeView/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:DepartmentViewModel}">
<local:DepartmentView/>
</DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="True">
<ContentControl x:Name="Pages" DockPanel.Dock="Right" Content="{Binding SelectedViewModel}"/>
</DockPanel>
NavigationViewModel
class NavigationViewModel : INotifyPropertyChanged
{
public EmployeeViewModel EmployeeViewModel { get; set; }
public DepartmentViewModel DepartmentViewModel { get; set; }
private object selectedViewModel;
public object SelectedViewModel
{
get { return selectedViewModel; }
set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
}
public NavigationViewModel()
{
SelectedViewModel = new EmployeeViewModel(OpenEmp);
}
private void OpenEmp(object obj)
{
if (obj.ToString() == "Dept")
{
SelectedViewModel = new DepartmentViewModel();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
EmployeeView
<Grid>
<StackPanel>
<TextBlock Text="This is employee view"/>
<Button Content="Navigate to Dept View" Command="{Binding Navigate}"/>
</StackPanel>
</Grid>
EmployeeViewModel
class EmployeeViewModel
{
private readonly Action<object> navigate;
public ICommand Navigate { get; set; }
public EmployeeViewModel(Action<object> navigate)
{
Navigate = new BaseCommand(OnNavigate);
this.navigate = navigate;
}
private void OnNavigate(object obj)
{
navigate.Invoke("Dept");
}
}

Related

get Variable from running Task and display on View

The code below executes fine when MyActionFunc is called but not when the function is in another class. MessageBox displays the correct string but it is not shown on view. What I am missing?
class ViewModel : INotifyPropertyChanged
{
public MyCommand ActionCommand
{
get;
set;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyname = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
public ViewModel()
{
ActionCommand = new MyCommand();
ActionCommand.CanExecuteFunc = obj => true;
// ActionCommand.ExecuteFunc = MyActionFunc;
ActionCommand.ExecuteFunc = MyClass.MyActionFunc;
}
private string myname;
public string myName
{
get => myname;
set { myname = value;; OnPropertyChanged(); }
}
public void MyActionFunc(object parameter)
{
myName = "Fred";
}
}
class MyClass
{
public static void MyActionFunc(object parameter)
{
ViewModel name = new ViewModel();
name.myName = "Fred";
MessageBox.Show(name.myName);
}
}
... and the binding to the Textbox
<TextBox Name="textBox" Grid.Column="1" Grid.Row="1" Text="{Binding Path=myName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"/>

SizeToContent disables EventTrigger

I'm trying to make window size same as content, but when I use SizeToContent, it for some reason breaks my EventTrigger and it no longer invokes the command when the window is loaded.
Code sample:
WindowView:
<Window x:Class="Tasks.Views.TasksWindow"
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:Tasks.Views"
xmlns:ViewModels="clr-namespace:Tasks.ViewModels"
xmlns:Views="clr-namespace:Tasks.Views"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
SizeToContent="WidthAndHeight"
mc:Ignorable="d"
Title="TasksWindow">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadTasksCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Window.DataContext>
<ViewModels:TasksViewModel/>
</Window.DataContext>
<Views:TasksControl/>
</Window>
ViewModel:
public class TasksViewModel : ObservableObject
{
private readonly ObservableCollection<TaskModel> _tasks
= new ObservableCollection<TaskModel>();
public IEnumerable<TaskModel> Tasks
{
get { return _tasks; }
}
public ICommand LoadTasksCommand
{
get { return new DelegateCommand(LoadTasks); }
}
public void LoadTasks()
{
string directory = Path.GetDirectoryName(Assembly.GetEntryAssembly().Location);
string filePath = Path.Combine(directory, "Tasks.xml");
// Check if save file exists
if (!File.Exists(filePath))
return;
XmlSerializer xs = new XmlSerializer(typeof(TaskList));
using (StreamReader reader = new StreamReader("Tasks.xml"))
{
TaskList results = (TaskList)xs.Deserialize(reader);
foreach (var item in results.TasksList)
_tasks.Add(item);
}
}
}
Base:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
DelegateCommand:
public class DelegateCommand : ICommand
{
private readonly Action _action;
public DelegateCommand(Action action) { _action = action; }
public void Execute(object parameter) { _action(); }
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
}
Model:
[XmlRoot("ArrayOfTaskModel")]
public class TaskList
{
[XmlElement("TaskModel")]
public List<TaskModel> TasksList { get; set; }
}
[Serializable]
public class TaskModel : ObservableObject
{
private string _name;
[XmlElement("Name")]
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChangedEvent("Name");
}
}
}

WPF - MVVM Bindings

I have problem with my application, especially with Bindings in WPF MVVM.
I created Model, ViewModel and View, this is part of my code (only this connected with my problem) When I click the button nemed : PointUp i want to see the amount of Team1 points. Can anyone tell me what i'm doing wrong?
View
<Window x:Class="Tabu.Game
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:Tabu"
xmlns:vm="clr-namespace:Tabu.ViewModel"
mc:Ignorable="d"
Title="Game" Height="600" Width="900" Background="Beige">
<Window.DataContext>
<vm:TeamStatistic />
</Window.DataContext>
<Grid>
<Button x:Name="PointUp" Command="{Binding AddPoints }" Content="+"/>
<Label x:Name="PointsTeam1_label" Content="{Binding Team1.TeamPoints, UpdateSourceTrigger=PropertyChanged }"/>
</Grid>
Model
'namespace Tabu.Model
{
public class Team
{
public bool IsTeamActive { get; set; }
public int TeamMiss { get; set; }
public int TeamPoints { get; set; }
public int TeamMistake { get; set; }
}
}'
ViewModel
namespace Tabu.ViewModel
{
class TeamStatistic : INotifyPropertyChanged
{
public Team Team1 = new Team();
public int TeamPoints
{
get { return TeamPoints; }
set { TeamPoints = value; OnPropertyChanged("TeamPoints"); }
}
public ICommand AddPoints
{
get { return new RelayCommand(() => Add_Points()); }
}
public void Add_Points()
{
Team1.TeamPoints++;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propsName)
{
if (PropertyChanged!=null)
{
foreach(string propName in propsName)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class RelayCommand : ICommand
{
private readonly Func<bool> canExecute;
private readonly Action execute;
public RelayCommand(Action execute)
: this(execute, null) { }
public RelayCommand(Action execute, Func<bool> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { if (this.canExecute != null) CommandManager.RequerySuggested += value; }
remove { if (this.canExecute != null) CommandManager.RequerySuggested -= value; }
}
public Boolean CanExecute(object parameter) { return this.canExecute == null ? true : this.canExecute(); }
public void Execute(object parameter) { this.execute(); }
}
}
The problem is here:
public int TeamPoints
{
get { return TeamPoints; } //should be Team1.TeamPoints
set { TeamPoints = value; OnPropertyChanged("TeamPoints"); } //should be Team1.TeamPoints
}
Inside your TeamPoints property in ViewModel you return and set the same property TeamPoints from ViewModel but you should set from Model (Team1). You should return and set Team1.TeamPoints.
public int TeamPoints
{
get { return Team1.TeamPoints; }
set { Team1.TeamPoints = value; OnPropertyChanged("TeamPoints"); }
}
And Add_Points():
public void Add_Points()
{
Team1.TeamPoints++;
OnPropertyChanged("TeamPoints");
}
You have to update your Binding like this.
<Label x:Name="PointsTeam1_label" Content="{Binding TeamPoints, UpdateSourceTrigger=PropertyChanged }"/>
When you bind to Team1.TeamPoints you will not get the Notification from OnPropertyChanged which is inside your TeamPoints property.
I reckon it is because of AddPoints (the command binding). Since this command is binded & you are creating an instance of RelayCommand & returning each time it might be breaking the binding.
A better alternative with CommandBindings are to declare the property and initialize them in the Constructor of the view model.
Ex:
namespace Tabu.ViewModel
{
class TeamStatistic : INotifyPropertyChanged
{
public Team Team1 = new Team();
public int TeamPoints
{
get { return Team1.TeamPoints; }
set { Team1.TeamPoints = value; OnPropertyChanged("TeamPoints"); }
}
private ICommand _AddPoints;
public ICommand AddPoints
{
get { return _AddPoints; }
set { _AddPoints = value; }
}
public void Add_Points()
{
Team1.TeamPoints++;
}
public TeamStatistic ()
{
_AddPoinss = new RelayCommand(Add_Points);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(params string[] propsName)
{
if (PropertyChanged!=null)
{
foreach(string propName in propsName)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}

C# Bind class field to Textbox

I want to bind the property FilePath from the class SExcelFile to a textblox. Here is my file structur.
Inside my MainWindow.xaml is just an textblox.
<TextBox Text="{Binding FilePath}" Grid.ColumnSpan="2" Grid.Row="3" Margin="5" Padding="5"/>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public SManagerRoot ManagerRoot = new SManagerRoot();
public MainWindow()
{
InitializeComponent();
this.DataContext = this.ManagerRoot.ExcelFileManager.ExcelFile;
}
}
SManagerRoot.cs
public class SManagerRoot
{
#region Properties
public SExcelFileManager ExcelFileManager = new SExcelFileManager();
#endregion Properties
}
SExcelFileManager.cs
public class SExcelFileManager
{
#region Properties
public SExcelFile ExcelFile { get; set; }
public SPersonManager PersonManager = new SPersonManager();
#endregion Properties
}
SExcelFile.cs
public class SExcelFile : INotifyPropertyChanged
{
#region Properties
private string _filePath;
public string FilePath
{
set { this._filePath = value; OnPropertyChanged("FilePath"); }
get { return this._filePath; }
}
#endregion Properties
#region c'tor
public SExcelFile()
{
}
public SExcelFile(string filePath)
{
this.FilePath = filePath;
}
#endregion c'tor
#region PropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(name));
}
#endregion PropertyChanged
}
I added a datacontext. The Binding is to the property FilePath and the onpropertychanged is called when the value is changed. Where is the problem?

c# ListView not updating when Property Changed

My UI is not updating when more data is added to the ObservableCollection. The console output says A first chance exception of type 'System.NullReferenceException' occurred. Should I be using Inotifycollectionchanged instead? Here is some of the code:
<ListView x:Name="ListView2" ItemsSource="{Binding Source={x:Static d:GrabUserConversationModel._Conversation}, UpdateSourceTrigger=PropertyChanged}" SelectionChanged="ListView1_SelectionChanged">
UserConversationModel.cs
public class UserConversationModel : INotifyPropertyChanged
{
public UserConversationModel()
{
}
public string Name
{ get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string Obj)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(Obj));
}
}
}
MainWindow.xaml.cs
public partial class MainWindow
{
static GrabUserConversationModel grabUserConversationModel;
public MainWindow()
{
InitializeComponent();
...
}
static void AddData()
{
grabUserConversationModel.Conversation.Add(new UserConversationModel { Name = "TestName" });
}
GrabUserConversationModel.cs
class GrabUserConversationModel
{
public static ObservableCollection<UserConversationModel> _Conversation = new ObservableCollection<UserConversationModel>();
public ObservableCollection<UserConversationModel> Conversation
{
get { return _Conversation; }
set { _Conversation = value; }
}
...
your property ObservableCollection<UserConversationModel> Conversation is not implementing the INotifyPropertyChanged
public ObservableCollection<UserConversationModel> Conversation
{
get { return _Conversation; }
set { _Conversation = value; OnPropertyChanged("Conversation");}
}

Categories