Does using the Click method in WPF break MVVM? - c#

In a wpf interface with MVVM I have a close button and I'm using the button's Click method to close the application like so:
<Button Content="Close" Click="CloseFunction"/>
My question is, am I breaking MVVM by doing this?

Does using the Click method in WPF break MVVM?
Not necessarily as long as you don't implement any application logic in the click event handler in the code-behind of the view.
For example, consider the following implementation that simply invokes a command of the view model from the event handler:
private void CloseFunction(object sender, RoutedEventArgs e)
{
ViewModel vm = this.DataContext as ViewModel;
if (vm != null)
vm.SomeCommand?.Invoke(null);
}
It's as MVVM compliant as the following XAML markup which invokes the very same command from the very same view:
<Button Content="Close" Command="{Binding SomeCommand}" />
MVVM is not about eliminating code from the views. It's mainly about separation of concerns.

Related

In WPF and by using Prism, how to use the MouseButtonEventArgs as a parameter of the command for a window?

I want to move a borderless windows, and before I adopt the Prism framework, I'd do it as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MouseDown += Window_MouseDown;
}
private void Window_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Left)
{
DragMove();
}
}
}
but I don't know how to implement this while using Prism in MainWindowViewModel.cs (the view model), it seems the InvokeCommandAction can pass the event argument for an element like button or so, but it doesn't work for a window in my case.
Can anyone help me on this? Thanks in advance.
I don't know how to implement this while using Prism
I don't know what this is supposed to be exactly, but I'll assume it's how to call the view model when some event on the view happens:
The cleanest option is an attached behavior. Alternatively, you can use an InvokeCommandAction variant like DevExpress' EventToCommand that supports forwarding the parameter.
Well, finally I got the event fired, but it seems this approach contradict the concept of MVVM pattern, which requires that the view model should not know anything about nor has any dependency upon any view elements.
In my case, I can add the Interaction.Triggers to the Window and pass the MouseButton to the view model by usng Prism’s InvokeCommandAction, as follows:
<Window
xmlns:prism="http://prismlibrary.com/"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
/>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<prism:InvokeCommandAction Command="{Binding WindowMouseCommand}" TriggerParameterPath="ChangedButton" />
</i:EventTrigger>
</i:Interaction.Triggers>
and in the view model:
public DelegateCommand<object> WindowMouseCommand { get; private set; }
...
WindowMouseCommand = new DelegateCommand<object>(WindowMouse);
...
private void WindowMouse(object mouseButton)
{
if (mouseButton is MouseButton m)
{
if (m == MouseButton.Left)
{
// DragMove();
}
}
}
if I want to call the .DragMove(), I need a reference of the Window... it's not the correct implementation of MVVM pattern.
So what is the best approach/practice for that?
I was suddenly enlightened when I saw this answer:
https://stackoverflow.com/a/3426183/10958770
yes, moving a window is a pure UI logic, therefore it's not necessary to move it to a ViewModel... so let me just leave it in the view.

C#: WPF MVVM Command Binding vs. Event Callback

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.

WPF + Caliburn Micro: how to catch Window Close event?

I am new in Caliburn Micro and learn it from this helloworld example. In the example there are only 2 views (.xaml) of type Application and UserControl, and 1 view model.
I avoid to use code behind. Therefore I have only view and view model. I want to know how to catch the window close event of my helloworld application so I can handle it in view model. My target: when user is going to close the app by pressing close [x] button on top-right corner the app gives feedback to the user.
I have read about IViewAware and IScreen, but I find no specific example related to my question.
A simple sample code for view and view model are highly appreciated. Thanks in advance.
PS. I use VS2013, C#.
What you can do is in your View you can attach Caliburn Micro by using
cal:Message.Attach="[Event Closing] = [Action OnClose($eventArgs)]"
So it will look like
<Window cal:Message.Attach="[Event Closing] = [Action OnClose($eventArgs)]">
And on your ViewModel you can just define a public method that says OnClose with CancelEventArgs as the parameter and you can handle it from there.
If your ViewModel inherits Screen, Caliburn Micro has some methods that you can override like
protected override void OnDeactivate(bool close);
this is called when a screen is closed or deactivated or
public override void CanClose(Action<bool> callback)
you can check CanClose usage here
If you are using the BootstrapperBase class you can use:
protected override void OnExit(object sender, EventArgs e)
You're looking for a way to bind an Event to a Command. The typical approach here is to use the EventToCommand behavior from MVVMLight.
Example usage (from the linked article):
<StackPanel Background="Transparent">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Tap">
<command:EventToCommand
Command="{Binding Main.NavigateToArticleCommand,
Mode=OneWay,
Source={StaticResource Locator}}"
CommandParameter="{Binding Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<!--...-->
</StackPanel>
For your specific scenario, you are not using MVVMLight. Since that framework is open-source, you could copy the implementation of EventToCommand into your own project, or - more simply - you can use the InvokeCommandAction, which is part of the System.Windows.Interactivity.dll library, included with Expression Blend.
Example of InvokeCommandAction:
<TextBox x:Name="TicketNumber">
<i:Interaction.Triggers>
<i:EventTrigger EventName="KeyDown">
<i:InvokeCommandAction Command="{Binding OpenTicketCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Lastly, this entire MVVM dogma that you "can't have any code behind" has been shot down time | and | time again (that last link is particularly relevant). MVVM is supposed to be unit-testable, and separates the "View logic" from the "Business logic." The "Close" event is admittedly a bit of a gray area between View and Business logic. But, if you can write an event handler in your code behind, which invokes your ViewModel's appropriate method or command, and if you can unit test that code, then you're as good as gold. Don't worry about removing all traces of code-behind from your project.

WP8 Databind button click/Command with RoutedEventHandler

Fairly new to Windows Phone and Xaml and I decided to start using the DataTemplates as it looked neater and I could easily switch them etc.
I have a requirement where on a button click depending on the data on the item in the list I want to call a different function or with different parameters. I thought the easiest way would be to bind a RoutedEventHandler to it via an anonymous function.
When I did this in code-behind with static controls on the formed it worked perfectly. It also worked when I added my own controls to a stack panel etc. But it was all quite messy.
// Example of RoutedEventHandler that works when I create the button in code behind
model.clickEventHandler = (s, e) => LoadResult(r.id);
<ScrollViewer Name="scrvResults" >
<ListBox Name="lbResults" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Command="{Binding clickEventHandler}" >
// Stuff
// Doesn't crash but doesn't fire the event
</Button>
<Button Click="{Binding clickEventHandler}" >
// Stuff
// Throws a com exception
</Button>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
I've tried various sub options. All the examples i've seen seem to link to a static function. Is this just some syntax i'm getting wrong is can I not bind to it this way?
You need to bind your command to a type of ICommand. See here for more info:
ICommand interface
Command Binding
Button click event can be bound by using interaction triggers, not by simply binding the event to the click attribute:
Using EventTrigger in XAML for MVVM – No Code Behind Code

How to refresh custom user control in WPF from code behind or by binding

I have a TabControl and users controls are in TabItem content like that:
\\...
<TabItem.Content>
<vm:myUserControl />
</TabItem.Content>
\\...
<TabItem.Content>
<vm:otherUserControl/>
</TabItem.Content>
How can I update myUserControl when I change data in otherUserControl (example add element in list who must be showed in myUserControl) . This controls have different datacontext (from different viem model class, who inherits BaseViewModel. who impelments INotifyPropertyChanged). Data is provided by WCF client service. Thank you for help.
you can use a Mediator/Messenger or EventAggregator. so your otherUsercontrol raise the message/event and your myUserControl subscribe and react to this message/event.
or if you dont want loose coupling you could of course couple your two viewmodels direct and use some event.
There are a number of ways to achieve this. One way would be to fire an event in otherUserControl and subscribe to that event in your MainWindow and allow your MainWindow to update myUserControl.
MyUserControl XAML
<TextBlock x:Name="TextValue">Initial Text</TextBlock>
OtherUserControl XAML
<Button Click="Button_Click">Click Me</Button>
OtherUserControl C#
public event EventHandler ButtonClicked;
private void Button_Click(object sender, RoutedEventArgs e)
{
if(this.ButtonClicked != null)
{
this.ButtonClicked(this, EventArgs.Empty);
}
}
MainWindow XAML
<StackPanel>
<vm:MyUserControl x:Name="MyUserControl"/>
<vm:OtherUserControl x:Name="OtherUserControl"/>
</StackPanel>
MainWindow C#
public MainWindow()
{
InitializeComponent();
this.OtherUserControl.ButtonClicked += OtherUserControl_ButtonClicked;
}
void OtherUserControl_ButtonClicked(object sender, EventArgs e)
{
this.MyUserControl.TextValue.Text = "Updated Text";
}
Another option is to use something like the Prism Event Aggregator which will allow MyUserControl to subscribe to events raised by OtherUserControl without requiring that MainWindow setup the communication between the two. This is a much better option on larger projects since it allows your components to truly be loosely coupled.

Categories

Resources