How do I update\refresh the same usercontrol used on multiple views - c#

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>

Related

WPF ContentControl binding of dynamic content does not works properly

Again, I apologize in advance for any mistakes, English is not my native.
I'm making an MVVM app, and I want to dynamically change views using ContentControl in the MainWindow, here is a necessary part of the code to understand:
Firstly, views:
MainWindow.xaml
<Window x:Class="Vernam.MainWindow"
x:Name="MainWindowID"
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:Vernam"
xmlns:viewModel="clr-namespace:Vernam.ViewModels"
mc:Ignorable="d"
Height="600"
Width="900"
...>
<Window.Resources>
<viewModel:MainViewModel x:Key="vm"></viewModel:MainViewModel>
</Window.Resources>
...
<Grid>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition Width="40"></ColumnDefinition>
<ColumnDefinition Width="454"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
...
<StackPanel Grid.Column="2" Orientation="Horizontal"
DataContext="{Binding Source={StaticResource vm}}">
<RadioButton x:Name="CorrRadioButton"
Content="Correspondences"
Width="176"
Foreground="White"
FontSize="18"
Style="{StaticResource HeadButtonTheme}"
GroupName="Head"
IsChecked="True"
Command="{Binding Path=CorrCommand}"/>
<RadioButton x:Name="ProfileRadioButton"
Content="Profile"
Width="89"
Foreground="White"
FontSize="18"
Style="{StaticResource HeadButtonTheme}"
GroupName="Head"
Command="{Binding Path=ProfileCommand}"/>
</StackPanel>
...
</Grid>
<ContentControl Grid.Row="1"
Content="{Binding CurrentView}"/>
</Grid>
</Border>
</Window>
and MainWindow.xaml.cs:
public partial class MainWindow : Window
{
bool isMinimized = false;
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
Two views, that I want to be shown in MainWindow:
CorrespondensesView.xaml
<UserControl x:Class="Vernam.Views.CorrespondensesView"
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:Vernam.Views"
mc:Ignorable="d"
Height="540"
Width="900">
<Grid Background="#035251">
...
</Grid>
</UserControl>
CorrespondensesView.xaml.cs
public partial class CorrespondensesView : UserControl
{
public CorrespondensesView()
{
InitializeComponent();
}
}
ProfileView.xaml:
<UserControl x:Class="Vernam.Views.ProfileView"
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:Vernam.Views"
mc:Ignorable="d"
Height="540"
Width="900">
<Grid Background="#035251">
...
</Grid>
</UserControl>
ProfileView.xaml.cs:
public partial class ProfileView : UserControl
{
public ProfileView()
{
InitializeComponent();
}
}
Here are MainWindow view model
namespace Vernam.ViewModels
{
public class MainViewModel : ObservableObject
{
private RelayCommand corrCommand;
private RelayCommand profileCommand;
private object currentView;
public CorrespondensesViewModel CorrVM { get; set; }
public ProfileViewModel ProfileVM { get; set; }
public object CurrentView
{
get { return currentView; }
set
{
currentView = value;
this.OnPropertyChanged("CurrentView");
}
}
public RelayCommand CorrCommand
{
get
{
return corrCommand ??
(corrCommand = new RelayCommand(obj =>
{
CurrentView = CorrVM;
}));
}
}
public RelayCommand ProfileCommand
{
get
{
return profileCommand ??
(profileCommand = new RelayCommand(obj =>
{
CurrentView = ProfileVM;
}));
}
}
public MainViewModel()
{
CorrVM = new CorrespondensesViewModel();
ProfileVM = new ProfileViewModel();
CurrentView = CorrVM;
}
}
}
CorrespondencesViewModel and ProfileViewModel are empty.
And, finally, App.xaml
<Application x:Class="Vernam.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Vernam"
xmlns:viewModel="clr-namespace:Vernam.ViewModels"
xmlns:view="clr-namespace:Vernam.Views"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
...
<DataTemplate DataType="{x:Type viewModel:CorrespondensesViewModel}">
<view:CorrespondensesView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ProfileViewModel}">
<view:ProfileView/>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
You may need to look at the ObservableObject class:
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
Then I run the app, I can actually see view, that I assign to CurrentView in MainViewModel constructor:
public MainViewModel()
{
CorrVM = new CorrespondensesViewModel();
ProfileVM = new ProfileViewModel();
CurrentView = CorrVM;
}
If I assign CorrVM or ProfileVM to CurrentView, I actually see CorrespondensesView or ProfileView, but I can't change view dynamically:
RadioButton Command binding works properly, CurrentView is reassigned every time I click on the corresponding button, but I can't see any changes in MainWindow.
So I think the problem is in the binding, do you have any ideas how to fix it?
UPD:
Get section of this property is called only during the initialization, so the problem is definitely with binding.
public ObservableObject CurrentView
{
get { return currentView; }
set
{
currentView = value;
this.OnPropertyChanged("CurrentView");
}
}
Tried to use different binding modes and update triggers, but to no avail.
<ContentControl Grid.Row="1"
Content="{Binding Path=CurrentView, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}"/>
I believe that because you are using two different view model instances for the radio buttons and the content view in MainWindow.
Here you use an instance from the window resource.
<StackPanel Grid.Column="2" Orientation="Horizontal"
DataContext="{Binding Source={StaticResource vm}}">
Here you use an instance from the data context of MainWindow.
<ContentControl Grid.Row="1" Grid.Column="0"
Content="{Binding CurrentView}" />
So the fixing is quite simple, use only one of them.
Try the following:
Create the following folders:
Model
Theme
View
ViewModel
Add the files below to the appropriate folder (see image above).
HeadButtonTheme.xaml (ResourceDictionary)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}"
x:Key="HeadButtonTheme">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{TemplateBinding Background}">
<TextBlock Text="{TemplateBinding Property=Content}"
VerticalAlignment="Center"
Margin="5, 0, 0, 0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#22202f" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
ObservableObject.cs (Class)
using System;
using System.ComponentModel;
using System.Windows.Input;
using System.Runtime.CompilerServices;
namespace Vernam
{
class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
}
}
RelayCommand.cs (Class)
using System;
using System.Windows.Input;
namespace Vernam
{
class RelayCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested += value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
CorrespondencesViewModel.cs (Class)
namespace Vernam.ViewModel
{
class CorrespondencesViewModel
{
}
}
CorrespondencesView.xaml (UserControl)
<UserControl x:Class="Vernam.View.CorrespondencesView"
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:Vernam.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="Correspondences" Foreground="Red"/>
</Grid>
</UserControl>
ProfileViewModel.cs (Class)
namespace Vernam.ViewModel
{
class ProfileViewModel
{
}
}
ProfileView.xaml (UserControl)
<UserControl x:Class="Vernam.View.ProfileView"
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:Vernam.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock Text="Profile" Foreground="Red"/>
</Grid>
</UserControl>
MainViewModel.cs (Class)
namespace Vernam.ViewModel
{
class MainViewModel : ObservableObject
{
public RelayCommand CorrCommand { get; set; }
public RelayCommand ProfileCommand { get; set; }
public CorrespondencesViewModel CorrVM { get; set; }
public ProfileViewModel ProfileVM { get; set; }
private object currentView;
public object CurrentView
{
get { return currentView; }
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
public MainViewModel()
{
//create new instance
CorrVM = new CorrespondencesViewModel();
ProfileVM = new ProfileViewModel();
CorrCommand = new RelayCommand(obj =>
{
CurrentView = CorrVM;
});
ProfileCommand = new RelayCommand(obj =>
{
CurrentView = ProfileVM;
});
//set default view
CurrentView = CorrVM;
}
}
}
App.xaml
<Application x:Class="Vernam.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Vernam"
xmlns:viewModel="clr-namespace:Vernam.ViewModel"
xmlns:view="clr-namespace:Vernam.View"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme\HeadButtonTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type viewModel:CorrespondencesViewModel}">
<view:CorrespondencesView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ProfileViewModel}">
<view:ProfileView />
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
MainWindow.xaml
<Window x:Class="Vernam.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:Vernam"
xmlns:viewModel="clr-namespace:Vernam.ViewModel"
xmlns:view="clr-namespace:Vernam.View"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<viewModel:MainViewModel x:Key="vm"></viewModel:MainViewModel>
</Window.Resources>
<Border Background="LightGray">
<Grid DataContext="{Binding Source={StaticResource vm}}">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="350" />
<ColumnDefinition Width="350" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal">
<RadioButton x:Name="CorrRadioButton"
Content="Correspondences"
Width="176"
Foreground="White"
FontSize="18"
Style="{StaticResource HeadButtonTheme}"
GroupName="Head"
IsChecked="True"
Command="{Binding CorrCommand}"/>
<RadioButton x:Name="ProfileRadioButton"
Content="Profile"
Width="89"
Foreground="White"
FontSize="18"
Style="{StaticResource HeadButtonTheme}"
GroupName="Head"
Command="{Binding ProfileCommand}"/>
</StackPanel>
</Grid>
<ContentControl Grid.Row="1" Grid.Column="0"
Content="{Binding CurrentView}" />
</Grid>
</Border>
</Window>
Resources:
WPF C# Professional Modern Flat UI Tutorial

Binding property of MainViewModel to SubView

I'm trying to figure out how to bind a property from my MainWindowViewModel to a ContentControl that is based on another View.
RelativeSource Binding seems not to work since the View is in another xaml file?
I tried with dependancy property but I didn't understand how to do this correctly I guess.
What I want to achieve here is that when I type in the TextBox of the MainWindow that all 3 views (contentcontrols) also view the updated data. It should be a demo to illustrate that in MVVM the ViewModel can change without knowledge of the Views and different Views react to it.
Sadly RelativeSource Binding and DependancyProperty didn't work for me or I missed a point.
MainWindow.xaml
<Window x:Class="MVVMDemo.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<TextBlock Text="MVVM Demo" HorizontalAlignment="Center" FontSize="20" FontWeight="Bold"/>
<TextBox Text="{Binding TestString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="200" Background="Black" Foreground="White" Margin="0 20 0 0"/>
<Grid Margin="0 20 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
</Grid.RowDefinitions>
<ContentControl Grid.Column="0" Grid.Row="0" Content="{Binding ViewOne}"/>
<ContentControl Grid.Column="1" Grid.Row="0" Content="{Binding ViewTwo}"/>
<ContentControl Grid.Column="2" Grid.Row="0" Content="{Binding ViewThree}"/>
</Grid>
</StackPanel>
</Window>
MainWindowViewModel
public class MainWindowViewModel : ViewModelBase
{
/// <summary>
/// String to change the reaction of views
/// </summary>
private string _TestString;
public string TestString
{
get { return _TestString; }
set { _TestString = value; NotifyPropertyChanged(); }
}
private object _ViewOne { get; set; }
public object ViewOne
{
get { return _ViewOne; }
set { _ViewOne = value; NotifyPropertyChanged(); }
}
private object _ViewTwo { get; set; }
public object ViewTwo
{
get { return _ViewTwo; }
set { _ViewTwo = value; NotifyPropertyChanged(); }
}
private object _ViewThree { get; set; }
public object ViewThree
{
get { return _ViewThree; }
set { _ViewThree = value; NotifyPropertyChanged(); }
}
public MainWindowViewModel()
{
ViewOne = new ViewOne();
ViewTwo = new ViewTwo();
ViewThree = new ViewThree();
TestString = "ABC";
}
}
ViewOneViewModel
<UserControl x:Class="MVVMDemo.Views.ViewOne"
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:MVVMDemo.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="White">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=TestString, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Foreground="Black"/>
<TextBlock Text="{Binding TestStringProperty}" Foreground="Black"/>
</StackPanel>
</Grid>
</UserControl>
ViewOneViewModel
public class ViewOneViewModel : ViewModelBase
{
// this doesn't help
public static readonly DependencyProperty TestStringProperty =
DependencyProperty.Register("TestString", typeof(string), typeof(MainWindowViewModel), new PropertyMetadata(null));
}
I believe your issue lies here:
<TextBlock Text="{Binding Path=TestString, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Foreground="Black"/>
Binding Path=TestString should instead be Binding Path=DataContext.TestString as the RelativeSource is looking at the window now, not the window's model.

WPF database Null Reference Exception

I have the following database http://merc.tv/img/fig/Northwind_diagram.jpg and I'm making a WPF application that when I click on an employee, it shows me the orders done by that employee. Whenever I run the code, I get a NullReferenceException at this part:
public List<Order> orders {
get { return selEmp.Orders.ToList(); }
}
This is my WPF code:
<Window x:Class="_ForPLUENorthwind1.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:_ForPLUENorthwind1"
xmlns:localn="clr-namespace:_ForPLUENorthwind1.model"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ObjectDataProvider x:Key="vm" ObjectType="{x:Type localn:viewmodel}" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource vm}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="liemp" Grid.Column="0" Grid.Row="0"
ItemsSource="{Binding Allemp}"
DisplayMemberPath="FirstName"
SelectedValuePath="EmployeeID"
SelectedItem="{Binding Path=selEmp, Mode=TwoWay}"
/>
<ListBox x:Name="liorders" Grid.Column="1" Grid.Row="0" ItemsSource="{Binding orders}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text="{Binding OrderID}" />
<Run Text="{Binding OrderDate}" />
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="2" Grid.Row="0">
<StackPanel>
<TextBlock Text="" />
<TextBox />
<TextBlock Text="" />
<TextBox />
<TextBlock Text="" />
<TextBox />
</StackPanel>
<Button x:Name="edit">Edit</Button>
<Button x:Name="add" >Add</Button>
</StackPanel>
</Grid>
</Window>
And this is my viewmodel.cs:
class viewmodel : INotifyPropertyChanged
{
NorthwindEntities db = new NorthwindEntities();
public event PropertyChangedEventHandler PropertyChanged;
private Employee _emp;
public List<Employee> Allemp {
get { return db.Employees.ToList(); }
}
public Employee selEmp {
get { return _emp; }
set {
_emp = value;
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs("orders"));
}
}
}
public List<Order> orders {
get { return selEmp.Orders.ToList(); }
}
}
The second ListBox should contain the Orders
Update: When I go through the software using debug mode, it runs and shows all the orders, but I still get the null reference
First of all, use this for setter:
set {
_emp = value;
NotifyPropertyChange();
if _emp != null
NotifyPropertyChange("orders");
}
And the NotifyPropertyChange should be
private void NotifyPropertyChange([CallerMemberName]string prop = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
}
And change the orders to:
public List<Order> orders {
get { return (selEmp.Orders==null? null : selEmp.Orders.ToList()); }
}
See if you getting NullReferenceException at this point. Even then, you might have problems with the binding. You should use ObservableCollection for binding, not List.

How to dynamically change DataTemplate according to bound object's type?

I'm trying to create a DataTemplate for a View, to show a specific UserControl type (like a texbox, combobox, custom control or another View) based on the type of object it is bound to.
I have the following MVVM framework:
FieldView is tied to an instance of FieldPresenter, and should display a <Textblock /> for the "Label" property, and a UserControl or another View for the Value (based on the Type of the value), with it's DataSource set to the Value property of the Presenter. Currently, I do not have the second part working. I can't figure out how to write a WPF template for what I need.
ViewModel:
public class FieldPresenter : Observable<object>, IFieldPresenter, INotifyPropertyChanged
{
public FieldPresenter() { }
public FieldPresenter(object value)
{
Value = value;
}
object IFieldPresenter.Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
OnPropertyChanged("Value");
}
}
private string _label;
public virtual string Label
{
get
{
return _label;
}
private set
{
_label = value;
OnPropertyChanged("Label");
}
}
}
View:
<UserControl x:Class="My.Views.FieldView"
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:ViewModels="clr-namespace:My.ViewModels"
mc:Ignorable="d"
d:DesignHeight="24" d:DesignWidth="100">
<UserControl.DataContext>
<ViewModels:FieldPresenter/>
</UserControl.DataContext>
<UserControl.Template>
<ControlTemplate>
<Grid Margin="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Key" />
</Grid.ColumnDefinitions>
<StackPanel Margin="0,0,0,0" HorizontalAlignment="Stretch" Width="{Binding RelativeSource={RelativeSource AncestorType=Grid}, Path=ActualWidth}">
<TextBlock Text="{Binding Label}" FontWeight="Bold" Height="32" HorizontalAlignment="Stretch"/>
<TextBox Text="{Binding Value}" Height="Auto" HorizontalAlignment="Stretch"/>
</StackPanel>
</Grid>
</ControlTemplate>
</UserControl.Template>
</UserControl>
I'm curious if what I'm trying to do is even possible, or if I can workaround it by making my Presenter viewmodel return a UserControl rather than an object value, and have the Presenter parse the UserControl Type from the object type, but I don't feel like my Presenter should be instantiating Controls (or what is technically an unbound view). Should I make an interface, something like IViewAs<controlType> { controlType View { get; } }?
How else would I replace <TextBox Text="{Binding Value}" /> in the above script with some kind of template of a UserControl based on the databound object's type?
You almost certainly want a ContentTemplateSelector :
Code:
using System.Windows;
using System.Windows.Controls;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Primitive primitive;
primitive = new Sphere();
// primitive = new Cube();
DataContext = primitive;
}
}
internal abstract class Primitive
{
public abstract string Description { get; }
}
internal class Cube : Primitive
{
public override string Description
{
get { return "Cube"; }
}
}
internal class Sphere : Primitive
{
public override string Description
{
get { return "Sphere"; }
}
}
public class MyTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var frameworkElement = container as FrameworkElement;
if (frameworkElement != null && item != null)
{
if (item is Cube)
{
return frameworkElement.FindResource("CubeTemplate") as DataTemplate;
}
if (item is Sphere)
{
return frameworkElement.FindResource("SphereTemplate") as DataTemplate;
}
}
return base.SelectTemplate(item, container);
}
}
}
XAML:
<Window 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:local="clr-namespace:WpfApplication1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="Window"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<local:MyTemplateSelector x:Key="myTemplateSelector" />
<DataTemplate x:Key="CubeTemplate" DataType="local:Cube">
<Border BorderBrush="Blue"
BorderThickness="1"
CornerRadius="5" />
</DataTemplate>
<DataTemplate x:Key="SphereTemplate" DataType="local:Sphere">
<Border BorderBrush="Red"
BorderThickness="1"
CornerRadius="50" />
</DataTemplate>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="1*" />
<RowDefinition />
</Grid.RowDefinitions>
<Label Grid.Row="0"
Content="{Binding Description}"
d:DataContext="{d:DesignInstance local:Primitive}" />
<ContentControl Grid.Row="1"
Content="{Binding}"
ContentTemplateSelector="{StaticResource myTemplateSelector}" />
</Grid>
</Window>
Result:
See the documentation for more:
https://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector(v=vs.110).aspx

Navigate to other page IocContainers and MVVM light

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/

Categories

Resources