Just want to make mvvm dialog using answer from this question Using MVVM show new window and get updates data. I also use example from MVVM survival guide book.
I have this ViewModel class for dialog:
public class OrganizationsViewModel
{
public OrganizationsViewModel()
{
TestProp = "TEST prop";
}
public override string ToString()
{
return "Organization";
}
public string TestProp { get; set; }
}
ShowDialog method in DialogService
public void ShowDialog(OrganizationsViewModel viewModel)
{
var dialog = new DialogView() { DataContext = viewModel };
dialog.Owner = Application.Current.MainWindow;
dialog.ShowInTaskbar = false;
dialog.ShowDialog();
}
DialogView.xaml:
<Window x:Class="testlayout.DialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DialogView" Height="300" Width="300">
<StackPanel>
<ContentControl Content="{Binding}" />
</StackPanel>
</Window>
DataTemplate in Application.xaml
<Application.Resources>
<DataTemplate x:Key="OrganizationsTemplate" DataType="{x:Type vm:OrganizationsViewModel}">
<vw:OrganizationsView/>
</DataTemplate>
</Application.Resources>
I call ShowDialog from MainWindowViewModel
DialogService.Instance.ShowDialog(new OrganizationsViewModel());
And I can see only Organization string in dialog, but don't see OrganizationsView. Don't understand what is wrong.
Related
I get this error:- System.NullReferenceException: 'Object reference not set to an instance of an object.'
objectPlacement was null.
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectPlacement w = new ObjectPlacement() {Topmost = };// ObjectPlacement is new WPF window
objectPlacement.WindowStyle = WindowStyle.None;
settingpanel.Children.Add(objectPlacement);//settingpanel stack is panel name
w.Show();
}
It would be much more usual to define a usercontrol or datatemplate for whatever you're trying to show in your window. A window is a kind of content control. One way to think of a window ( or contentcontrol ) is something that shows you some UI. All the UI in a window is that content.
When you add window to a project it is templated out with a grid in it.
This is the content and everything you want to see in that window goes in it.
You could replace that grid with something else instead.
If you made that a contentpresenter then you can bind or set what that'll show to some encapsulated re-usable UI.
Usually the best way to encapsulate re-usable UI is as a usercontrol.
A datatemplate can reference a usercontrol.
It is not usually your entire UI for a window you want to switch out. But you can and that is occasionally useful - say if you want a generic way to show dialogs.
The usual way to write wpf is mvvm so most devs will want some mvvm way of switching out UI.
I'll show you some code might make the description clearer.
There are some corners cut in what follows, so this is illustrative. Don't just run with this for your next lead developer interview at a stock traders.
But, basically you click a button for Login you "navigate" to a LoginUC view. Click a button for User and you "navigate" to UserUC.
My mainwindow.
<Window.Resources>
<DataTemplate DataType="{x:Type local:LoginViewModel}">
<local:LoginUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:UserViewModel}">
<local:UserUC/>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ItemsControl ItemsSource="{Binding NavigationViewModelTypes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
CommandParameter="{Binding VMType}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentPresenter Grid.Column="1"
Content="{Binding CurrentViewModel}"
/>
</Grid>
</Window>
Notice the datatemplates which associate the type of a viewmodel with a usercontrol.
https://learn.microsoft.com/en-us/dotnet/desktop/wpf/data/data-templating-overview?view=netframeworkdesktop-4.8
What will happen is you present your data in a viewmodel to the UI via that contentpresenter and binding. That viewodel is then templated out into UI with your viewmodel as it's datacontext. The datacontext of a UserUC view will therefore be an instance of UserViewModel. Change CurrentViewModel to an instance of LoginViewModel and you get a LoginUC in your mainwindow instead.
The main viewmodel.
public class MainWindowViewModel : INotifyPropertyChanged
{
public string MainWinVMString { get; set; } = "Hello from MainWindoViewModel";
public ObservableCollection<TypeAndDisplay> NavigationViewModelTypes { get; set; } = new ObservableCollection<TypeAndDisplay>
(
new List<TypeAndDisplay>
{
new TypeAndDisplay{ Name="Log In", VMType= typeof(LoginViewModel) },
new TypeAndDisplay{ Name="User", VMType= typeof(UserViewModel) }
}
);
private object currentViewModel;
public object CurrentViewModel
{
get { return currentViewModel; }
set { currentViewModel = value; RaisePropertyChanged(); }
}
private RelayCommand<Type> navigateCommand;
public RelayCommand<Type> NavigateCommand
{
get
{
return navigateCommand
?? (navigateCommand = new RelayCommand<Type>(
vmType =>
{
CurrentViewModel = null;
CurrentViewModel = Activator.CreateInstance(vmType);
}));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Type and display relates the type for a viewmodel with text displayed in the UI.
public class TypeAndDisplay
{
public string Name { get; set; }
public Type VMType { get; set; }
}
This is "just" quick and dirty code to illustrate a principle which is usually called viewmodel first navigation. Google it, you should find a number of articles explaining it further.
For completeness:
<UserControl x:Class="wpf_Navigation_ViewModelFirst.LoginUC"
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:wpf_Navigation_ViewModelFirst"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Background="Yellow">
<TextBlock Text="This is the Login User Control"/>
<TextBox>
<TextBox.InputBindings>
<KeyBinding Key="Return" Command="{Binding LoginCommand}"/>
</TextBox.InputBindings>
</TextBox>
</StackPanel>
</UserControl>
public class LoginViewModel
{
private RelayCommand loginCommand;
public RelayCommand LoginCommand
{
get
{
return loginCommand
?? (loginCommand = new RelayCommand(
() =>
{
string s = "";
}));
}
}
}
<UserControl x:Class="wpf_Navigation_ViewModelFirst.UserUC"
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:wpf_Navigation_ViewModelFirst"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="pink">
<TextBlock Text="This is the User module Control"
VerticalAlignment="Top"
/>
<TextBlock Text="{Binding Path=DataContext.MainWinVMString, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
VerticalAlignment="Bottom"
/>
</Grid>
</UserControl>
public class UserViewModel
{
}
I put this together some years ago, I would now recommend the community mvvm toolkit with it's code generation, base classes, messenger etc.
I'm pretty new in MvvmCross but I'm working with mvvm for awhile. I know how to compose usercontrols with nested usercontrols. Now with mvvmcross I got stucked to show two or more usercontrols in another usercontrol. I don't use any other framework than MvvmCross.
My Rootview looks like this:
`<views:MvxWpfView
x:Class="MvvmCrossTest.Wpf.Views.RootView"
xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<DockPanel>
<TextBlock Text="Root" DockPanel.Dock="Top"/>
<ContentControl x:Name="MainMenuVM" Content="{Binding MainMenuVM}" DockPanel.Dock="Top" />
</DockPanel>
</views:MvxWpfView>`
The corresponding ViewModel looks like this:
using MvvmCross.Commands;
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
namespace MvvmCrossTest.Core.ViewModels
{
public class RootViewModel: MvxNavigationViewModel
{
private readonly IMvxViewModelLoader _mvxViewModelLoader;
public RootViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService, IMvxViewModelLoader mvxViewModelLoader) : base(logProvider, navigationService)
{
_mvxViewModelLoader = mvxViewModelLoader;
ShowMainMenu();
}
private MainMenuViewModel _mainMenuVM;
public MainMenuViewModel MainMenuVM
{
get { return _mainMenuVM; }
set
{
SetProperty(ref _mainMenuVM, value);
RaisePropertyChanged(() => MainMenuVM);
}
}
public MvxCommand ShowMainMenuCommand { get; set; }
public void ShowMainMenu()
{
MainMenuVM = (MainMenuViewModel)_mvxViewModelLoader.LoadViewModel(MvxViewModelRequest.GetDefaultRequest(typeof(MainMenuViewModel)), null, null);
}
}
}
The simplified View I want to show in the contentcontrol looks like this:
<views:MvxWpfView
x:Class="MvvmCrossTest.Wpf.Views.MainMenuView"
xmlns:views="clr-namespace:MvvmCross.Platforms.Wpf.Views;assembly=MvvmCross.Platforms.Wpf"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
Background="Aqua"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
</Grid>
</views:MvxWpfView>
And also the corresponding ViewModel.
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
namespace MvvmCrossTest.Core.ViewModels
{
public class MainMenuViewModel : MvxNavigationViewModel
{
public MainMenuViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
}
}
}
Instead of the View/ViewModel I see a text that says "MvvmCrossTest.Core.ViewModels.MainMenuViewModel".
I don't want to use Xamarin!
I created a project where I show you some basics.
simple
You can find the samples here.
the RootView;
<views:MvxWpfView xmlns:local="clr-namespace:SomeProject.Views" ...>
<Grid>
<Label Content="Hello from RootView" />
<local:NestedView />
</Grid>
</views:MvxWpfView>
the NestedView;
<views:MvxWpfView ...>
<Grid>
<Label Content="Hello from NestedView" />
</Grid>
</views:MvxWpfView>
extended (custom presenter)
With this custom wpf presenter you can easily navigate to views while specified themn in a container. With that, you can close and open views from the view model while using the mvvmcross navigation service.
I copied this from another project. You can find the whole implementation in my sample project here.
the RootView;
<views:MvxWpfView ...>
<Grid>
<Label Content="Hello" />
<ItemsControl region:MvxContainer.Id="RootViewRegion"/>
</Grid>
</views:MvxWpfView>
the RootView code behind;
[MvxContentPagePresentation(WrapInNavigationPage = true, NoHistory = false)]
public partial class RootView: MvxWpfView<RootViewModel>
{
public RootView()
{
InitializeComponent();
}
}
the RootViewModel;
public class RootViewModel: MvxNavigationViewModel
{
private readonly IMvxNavigationService _navigationService;
public IMvxAsyncCommand ShowNestedViewModelCommand { get; protected set; }
public RootViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
this._navigationService = navigationService;
// navigate to nested view
this.ShowNestedViewModelCommand = new MvxAsyncCommand(() => this._navigationService.Navigate<NestedViewModel>());
ShowNestedViewModelCommand.Execute();
}
}
the NestedView code behind;
[MvxWpfPresenter("RootViewRegion", mvxViewPosition.NewOrExsist)]
public partial class NestedView : MvxWpfView<NestedViewModel>
{
public NestedView()
{
InitializeComponent();
}
}
good to know
when you have problems with navigation and view model connection use this code behind. It loads the viewmodel for the specific view;
[MvxContentPagePresentation(WrapInNavigationPage = true, NoHistory = false)]
public partial class SomeView : MvxWpfView<ViewModels.SomeViewModel>
{
public SomeView()
{
InitializeComponent();
if (!(ViewModel is ViewModels.SomeViewModel))
{
if (Mvx.IoCProvider.TryResolve<ViewModels.SomeViewModel>(out var someViewModel))
{
ViewModel = someViewModel;
return;
}
var _viewModelLoader = Mvx.IoCProvider.Resolve<IMvxViewModelLoader>();
var request = new MvxViewModelInstanceRequest(typeof(ViewModels.SomeViewModel));
request.ViewModelInstance = _viewModelLoader.LoadViewModel(request, null);
ViewModel = request.ViewModelInstance as ViewModels.SomeViewModel;
Mvx.IoCProvider.RegisterSingleton<ViewModels.SomeViewModel>(ViewModel);
}
}
}
it seems that MvvmCross is wiring up Views and ViewModels automatically
I know nothing about MvvmCross, but for "automatically" to work there should be an agreement and rules, e.g. attributes or matching names (xViewModel <-> xView). Currently there is nothing I can see what connects MainMenuViewModel with views:MvxWpfView.
Anyway what you see
I see a text that says "MvvmCrossTest.Core.ViewModels.MainMenuViewModel"
is the view trying to visualize non-control as text due to missing data template.
It's easy to fix by just adding data template
<DataTemplate DataType="{x:Type local:MainMenuViewModel}">
<views:MvxWpfView />
</DataTemplate>
somewhere (e.g. into views:MvxWpfView.Resources) and the view will be displayed.
So my situation is this: I want to be able to use MVVM with my WPF application using MongoDB. I am very new to MVVM (I know very little of it), but I've got some experience using .NET and WPF.
I have a namespace for recalling MongoDB collections, with the Model component stored there as a class called "User"
Model (in a separate namespace):
public class User
{
[BsonElement("_id")]
public ObjectId Id { get; set; }
public string name { get; set; }
// other methods listed here
public async static Task<List<User>> getUserList()
{
// allows me to get a list of users
var col = MongoDBServer<User>.openMongoDB("Users");
var filter = Builders<User>.Filter.Exists("name");
List<User> userList = await col.Find(filter).ToListAsync();
return userList;
}
}
I've created a very basic ViewModelBase (abstract ViewModelBase):
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if(handler == null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
And a derived class for handling the User Lists (ViewModel):
public class UserListViewModel : ViewModelBase
{
private User _user;
private ObservableCollection<User> _userList;
public User user
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("user");
}
}
public ObservableCollection<User> userList
{
get { return _userList; }
set
{
_userList = value;
OnPropertyChanged("userList");
}
}
public UserListViewModel()
{
user = new User();
this.userList = new ObservableCollection<User>();
// since MongoDB operations are asyncrhonous, the async method "getUserList()" is used to fill the observable collection
getUserList().Wait();
}
public async Task getUserList()
{
var UserListRaw = await User.getUserList();
this.userList = new ObservableCollection<User>(UserListRaw);
}
}
The view component is as a simple window with a listbox, as follows (View):
<Window x:Class="UserManagementMVVM.UsersWindow"
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:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.Resources>
<local:UserListViewModel x:Key="ViewModel"/>
<!-- Receiving error for this XAML block saying "Object reference not set to instance of an object -->
</Window.Resources>
<Grid DataContext="{Binding ViewModel}">
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
The App.Xaml and its codebehind are left untouched, as is the View's codebehind.
When I run the program, nothing shows up (ie: The Window starts, but the ListBox is empty even though there is data). I will soon add some button functionality that will perform atomic operations with MongoDB.
I've been trying for nearly 2 weeks to make my own MVVM program for this, with no success. Any assistance would be greatly appreciated.
You are not putting the getUserList() return value into a variable
I assume you mean to do the following
Task.Run(async ()=>this.userList = await getUserList());
this shall work you should think wether you want to wait for the task to finish or not, and than place a .Wait() after it.
Your other issue might be the way you bind to the ViewModel in the context it should use StaticResource instead of binding
like This:
<Grid DataContext="{StaticResource ViewModel}">
<Window x:Class="UserManagementMVVM.UsersWindow"
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:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.DataContext>
<!--You have to set the DataContext -->
<local:UserListViewModel x:Key="ViewModel"/>
</Window.DataContext>
<Grid>
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
You have to set the DataContext right. i changed your xaml. but i prefer setting the DataContext for the Mainwindow in Codebehind or app.xaml.cs.
eg: app.xaml.cs
protected override void OnStartup(StartupEventArgs e)
{
var data = new MainWindowViewmodel();
this.MainWindow = new MainWindow(data);
this.MainWindow.Show();
}
all other DataContext for my views are done with DataTemplates within the ResourceDictionary
<DataTemplate DataType="{x:Type local:MyOtherViewmodel}">
<local::MyOtherViewmodelView />
</DataTemplate>
I want to give credit to both gilMishal and blindmeis for pointing me in the right direction. Both of your answers have helped. Here is my updated (and functional!) code:
App.xaml.cs has been modified as follows (Credit to blindmeis):
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
UsersWindow window = new UsersWindow();
var ViewModel = new UserListViewModel();
window.DataContext = ViewModel;
window.Show();
}
}
The ViewModel has been updated:
public class UserListViewModel : ViewModelBase
{
private User _user;
private ObservableCollection<string> _userList; // changed from "User" class to string
public User user
{
get { return _user; }
set
{
_user = value;
OnPropertyChanged("user");
}
}
public ObservableCollection<string> userList
{
get { return _userList; }
set
{
_userList = value;
OnPropertyChanged("userList");
}
}
public UserListViewModel()
{
userList = new ObservableCollection<string>();
Task.Run(async () => this.userList = await getUserList()); // Credit to gilMishal
}
public async Task<ObservableCollection<string>> getUserList()
{
var UserListRaw = await User.getUserList();
var userListOC = new ObservableCollection<string>();
foreach (var doc in UserListRaw) // extracting the "name" property from each "User" object
{
userListOC.Add(doc.name);
}
return userListOC;
}
}
And the view:
<Window x:Class="UserManagementMVVM.UsersWindow"
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:UserManagementMVVM"
mc:Ignorable="d"
Title="UsersWindow" Height="300" Width="300">
<Window.Resources>
<local:UserListViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid> <!-- data context removed from here, credit blindmeis -->
<ListBox Margin="5" ItemsSource="{Binding userList}"/>
</Grid>
</Window>
I am new to MVVM and WPF but I know what's going on in MVVM. I have a problem with switching between user controls in mainwindow. In my app I have:
MainWindow.xaml with log and 2 links: Show all and Create new. Of course I have ViewModel for it. I have 2 more UserControls: ShowAll and Create with ViewModels and all logic in it (adding data etc). How can I show create form when I click link Create new or show all when I click ShowAll?
In windowForms I just hide UC, buto here is no code behind :)
My MainWindow.xaml:
<Window x:Class="Test.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="300">
<Grid>
<StackPanel>
<TextBox Text="{Binding Name}"/>
<Button Content="Change" Command="{Binding ChangeCommand}"/>
</StackPanel>
</Grid>
</Window>
My MainWindowViewModel:
class MainWindowViewModel : BaseViewModel
{
private Person _person;
private BaseCommand _changeCommand;
public MainWindowViewModel()
{
_person = new Person();
}
public string Name
{
get
{
return _person.Name;
}
set
{
if (_person.Name != value)
_person.Name = value;
OnPropertyChanged(() => Name);
}
}
public ICommand ChangeCommand
{
get
{
if (_changeCommand == null)
_changeCommand = new BaseCommand(() => change());
return _changeCommand;
}
}
private void change()
{
_person = new Person();
Name = _person.Imie;
}
}
In Create and ShowAll there is no code. In xaml only a label, VM is empty. Just for test.
Thank's for help!
You can use a ContentControl to display a specific DataTemplate based on the type of ViewModel that is bound to the ContentControl.
http://www.japf.fr/2009/03/thinking-with-mvvm-data-templates-contentcontrol/
The command that is bound to the ShowAll button can simply change a property on your main ViewModel which is what is bound to your content control.
I am trying my hands on WPF MVVM. I have written following code in XAML
<UserControl x:Class="Accounting.Menu"
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:Accounting"
mc:Ignorable="d"
d:DesignHeight="105" d:DesignWidth="300">
<UserControl.DataContext>
<local:MenuViewModel/>
</UserControl.DataContext>
<StackPanel>
<StackPanel>
<TextBlock Text="{Binding Path=MenuHeader}"/>
</StackPanel>
<ListBox ItemsSource="{Binding Path=MenuItems}" Height="70"/>
</StackPanel>
</UserControl>
I have got a MenuViewModel with properties MenuHeader and MenuItems. I get values in both the properties during runtime. Former is bound to text of TextBlock and latter to ItemSource of ListBox. But when I run the solution, TextBlock and ListBox are empty.
Edit: Code of ViewModel
public class MenuViewModel: ViewModelBase
{
AccountingDataClassesDataContext db;
private string _menuType;
public string MenuHeader { get; set; }
public ObservableCollection<string> MenuItems { get; set; }
public MenuViewModel()
{
}
public MenuViewModel(string menuType)
{
this._menuType = menuType;
db = new AccountingDataClassesDataContext();
if (menuType == "Vouchers")
{
var items = db.Vouchers.OrderBy(t => t.VoucherName).Select(v => v.VoucherName).ToList<string>();
if (items.Any())
{
MenuItems = new ObservableCollection<string>(items);
MenuHeader = "Vouchers";
}
}
else
{
System.Windows.MessageBox.Show("Menu not found");
}
}
}
Thanks in advance.
You are creating your ViewModel in the XAML using your ViewModel's default contructor which does nothing. All your population code is in the non-default contructor which is never called.
The more usual way is to create the ViewModel in code, and inject it into the view either explicitly using View.DataContext = ViewModel, or impllcitly using a DataTemplate.
I think you have to trigger the OnPropertyChanged event. I am not sure if you are using a MVVM library (since you inherit from ViewModelBase you might be using MVVM Light for example), there they wrap the OnPropertyChanged in the RaisePropertyChanged event handler.
Triggering the event will inform WPF to update the UI.
string m_MenuHeader;
public string MenuHeader
{
get
{
return m_MenuHeader;
}
set
{
m_MenuHeader=value; OnPropertyChanged("MenuHeader");
}
}