Prism 6 Navigation Wierdness - c#

I am still learning prism and I want to understand navigation. Having read the manual and followed #brianlagunas MVVM made simple webinar online, I decided to try a small project and see. I get a stackoverflow exception when I try to navigate to a view that has a viewmodel data-bound to some controls on my view. Here is the code below:
Profile View with empty viewmodel:
<UserControl x:Class="SampleLogin.Views.Profile"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:SampleLogin.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock x:Name="ProfileTextBlock" HorizontalAlignment="Center" Margin="74,29,78,0" TextWrapping="Wrap" Text="PROFILE PAGE" VerticalAlignment="Top" Height="45" Width="148" FontSize="21.333" FontWeight="Bold"/>
<TextBlock x:Name="Username" HorizontalAlignment="Left" Height="26" Margin="103,96,0,0" TextWrapping="Wrap" Text="{Binding username}" VerticalAlignment="Top" Width="130"/>
<TextBlock x:Name="LoginTime" HorizontalAlignment="Left" Margin="109,152,0,0" TextWrapping="Wrap" Text="{Binding LoginTime}" VerticalAlignment="Top" Width="124" Height="33"/>
<Label x:Name="label" Content="Username :" HorizontalAlignment="Left" Margin="10,96,0,0" VerticalAlignment="Top" Width="71" FontWeight="Bold"/>
<Label x:Name="label1" Content="Login-Time:" HorizontalAlignment="Left" Height="26" Margin="12,159,0,0" VerticalAlignment="Top" Width="86" FontWeight="Bold"/>
</Grid>
but it has an empty viewmodel and it loads fine.
Here is another view with a simple form that is data-bound to its viewmodel
<UserControl x:Class="SampleLogin.Views.LoginUser"
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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:SampleLogin.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="325">
<Grid>
<Label x:Name="usernameLabel" Content="Username :" HorizontalAlignment="Left" Margin="10,93,0,0" VerticalAlignment="Top" Width="113" Height="35" FontSize="18.667" FontWeight="Bold"/>
<Label x:Name="label" Content="Password :
" HorizontalAlignment="Left" Margin="10,146,0,0" VerticalAlignment="Top" Width="100" Height="38" FontSize="18.667" FontWeight="Bold"/>
<TextBox x:Name="Username" HorizontalAlignment="Left" Height="35" Margin="132,93,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="178" Text="{Binding Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<PasswordBox x:Name="passwordBox" HorizontalAlignment="Left" Margin="132,146,0,0" VerticalAlignment="Top" Width="178" Height="35"/>
<Button x:Name="Loginbtn" Content="Login" HorizontalAlignment="Left" Margin="85,208,0,0" VerticalAlignment="Top" Width="89" Height="42" FontSize="18.667" FontWeight="Bold"
Command="{Binding LoginCommand}" CommandParameter="{Binding ElementName=passwordBox}" />
<Button x:Name="Logoutbtn" Content="Logout" HorizontalAlignment="Left" Margin="230,208,0,0" VerticalAlignment="Top" Width="80" Height="42" FontWeight="Bold" FontSize="18.667"/>
</Grid>
Here is the viewmodel its bound to: left it very simple just to understand the concept:
public class LoginUserViewModel : BindableBase
{
private AuthenticationService _auth;
public DelegateCommand<object> LoginCommand { get; set; }
public LoginUserViewModel(AuthenticationService auth)
{
_auth = auth;
LoginCommand = new DelegateCommand<object>(LoginUser, CanLoginUser);
}
private void LoginUser(object obj)
{
var passwdbox = obj as PasswordBox;
_auth.LoginUser(Username, passwdbox.SecurePassword);
}
private bool CanLoginUser(object obj)
{
var password = obj as PasswordBox;
return string.IsNullOrWhiteSpace(Username);
}
private string _username = "Enter Username here";
public string Username
{
get { return _username; }
set { SetProperty(ref _username, value); }
}
}
And here is the MainWindowViewmodel that handles the navigation using the button at the top of my mainwindow view.
public class MainWindowViewModel : BindableBase
{
IRegionManager _regions;
public DelegateCommand<string> NavigationCommand { get; set; }
public MainWindowViewModel(IRegionManager regions)
{
_regions = regions;
NavigationCommand = new DelegateCommand<string>(Navigate);
}
private void Navigate(string uri)
{
_regions.RequestNavigate("ContentRegion", uri);
}
}
And lastly, here is the MainWindowViewModel that binds to the viewmodel:
<Window x:Class="SampleLogin.Views.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:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:SampleLogin.Views"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<Button Content="Login" Margin="5" Command="{Binding NavigationCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" CommandParameter="LoginUser" />
<Button Content="Home" Margin="5" Command="{Binding NavigationCommand, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" CommandParameter="Home"/>
<Button Content="Profile" Margin="5" Command="{Binding NavigationCommand}" CommandParameter="Profile"/>
</StackPanel>
<ContentControl Grid.Row="1" prism:RegionManager.RegionName="ContentRegion" />
</Grid>
I find this very strange. The first view works when I click the buttons at the top of the screen for navigation as long as I leave the ViewModel blank. Same with the view that has the form, if I comment out the ViewModel, it navigates with no issues, showing the form and all. But if the databinding is there in the viewmodel I get the exception. Any ideas?! I am stomped and not sure where my mistake is.
Edit:
Having thought about this problem and tried to move it forward, I discovered that the error comes from the fact that my LoginUserViewModel having constructor parameters is why I am having the errors, tried using unity container in the bootstrapper register LoginUserViewmodel's dependencies but I am still getting the errors, any ideas anyone?

After doing some more searching and question asking, I got the answer to this problem. I spoke to the awesome #brianLagunas and shared my code with him and he made understand the problem I had. Its in a domain model which I didn't include in my post.
public class User : IUser
{
public User()
{
}
public User(User user){} //this is the problem line
[Required]
[Key]
public int UserId { get; set; }
[Required(ErrorMessage ="You must supply a Username")]
[DataType(DataType.Text)]
public string Username { get; private set; }
[Required(ErrorMessage = "You must enter a valid password to Login!")]
public SecureString Password { get; private set; }
[DataType(DataType.Time)]
[Required]
public TimeSpan TimeLoggedIn { get; private set; }
[DataType(DataType.Date)]
public DateTime LoginHistory { get; private set; }
public void SetUserDetails(string username, TimeSpan date, DateTime History)
{
if(string.IsNullOrWhiteSpace(username))
{
throw new ArgumentNullException("You cannot have any of the Login Fields empty..!");
}
Username = username;
TimeLoggedIn = date;
LoginHistory = History;
}
}
The second constructor was casing an infinite loop this the stack overflow error I was getting. Removing that constructor fixed the issue and all is well.

Related

How can I use DelegateCommand correctly?

In the debug mode, goes from constructor to the set method, but in the get method does not enter at any point, which I think is the problem, but I don't know how to solved.
In the .xaml file I defined the button.
<dx:ThemedWindow
x:Class="ProductsModule.View.ProductView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dx="http://schemas.devexpress.com/winfx/2008/xaml/core"
Title="MenuView" Height="800" Width="1000">
<Grid Margin="20" VerticalAlignment="Top" Background="White" Height="420" DataContext="{Binding Detail}">
<Grid.Effect>
<DropShadowEffect BlurRadius="20" ShadowDepth="1"/>
</Grid.Effect>
<StackPanel Margin="750 70 70 70" HorizontalAlignment="Left">
<TextBlock Text="{Binding Name}" FontSize="18" Margin="0 5" Foreground="#FF6A6A6A"/>
<Button Command="{Binding ShowCommand }">Add data</Button>
</StackPanel>
</Grid>
</dx:ThemedWindow>
In .xaml.cs file I defined datacontext.
public SecondView()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
VIEW MODEL
public ViewModel()
{
ShowCommand = new DelegateCommand(ShowMethod);
}
public ICommand ShowCommand
{
get;
set;
}
private void ShowMethod()
{
OrderView orderView = new OrderView();
orderView.Show();
}
When I press the button, ShowMethod() is not called and does not do anything.

Databinding and DataContext in WPF Desktop application with multiple views using UserControls

I am working on my first project with WPF and as practice, have created an application that has a menu bar with two buttons on the side which are used to change the display two different views on the grid to its right. I am using the MVVM pattern. Using the ICommand interface, I am able to change the view in the grid using usercontrols for the two views from the menu bar buttons. The application is as follows:
The menu bar has buttons named View 1 and View 2, clicking any of them will open up the relevant view in the usercontrol. At startup a "defaultview" is displayed. The default view has a button which can also be used to move to View 1. Below is what I am stuck at:
I have a button in View 1 to change the display to View 2 (it calls the same command I made in the App View Model but even though it changes the variable value to which the sidegrid is bound too, the view displayed doesnt change).
I want to pass data (tried with First Name) from View 1 to View 2 but I cant get binding to work.
Can someone guide me or link me to examples or material that discuss this?
App View Model
using DataBindingAndViewsTestProject.Views;
using Hotel_Management_Project_WPF.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataBindingAndViewsTestProject.ViewModels
{
public class AppVM : ObservableObject
{
//Create a property that controls current view
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set { OnPropertyChanged(ref _currentView, value);
}
}
//Instantiate the relaycommands, we will need to instantiate relaycommand objects for every command we need to perform.
//This means that we will need to do this for preses of all buttons
public RelayCommand View1ButtonCommand { get; private set; }
public RelayCommand View2ButtonCommand { get; private set; }
public AppVM()
{
CurrentView = new DefaultVM();
View1ButtonCommand = new RelayCommand(ShowView1, AlwaysTrueCommand);
View2ButtonCommand = new RelayCommand(ShowView2, AlwaysTrueCommand);
}
public void ShowView1(object dummy)
{
CurrentView = new View1();
}
public void ShowView2(object dummy)
{
CurrentView = new View2();
}
public bool AlwaysTrueCommand(object dummy)
{
return true;
}
}
}
Main Window.xaml
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="600"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<Button x:Name="view1Button" Content="Go to View 1" Margin="10" Command="{Binding View1ButtonCommand}"></Button>
<Button x:Name="view2Button" Content="Go to View 2" Margin="10" Command="{Binding View2ButtonCommand}"></Button>
</StackPanel>
<Grid Grid.Column="1">
<ContentControl Content="{Binding CurrentView}"></ContentControl>
</Grid>
</Grid>
</Window>
View1.xaml
<UserControl x:Class="DataBindingAndViewsTestProject.Views.View1"
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:DataBindingAndViewsTestProject.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:vm="clr-namespace:DataBindingAndViewsTestProject.ViewModels">
<UserControl.Resources>
<vm:AppVM x:Name="AppVMinView1" x:Key="AppVMinView1"></vm:AppVM>
</UserControl.Resources>
<UserControl.DataContext>
<vm:View1VM></vm:View1VM>
</UserControl.DataContext>
<Grid Background="Aqua">
<StackPanel Margin="100">
<TextBlock Text="First Name"/>
<TextBox x:Name="firstNameTextBoxView1" Text="{Binding View1InfoClass.FirstName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Last Name"/>
<TextBox x:Name="lastNameTextBoxView1" Text="{Binding View1InfoClass.LastName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Random Useless Number" ></TextBlock>
<TextBox x:Name="randomUselessNumberView1" Text="{Binding View1InfoClass.Number, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="First Name Entered"></TextBlock>
<TextBlock Text="{Binding View1InfoClass.FirstName}"></TextBlock>
<TextBlock Text="Last Name Entered" ></TextBlock>
<TextBlock Text="{Binding View1InfoClass.LastName}"></TextBlock>
<TextBlock Text="Random Useless Number Entered"></TextBlock>
<TextBlock Text="{Binding View1InfoClass.Number}"></TextBlock>
<Button DataContext="{StaticResource AppVMinView1}" Content="Go to view2" Height="20" Width="70" Command="{Binding View2ButtonCommand}" />
</StackPanel>
</Grid>
</UserControl>
View2.xaml
<UserControl x:Class="DataBindingAndViewsTestProject.Views.View2"
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:DataBindingAndViewsTestProject.Views"
xmlns:vm="clr-namespace:DataBindingAndViewsTestProject.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<vm:View1VM x:Key="View1VMInView2" x:Name="View1VMInView2"></vm:View1VM>
</UserControl.Resources>
<UserControl.DataContext>
<vm:View2VM></vm:View2VM>
</UserControl.DataContext>
<Grid Background="Beige">
<StackPanel Margin="100">
<TextBlock Text="First Name"/>
<TextBox x:Name="firstNameTextBoxView2" Text="{Binding View2InfoClass.FirstName, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Last Name"/>
<TextBox x:Name="lastNameTextBoxView2" Text="{Binding View2InfoClass.LastName, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="Random Useless Number"></TextBlock>
<TextBox x:Name="randomUselessNumberView2" Text="{Binding View2InfoClass.Number, Mode=OneWayToSource,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Text="First Name Entered"></TextBlock>
<TextBlock DataContext="View1InView2" Text="{Binding View1InfoClass.FirstName}" ></TextBlock>
<TextBlock Text="Last Name Entered"></TextBlock>
<TextBlock></TextBlock>
<TextBlock Text="Random Useless Number Entered"></TextBlock>
<TextBlock ></TextBlock>
</StackPanel>
</Grid>
</UserControl>
Model (if relevant)
public class InfoClass:ObservableObject
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set {
OnPropertyChanged(ref _firstName, value);
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { OnPropertyChanged(ref _lastName, value); }
}
private int _number;
public int Number
{
get { return _number; }
set { OnPropertyChanged(ref _number, value); }
}
}

Closing an Open Window Using MVVM Pattern, Produces System.NullReferenceException error

I'm trying to learn MVVM pattern using WPF C#. And I'm running into an error when trying to close an opened window after saving information to an sqlite database. When the command to save a new contact is raised, I am getting an error on HasAddedContact(this, new EventArgs());
Error: System.NullReferenceException: 'Object reference not set to an instance of an object.'
My ViewModel:
public class NewContactViewModel : BaseViewModel
{
private ContactViewModel _contact;
public ContactViewModel Contact
{
get { return _contact; }
set { SetValue(ref _contact, value); }
}
public SaveNewContactCommand SaveNewContactCommand { get; set; }
public event EventHandler HasAddedContact;
public NewContactViewModel()
{
SaveNewContactCommand = new SaveNewContactCommand(this);
_contact = new ContactViewModel();
}
public void SaveNewContact()
{
var newContact = new Contact()
{
Name = Contact.Name,
Email = Contact.Email,
Phone = Contact.Phone
};
DatabaseConnection.Insert(newContact);
HasAddedContact(this, new EventArgs());
}
}
SaveNewContactCommand:
public class SaveNewContactCommand : ICommand
{
public NewContactViewModel VM { get; set; }
public SaveNewContactCommand(NewContactViewModel vm)
{
VM = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
VM.SaveNewContact();
}
}
NewContactWindow.Xaml.Cs code behind:
public partial class NewContactWindow : Window
{
NewContactViewModel _viewModel;
public NewContactWindow()
{
InitializeComponent();
_viewModel = new NewContactViewModel();
DataContext = _viewModel;
_viewModel.HasAddedContact += Vm_ContactAdded;
}
private void Vm_ContactAdded(object sender, EventArgs e)
{
this.Close();
}
}
Adding additional code where I call the new window:
public class ContactsViewModel
{
public ObservableCollection<IContact> Contacts { get; set; } = new ObservableCollection<IContact>();
public NewContactCommand NewContactCommand { get; set; }
public ContactsViewModel()
{
NewContactCommand = new NewContactCommand(this);
GetContacts();
}
public void GetContacts()
{
using(var conn = new SQLite.SQLiteConnection(DatabaseConnection.dbFile))
{
conn.CreateTable<Contact>();
var contacts = conn.Table<Contact>().ToList();
Contacts.Clear();
foreach (var contact in contacts)
{
Contacts.Add(contact);
}
}
}
public void CreateNewContact()
{
var newContactWindow = new NewContactWindow();
newContactWindow.ShowDialog();
GetContacts();
}
}
ContactsWindow.Xaml
<Window x:Class="Contacts_App.View.ContactsWindow"
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:Contacts_App.View"
xmlns:vm="clr-namespace:Contacts_App.ViewModel"
mc:Ignorable="d"
Title="Contacts Window" Height="320" Width="400">
<Window.Resources>
<vm:ContactsViewModel x:Key="vm"/>
</Window.Resources>
<StackPanel Margin="10">
<Button
Content="New Contact"
Command="{Binding NewContactCommand}"/>
<TextBox Margin="0,5,0,5"/>
<ListView
Height="200"
Margin="0,5,0,0"
ItemsSource="{Binding Contacts}">
<ListView.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Window>
NewContactWindow.Xaml
<Window x:Class="Contacts_App.View.NewContactWindow"
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:Contacts_App.View"
xmlns:vm="clr-namespace:Contacts_App.ViewModel"
mc:Ignorable="d"
Title="New Contact Window" Height="250" Width="350">
<Window.Resources>
<vm:NewContactViewModel x:Key="vm"/>
</Window.Resources>
<Grid>
<StackPanel
Margin="10">
<Label Content="Name" />
<TextBox
Text="{Binding Source={StaticResource vm}, Path=Contact.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Email" />
<TextBox
Text="{Binding Source={StaticResource vm}, Path=Contact.Email, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Phone Number" />
<TextBox
Text="{Binding Source={StaticResource vm}, Path=Contact.Phone, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Button
Content="Save"
Command="{Binding Source={StaticResource vm}, Path=SaveNewContactCommand}"/>
</StackPanel>
</Grid>
</Window>
You're creating NewContactWindow's viewmodel in the constructor, correctly assigning it to DataContext, and correctly adding a handler to that event. Unfortunately, you also create a second instance of the same viewmodel in resources, and you manually set the Source property of all the bindings to use the one in the resources, which doesn't have the event handler.
Window.DataContext, which you set in the constructor, is the default Source for any binding in the Window XAML. Just let it do its thing. I also removed all the redundant Mode=TwoWay things from the Bindings to TextBox.Text, since that property is defined so that all bindings on it will be TwoWay by default. I don't think UpdateSourceTrigger=PropertyChanged is doing anything necessary or helpful either: That causes the Binding to update your viewmodel property every time a key is pressed, instead of just when the TextBox loses focus. But I don't think you're doing anything with the properties where that would matter; there's no validation or anything. But TextBox.Text is one of the very few places where that's actually used, so I left it in.
You should remove the analagous viewmodel resource in your other window. It's not doing any harm, but it's useless at best. At worst, it's an attractive nuisance. Kill it with fire and bury the ashes under a lonely crossroads at midnight.
<Window x:Class="Contacts_App.View.NewContactWindow"
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:Contacts_App.View"
xmlns:vm="clr-namespace:Contacts_App.ViewModel"
mc:Ignorable="d"
Title="New Contact Window" Height="250" Width="350">
<Grid>
<StackPanel
Margin="10">
<Label Content="Name" />
<TextBox
Text="{Binding Contact.Name, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Email" />
<TextBox
Text="{Binding Contact.Email, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Label Content="Phone Number" />
<TextBox
Text="{Binding Contact.Phone, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,5"/>
<Button
Content="Save"
Command="{Binding SaveNewContactCommand}"/>
</StackPanel>
</Grid>
</Window>

Filling a ListBox with an ObservableCollection and display properties of selected items in StackPanel

I'm tring to databind properties of an ObservableCollection to a ListBox (Just the Title property for example).
By clicking on one of the ListItem (with an event ), i'd like to display all the properties of the Collection into a StackPanel. After many tries, I still don't know how can I figure it out...
Here is my code behind :
public partial class TestListView : Window
{
public TestListView()
{
ObservableCollection<Programme> pgr = new ObservableCollection<Programme>();
pgr = readfile();
InitializeComponent();
}
public class Programme
{
public String Title { get; set; }
public String Date { get; set; }
public String Chaine { get; set; }
public Programme(String Title, String Date, String Chaine)
{
this.Title = Title;
this.Date = Date;
this.Chaine = Chaine;
}
}
Here is my XAML :
<Window x:Class="Test.TestListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test;assembly=Test"
Title="TestListView" Height="500" Width="1000" x:Name="Window">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="249*"/>
<ColumnDefinition Width="743*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="20*"/>
<RowDefinition Height="428*"/>
<RowDefinition Height="21*"/>
</Grid.RowDefinitions>
<ListBox Name="l1" ItemsSource="{Binding pgr}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="1" Grid.Row="1">
<TextBox Margin="343,0,0,0" x:Name="Recherche"></TextBox>
<Button Height="37" Margin="669,0,0,0" ></Button>
<TextBlock x:Name="t1" Margin="214,0,293,0" Height="33" />
</StackPanel>
</Grid>
</Window>
You need to bind your data to (public) properties. Also, you don't need to use ObservableCollection; any selection changes will be picked up anyhow.
Here's a working sample, with layout and other bits and pieces changed to make it compile for me:
public partial class MainWindow
{
public IList<Programme> pgr { get; }
public MainWindow()
{
pgr = new List<Programme>
{
new Programme("First", "FirstDate", "FirstChaine"),
new Programme("Second", "SecondDate", "SecondChaine"),
new Programme("Third", "ThirdDate", "ThirdChaine"),
};
InitializeComponent();
}
public class Programme
{
// No changes
}
}
...and the XAML:
<Window
x:Name="self"
x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<StackPanel DataContext="{Binding ElementName=self}" Orientation="Horizontal">
<ListBox Name="l1" ItemsSource="{Binding pgr}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Orientation="Vertical">
<TextBox Margin="10" Text="{Binding ElementName=l1,Path=SelectedItem.Title}" />
<Button Height="37" Margin="0" Content="{Binding ElementName=l1,Path=SelectedItem.Date}"></Button>
<TextBlock Margin="10" Height="33" Text="{Binding ElementName=l1,Path=SelectedItem.Chaine}" />
</StackPanel>
</StackPanel>
</Window>
Note the bindings that reference the selected item:
Text="{Binding ElementName=l1,Path=SelectedItem.Title}"

WPF two way mode binding using INotifyPropertyChanged not working

So I am facing a tricky issue. This is my view model:
namespace Market.ViewModel
{
public class BillingViewModel : ViewModelBase
{
#region Private Members
private Customer _customer;
private Product _products;
private string _productId = "asd";
RelayCommand _numClickedCommand;
#endregion
public Customer Customer
{
get { return _customer; }
set
{
_customer = value;
NotifyPropertyChanged("Customer");
}
}
public Product Products
{
get { return _products; }
set
{
_products = value;
NotifyPropertyChanged("Products");
}
}
public bool CanClick
{
get { return true; }
}
public string ProductId
{
get { return _productId; }
set
{
_productId = value;
NotifyPropertyChanged("ProductId");
}
}
public ICommand NumClickedCommand
{
get
{
if (_numClickedCommand == null)
{
_numClickedCommand = new RelayCommand(param => this.NumClicked(param.ToString()),
param => this.CanClick);
}
return _numClickedCommand;
}
}
#region PrivateMethods
private void NumClicked(string numClicked)
{
ProductId = ProductId+numClicked;
}
#endregion
}
}
It inherits ViewModelBase which implements INotifyPropertyCanged.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And my view is:
<Window x:Class="Billing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:controls="clr-namespace:Billing"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:Market.ViewModel;assembly=Market.ViewModel"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<viewModel:BillingViewModel x:Key="ViewModel" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource ViewModel}}">
<controls:NumPad HorizontalAlignment="Left" Margin="265,205,0,-192" VerticalAlignment="Top" Height="307" Width="242"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="247,29,0,0" TextWrapping="Wrap" Text="{Binding ProductId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
</Grid>
</Window>
NumClickedCommand is used in this xaml:
<UserControl x:Class="Billing.NumPad"
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:viewModel="clr-namespace:Market.ViewModel;assembly=Market.ViewModel"
mc:Ignorable="d" Height="109" Width="248">
<UserControl.Resources>
<viewModel:BillingViewModel x:Key="ViewModel"/>
</UserControl.Resources>
<Grid Height="109" VerticalAlignment="Top" DataContext="{Binding Source={StaticResource ViewModel}}">
<Button Content="1" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Top" Width="75" CommandParameter="1" Command="{Binding NumClickedCommand}"/>
<Button Content="2" HorizontalAlignment="Left" Margin="90,0,0,0" VerticalAlignment="Top" Width="75" CommandParameter="2" Command="{Binding NumClickedCommand}"/>
<Button Content="3" HorizontalAlignment="Left" Margin="170,0,0,0" VerticalAlignment="Top" Width="75" CommandParameter="3" Command="{Binding NumClickedCommand}"/>
<Button Content="4" HorizontalAlignment="Left" Margin="10,27,0,0" VerticalAlignment="Top" Width="75" CommandParameter="4" Command="{Binding NumClickedCommand}"/>
<Button Content="5" HorizontalAlignment="Left" Margin="90,27,0,0" VerticalAlignment="Top" Width="75" CommandParameter="5" Command="{Binding NumClickedCommand}"/>
<Button Content="6" HorizontalAlignment="Left" Margin="170,27,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="1.034,2.171" CommandParameter="6" Command="{Binding NumClickedCommand}"/>
<Button Content="7" HorizontalAlignment="Left" Margin="10,54,0,0" VerticalAlignment="Top" Width="75" CommandParameter="7" Command="{Binding NumClickedCommand}"/>
<Button Content="8" HorizontalAlignment="Left" Margin="90,54,0,0" VerticalAlignment="Top" Width="75" CommandParameter="8" Command="{Binding NumClickedCommand}"/>
<Button Content="9" HorizontalAlignment="Left" Margin="170,54,0,0" VerticalAlignment="Top" Width="75" CommandParameter="9" Command="{Binding NumClickedCommand}"/>
<Button Content="0" HorizontalAlignment="Left" Margin="10,81,0,0" VerticalAlignment="Top" Width="75" CommandParameter="0" Command="{Binding NumClickedCommand}"/>
<Button Content="Submit" HorizontalAlignment="Left" Margin="90,81,0,0" VerticalAlignment="Top" Width="155" />
</Grid>
</UserControl>
The problem is that updaditing ProductId in the viewmodel doesnt reflect in the view. The initial value of asd is updated at the launch of the application. The controls contain set of buttons that implements ICommand interface and it all calls the NumClicked() in viewmodel. While debugging if i click the button NumClicked() is called then ProductId is updated and NotifyPropertyChanged() is also called but UI does not update, it remains the same. But in case i directly update the UI i.e i enter some value in the textbox the same flow happens, PropertyChanged() is called and after that get is also called to update the value in the viewmodel.
I have gone through many such questions already available but not able to get what exactly is stopping the update of the UI. Any help is appreciated and do ask if anything is missing.
Thank You.
DataContext of Grid and TextBox are bound to a view model instance from Window resources.
NumPad control declares its own instance of view model
NumClickedCommand works with wrong data, not with the displayed object
make sure you have only one instance of view model
NumPad inherits DataContext and shouldn't create a new object and change DataContext
<UserControl x:Class="Billing.NumPad"
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:viewModel="clr-namespace:Market.ViewModel;assembly=Market.ViewModel"
mc:Ignorable="d" Height="109" Width="248">
<Grid Height="109" VerticalAlignment="Top">
<!--all Buttons here-->
</Grid>
you are passing same viewmodel into numpadUser controll and mainWindow also as DataContext, so that will create new instance of viewmodel,so according to me you can use mainwindow's DataContext in NumPad also using Minwindow's GridName, you have to name your grid like
<Grid DataContext="{Binding Source={StaticResource ViewModel}}" x:Name="grdNumPad">
and you can access that DataContext in your NumPad.XAML this way
<Button Content="1" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Top" Width="75" CommandParameter="1" Command="{Binding DataContext.NumClickedCommand,ElementName=grdNumPad}"/>

Categories