WPF Change View from a User Control MVVM - c#

I'm trying to navigate from one View Model to another without any side panel.
For example, I have a Main Window View, this is where I load my User Control.
I have tried to access the static instance from the MainViewModel to change the Views, but it's not working.
MainWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vm:FirstViewModel}">
<v:FirstView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:SecondViewModel}">
<v:SecondView/>
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding CurrentViewModel}"/>
MainViewModel.cs
class MainviewModel : ObservableObject
{
private ObservableObject _currentViewModel = new FirstViewModel();
public ObservableObject CurrentViewModel
{
get { return _currentViewModel; }
set
{
_currentViewModel = value;
RaisePropertyChangedEvent("CurrentViewModel");
}
}
private static MainViewModel _instance = new MainViewModel();
public static MainViewModel Instance { get { return _instance; } }
}
Here, I have my FirstView, it just contains a button and several other UI designs
FirstView.xaml
<Button Command="{Binding goToSecondView}" />
FirstViewModel.cs
class FirstViewModel : ObservableObject
{
public ICommand goToSecondView
{
get
{
return new DelegateCommand(() =>
{
MainViewModel.Instance.CurrentViewModel = new SecondViewModel();
});
}
}
}
And I have a SecondView, which is similar to FirstView, just that it navigates to FirstView
I have tried searching for a solution, but so far, I have only managed to find examples that shows buttons on a panel which then allow switching of the User Control from clicking those button.
What I am trying to achieve is to enable switching of User Control via the buttons on the User Control itself, without any side panel.
Any help would be very much appreciated and would definitely aid me in my future projects.
Thank You.

You're creating two different instances of MainViewModel. The first one is probably being created by a locator or something and it's the one that your view is binding to when the window is first created. It's then creating a second instance and assigning it to its own static Instance member:
private static MainViewModel _instance = new MainViewModel();
public static MainViewModel Instance { get { return _instance; } }
It's this instance that your ICommand handler is changing when you press the button, which isn't bound to anything. Generally speaking you should be using a dependency injection framework to ensuring it's a true singleton, but for now just do something like this:
public MainViewModel()
{
Instance = this;
}
public static MainViewModel Instance { get; private set; }
Also your code calls RaisePropertyChangedEvent("CurrentViewModel")...I'm pretty sure you meant that to be RaisePropertyChanged("CurrentViewModel").

i would go another way and expose an Event from your first and Second viewmodel like
public event EventHandler<ShowMeEventArgs> ShowMeEvent;
then your main just need to subscribe to this event and can show the viewmodel. and even more your first and second viewmodel dont need to know anything from mainviewmodel

Related

How do you bind an observable collection to a listview located in a modal/window?

I have a wpf application that I want to be able to launch a separate window in which I will have a listview bound to an observable collection. However I am unable to get the collection values to appear in the list view. Here is some of the code.
Window (Named WizardView):
(Data context defined like so at top of xaml):
d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}"
<Border Grid.Row="0" Grid.Column="0" Grid.RowSpan="3" BorderBrush="Black">
<ListView BorderThickness="0" ItemsSource="{Binding TestModel.FailedTests}">
<Label Content="Introduction" FontWeight="Bold" />
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding }"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
MainViewModel Code:
public class MainViewModel : BaseViewModel
{
public MainViewModel()
{
TestModel = new TestViewModel();
WizardModel = new WizardViewModel(TestModel);
}
private WizardViewModel _wizardModel;
public WizardViewModel WizardModel
{
get
{
return _wizardModel;
}
set
{
_wizardModel = value;
RaisePropertyChanged();
}
}
private TestViewModel _testViewModel;
public TestViewModel TestModel
{
get
{
return _testViewModel;
}
set
{
_testViewModel = value;
RaisePropertyChanged();
}
}
WizardViewModel Code:
public class WizardViewModel : TestViewModel
{
internal TestViewModel TestModel;
public WizardViewModel(TestViewModel testModel)
{
TestModel = testModel;
(TroubleShootCommand is defined in seperate UC, and launches fine)
TestModel.TroubleShootCommand = new DelegateCommand(Load, CanTroubleShoot);
}
public void Load()
{
(Sync Root is used because it is running on worker thread. Issue somewhere here?)
_syncRoot.Send(o =>
{
var troubleShootWizard = new WizardView();
troubleShootWizard.Owner = Application.Current.MainWindow;
troubleShootWizard.ShowDialog();
}, null);
}
Observable Collection in TestViewModel (Initialized in ctor):
private ObservableCollection<string> _failedTests;
public ObservableCollection<string> FailedTests
{
get { return _failedTests; }
set
{
_failedTests = value;
RaisePropertyChanged();
}
}
Any Help is appreciated, I feel like I have tried everything. I have watched values through the watch window under TestModel.FailedTests in the collection right before and right after launch.
First,
(Data context defined like so at top of xaml): d:DataContext="{d:DesignInstance Type=viewModels:MainViewModel}"
This is a mistake, this way d: you are defining the DataContext at design time..
You can create the viewmodel inside .xaml this way:
<WizardView.DataContext>
<viewModels:MainViewModel/>
</WizardView.DataContext>
Using the design time declaration can help in many ways like knowing the viewmodel in case you are creating it and assigning it in C# (or via a IoC mechanism), also it helps tools like IntelliSense and ReSharper to analyze your bindings and warn you if you misspell a property's name in xaml, auto-completion, etc... (more on this can be found here, and here)
Second, if you are assigning the WizardViewModel in your .xaml the same way (i.e. design-time), then you can either do it in your Load() function (add troubleShootWizard.DataContext = this;) or assign it in .xaml the same way I've mentioned before.

Why is my view not updating when adding data from my service to my viewmodel?

So I just created a new project and I am trying to add some data to the collection in my ViewModel, however when I add data to it, it does add but it wont update the UI.
This is where I set the DataContext and where I am trying to add some content to the collection
ProxyService ps;
public MainWindow()
{
InitializeComponent();
DataContext = new BaseViewModel();
ps = new ProxyService();
ps.AcceptConnection();
}
Keep in mind, it does add it to the collection there are no errors I've debugged it and it's infact in the collection.
ProxyServer.cs
public class ProxyService : MessageViewModel
{
public void AcceptConnection()
{
Messages.Add(new MessageModel { Message = "Awaiting connection..." });
Here is the BaseViewModel
public class BaseViewModel
{
public MessageViewModel MessageViewModel { get; set; } = new MessageViewModel();
}
And the MessageViewModel of course
public class MessageViewModel : ObservableObject
{
private ObservableCollection<MessageModel> _messages;
public ObservableCollection<MessageModel> Messages
{
get { return _messages; }
set
{
_messages = value;
OnPropertyChanged();
}
}
public MessageViewModel()
{
Messages = new ObservableCollection<MessageModel>();
}
}
And here is the XAML for the ScrolLViewer to which I am adding the data
<ScrollViewer Height="380"
Margin="10"
>
<StackPanel>
<ItemsControl ItemsSource="{Binding MessageViewModel.Messages}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock FontFamily="Consolas"
Foreground="#61d73d"
Text="{Binding Message}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
The issue I am facing is that it's not updating the UI when it adds something
However! If I add something in the constructor it works just fine. As an example this works just fine, it shows it in the view accoringly
public MessageViewModel()
{
Messages = new ObservableCollection<MessageModel>();
Messages.Add(new MessageModel { Message = "Hello World!" });
}
My best guess is that it's adding to another instance of some sort but I am not sure, I really don't want to have to use singleton because I feel like that will ruin the MVVM pattern.
Three points of note.
You're setting the Window's DataContext to one instance of BaseViewModel, and then creating a separate instance of the ProxyServiceClass.
Your binding for the ItemsControl.ItemsSource should just be binding to a property of the DataContext, in this case Messages.
Don't keep recreating the ObservableCollection - just create it once and add / remove items as required. Any bound control will detect that it implements INotifyCollectionChanged and refresh itself automatically when the collection is updated.
BaseViewModel is one class and ProxyService another one. You can't expect MessageModel objects that you add to the latter affect the former and vice versa. Try to set the DataContext to a BaseViewModel:
public MainWindow()
{
InitializeComponent();
ps = new ProxyService();
ps.AcceptConnection();
DataContext = new BaseViewModel { MessageViewModel = ps };
}

Trying to: Avoid static instance as datacontext

Im having problem sharing a windows viewmodel to the windows hosted frame.
Therefore I made a static viewmodel for the mainwindow, so any class can edit it´s properties:
class GUICollection
{
public static MainWindowViewModel MainWindowViewModel = new MainWindowViewModel();
}
This is then set into the MainWindows datacontext:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = GUICollection.MainWindowViewModel;
}
}
This is the windows xaml:
<Window x:Class="MVVMFrameQuestiontoStackOverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Frame NavigationUIVisibility="Hidden" Source="{Binding MainWindow.FrameURI}"/>
</Grid>
It´s view model:
class MainWindowViewModel
{
private string startUpURI;
private object startUpDataContext;
private MainWindowModel mainWindow;
public MainWindowViewModel()
{
startUpURI = "pack://application:,,,/MVVMFrameQuestiontoStackOverflow;component/Page1.xaml";
mainWindow = new MainWindowModel(startUpURI);
}
/// <summary>
/// Gets the MainWindow instance
/// </summary>
public MainWindowModel MainWindow
{
get
{
return mainWindow;
}
}
}
So from here I can choose the frame Source, which means I can choose which view to show. However Im wondering if I could avoid the static initiliazing and still being able to access the mainwindows FrameURI property (Here is my current logic):
public Page1()
{
InitializeComponent();
DataContext = new MainMenuViewModel();
//Statement below causes an exception, but the whole issue is about accesing this without using a static instance.
GUICollection.MainWindowViewModel.MainWindow.FrameURI = "Change MainWindows FrameURI property!";
}
Is the same behaviour able to produce without using a static class? If so an example would be warmly appreciated.
Thanks on advance!
I think in fact you problem is that you haven't understood MVVM and use a mix between MVC and MVVM : You create the view (mainWindows) in the ViewModel wich is forbidden.
Your App must load the main view.
In the constructor of the main view you should create the view-model as a private field.
When you will create new windows (and you should only do that from view, never from viewmodel) you will give the viewmodel datacontext object as a parameter for the new view, wich will give it to it's own viewmodel via parameters.
If you have Model object(s) wich is(are) shared throught all the application, create it in the App launch method, and pass it throught views via their constructors as a parameter.

How to make a list of buttons dynamically and show them in the MainForm in a ListBox or ItemsControl

I am new in the WPF.
I want to make the list of buttons from a list of a class say "Buttons" and having two fields (ButtonContent,ButtonID) in the MainForm in WPF.
The idea behind this is that when the MainForm will be loaded then i want to make buttons list dynamically.
The Best Example is already given in the Link
http://www.codeproject.com/Articles/25030/Animating-Interactive-D-Elements-in-a-D-Panel
But i only want to make the equal size buttons stacked horizontally.
How can i do this in WPF? Thank you in advance.
This is the way I would do it. There are some areas here that will need further research on your part, but this will get you started.
First you need your ViewModel. This is the plain old object. It exposes the properties and methods that are pertinent to your business. You mentioned ButtonContent and ButtonID. Let's assume that both of those are strings for now. You'll also need a command for your button I assume.
public class ButtonViewModel : INotifyPropertyChanged
{
private string _content;
public string Content
{
get{ return _content; }
set{ _content = value; OnPropertyChanged("Content"); }
}
// you'll need to implement INotifyPropertyChanged
// also take a look at RelayCommand
// here is your command for you're button
public ICommand ButtonCommand
{
get { return new RelayCommand(execute, canExecute); }
}
private void execute()
{
// my actual command code
}
private bool canExecute()
{
// this will automatically toggle the enabled state on my button
}
}
You'll have one more view model. This will be the DataContext of your MainWindow.
public class AppViewModel
{
public ObservableCollection<ButtonViewModel> MyButtons {get; set; }
public AppViewModel()
{
MyButtons = new ObservableCollection<ButtonViewModel>();
// add some demo data
MyButtons.Add(new ButtonViewModel() {ButtonContent = "Click Me!"});
}
}
Now you need to implement the view. In the MainWindow xaml code. Add your xmlns:local namespace.
<Window.DataContext>
<local:AppViewModel />
</Window.DataContext>
<ListBox ItemsSource="{Binding MyButtons}">
<ListBox.ItemsTemplate>
<DataTemplate>
<Button Command="{Binding ButtonCommand}" Content="{Binding Content}"/>
</DataTemplate>
<ListBox.ItemsTemplate>
</ListBox>
Hopefully this can get you started in the right direction.

Control Databinding to View not ViewModel in MVVM scenario

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}" />

Categories