As the title says, I'm looking for a way to assign keyboard shortcuts from a user at runtime, using WPF MVVM pattern. I know that I can define keyboard shortcuts at start like this:
<Window.InputBindings>
<KeyBinding Command="{Binding MyCommand}" Key="A"/>
</Window.InputBindings>
I've also seen that there's a way to parse input bindings from a user. What I'm struggling with, however, is binding an inputbinding from my ViewModel to the MainWindow's InputBinding. I don't know how to achieve this. Here's the code in my MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel();
}
}
And here's some sample code from my ViewModel:
public partial class MainWindowViewModel : Window, INotifyPropertyChanged
{
public MainWindowViewModel()
{
KeyBinding kb = new KeyBinding { Key = Key.T, Command = MyCommand };
this.InputBindings.Add(kb);
}
}
I know that the this.InputBindings.Add(kb); part should probably be replaced with something else; adding the keybinding to the MainWindow's InputBinding instead. However, I don't know how to do this with the MVVM pattern. Therefore: how would I go about doing this?
You might define the input bindings in the view model, but you still need to add them to the view somehow.
You could for example use an attached behaviour that does this for you:
public class InputBindingsBehavior
{
public static readonly DependencyProperty InputBindingsProperty = DependencyProperty.RegisterAttached(
"InputBindings", typeof(IEnumerable<InputBinding>), typeof(InputBindingsBehavior), new PropertyMetadata(null, new PropertyChangedCallback(Callback)));
public static void SetInputBindings(UIElement element, IEnumerable<InputBinding> value)
{
element.SetValue(InputBindingsProperty, value);
}
public static IEnumerable<InputBinding> GetInputBindings(UIElement element)
{
return (IEnumerable<InputBinding>)element.GetValue(InputBindingsProperty);
}
private static void Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
UIElement uiElement = (UIElement)d;
uiElement.InputBindings.Clear();
IEnumerable<InputBinding> inputBindings = e.NewValue as IEnumerable<InputBinding>;
if (inputBindings != null)
{
foreach (InputBinding inputBinding in inputBindings)
uiElement.InputBindings.Add(inputBinding);
}
}
}
View Model:
public partial class MainWindowViewModel
{
public MainWindowViewModel()
{
KeyBinding kb = new KeyBinding { Key = Key.T, Command = MyCommand };
InputBindings.Add(kb);
}
public List<InputBinding> InputBindings { get; } = new List<InputBinding>();
public ICommand MyCommand => ...
}
View:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="Window18" Height="300" Width="300"
local:InputBindingsBehavior.InputBindings="{Binding InputBindings}">
<Grid>
</Grid>
</Window>
If these are to be persisted so they work next time the user runs the app then you could consider creating a resource dictionary as a string or uncompiled flat file.
This would allow you to work with xaml as strings. You could write that to disk and xamlreader.load into a resource dictionary then merge it into application resources.
https://social.technet.microsoft.com/wiki/contents/articles/28797.wpf-dynamic-xaml.aspx
This approach offers several benefits:
The styling is easily persisted.
You can try it out and see what's going on.
You can write a file to disk using a model method called from your viewmodel.
Related
I am new to creating apps. I am trying to build a WPF app that requires me to have a combobox with hundreds of items. I have all of these items saved in a txt/excel file. Obviously, I don't want to hardcode all the options in XAML/C#.
I have no idea how to go about this. I tried to store the list as a resource but don't know how to access the resource once I've put it in there.
I am looking for the easiest approach that can accomplish what I need.
Thanks!
To get the values in your txt/csvfile is trivial (What's the fastest way to read a text file line-by-line?). To get these values to update your ComboBox's contents is actually fairly involved, but the process is at the heart of WPF and MVVM.
The basic idea is to bind an ObservableCollection<string> object to the ItemSource property in your ComboBox and fill it with the items in your text file. I read the file in the constructor of my view model (more on that below) and put all the lines in the Collection (which is bound to the box) on startup. You can also do this elsewhere dynamically if need be.
The usual way to wire it up to the GUI is to use a DataContext in your MainWindow.xaml file. Typically this is done using the Model View ViewModel (MVVM) pattern. The ViewModel is responsible for communicating between the business logic (Model) and the GUI (View). In WPF this is done by having the ViewModel handle event changes in the GUI and also notify the GUI when the data from the model changes. Here I do this with a class called Notifier that implements INotifyPropertyChanged. Then, ViewModel inherits this class and can talk to the GUI via bindings in the xaml. There are other ways of doing the View notifying in WPF/MVVM, but I find this is the simplest. Every WPF project I work on has this class in it.
You will also need bindings for the SelectedItem and SelectedIndex properties in your ComboBox. By binding these to properties that notify in your view model (via calling Update in the setter) you can control all the behavior you need.
Note that I had to remove the StartupUri="MainWindow.xaml" line in the App.xaml file because I instantiate the MainWindow object in the code behind.
Here is how I did it using the standard Visual Stduio WPF application template. Just to clarify how this conforms to MVVM: the 'View' is the GUI itself (defined in MainWindow.xaml), the 'ViewModel' is the ViewModel class, and the 'Model is trivial. It is just a static set of items from the text file to shove into the combobox.
App.xaml:
<Application x:Class="ComboBoxDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ComboBoxDemo">
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs:
using System.Windows;
namespace ComboBoxDemo
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
ViewModel viewModel = new();
MainWindow mainWindow = new() { DataContext = viewModel };
mainWindow.Show();
}
}
}
MainWindow.xaml:
<Window x:Class="ComboBoxDemo.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:ComboBoxDemo"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:ViewModel}"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ComboBox Grid.Column="0"
ItemsSource="{Binding Items, Mode=OneWay}"
SelectedItem="{Binding SelectedItem, Mode=OneWay}"
SelectedIndex="{Binding ItemIndex}"
Margin="5"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace ComboBoxDemo
{
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
}
}
ViewModel.cs
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
namespace ComboBoxDemo
{
public abstract class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged = null;
protected void Update<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return;
field = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChangedEventHandler? handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ViewModel : Notifier
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
// event handling for gui/business logic
// private fields for bound variables
private string _selectedItem = "";
private int _itemIndex = 0;
// properties for binding to GUI
public ObservableCollection<string> Items { get; } = new();
public string SelectedItem
{
get => _selectedItem;
set => Update(ref _selectedItem, value);
}
public int ItemIndex
{
get => _itemIndex;
set
{
Update(ref _itemIndex, value);
// here you can use the index to affect business logic as well
}
}
public ViewModel()
{
Items = new();
string textFile = "items.txt";
using StreamReader file = new StreamReader(textFile);
string? line;
while ((line = file.ReadLine()) is not null)
Items.Add(line);
}
}
}
and my combobox items come from 'items.txt' which I put in the same directory as the .exe:
item1
item2
item3
Screenshot of output:
I've made a user control which contains a command, to be called in response to a certain event. This command is a dependency property. I want to use it in the main window like this:
<local:myUserControl Command="{Binding someCommand}"/>
The "myCommand" is the dependency property I created for this user control. And I bind it to a command of the view model of the main window ("someCommand").
The problem is that I am setting the datacontext of my usercontrol (I have a view model for it), and it seems to reset the "Command" to null… Here is the code-behind of my view model:
public partial class myUserControl : UserControl, ICommandSource
{
public myUserControl()
{
this.DataContext = new myViewModel();
InitializeComponent();
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(myUserControl), new PropertyMetadata(null));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(myUserControl), new PropertyMetadata(0));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(myUserControl), new PropertyMetadata(null));
private void TextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{
Command.Execute(this.CommandParameter);
}
}
The code of my user control could be the Following:
<UserControl x:Class="myApp.myUserControl"
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:myApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock MouseUp="TextBlock_MouseUp">
</TextBlock>
</Grid>
</UserControl>
(I know that this element seems a bit silly (or useless), but I have simplified it to test what didn't worked and also in order to ask a rather simple question).
I have discovered that, if I comment the "this.DataContext = new myViewModel();" line, the binding to the command works perfectly. And when I uncomment this line and put a breakpoint in the "TextBlock_MouseUp", the "Command" property is equal to null...
Would there be a way to resolve this problem? I have some complicated code in my view model (so I'm quite forced to keep this line "this.DataContext = new myViewModel();"), and I am not sure I could find another solution than having a "Command" dependency property in my user control…
To be sure I give a maximum of informations, I have the following code in the view model of my main window:
public ICommand someCommand { get; set; }
//Constructor
public MainWindowViewModel()
{
this.someCommand = new RelayCommand((obj) => { return true; },
(obj) =>
{
//I put a breakpoint here
int dummy = 0;
});
}
(The RelayCommand class is a standard RelayCommand class, with a "Predicate" CanExecute and an "Action Execute).
I hope this question is not a duplicate… I have found several similar question, but they did not seem to answer mine...
I'm really sorry for this question which was in fact a bit silly. I hadn't understand very well what happens during a binding. I thought that this code line in the MainWindow…
<local:myUserControl Command="{Binding someCommand}"/>
…would have made an attempt to bind the UserControl's "Command" property to the "someCommand" of the datacontext of the MainWindow. In fact, as #elgonzo pointed out, the binding looks up in the UserControl's datacontext for the "someCommand" property (and not in the MainWindow's datacontext!!). Therefore, setting the UserControl's datacontext with this line…
this.DataContext = new myViewModel();
...was preventing the binding to be correctly done (since it looks for the "someCommand" property of the UserControl's datacontext, which is now "myViewModel", which does not contain "someCommand"...).
To fix this, I had to change the binding like this:
<local:myUserControl Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.someCommand}"/>
I've found this solution here: https://stackoverflow.com/a/1127964/11609068.
Maybe it is not the best way to do it (the "Path= DataContext. someCommand" make me think this, it doesn't seem very elegant), but it works. Another way to do it is to name the MainWindow (x:Name="someName"), so that the binding is a bit simpler:
<local:myUserControl Command="{Binding ElementName=someName, Path=DataContext.someCommand}"/>
Again, sorry and many thanks to #elgonzo.
I am developing a Windows Store App using the MVVM pattern (no framework, just raw MVVM).
I have a user control DropboxFileplanUserControl.xaml which has an associated view model DropboxFileplanViewModel.cs. DropboxFileplanUserControl is embedded in MainPage.xaml and MainPage.xaml has an associated view model, MainPageViewModel.cs.
My question is how can I define and raise an event in DropboxFileplanViewModel.cs and handle it in MainPageViewModel.cs? Assume the event to be raised is called ImageLoaded.
EDIT: I have added the following specific code snippets...
DropboxFileplanUserControl.xaml
<UserControl
x:Class="PhotoBox.Controls.DropboxFileplanUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PhotoBox.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:PhotoBox.ViewModels"
xmlns:triggers="using:WinRT.Triggers"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="200">
<UserControl.DataContext>
<viewModels:DropboxFileplanViewModel />
</UserControl.DataContext>
<Grid>
<ListBox>
<!--
...
...
Here I define a ListBox and use interaction triggers to bind the SelectionChanged event to FileplanSelectionChangedCommand on the ViewModel -->
<triggers:Interactions.Triggers>
<triggers:EventTrigger EventName="SelectionChanged">
<triggers:InvokeCommandAction Command="{Binding FileplanSelectionChangedCommand}" PassEventArgsToCommand="True" />
</triggers:EventTrigger>
</triggers:Interactions.Triggers>
</ListBox>
</Grid>
DropboxFileplanViewModel.xaml Note: I've stripped out a lot of code from this snippet
public class DropboxFileplanViewModel : ViewModel
{
public DelegateCommand FileplanSelectionChangedCommand { get; set; }
public DropboxFileplanViewModel()
{
FileplanSelectionChangedCommand = new DelegateCommand(FileplanSelectionChanged);
}
private void FileplanSelectionChanged(object parameter)
{
var args = (SelectionChangedEventArgs) parameter;
// Some other stuff is done here but ultimately,
// GetImageFile is called
}
private async void GetImageFile(MetaData file)
{
// Stuff is done here to get the image
// ******************************
// Here I want to raise the event
// ******************************
}
}
DropboxFileplanUserControl is embedded in MainPage.xaml as follows...
MainPage.xaml
<controls:DropboxFileplanUserControl
Grid.Row="0"
DataContext="{Binding FileplanControl}"
Visibility="{Binding IsOpen, Converter={StaticResource BooleanToVisibilityConverter}}"
IsEnabled="{Binding IsOpen}"
<!-- *** Here I need to access the ImageLoaded event and bind it to a command in MainPageViewModel.cs *** -->
/>
So, to summarise, I need to declare and raise an event in DropboxFileplanViewModel.cs and access this event in MainPage.xaml so I can then handle it in MainPageViewModel.cs. I know how to bind the event in MainPage.xaml to a command in MainPageViewModel, I just need to know how to do the first bit, i.e. declaring and raising an event in DropboxFileplanViewModel.cs and accessing it in MainPage.xaml.
In XAML:
<Image Loaded="ImageLoaded" ... />
In xaml.cs:
public MainPageViewModel ViewModel
{
get
{
return this.DataContext as MainPageViewModel;
}
}
public void ImageLoaded( object sender, RoutedEventArgs args )
{
// call down to your view model
if( ViewModel != null )
{
ViewModel.ImageLoadedHandler( );
}
}
In response to your comment, the idea is the same for the custom UserControl. I have (what I think is) and interesting solution I don't often see others implement. It's the idea that each ViewModel has an associated View (I call it owner) and a logical parent. Similar to the visual tree XAML/WinRT constructs that allows for traversal of UI elements, the parent/owner relationship we can create in our ViewModels allow this same style of traversal in our back-end code. Consider the following:
Assume we have a custom UserControl called MyUserControl that resides in the namespace MyProject.
In MainPageView.xaml:
<Page xmlns:local="MyProject"> // or whatever your fancy-pants namespace scheme is
<Grid>
<local:MyUserControl DataContext="{Binding InnerVM}" />
</Grid>
</Page>
In you MainPageView.xaml.cs
public MainPageViewModel ViewModel
{
get
{
return this.DataContext as MainPageViewModel;
}
}
public MainPageView()
{
InitializeComponent( );
DataContext = new MainPageViewModel( null, this );
}
We're getting there. Now let's look at MainPageViewModel.cs
public MainPageViewModel : ViewModelBase // I'll explain ViewModelBase momentarily
{
public MyUserControlViewModel InnerVM { get; set; } // should be a notifying property
public MainPageViewModel( ViewModelBase parent, FrameworkElement owner )
: base( parent, owner )
{
}
}
For all intents and purposes, MyUserControlViewModel.cs is the same.
Here is ViewModelBase.cs (with some abridgments):
public ViewModelBase
{
public ViewModelBase Parent { get; set; } // should be a notifying property
public FrameworkElement Owner { get; set; } // should be a notifying property
public ViewModelBase( ViewModelBase parent, FrameworkElement owner )
{
Parent = parent;
Owner = owner;
}
}
Simple! Right? Now what does that actually do for us? Let's see. Consider the following:
In MyUserControl.xaml:
<Image Loaded="ImageLoaded" ... />
In MyUserControl.xaml.cs:
public MyUserControlVieWModel ViewModel
{
get
{
return this.DataContext as MyUserControlVieWModel;
}
}
public void ImageLoaded( object sender, RoutedEventArgs args )
{
// call down to your view model
if( ViewModel != null )
{
ViewModel.ImageLoadedHandler( );
}
}
MyUserControlViewModel.cs
Now you have two options here (and I just realized I may be over-explaining the issue for you, and I apologize. Please heavily consider option 1 for your question):
1- Use Events!
public event EventHandler ImageLoaded = delegate { };
public void OnImageLoaded( )
{
ImageLoaded( );
}
Then in MainPageViewModel.cs
public void OnImageLoaded( )
{
// handle the image loading
}
And now maybe put this in your constructor:
...
InnerVM.ImageLoaded += OnImageLoaded;
...
Now when the event is fired from within MyUserControl, MainPageViewModel will be able to respond.
The second option requires more explination, and I have to run for now. But hopefully this gets you going. Sorry for the short ending. Please respond with questions if you need to. Good luck!
I've been working with the MVVM model for a week or so now and I think I have a handle on what should go where now. Note the "think" in that.
I have a single ViewModel that my view (MainWindow) binds to
_ViewModel = new MainViewModel();
this.DataContext = _ViewModel;
I have a few ICommands that do work within the ViewModel and subsequently the Model, which I'm fine with.
Now I initiate a few windows from my View (MainWindow) which I do in codebehind, as it's all purely view related stuff. I am trying to replicate the ICommand setup I have in the ViewModel in the View to simplify my life, or so I thought. I have the following commands set-up:
public ICommand comInitialiseWindows { get; private set; }
private bool _windowsactive = false;
public bool WindowsActive
{
get { return _windowsactive; }
set { SetProperty(ref _windowsactive, value); }
}
public bool comInitialiseWindows_CAN()
{
return !_windowsactive;
}
private void comInitialiseWindows_DO()
{
... Code to do the window creation, etc.
}
I have this relay command in the MainWindow code:
comInitialiseWindows = new RelayCommand(() => comInitialiseWindows_DO(), comInitialiseWindows_CAN);
If I put this in the ViewModel it works a treat apart from the window creation stuff, but as it's View related I'm not surprised.
So the problem is the code doesn't run when I click the button. I'm guessing that the XAML is bound to the ViewModel, but I can't figure a way around this without setting the Binding for each button to the MainWindow in codebehind. I had assumed that the following would work, but it doesn't:
<Button x:Name="ribbutLayoutWindows"
Command="{Binding local:comInitialiseWindows}"
IsEnabled="{Binding local:comInitialiseWindows_CAN}"/>
I'm pretty sure I'm just not getting something somewhere. Or I'm trying to overcomplicate matters where a normal button click would have sufficed as it's View only.
Any suggestions?
There are two possibilities:
Through the ViewModel:
You could expose a Property on your ViewModel:
class MainViewModel
{
ICommand comInitialiseWindows {get; set;}
}
And in your MainWindow:
MainViewModel vm = this.DataContext as MainViewModel;
vm.comInitialiseWindows = new RelayCommand(() => comInitialiseWindows_DO(), comInitialiseWindows_CAN);
XAML:
<Button x:Name="ribbutLayoutWindows" Command="{Binding comInitialiseWindows}" />
Note: you don't need to bind the IsEnabled property. WPF will handle that for you and automatically call into the CanExecute-method of your ICommand.
Through a DependencyProperty
Declare this dependecyProperty in your code-behind:
public ICommand comInitialiseWindows
{
get { return (ICommand)GetValue(MyPropertyProperty); }
set { SetValue(MyPropertyProperty, value); }
}
public static readonly DependencyProperty comInitialiseWindowsProperty =
DependencyProperty.Register("comInitialiseWindows", typeof(ICommand), typeof(MainWindow), new PropertyMetadata(null));
Assign a value in the code-behind:
comInitialiseWindows = new RelayCommand(() => comInitialiseWindows_DO(), comInitialiseWindows_CAN);
After that, you need to break out of your data-context in the XAML. First of all, give your Page a name:
<Window x:Class="Web_Media_Seeker_WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:Web_Media_Seeker_WPF"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="myWindow"
Title="MainWindow" Height="350" Width="525">
And then declare your binding as follows:
<Button x:Name="ribbutLayoutWindows" Command="{Binding comInitialiseWindows, ElementName=myWindow}" />
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");
}
}