Can anyone please explain me that ?
I am using this simple Button with Binding to a command that should be executed when pressing on the button :
<Button Content="Save" Grid.Column="1" Grid.Row="6" Height="23"
HorizontalAlignment="Left" Margin="25,10,0,0"
VerticalAlignment="Top"
Width="75" Command="{Binding Path=SaveCommand}" />
On the view model code :
//this is a member
public ICommand SaveCommand{ get; set; }
//this instantiation on the constructor of the view model
public ViewModelBase()
{
SaveCommand = new RelayCommand(Foo);
}
The interesting thing is that when SaveCommand is declared as public member and I leave this view model (meaning I transfer to show other page on my app) I can see that the instance of the previews page view model is still alive using visual studio diagnostics tools.
But when its declared as private member, the memory released.
What ?
Thanks you very much !
Edit 5 minunts later ^-^ :
I understand, when its private the property never get called,accessed from outside by the WPF framework, and there is no memory leak.
So Hmm, can some one explain me why there is memory leak when I set it like that to be public ?
The RelayCommand is implementation from GalaSoft.MvvmLight, Version=5.4.1.0
Related
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 building my first WPF using MVVM pattern. With the help of this community, I manage to create my Model, my first ViewModel and view. Now I want to add some complexity to the app designing the basic application layout interface. My idea is to have at least 2 child views and one main view and separate them on several XAML:
Main.XAML
Products.XAML
Clients.XAML
Main will have a menu and a space to load child views (Products and Clients). Now following MVVM pattern all the navigation logic between views should be write on a ViewModel. So mi idea is to have 4 ViewModels:
MainViewModel
ProductsViewModel
ClientsViewModel
NavigationViewModel
So NavigationViewModel should contain a collection of child viewmodels? and an active viewmodel is that right?
So my questions are:
1) How can I load different views (Products, Clients) on Main view using MVVM pattern?
2) How do I implement navigation viewModel?
3) How can I control the max number of open or active views?
4) How can I switch between open views?
I have been doing a lot of search and reading and couldn't find any simple working example of MVVM navigation with WPF that loads multiple views inside a main view. Many of then:
1) Use external toolkit, which I don't want to use right now.
2) Put all the code for creating all the views in a single XAML file, which doesn't seems like a good idea because I need to implement near 80 views!
I'm in the right path here? Any help, especially with some code will be appreciated.
UPDATE
So, I build a test project following #LordTakkera advices, but get stuck. This is how my solution looks like:
I create:
Two Models (Clients and Products)
One MainWindow and two wpf user controls(Clients and Products) XAML.
Three ViewModels (Clients, Products and Main ViewModel)
Then I set dataContext on each view to corresponding viewModel. After that I create MainWindow with the ContentPresenter like this and bind it to a property of the viewmodel.
MainWindow.XAML
<Window x:Class="PruevaMVVMNavNew.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="519" Width="890">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="*"/>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Border Grid.Column="0" Grid.ColumnSpan="2" Background="AntiqueWhite" ></Border>
<Border Grid.Row="1" Grid.RowSpan="2" Background="AliceBlue"></Border>
<Border Grid.Row="1" Grid.Column="1" Background="CadetBlue"></Border>
<ContentPresenter Grid.Row="1" Grid.Column="1" x:Name="ContentArea" Content="{Binding CurrentView}"/>
<StackPanel Margin="5" Grid.Column="0" Grid.Row="1">
<Button>Clients</Button>
<Button>Products</Button>
</StackPanel>
</Grid>
And also this is viewmodel from MainWindow:
class Main_ViewModel : BaseViewModel
{
public Main_ViewModel()
{
CurrentView = new Clients();
}
private UserControl _currentView;
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
So this load by default clients view and looks like this (which is just right!):
So I suppose I need a way to relate the buttons on the left, with a certain viemodel and then bind them with CurrentView Property of Main viewModel. How can I do that?
UPDATE2
According to #LordTakkera advice I modify my main viewModel this way:
class Main_ViewModel : BaseViewModel
{
public ICommand SwitchViewsCommand { get; private set; }
public Main_ViewModel()
{
//CurrentView = new Clients();
SwitchViewsCommand = new RelayCommand((parameter) => CurrentView = (UserControl)Activator.CreateInstance(parameter as Type));
}
private UserControl _currentView;
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (value != _currentView)
{
_currentView = value;
OnPropertyChanged("CurrentView");
}
}
}
}
I use RelayCommand instead of DelegateCommand but I think it works the same way. The command is executed when I hit the buttons and the type parameter string its ok but i get this error:
Translation: Value cannot be null. Parameter name: type. Suggestion use New keyword to create object instance
I don't know where to put the New keyword. I have try on CommandParameter but it wont work. Any idea? Thanks
UPDATE 3
After all the advices and help received here, and a lot of work, here is my final navigation menu and the base for my application interface.
I'm not sure you need a separate "navigation" view model, you could easily put it into the main. Either way:
To separate your "child" views, I would use a simple ContentPresenter on your "main" view:
<ContentPresenter Content="{Binding CurrentView}"/>
The easiest way to implement the backing property is to make it a UserControl, though some would argue that doing so violates MVVM (since the ViewModel is now dependent on a "View" class). You could make it an object, but you lose some type safety. Each view would be a UserControl in this case.
To switch between them, you are going to need some sort of selection control. I've done this with radio buttons before, you bind them like so:
<RadioButton Content="View 1" IsChecked="{Binding Path=CurrentView, Converter={StaticResource InstanceEqualsConverter}, ConverterParameter={x:Type views:View1}"/>
The converter is pretty simple, in "Convert" it just checks if the current control is a type of the parameter, in "ConvertBack" it returns a new instance of the parameter.
public class InstanceEqualsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (parameter as Type).IsInstanceOfType(value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (bool)value ? Activator.CreateInstance(parameter as Type) : Binding.DoNothing;
}
}
Binding to a combobox or other selection control would follow a similar pattern.
Of course you could also use DataTemplates (with a selector, unfortunately not something I have done before) and load them into your resources using merged dictionaries (allowing separate XAML). I personally prefer the user control route, pick which is best for you!
This approach is "one view at a time". It would be relatively easy to convert to multiple views (your UserControl becomes a collection of user controls, use .Contains in the converter etc.).
To do this with buttons, I would use commands and take advantage of the CommandParameter.
The button XAML would look like:
<Button ... Command={Binding SwitchViewsCommand} CommandParameter={x:Type local:ClientsView}/>
Then you have a delegate command (tutorial here) that runs the activator code from the converter:
public ICommand SwitchViewsCommand {get; private set;}
public MainViewModel()
{
SwitchViewsCommand = new DelegateCommand((parameter) => CurrentView = Activator.CreateInstance(parameter as Type));
}
That is off the top of my head, but should be pretty close. Let me know how it goes!
Let me know if I provide any further information!
Update:
To answer your concerns:
Yes, each time you push the button a new instance of the view is created. You could easily fix this by holding a Dictionary<Type, UserControl> that has pre-created views and index into it. For that matter, you could use a Dictonary<String, UserControl> and use simple strings as the converter parameters. The disadvantage is that your ViewModel becomes tightly coupled to the kinds of views it can present (since it has to populate said Dictionary).
The class should get disposed, as long as no one else holds a reference to it (think event handlers that it registered for).
As you point out, only one view is created at a time so you shouldn't need to worry about memory. You are, of course, calling a constructor but that isn't THAT expensive, particularly on modern computers where we tend to have plenty of CPU time to spare. As always, the answer to performance questions is "Benchmark it" because only you have access to the intended deployment targets and entire source to see what actually performs the best.
IMHO the best choose for you is to use MVVM framework (PRISM, MMVM Light, Chinch, etc) because navigation is already implemented. If you want to create your own navigation - try DataTemplate.
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.
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.
I want to get into DataBinding and currently I'm stuck. I just can't get it to work. I read many tutorials, but honestly, none of the really helped me. I know what DataBinding is and why it's cool to use it, but I never came across a tutorial that showed me what to do in my code. They all just assume I know what I have to do there and only show the XAML side.
This is my class:
public class Test : Window
{
public IList<String> data { get; set; }
public Test() {
data = new List<String>();
InitializeComponents();
data.Add("Hello");
data.Add("World");
}
}
And here's my XAML
<ListBox HorizontalAlignment="Left" Margin="6,6,0,6"
Name="SourceDocumentsList" Width="202"
ItemsSource="{Binding Source={x:Static Application.Current}, Path=data}" />
Yet, nothing is displayed when I render the window. How can something this easy fail? What am I doing wrong here?
The way I understand it, I tell the Listbox that it should bind itself to the data property of the currently running application, which is my class Test.
The currently running application is not that class, it's just a window, what you bind to is the instance of the App class. You cannot statically get that window instance this way. How the binding should be made depends on where that XAML is (if it is in the Test window you can for example use RelativeSource={RelativeSource AncestorType=Window} instead).
I would recommend reading the MSDN documentation on data binding and this article on debugging.
Move those properties into a separate class like
public class ViewModel
{
public IList<String> Data { get; set; }
public ViewModel()
{
Data = new ObservableCollection<string>();
Data.Add("Hello");
Data.Add("World");
}
}
Change your Window Code Behind as
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
Your Xaml will look less complicated
<ListBox HorizontalAlignment="Left" Margin="6,6,0,6"
Name="SourceDocumentsList" Width="202"
ItemsSource="{Binding Data}" />
This is what we call moving into MVVM pattern. Happy Coding !