I have been trying to figure out these past 2 days how to switch between 2 User Controls back and forward with buttons inside those User Controls.
I managed to make this happen but with the buttons outside those User Controls.
This is how my project files look
BaseCommand.cs
public class BaseCommand : ICommand
{
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
{
_method = method;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
public ICommand LogInCommand { get; set; }
public ICommand SetupCommand { get; set; }
private object selectedViewModel;
public object SelectedViewModel
{
get { return selectedViewModel; }
set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
}
public MainViewModel()
{
LogInCommand = new BaseCommand(OpenLogIn);
SetupCommand = new BaseCommand(OpenSetup);
}
private void OpenLogIn(object obj)
{
SelectedViewModel = new LogInViewModel();
}
private void OpenSetup(object obj)
{
SelectedViewModel = new SetupViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
MainWindow.xaml
<StackPanel>
<ContentControl Content="{Binding SelectedViewModel}"/>
<Button Content="Open LogIn" Height="24" Command="{Binding LogInCommand}"/>
<Button Content="Open Setup" Height="24" Command="{Binding SetupCommand}"/>
</StackPanel>
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainViewModel();
}
LoginViewModel and SetupViewModel are empty classes and their corresponding views have a text block indicating what they are.
What I want is to have instead 2 buttons in my MainWindow.xaml I want 1 in my LogInView.xaml that opens SetupView.xaml and vice versa.
App.xaml
<Application.Resources>
<DataTemplate DataType="{x:Type viewmodels:LogInViewModel}">
<views:LogInView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:SetupViewModel}">
<views:SetupView/>
</DataTemplate>
</Application.Resources>
Move "Open Setup" button to LogInView and "Open LogIn" button to SetupView.
In LogInViewModel create SetupCommand and pass the MainViewModel in the ctor. When the button "Open Setup" is clicked and SetupCommand is invoked then call new OpenSetup method on the MainViewModel.
public class LogInViewModel : INotifyPropertyChanged // etc.
{
private readonly MainViewModel mainViewModel;
public ICommand SetupCommand { get; set; }
public LogInViewModel(MainViewModel mainViewModel)
{
this.mainViewModel = mainViewModel;
SetupCommand = new BaseCommand(OpenSetup);
}
private void OpenSetup(object obj)
{
mainViewModel.OpenSetup();
}
....
}
Similar for SetupViewModel
public class SetupViewModel : INotifyPropertyChanged // etc.
{
private readonly MainViewModel mainViewModel;
public ICommand LogInCommand { get; set; }
public SetupViewModel(MainViewModel mainViewModel)
{
this.mainViewModel = mainViewModel;
LogInCommand = new BaseCommand(OpenLogIn);
}
private void OpenLogIn(object obj)
{
mainViewModel.OpenLogIn();
}
....
}
Finally, remove commands from MainViewModel, modify OpenLogIn and OpenSetup methods and pass reference to the MainViewModel when you create new instance of LogInViewModel or SetupViewModel.
public class MainViewModel : INotifyPropertyChanged
{
private object selectedViewModel;
public object SelectedViewModel
{
get { return selectedViewModel; }
set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
}
public MainViewModel()
{
OpenLogIn();
}
public void OpenLogIn()
{
SelectedViewModel = new LogInViewModel(this);
}
public void OpenSetup()
{
SelectedViewModel = new SetupViewModel(this);
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
Related
MainWindow.xaml:
<TextBox HorizontalAlignment="Left" Height="23" Margin="308,90,0,0" TextWrapping="Wrap" Text = "{Binding Login, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
MainWindow.cs:
public partial class MainWindow : Window
{
UserViewModel userViewModel = new UserViewModel();
UserService userService = new UserService();
public MainWindow()
{
InitializeComponent();
DataContext = userViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
userService.CheckUserExist();
}
Model:
public string Login { get; set; }
ViewModel:
public class UserViewModel : INotifyPropertyChanged
{
private string _login;
public string Login
{
get { return _login; }
set
{
_login = value;
OnPropertyChange("Login");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChange(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Service:
public string Login { get; set; }
public void CheckUserExist()
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain))
{
var user = UserPrincipal.FindByIdentity(principalContext, Login);
if (user == null)
{ UserMessageText = "xx"; }
{
}
}
}
Why Login in method CheckUserExist() is always null when i start it?
I tried with UpdateSourceTrigger=LostFocus and RaisePropertyChanged();
From method data are correctly sent to VM i View.
The value is always null because you never set it. Also when following the MVVM pattern, your view is not allowed to have a dependency to the model or the UserService to be specific.
It's the responsibility of the view model to pass the value from UserViewModel.Login to UserService.Login:
MainWindow.xaml.cs
public partial class MainWindow : Window
{
private UserViewModel userViewModel = new UserViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = this.userViewModel;
}
}
MainWindow.xaml
<StackPanel>
<TextBox Text="{Binding Login}"/>
<Button Content="Submit" Command="{Binding SubmitLoginDataCommand}"/>
</StackPanel>
UserViewModel.cs
public class UserViewModel
{
public void SubmitLoginData(object loginData)
{
this.userService.CheckUserExist(this.Login);
}
public ICommand SubmitLoginDataCommand => new RelayCommand(SubmitLoginData, param => true);
public string Login { get; set; }
private UserService userService { get; set; }
}
UserService.cs
public void CheckUserExist(string login)
{
using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain))
{
var user = UserPrincipal.FindByIdentity(principalContext, login);
if (user == null)
{
this.UserMessageText = "xx";
}
}
}
RelayCommand.cs
Implementation taken from Microsoft Docs: Relaying Command Logic
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this._execute = execute; this._canExecute = canExecute;
}
#endregion // Constructors
#region ICommand Members
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return this._canExecute == null ? true : this._canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { this._execute(parameter); }
#endregion // ICommand Members
}
In the shown code i need to know the coding to be replaced in place of question mark in the code. I need to delete,edit and update the item in the list view without writing any code in code behind. I only want to do these operations by bindin view with view model through Icommand
This a class in my model Playlist.cs
namespace MvvmDemo.Models
{
public class Playlist
{
public string Title { get; set; }
}
}
This is a class in my viewmodel PlaylistsViewModel.cs
namespace MvvmDemo.ViewModels
{
public class PlaylistsViewModel
{
public ObservableCollection Playlists { get; private set; } = new ObservableCollection();
public ICommand AddPlaylistCommand { get; private set; }
public ICommand DeletePlaylistCommand { get; private set; }
public ICommand EditPlaylistCommand { get; private set; }
public PlaylistsViewModel()
{
AddPlaylistCommand = new Command(AddPlaylist);
DeletePlaylistCommand = new Command(DeletePlaylist);
}
public void AddPlaylist()
{
var newPlaylist = "Playlist " + (Playlists.Count + 1);
Playlists.Add(new Playlist { Title = newPlaylist });
}
public void DeletePlaylist()
{
????????????????
}
public void EditPlaylist()
{
????????????????
}
}
}
you have to make the command is parameterised and pass binding data through the parameter.
and from that data you can get the index value of selected.using that remove the item from the list.
Playlists.RemoveAt("INDEX_NUMBER");
To update it in the view use "INotifyProperty" also
If you want to delete and edit item in ListView, firstly, you should need to use ICommand, then you could need to use INotifyPropertyChanged to implement Inotify.
I do one sample that you can take a look. Choosing one Item and long press with the left mouse button, you will see two ways, delete Item and Edit Item action.
<ContentPage.Content>
<StackLayout>
<ListView
x:Name="mylistview"
ItemsSource="{Binding lists}"
SelectedItem="{Binding selecteditem}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem
Command="{Binding BindingContext.DeletePlaylistCommand, Source={x:Reference Name=mylistview}}"
IsDestructive="true"
Text="Delete Item" />
<MenuItem
Command="{Binding BindingContext.EditPlaylistCommand, Source={x:Reference Name=mylistview}}"
IsDestructive="true"
Text="Edit Item" />
</ViewCell.ContextActions>
<StackLayout Padding="15,0">
<Label Text="{Binding Title}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page19 : ContentPage, INotifyPropertyChanged
{
public ObservableCollection<Playlist> lists { get; set; }
//public RelayCommand1 AddPlaylistCommand { get; set; }
public RelayCommand DeletePlaylistCommand { get; set; }
public RelayCommand EditPlaylistCommand { get; set; }
private Playlist _selecteditem;
public Playlist selecteditem
{
get { return _selecteditem; }
set
{
_selecteditem = value;
RaisePropertyChanged("selecteditem");
}
}
public Page19 ()
{
InitializeComponent ();
lists = new ObservableCollection<Playlist>()
{
new Playlist(){Id=1,Title="list 1"},
new Playlist(){Id=2, Title="list 2"},
new Playlist(){Id=3,Title="list 3"},
new Playlist(){Id=4,Title="list 4"},
new Playlist(){Id=5,Title="list 5"},
new Playlist(){Id=6,Title="list 6"},
};
DeletePlaylistCommand = new RelayCommand(DeletePlaylist);
EditPlaylistCommand = new RelayCommand(EditPlaylist);
selecteditem = lists[0];
this.BindingContext = this;
}
public void AddPlaylist()
{
}
public void DeletePlaylist()
{
Playlist item = selecteditem;
lists.Remove(item);
}
public void EditPlaylist()
{
Playlist item = selecteditem;
int id = item.Id;
foreach(Playlist playl in lists.Where(a=>a.Id==id))
{
playl.Title = "chenge title";
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Playlist: INotifyPropertyChanged
{
private int _Id;
public int Id
{
get { return _Id; }
set
{
_Id = value;
RaisePropertyChanged("Id");
}
}
private string _Title;
public string Title
{
get { return _Title;}
set
{
_Title = value;
RaisePropertyChanged("Title");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Here is the RelayCommd:
public class RelayCommand : ICommand
{
readonly Action _execute;
public RelayCommand(Action execute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_execute();
}
}
You can use observablecollection. It will reflect add,remove operation of item to the listview. And for editing item you have to raise property changed for all property you are editing.To simplify that property changed you can implement property changed event to your Playlist model class.
Like
public void DeletePlaylist()
{
Playlists.Remove(newPlaylist);
}
public void EditPlaylist()
{
newPlaylist.Title="Refreshed Playlist"
}
public class Playlist:INotifyPropertyChanged
{
private string title;
public string Title
{
get{return title;}
set{title=value;
NotifyPropertyChanged();}
}
}
I am trying to use a command that I have created. I am using c# and the MVVM structure for my application. I have 1 Window (ApplicationView) that is used to display my userContols(MainWindow, Window1). On startup I have the MainWindow user control showing and there is a button on that user control that when you press will change the user contol from the main window usercontol to the Window1 userContol.
ApplicationView.xaml(Window)
<Window x:Class="Nmenq.ApplicationView"
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:Nmenq"
xmlns:my="clr-namespace:Nmenq"
Title="ApplicationView" Height="400" Width="575">
<Window.Resources>
<DataTemplate DataType="{x:Type local:MainWindowViewModel}">
<local:MainWindow/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Window1ViewModel}">
<local:Window1/>
</DataTemplate>
</Window.Resources>
<DockPanel LastChildFill="True">
<ContentControl x:Name="Pages" DockPanel.Dock="Right" Content="{Binding CurrentPageViewModel}"/>
</DockPanel>
ApplicationView.xaml.cs
public partial class ApplicationView : Window
{
public ApplicationView()
{
InitializeComponent();
this.DataContext = new NavigationViewModel();
}
}
MainWindow.xaml(UserContol)
<UserControl x:Class="Nmenq.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:Nmenq"
xmlns:viewmodel="clr-namespace:Nmenq.ViewModel"
xmlns:my="clr-namespace:Nmenq"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="575">
<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}">
<Button x:Name="Accept_Button" Content="Accept" HorizontalAlignment="Left" Margin="475,316,0,0" VerticalAlignment="Top" Width="81" Command="{Binding Window1Command}"/>
</Grid>
MainWindow.xmal.cs
public partial class MainWindow : UserControl, INotifyPropertyChanged
{
NavigationViewModel object1 = new NavigationViewModel();
public ICommand Window1Command { get; set; }
public MainWindow()
{
InitializeComponent();
Window1Command = object1.Window1Command;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
NavigationViewModel(Where My command is set up)
class NavigationViewModel : INotifyPropertyChanged
{
public ICommand MainWindowCommand { get; set; }
public ICommand Window1Command { get; set; }
private object currentPageViewModel;
public object CurrentPageViewModel
{
get { return currentPageViewModel; }
set { currentPageViewModel = value; OnPropertyChanged("CurrentPageViewModel"); }
}
public NavigationViewModel()
{
MainWindowCommand = new BaseCommand(OpenMainWindow);
Window1Command = new BaseCommand(OpenWindow1);
CurrentPageViewModel = new MainWindowViewModel();
}
public void OpenMainWindow(object obj)
{
CurrentPageViewModel = new MainWindowViewModel();
}
public void OpenWindow1(object obj)
{
CurrentPageViewModel = new Window1ViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
: this(method, null)
{
}
public BaseCommand(Action<object> method, Predicate<object> canExecute)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
} class NavigationViewModel : INotifyPropertyChanged
{
public ICommand MainWindowCommand { get; set; }
public ICommand Window1Command { get; set; }
private object currentPageViewModel;
public object CurrentPageViewModel
{
get { return currentPageViewModel; }
set { currentPageViewModel = value; OnPropertyChanged("CurrentPageViewModel"); }
}
public NavigationViewModel()
{
Window1Command = new BaseCommand(OpenWindow1);
CurrentPageViewModel = new MainWindowViewModel();
}
public void OpenWindow1(object obj)
{
CurrentPageViewModel = new Window1ViewModel();
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
public class BaseCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _method;
public event EventHandler CanExecuteChanged;
public BaseCommand(Action<object> method)
: this(method, null)
{
}
public BaseCommand(Action<object> method, Predicate<object> canExecute)
{
_method = method;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_method.Invoke(parameter);
}
}
Any Help or advice would be greatly appreciated :)
You could bind directly to the Window1Command property of the NavigationViewModel and remove the Window1Command property from the UserControl:
<Button x:Name="Accept_Button" Content="Accept" HorizontalAlignment="Left" Margin="475,316,0,0" VerticalAlignment="Top" Width="81"
Command="{Binding DataContext.Window1Command, RelativeSource={RelativeSource AncestorType=Window}}"/>
The following in my code in MainWindow.xaml.cs:
namespace Test
{
public partial class MainWindow : Window
{
public class ChannelInfo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Notify(string propertyName)
{
if (this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string _channelDescription;
public string ChannelDescription
{
get
{
return _channelDescription;
}
set
{
if (value != _channelDescription)
{
_channelDescription = value;
Notify("ChannelDescription");
}
}
}
}
public ObservableCollection<ChannelInfo> Channels { get; set; }
public MainWindow()
{
InitializeComponent();
Channels = new ObservableCollection<ChannelInfo>()
{
new ChannelInfo() { ChannelDescription = "Ib" }
};
DataContext = Channels;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//((ObservableCollection<ChannelInfo>)DataContext).Add(new ChannelInfo() { ChannelDescription = "Ib" });
}
}
}
In my XAML I have a TextBox defined as follows:
<TextBox Height="23" Text="{Binding ChannelDescription}" HorizontalAlignment="Left" Margin="180,106,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
Now my problem is if I add an item to Channels in the constructor itself, then the TextBox is displaying the bound text. But when I add it in the Window_Loaded like above (uncomment the line), the Text is not displayed.
This is probably duplicated question, but i could not find solution for my problem.
I'm working on WPF application using MVVM pattern.
There are four views which are binded to their ViewModels. All ViewModels have BaseViewModel as parent.
public abstract class ViewModelBase : INotifyPropertyChanged
{
private bool isbusy;
public bool IsBusy
{
get
{
return isbusy;
}
set
{
isbusy = value;
RaisePropertyChanged("IsBusy");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainView contains BusyIndicator:
<extWpfTk:BusyIndicator IsBusy="{Binding IsBusy}">
<ContentControl />
</extWpfTk:BusyIndicator>
If I set IsBusy = true in MainViewModel, BusyIndicator is shown.
If I try to set IsBusy = true from other ViewModels, BusyIndicator is not shown.
Just to notice, I can not use 3rd party libraries in my project like MVVMLight in order to use their Messenger to communicate between ViewModels.
MainView:
public class MainWindowViewModel : ViewModelBase
{
public ViewModel1 ViewModel1 { get; set; }
public ViewModel2 ViewModel2 { get; set; }
public ViewModel3 Model3 { get; set; }
public MainWindowViewModel()
{
ViewModel1 = new ViewModel1();
ViewModel2 = new ViewModel2();
ViewModel3 = new ViewModel3();
//IsBusy = true; - its working
}
}
ViewModel1:
public class ViewModel1 : ViewModelBase
{
RelayCommand _testCommand;
public ViewModel1()
{
}
public ICommand TestCommand
{
get
{
if (_testCommand == null)
{
_testCommand = new RelayCommand(
param => this.Test(),
param => this.CanTest
);
}
return _testCommand;
}
}
public void Test()
{
//IsBusy = true; - BusyIndicator is not shown
}
bool CanTest
{
get
{
return true;
}
}
}
public class MainWindowViewModel : ViewModelBase
{
public ViewModel1 ViewModel1 { get; set; }
public ViewModel2 ViewModel2 { get; set; }
public ViewModel3 Model3 { get; set; }
public MainWindowViewModel()
{
ViewModel1 = new ViewModel1();
ViewModel2 = new ViewModel2();
ViewModel3 = new ViewModel3();
ViewModel1.PropertyChanged += (s,e) =>
{
if(e.PropertyName == "IsBusy")
{
// set the MainWindowViewModel.IsBusy property here
// for example:
IsBusy = ViewModel1.IsBusy;
}
}
//IsBusy = true; - its working
}
}
Repeate subcsription to all your viewModels.
Don't forget to unsubscribe from the events, when you don't need it more, to avoid memory leacks.
Your problem was: you binded to the MainWindowViewModel's propetry, not to inner ViewModel's properties.