I am developping a .net desktop WPF app in MVVM pattern using Visual Studio. I want to add confirmation dialog and bind "yes" or "no" according to the user click. I made some research but solutions offered either is not appropriate for MVVM pattern or requires adding a lot of external packages which I dont want to. Could anyone help me to find a proper solution that solves my problem?
CodePudding user response:
I would like to suggest a solution that is MVVM oriented. One way to judge if we have an MVVM solution is if we can plan a unit test. In this regard we would like to build a view model that is not attempting to raise a dialog and is just setting properties. I use a Popup that we bind to the view model.
The view model will look as following:
public class MainViewModel:Binding
{
bool _isQuestionRaised;
public bool IsQuestionRaised
{
get { return _isQuestionRaised; }
set { _isQuestionRaised = value; NotifyPropertyChanged(nameof(IsQuestionRaised)); }
}
bool _yes;
public bool Yes
{
get { return _yes; }
set {
_yes = value;
NotifyPropertyChanged(nameof(Yes));
if (_yes) DoYesThings();
}
}
bool _no;
public bool No
{
get { return _no; }
set {
_no = value;
NotifyPropertyChanged(nameof(No));
if (_no) DoNoThings();
}
}
public void DoYesThings()
{
IsQuestionRaised = false;
}
public void DoNoThings()
{
IsQuestionRaised = false;
}
public void QuestionIsRaised()
{
IsQuestionRaised = true;
}
public void QuestionIsDismissed()
{
IsQuestionRaised = false;
}
}
The XAML code :
<Grid>
<Grid.Resources>
<Style x:Key="btnStyle" TargetType="Button">
<Setter Property="Height" Value="20"/>
<Setter Property="Width" Value="40"/>
<Setter Property="Margin" Value="10,10,10,10"/>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Click="Button_Click">Raise A Question</Button>
<Popup IsOpen="{Binding IsQuestionRaised}" Width="300" Height="100" Placement="Center" >
<Border BorderThickness="3">
<StackPanel Background="Aqua" Orientation="Vertical">
<TextBlock Margin="20,0,0,20">Yes or No ?</TextBlock>
<StackPanel Orientation="Horizontal">
<Button Click="Button_Click_Yes" Style="{StaticResource btnStyle}">Yes</Button>
<Button Click="Button_Click_No" Style="{StaticResource btnStyle}">No</Button>
<Button Click="Button_Click_Close" Style="{StaticResource btnStyle}">Close</Button>
</StackPanel>
</StackPanel>
</Border>
</Popup>
</Grid>
I use here code behind but we can definitely use delegate command in the ViewModel instead.
public partial class MainWindow : Window
{
MainViewModel _mainViewModel = new MainViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = _mainViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_mainViewModel.QuestionIsRaised();
}
private void Button_Click_Yes(object sender, RoutedEventArgs e)
{
_mainViewModel.Yes = true;
}
private void Button_Click_No(object sender, RoutedEventArgs e)
{
_mainViewModel.No = true;
}
private void Button_Click_Close(object sender, RoutedEventArgs e)
{
_mainViewModel.QuestionIsDismissed();
}
}
CodePudding user response:
I would recommend you to make a Window with its own Viewmodel and View. Like this: Add a new Window.
DialogWindow.xaml
<Window x:Class="ComboBoxItemPanel_Testing.Dialog_Window"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ComboBoxItemPanel_Testing"
mc:Ignorable="d"
Title="Dialog_Window" Height="150" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="Give me a number!"/>
<TextBox Text="{Binding Path=MyNumber}"/>
</StackPanel>
<StackPanel Orientation="Horizontal"
Grid.Row="1"
HorizontalAlignment="Center">
<Button Content="Yes"
Click="Yes_Button"
Margin="10"/>
<Button Content="No"
Margin="10"
Click="No_Button"/>
</StackPanel>
</Grid>
DialogWinodw.xaml.cs
using System.Windows;
namespace ComboBoxItemPanel_Testing
{
/// <summary>
/// Interaction logic for Dialog_Window.xaml
/// </summary>
public partial class Dialog_Window : Window
{
public Dialog_Window(object datacontext)
{
InitializeComponent();
DataContext = datacontext;
}
private void Yes_Button(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
private void No_Button(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
}
}
Add a new class to define your ViewModel. DialogWindowViewModel.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ComboBoxItemPanel_Testing
{
public class DialogWindowViewModel : INotifyPropertyChanged
{
private int _myNumber;
public int MyNumber
{
get => _myNumber;
set
{
if (_myNumber != value)
{
_myNumber = value;
}
}
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
try
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
catch (Exception ex)
{
Console.WriteLine($"PropertyChanged event handler FAILED : {ex.Message}");
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And u can use this like this:
DialogWindowViewModel vm = new DialogWindowViewModel();
Dialog_Window dialog = new Dialog_Window(vm);
if (dialog.ShowDialog().Value)
{
//Clicked Yes button
//use ur viewModel
Console.WriteLine("Selected number: " vm.MyNumber);
}
else
{
//Clicked No Button
Console.WriteLine("You didnt selected a number!");
}