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
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.
What would be the point to using MVVM for the close action of File->Exit.
It seems like a lot of work to make a close command when you can simply create an event callback for the Click event. For something like this that doesn't have anything to do with data or business logic, I don't see the point to using MVVM approach. Why not just use the following:
xaml:
<MenuItem Header="_File" Background="WhiteSmoke">
<MenuItem Name ="Exit" Tag="Exit" Header="Exit" Click="Exit_Click"/>
</MenuItem>
Code behind:
private void Exit_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
For that case, you have a bit of an argument. If nothing else, closing the view could easily be construed as entirely view related, so a Click event handler makes sense to start with. View-related code goes in the view, not the VM.
However, I would challenge your notion that a Command is that hard to set up. Using a DelegateCommand: http://wpftutorial.net/DelegateCommand.html requires two lines of additional code:
public ICommand ExitCommand {get; private set;}
public MyViewModel()
{
ExitCommand = new DelegateCommand(ExitApp);
}
The handler is the same either way. While Exit may not need a command, in general, ICommand is the right way to go and isn't actually that hard.
You're absolutely right - if handler is pure-UI related then it doesn't break MVVM by any means (despite what some extremists say). MVVM is pattern created to decouple UI and business logic - "UI things" in code are ok.
I'm new to both Caliburn and WPF, so excuse me if it is a rather trivial question.
The scenario is the following:
I have multiple controls (like buttons and textboxes - the latter is the important part).
Their state (Enabled/Disabled) are dependent on a boolean property.
The first suggested method I tried was using the Can[FunctionName] convention and NotifyOfPropertyChange(() => Can[FunctionName]). It worked well with the button, but it did not work with the textbox.
How do I bind IsEnabled property to a state without using the code-behind of the View?
The code I tried in the ViewModel didn't work for the textbox:
private bool _buttonEnableState = true;
public bool ButtonEnableState
{
get
{
return _buttonEnableState;
}
set
{
_buttonEnableState = value;
NotifyOfPropertyChange(() => CanTheButton);
NotifyOfPropertyChange(() => CanTheTextBox);
}
}
public bool CanTheButton
{
get
{
return ButtonEnableState;
}
}
public void TheButton()
{
}
public bool CanTheTextBox
{
get
{
return ButtonEnableState;
}
}
From the View:
<Button x:Name="TheButton" Content="This is the button" ... />
<TextBox x:Name="TheTextBox" ... />
Thanks in advance!
Have you tried the obvious?:
<Button Content="This is the button" IsEnabled="{Binding ButtonEnableState}" />
<TextBox x:Name="TheTextBox" IsEnabled="{Binding ButtonEnableState}" />
UPDATE >>>
So, continuing the conversation from the comments... now you have a public property in your AppViewModel class and an instance of that class is set as the DataContext of your view that contains the Button and TextBox controls?
Let's see if the Binding is really working or not... try changing your code to this:
<Button Content="{Binding ButtonEnableState}" />
If the Button.Content is set then the Binding works just fine and you have a different problem.
UPDATE 2 >>>
As #Charleh mentioned, you also need to make sure that you have notified the INotifyPropertyChanged interface of the change of property value:
NotifyOfPropertyChange(() => ButtonEnableState);
I don't think what I'm about to suggest is necessarily the correct way of doing things, but it may give you the result you're after.
In order to get the control to be disabled based on a Can<name> property, you need to confirm to the conventions that Caliburn uses, so in this case, supplying a function <name> should work:
public void TheTextBox()
{
}
As a result of the default conventions, I believe this will be called every time the KeyDown event is fired.
That said, you probably want to bind your text content to something, and you'll want to use the x:Name property convention to choose which property, that means you'll have to attach the TheTextBox() function in a different way, you should be able to do that using the Message.Attach property in the Caliburn namespace.
So your TextBox could look like this (where you've added the following namespace xmlns:cal="http://www.caliburnproject.org"):
<TextBox cal:Message.Attach="TheTextBox" Name="SomeTextProperty" />
Backing that up in your ViewModel, you'd have:
// Your Enabled Property (using your existing code).
public bool CanTheTextBox
{
get
{
return ButtonEnableState;
}
}
// Your dummy function
public void TheTextBox()
{
}
// Some text property (Just for demo, you'd probably want to have more complex logic in the get/set
public string SomeTextProperty
{
get; set;
}
You should then see the Enabled/Disabled behaviour, and be use the SomeTextProperty.
I'm not entirely sure I like this way of doing things, I just had a quick play to see if it worked. The following answer might be a cleaner solution, and establishes a new re-usable convention:
Adding a convention for IsEnabled to Caliburn.Micro
As a slight aside (not a direct answer), depending on how complicated your control/form is, you could investigate using multiple Views for the same ViewModel, in the past I've set up a ReadOnly and Editable view, and used a single property on the ViewModel to toggle between the two (essentially setting the entire state of the ViewModel). There are already default conventions so you can use multiple views with relative ease.
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 !