I'M having a problem with start a new WPF page with use of commands. I tried with use a new WPF window by writing but nothing happens.
I can't see the error? And the program works fine but when the button is pressed, nothing happens
My XAML.
<Button
Command="{Binding Path=OpenCrudCommand, UpdateSourceTrigger=PropertyChanged}"
Content="CRUD"
HorizontalAlignment="Left"
Margin="10,352,0,0"
VerticalAlignment="Top"
Width="83"/>
My OpenCrudCommand.
As you can see, I have tried with a new WPF window, not a WPF page and it didn't work either.
Page 1 is a WPF window form and Page 2 is a WPF page form
{
class OpenCrudCommand
{
ProductViewModel _avm;
public OpenCrudCommand(ProductViewModel avm)
{
_avm = avm;
}
public void Execute(object parameter)
{
var hej = new Page2();
hej.Show();
}
}
}
I have another question, should i write the code for opening a new page in command or in the viewmodel?
For more clarity, you're binding to a generic (POCO) class. Typically you must bind to a class that implements the ICommand interface. Do a search for RelayCommand or DelegateCommand to see several implementations. Now once that is done, you will set up a class (typically a ViewModel class) that will serve as the DataContext for your WPF window. Then you will expose a property on your ViewModel that exposes the command (i.e.)
public ICommand MyCommand
{
get
{
return this.myCommand;
}
}
Then your binding will be along the lines of Command="{Binding MyCommand}" (You do not need the UpdateSourceTrigger property).
If this is still confusing, feel free to follow up with additional questions, but I would suggest reading more about the MVVM pattern.
Related
I've got a problem I couldn't get solved until now:
I am developing an application in C#/WPF and am using the Caliburn.micro as framework. I have multiple menu panels (as user controls) that I want to reuse all over the application (e.g. data filtering menu for a grid) and show in a <ContentControl />. Depending on the state of the application a different menu panel can be shown.
Now I could get managed to let events bubble up from the menu's View to the parent's ViewModel. But I'm stuck with properties:
For example in the filtering menu, one should enter a text while the filter is instantly applied. When I had the menu in the parent's View it was easy: I just made the filtering in the property's setter method.
Is there a possibility to make a kind of "property-bubbling" similar to the message bubbling in c.m (it has to be twoWay!)? Or any other (better) MVVM-compliant approach?
Thanks in advance!
Jan
Minimal example:
ParentView.xaml
<UserControl x:Class="App.ParentView">
<Grid>
<ContentControl x:Name="Toolbar" />
</Grid>
</UserControl>
ParentViewModel.cs
class ParentViewModel : Screen
{
public ParentViewModel()
{
Toolbar = new MenuViewModel();
}
private Screen _toolbar;
public Screen Toolbar
{
// get, set ...
}
public void LoadDifferentMenu()
{
this.Toolbar = new DifferentMenuViewModel();
}
}
MenuView.xaml
<UserControl x:Class="App.MenuView">
<Grid>
<TextBox x:Name="MyText" />
</Grid>
</UserControl>
MenuViewModel.cs
class MenuViewModel : Screen
{
public MenuViewModel()
{
}
private string _myText;
public string MyText
{
// get, set...
}
}
Use Event Aggregator in caliburn micro to implement publisher and subscriber pattern in MVVM.
Communication is based on message type so it can be used for one way or two way communication with appropriate types.
Kindly refer to the link https://caliburnmicro.com/documentation/event-aggregator for implementation details.
I am trying to learn MVVM and have come across a weird snag. I have a main menu with a drawer control that comes out and shows a menu:
In the main window where this drawer is, I have a ContentControl where I set its content with a Binding.
<ContentControl x:Name="MainWindowContentControl" Content="{Binding Path=WindowContent}"/>
This window's binding is set to a view model.
<Window.DataContext>
<viewmodels:MainWindowViewModel/>
</Window.DataContext>
and here is the ViewModel:
MainWindowViewModel.cs
public class MainWindowViewModel: ViewModelBase
{
private object _content;
public object WindowContent
{
get { return _content; }
set
{
_content = value;
RaisePropertyChanged(nameof(WindowContent));
}
}
public ICommand SetWindowContent { get; set; }
public MainWindowViewModel()
{
SetWindowContent = new ChangeWindowContentCommand(this);
}
}
So far up to this point, everything works fine. So for example, if I click "Recovery Operations", I get this:
RecoveryOperationsView.xaml
In "RecoveryOperationsView.xaml" (which is a UserControl) I also reference the view model from above like so..
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
and have a button to call the command to change the Content property of the ContentControl from the main window..
<Button Grid.Row="2" Content="Restore Database" Width="150" Style="{StaticResource MaterialDesignFlatButton}" Command="{Binding SetWindowContent}" CommandParameter="DatabaseRecovery" >
In my class to process the commands, I change the content based off of the passed parameter using a switch statement like so
ChangeWindowContentCommand.cs
public class ChangeWindowContentCommand : ICommand
{
private MainWindowViewModel viewModel;
public ChangeWindowContentCommand(MainWindowViewModel vm)
{
this.viewModel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
switch (parameter)
{
case "Home":
viewModel.WindowContent = new HomeView();
break;
case "RecoveryOps":
viewModel.WindowContent = new RecoveryOperationsView();
break;
case "DatabaseRecovery":
viewModel.WindowContent = new DatabaseRestoreView();
break;
}
}
}
However, this is where I get lost... If I click something within this new window, say "Restore Database" and inspect it with a breakpoint, I can see the property being changed but the actual ContentControl Content property doesnt change to the new UserControl I made... I can change the content with anything in the drawer, but if I try to click a button in the hosted Content of the ContentControl nothing changes. What am I missing?
It's hard to be 100% sure without having your project to test with, but I am fairly confident that at least one of the issues is that your UserControl and your MainWindow use different instances of the MainWindowViewModel. You do not need to instantiate the VM for the user control, as it will inherit the DataContext from the MainWindow. The way it works in WPF is that if any given UIElement does not have theDataContext assigned explicitly, it will inherit it from the first element up the logical tree that does has one assigned.
So, just delete this code, and it should solve at least that issue.
<UserControl.DataContext>
<viewmodels:MainWindowViewModel/>
</UserControl.DataContext>
And since you're learning WPF, I feel obligated to provide a couple other tips. Even though you're using a ViewModel, you are still mixing UI and logic by creating a very specific implementation of ICommand and assigning a UI element through your ViewModel. This breaks the MVVM pattern. I know MVVM takes a little time to understand, but once you do, it is very easy to use and maintain.
To solve your problem, I would suggest creating View Models for each of your user controls. Please see this answer, where I go into quite a bit of detail on the implementation.
For switching the different views, you have a couple of options. You can either use a TabControl, or if you want to use a command, you can have a single ContentControl bound to a property of MainWindowViewModel that is of type ViewModelBase. Let's call it CurrentViewModel. Then when the command fires, you assign the view model of the desired user control to that bound property. You will also need to utilize implicit data templates. The basic idea is that you create a template for each of the user control VM types, which would just contains an instance of the Views. When you assign the user control VM to the CurrentViewModel property, the binding will find those data templates and render the user control. For example:
<Window.Resources>
<DataTemplate DataType = "{x:Type viewmodels:RecoveryOperationsViewModel}">
<views:RecoveryOperationsView/>
</DataTemplate>
<!-- Now add a template for each of the views-->
</Window.Resources>
<ContentControl x:Name="MainWindowContentControl" Content="{Binding CurrentViewModel}"/>
See how this approach keeps UI and logic at an arm's length?
And lastly, consider creating a very generic implementation of ICommand to use in all your ViewModels rather than many specific implementations. I think most WPF programmers have more or less this exact RelayCommand implementation in their arsenal.
How to bind button to function in "MVVM style".
I just started to convert my app to mvvm style.
my code:
XAML in Page1.xaml class:
<Button x:Name="my_button" Content="Add" Command="{Binding msgbox}" Margin="451,82,39,0" Width="50" Height="31"/>
ViewModelPage1.cs class:
public class ViewModelPage6
{
public void msgbox()
{
MessageBox.Show("mvvm is great");
}
}
Can someone give a simpler answer then here
Thanks,
Flufy.
Here's a simple example which binds a button:
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
Using icommand directly is a bit of a nuisance for most purposes, so you want to look into a framework which makes that easier. The sample uses mvvmlight, other frameworks are available but this is a good one. Especially for a beginner.
You cannot bind a method to the Command property of a Button directly. You will have to use an implementation of ICommand.
This should get you started..
public ICommand msgboxCommand = new DelegateCommand(msgbox);
private void msgbox()
{
...
}
See this for more details: https://www.wpftutorial.net/DelegateCommand.html
Besides, using a UI component like Messagebox in the ViewModel directly; isn't exactly MVVMish
I'm probably missing the point here, I've been digging around for some time now, looked at the different approaches to bind views and view models and how to navigate between them.
Setup:
1 MainWindow (ViewModel: MainWindowViewModel)
MainWindow contains some visuals and a ContentControl that is binded to a ViewModelBase. So in MainWindowViewModel I can set any other view(model) to display.
Got two user controls, one is a login form, the other one is for now a loading indicator.
Inside App.xaml
<DataTemplate DataType="{x:Type vms:LoginViewModel}">
<Views:LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:LoadingViewModel}">
<Views:LoadingView />
</DataTemplate>
Goal:
From the ICommand inside LoginViewModel go back to the MainWindowViewModel, a long with the form data. MainWindowViewModel will then switch to LoadingViewModel, do async call to a service. Next time on startup when a refresh token is saved, I will show the LoadingViewModel instead of the login form. On completion a new window will open (or something else, don't know yet).
Problem: Most examples show how to do it when a button is outside of both User Controls, so when the ICommand is inside the MainWindow, then it would be easy, yet the event is called from one of the subviews. Using PropertyChange seems a bit off as well.
Let's dig into some code, shell we?
MainWindow.xaml, only one important line
<ContentControl Content="{Binding CurrentView}" Grid.Column="1" Grid.Row="1" />
MainWindow.xaml.cs, in constructor
this.DataContext = new MainWindowViewModel();
MainWindowViewModel
public class MainWindowViewModel
{
public ViewModelBase CurrentView { get; set; }
public MainWindowViewModel()
{
CurrentView = new LoginViewModel();
}
}
LoginViewModel
public class LoginViewModel : ViewModelBase
{
public DelegateCommand loginCommand { get; set; }
public LoginViewModel()
{
loginCommand = new DelegateCommand(Execute, CanExecute);
}
private bool CanExecute()
{
return true;
}
private void Execute()
{
//I need to go to MainWindowViewModel
throw new NotImplementedException();
}
//more properties below heere.
ViewModelBase inherits from BindableBase (from Prism), so that handles the PropertyChanged events. The properties inside my view models use the correct SetProperty methods. I prefer not to use Prism's region, IEventAggregator or Unitiy.
Solutions
What comes to mind is sending an interface a long with the constructor, and use the interface to do 'callbacks' to the MainWindowViewModel, but I guess this will give errors, since I will change the view, and thus setting MainWindowViewModel.CurrentView to something else, leaving LoginViewModel null. Since the request is coming from that object I can imagine it is not really good.
A good way to communicate is the Messanger (MVVM Light) or EventAggregator (Prism) concept.
It's basically an in memory pub/sub system.
Here is an example from an article on MSDN
MVVM - Messenger and View Services in MVVM
Using the event aggregator pattern to communicate between view models
I am not sure which framework/library are you using but most MVVM framework have a similar concept for lousily coupled communication.
This is a very powerful concept for handling communication. But with great power comes responsibility =)...
HTH
I have a simple use case which I am struggling with in Caliburn.Micro. I can get this to work easily with traditional bindings, but I'd like to use the framework properly.
In short, this is an MDI style app with a single top level toolbar of which I'd like to bind the context to the Conductor.ActiveItem. Basically, the issue I'm seeing is that Calibun set up the Actions for the toolbar buttons for the first opened tab, but later when ActiveItem is changed, the connected actions continue to point to the first assigned ActiveItem and not the new one.
My main ViewModel is of type Conductor.Collection.OneActive.
public sealed class MainViewModel : Conductor<ITabPage>.Collection.OneActive
{
}
This view model contains a simple list of tabs each with public methods Save() and Undo() (along with bool property implementations for CanSave and CanUndo).
public interface ITabPage : IScreen, IDisposable
{
void Save();
void Undo();
bool CanSave { get; }
bool CanUndo { get; }
}
Now the view contains the top-level toolbar with buttons invoking the actions on the ActiveItem and a TabControl to display the conductor items.
<Window xmlns:cal="http://www.caliburnproject.org" ...>
<DockPanel>
<ToolBar DockPanel.Dock="Top" cal:Bind.Model="{Binding ActiveItem}">
<Button Name="Save">Save</Button>
<Button Name="Undo">Undo</Button>
</ToolBar>
<TabControl x:Name="Items">
</TabControl>
</DockPanel>
</Window>
Using normal binding and ICommands works fine, but I'd like to not fight the framework on this. Is there something I'm missing or misusing with cal:Bind.Model? Or perhaps a way to let it know to refresh? I've also tried calling Refresh() when ActiveItem is changed and I'm *absolutely" sure the CanSave and CanUndo are notifying properly (I've set break points and I've had success with normal bindings.)
Found a solution: I was misusing caliburn:Bind.Model.
The correct bind type is
caliburn:Bind.ModelWithoutContext="{Binding ... }"
Using that dependency property helper instead allows the Actions to be routed correctly to the ActiveItem as it changes.