Good or Bad Practice For Dialogs in Wpf With Mvvm

Good or bad practice for Dialogs in wpf with MVVM?

This is a good approach and I used similar ones in the past. Go for it!

One minor thing I'd definitely do is make the event receive a boolean for when you need to set "false" in the DialogResult.

event EventHandler<RequestCloseEventArgs> RequestCloseDialog;

and the EventArgs class:

public class RequestCloseEventArgs : EventArgs
{
public RequestCloseEventArgs(bool dialogResult)
{
this.DialogResult = dialogResult;
}

public bool DialogResult { get; private set; }
}

Handling Dialogs in WPF with MVVM

I suggest forgoing the 1990's modal dialogs and instead implementing a control as an overlay (canvas+absolute positioning) with visibility tied to a boolean back in the VM. Closer to an ajax type control.

This is very useful:

<BooleanToVisibilityConverter x:Key="booltoVis" />

as in:

<my:ErrorControl Visibility="{Binding Path=ThereWasAnError, Mode=TwoWay, Converter={StaticResource booltoVis}, UpdateSourceTrigger=PropertyChanged}"/>

Here's how I have one implemented as a user control. Clicking on the 'x' closes the control in a line of code in the usercontrol's code behind. (Since I have my Views in an .exe and ViewModels in a dll, I don't feel bad about code that manipulates UI.)

Wpf dialog

Is this a bad way to create a dialog in WPF and MVVM?

i do this for dialogs in wpf

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

... do anything with the dialog result...

Is this bad MVVM practice?

Yes it is bad practice, because you are directly referencing the ViewModel from the View which implies a dependency between the View and the ViewModel and thus tight coupling.

The pattern specifically calls for the View NOT to be dependent on a specific ViewModel instance or type. The idea here being decoupled Views and ViewModels for polymorphic Views (the idea that you could show the same data multiple ways). In order for your View to be reusable, it would need to not have a concrete dependency on a specific ViewModel type.

A better way to handle this would be to use Commands as Kamel says. However, not all events/interactions/controls necessarily allow Commands to be invoked. However, you can attach Commands to specific events using the System.Windows.Interactivity.dll assembly which comes with the Blend SDK.

This allows you to create XAML that looks like the following:

 <i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction
Command="{Binding UpdateTextBoxBindingOnEnterCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type TextBox}}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>

..which allows the same functionality as an event handler to invoke a Command, but in a decoupled MVVM-friendly fashion.

Localizing dialogs in MVVM: is it good to reference the resources from the ViewModel?

Resources is the data that uses a View, and my opinion is that is not advisable from the ViewModel refer to resources. On the other hand, if it is a class (may be static) that stores a specific strings, and knows nothing of the View it will be some abstraction that can be in the ViewModel. In any case, you should try to work with the resources on the side View using techniques that I will give, or any other.

Using x:Static Member

In WPF, it is possible to bind static data from a class like this:

<x:Static Member="prefix : typeName . staticMemberName" .../>

Below is an example where the format string is in a class, the format used to display the date and time.

XAML

xmlns:local="clr-namespace:YourNameSpace"
xmlns:sys="clr-namespace:System;assembly=mscorlib"

<Grid>
<TextBlock Text="{Binding Source={x:Static sys:DateTime.Now}, StringFormat={x:Static Member=local:StringFormats.DateFormat}}"
HorizontalAlignment="Right" />

<TextBlock Text="{Binding Source={x:Static sys:DateTime.Now}, StringFormat={x:Static Member=local:StringFormats.Time}}" />
</Grid>

Code behind

public class StringFormats 
{
public static string DateFormat = "Date: {0:dddd}";

public static string Time = "Time: {0:HH:mm}";
}

In this case, the StringFormats class be regarded as a resource, although actually it is a normal class. For more information, please see x:Static Markup Extension on MSDN.

Using Converter

If you have the resources stored in Application.Current.Resources and need to add some logic, in this case, you can use the converter. This example is taken from here:

XAML

<Button Content="{Binding ResourceKey, Converter={StaticResource resourceConverter}}" />

Code behind

public class StaticResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var resourceKey = (string)value;

// Here you can add logic

return Application.Current.Resources[resourceKey];
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}

Note: In the converter, it is better not to use heavy logic, because it can affect the performance. For more complex logic, see below.

Attached Behavior

Attached behavior should be used for complex actions with visual elements when no x:Static Member and converter is not helped. Attached behavior is very powerful and convenient solution that fully satisfies the MVVM pattern, which can also be used in the Blend (with a pre-defined interface). You can define an attached property in which property handler to access elements and to its resources.

Examples of implementation attached behaviors, see below:

Set focus to a usercontrol when it is made visible

Animated (Smooth) scrolling on ScrollViewer

Setting WindowStartupLocation from ResourceDictionary throws XamlParseException

Example with converter

App.xaml

Here I store strings for each culture.

<Application x:Class="MultiLangConverterHelp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
StartupUri="MainWindow.xaml">

<Application.Resources>
<sys:String x:Key="HelloStringEN">Hello in english!</sys:String>
<sys:String x:Key="HelloStringRU">Привет на русском!</sys:String>
</Application.Resources>
</Application>

MainWindow.xaml

The input is the current culture, which can be obtained within the converter, for simplicity of an example I did so.

<Window x:Class="MultiLangConverterHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiLangConverterHelp"
WindowStartupLocation="CenterScreen"
Title="MainWindow" Height="350" Width="525">

<Window.Resources>
<local:StaticResourceConverter x:Key="converter" />
<local:TestViewModel x:Key="viewModel" />
</Window.Resources>

<Grid DataContext="{StaticResource viewModel}">
<TextBlock Text="{Binding Path=CurrentCulture, Converter={StaticResource converter}}" />
</Grid>
</Window>

MainWindow.xaml.cs

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}

public class StaticResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var currentCulture = (string)value;

if (currentCulture.Equals("EN-en"))
{
return Application.Current.Resources["HelloStringEN"];
}
else if (currentCulture.Equals("RU-ru"))
{
return Application.Current.Resources["HelloStringRU"];
}

return null;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}

public class TestViewModel : NotificationObject
{
private string _currentCulture = "EN-en";

public string CurrentCulture
{
get
{
return _currentCulture;
}

set
{
_currentCulture = value;
NotifyPropertyChanged("CurrentCulture");
}
}
}

Also, I advise you to learn more simple ways, which is already in the WPF technology:

WPF Localization for Dummies

WPF Globalization and Localization Overview

How to handle multiple windows and dialogs in MVVM?

thats what i do when working with dialogs in mvvm :)

var result = this.uiDialogService.ShowDialog("Dialogwindow title goes here", dialogwindowVM);

Is MVVM pattern broken?

What do you think, have I broken the MVVM-Pattern?

No. The view model have no dependency upon the view, it only knows about an interface that you could easily mock in your unit tests. So this doesn't really break the pattern as long as "View" is just an abstraction of something.

For type-safety reasons you should probably consider changing the type of the parameter from object to a strongly-typed interface type though.



Related Topics



Leave a reply



Submit