I am trying to implement modal dialog in the WPF Prism Desktop application.
From Prism guidance I can see that proper way should be using Interaction:
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger
SourceObject="{Binding ConfirmCancelInteractionRequest}">
<prism:PopupChildWindowAction
ContentTemplate="{StaticResource ConfirmWindowTemplate}"/>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
But PopupChildWindowAction is not available in the Microsoft.Practices.Prism.Interactivity.DLL library for Desktop, only Silverlight?
I could google for many different implementations of the Modal Dialog in WPF (Prism), but just wondering why this feature is missing from Prism Desktop DLL and is available in Silverlight DLL?
I could use Interaction Service but Interaction Request is suggested as more appropriate approach for MVVM application.
That's true it only exists in the Silverlight prism library ,
What you can do is create your own .
CS :
public class OpenPopupWindowAction : TriggerAction<FrameworkElement>
{
protected override void Invoke(object parameter)
{
var popup = (ChildWindow)ServiceLocator.Current.GetInstance<IPopupDialogWindow>();
popup.Owner = PlacementTarget ?? (Window)ServiceLocator.Current.GetInstance<IShell>();
popup.DialogResultCommand = PopupDailogResultCommand;
popup.Show();
}
public Window PlacementTarget
{
get { return (Window)GetValue(PlacementTargetProperty); }
set { SetValue(PlacementTargetProperty, value); }
}
public static readonly DependencyProperty PlacementTargetProperty =
DependencyProperty.Register("PlacementTarget", typeof(Window), typeof(OpenPopupWindowAction), new PropertyMetadata(null));
public ICommand PopupDailogResultCommand
{
get { return (ICommand)GetValue(PopupDailogResultCommandProperty); }
set { SetValue(PopupDailogResultCommandProperty, value); }
}
public static readonly DependencyProperty PopupDailogResultCommandProperty =
DependencyProperty.Register("PopupDailogResultCommand", typeof(ICommand), typeof(OpenPopupWindowAction), new PropertyMetadata(null));
}
XAML :
<i:EventTrigger SourceObject="{Binding}" EventName="NavigatedFrom">
<popup:OpenPopupWindowAction PopupDailogResultCommand="{Binding OnNavigationConfirmed}"/>
</i:EventTrigger>
And if you need here is the Code for the DialogWindow it self .
cs:
public partial class ChildWindow : Window, IPopupDialogWindow
{
public ChildWindow()
{
InitializeComponent();
DataContext = this;
}
public new PopupDialogResult DialogResult
{
get;
set;
}
public System.Windows.Input.ICommand DialogResultCommand
{
get;
set;
}
}
xaml :
<Window x:Class="Utils.ActionPopupWindow.ChildWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="300" Width="400" WindowStartupLocation="CenterOwner"
xmlns:popup="clr-namespace:Utils.ActionPopupWindow"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Name="popUpWindow"
>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="30">
This is a child window <LineBreak/> launched from the <LineBreak/>main window
</TextBlock>
<StackPanel Grid.Row="1" Background="#FFA6A6A6">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Ok"
MinWidth="100"
Command="{Binding DialogResultCommand}"
CommandParameter="{x:Static popup:PopupDialogResult.OK}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="Close" TargetObject="{Binding ElementName=popUpWindow}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="Cancel"
MinWidth="100"
Command="{Binding DialogResultCommand}"
CommandParameter="{x:Static popup:PopupDialogResult.Cancel}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:CallMethodAction MethodName="Close" TargetObject="{Binding ElementName=popUpWindow}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</StackPanel>
</Grid>
Here is an example made in WPF:
http://blogs.southworks.net/dcherubini/2012/05/24/popupwindowaction-using-custom-views-instead-of-windows-in-wpf-and-prism/
Related
I'm creating "layout" View which includes few ContentControl elements, which results in need for me to register a "default" control to be desplayed at start. As I realize RegisterViewWithRegion solves this problem, but I am not sure where I'm supposed to use it?
So far I have been using RegisterViewWithRegion inside constructor of layout's ViewModel, but this results in
ArgumentException: Region with the given name is already registered: AuthContentRegion
error once ViewModel is constructed again (second time).
View:
<DockPanel LastChildFill="True">
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.NavRegion}" DockPanel.Dock="Top" Margin="5" />
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.AuthContentRegion}" Margin="5" />
</DockPanel>
View Model:
public AdminViewModel(IRegionManager regionManager)
{
regionManager.RegisterViewWithRegion(RegionNames.NavRegion, typeof(Views.AdminNavView));
regionManager.RegisterViewWithRegion(RegionNames.AuthContentRegion, typeof(Views.RailwayListView));
}
Essentially what I want is to have default UserControl registered as ContentControl/Region without getting any exceptions once View/ViewModel is constructed again.
Implementing IRegionMemberLifetime with KeepAlive set to false seems to have fixed issue with exception.
Also i realized that RegisterViewWithRegioncould be substituted with RequestNavigate method by calling it in Loaded event.
View:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding OnLoadedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<DockPanel LastChildFill="True">
<views:AdminNavView DockPanel.Dock="Top" Margin="5" />
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.AuthContentRegion}" Margin="5" />
</DockPanel>
</Grid>
ViewModel:
public class AdminViewModel : BindableBase, IRegionMemberLifetime
{
private readonly IRegionManager _regionManager;
public AdminViewModel(IRegionManager regionManager)
{
_regionManager = regionManager;
OnLoadedCommand = new DelegateCommand(OnLoaded);
}
public bool KeepAlive => false;
public ICommand OnLoadedCommand { get; }
public void OnLoaded()
{
_regionManager.RequestNavigate(RegionNames.AuthContentRegion, NavigationPaths.RailwayListPath);
}
}
Ok, so I'm trying to figure out how to set a status bars label text to show information about the current control that a mouse is hovering over. I have seen this numerous times on many programs so I know it can be done and I'm sure there are explanations out there that could help me but I can't seem to find the right words to search for the answer unfortunately...
The closest thing I could find was in the link below. I tried to utilize this but it gave me an error when I tried to set the text property.
Anyone have some information or a link to help me by chance?
Thanks,
Ryan
Display text in a label when hovering over a control without using events
My XAML Code:
<StatusBar>
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="75" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem Grid.Column="0">
<Label Content="New Lead Inquiry" />
</StatusBarItem>
<Separator Grid.Column="1" Style="{StaticResource StylingStatusBarSeparator}" />
<StatusBarItem Grid.Column="2">
<Label x:Name="infoStatusBar" Content="Label for text about the currently hovered item" />
</StatusBarItem>
<Separator Grid.Column="3" Style="{StaticResource StylingStatusBarSeparator}" />
<StatusBarItem Grid.Column="4">
<Label Content="Not Saved" />
</StatusBarItem>
</StatusBar>
Here's a solution that doesn't require you to modify each child control or use any frameworks.
This isn't really related to MVVM, since it's pure UI stuff. There's nothing here that would involve a viewmodel.
Handle Window.PreviewMouseMove:
MainWindow.xaml
<Window
...
PreviewMouseMove="Window_PreviewMouseMove"
>
MainWindow.xaml.cs
Define a dependency property of type Object, and in the preview mousemove handler, give it the nearest parent tooltip of the control the mouse is over:
private void Window_PreviewMouseMove(object sender, MouseEventArgs e)
{
var element = Mouse.DirectlyOver as FrameworkElement;
HoverToolTip = GetTooltip(element);
}
#region HoverToolTip Property
public object HoverToolTip
{
get { return (object)GetValue(HoverToolTipProperty); }
set { SetValue(HoverToolTipProperty, value); }
}
public static readonly DependencyProperty HoverToolTipProperty =
DependencyProperty.Register(nameof(HoverToolTip), typeof(object), typeof(MainWindow),
new PropertyMetadata(null));
#endregion HoverToolTip Property
protected static Object GetTooltip(FrameworkElement obj)
{
if (obj == null)
{
return null;
}
else if (obj.ToolTip != null)
{
return obj.ToolTip;
}
else
{
return GetTooltip(VisualTreeHelper.GetParent(obj) as FrameworkElement);
}
}
And bind that to whatever in the XAML.
<Label
x:Name="StatusBar"
Content="{Binding HoverToolTip, RelativeSource={RelativeSource AncestorType=Window}}"
Grid.Row="2"
/>
That Label is just the quickie I put in my test XAML. This binding is the important part there:
{Binding HoverToolTip, RelativeSource={RelativeSource AncestorType=Window}}
You can wire the MouseEnter and MouseLeave commands on your controls to set a HelpText property in your viewmodel, and then bind the status bar label to HelpText so that when it is set to something new, the new value appears in the status bar.
This answer uses the MVVM Light toolkit, but should be adaptable for any MVVM setup:
In XAML:
<Window x:Class="MvvmLightPlayground.MainWindow"
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:ignore="http://www.galasoft.ch/ignore"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d ignore"
Height="300"
Width="600"
Title="MVVM Light Application">
<Window.DataContext>
<Binding Path="Main" Source="{StaticResource Locator}" />
</Window.DataContext>
<StackPanel>
<Label Content="This is Label1" x:Name="Label1">
<!-- Triggers like this are easy to wire up using blend. If you do it manually, add the i: definition to your window tag as shown above -->
<i:Interaction.Triggers>
<i:EventTrigger SourceName="Label1" EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding MouseEnter}" CommandParameter="This is Label1. Look how neat it is!" />
</i:EventTrigger>
<i:EventTrigger SourceName="Label1" EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding MouseLeave}" CommandParameter="This is Label1. Look how neat it is!" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
<Label Content="This is Label2" x:Name="Label2">
<i:Interaction.Triggers>
<i:EventTrigger SourceName="Label2" EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding MouseEnter}" CommandParameter="This is Label2. It's a different label." />
</i:EventTrigger>
<i:EventTrigger SourceName="Label2" EventName="MouseLeave">
<i:InvokeCommandAction Command="{Binding MouseLeave}" CommandParameter="This is Label2. It's a different label." />
</i:EventTrigger>
</i:Interaction.Triggers>
</Label>
<StatusBar>
<StatusBar.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</StatusBar.ItemsPanel>
<StatusBarItem>
<Label Content="{Binding HelpText, Mode=OneWay}" /> <!-- Bind to your HelpText property in the VM -->
</StatusBarItem>
</StatusBar>
</StackPanel>
</Window>
In your viewmodel:
First, add properties for your HelpText and your ICommands:
private string _helpText = "Testing";
public string HelpText
{
get
{
return _helpText;
}
set
{
Set(() => HelpText, ref _helpText, value);
}
}
private ICommand _mouseEnter;
public ICommand MouseEnter
{
get
{
return _mouseEnter;
}
set
{
Set(() => MouseEnter, ref _mouseEnter, value);
}
}
private ICommand _mouseLeave;
public ICommand MouseLeave
{
get
{
return _mouseLeave;
}
set
{
Set(() => MouseLeave, ref _mouseLeave, value);
}
}
Then initialize your ICommands in your viewmodel constructor to point at methods in the viewmodel:
public MainViewModel()
{
MouseEnter = new RelayCommand<string>(SetHelpText);
MouseLeave = new RelayCommand<string>(ClearHelpText);
}
Then create your helper methods to set the HelpText property:
public void SetHelpText(string helpText)
{
HelpText = helpText;
}
public void ClearHelpText(string textToClear)
{
// check to see whether it has already been set to something else by another MouseEnter event...
if (HelpText == textToClear)
{
HelpText = "";
}
}
This is the running sample shown with the mouse hovered over Label2:
I am trying to bind a click on button event in WPF to a command defined in a View Model, here is how I am doing that for now :
In the xaml code :
<Grid>
<Button Content="Module A" Background="Green" FontWeight="Bold">
<i:Interaction.Triggers>
<i:EventTrigger EventName="click">
<i:InvokeCommandAction Command="{Binding ChargeModuleDCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
and in the ViewModel class :
class ModuleAViewModel
{
public DelegateCommand<object> ChargeModuleDCommand { get; set; }
public ModuleAViewModel()
{
ChargeModuleDCommand = new DelegateCommand<object>(LaunchDModule);
}
private void LaunchDModule(object parm)
{
Console.WriteLine("I am in the function");
}
}
but it does not work. I've tried to do it as specified in this question : How to trigger ViewModel command for a specific button events
but it does not work either.
Is there any way that I can make it work ?
<Button
Command="{Binding ChargeModuleDCommand}"
Content="Module A"
Background="Green"
FontWeight="Bold"
/>
If ModuleAViewModel is the Button's DataContext, that should work.
I am having an scenario where I show a window to the user and ask them to choose anything by left click on it. See attached pix
So in this Window I have corresponding WindowViewModel following a Prism 6.1.0 framework. I want to bind this click event to the Grid instead of Binding with the each TextBlock each. Is it possible?
if yes, I tried this. In the grid Control my code is this.
<Grid x:Name="Locals1">
<Grid.InputBindings>
<MouseBinding MouseAction="LeftClick"
Gesture="LeftClick" Command="{Binding MouseCommand,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
CommandParameter="{Binding ElementName=Locals1,
Path=SelectedItem, Mode=TwoWay}"
/>
</Grid.InputBindings>
<TextBlock Focusable="True" text="textblock1" />
<TextBlock Focusable="True" text="textblock2" />
<TextBlock Focusable="True" text="textblock3" />
<TextBlock Focusable="True" text="textblock4" />
</Grid>
And in the WindowViewModel I have a code like this.
public WindowViewModel(IEventAggregator eventAggregator)
{
MouseCommand = new DelegateCommand<string>(OnConnection);
}
private void OnConnection(string obj)
{
...
}
But I don't get the TextBlock.Text value in that OnConnection method. Is it really so tough? What I know about WPF and MVVM that we can handle the child click event in the parent control itself. This will reduce duplicate codes.
I know I am doing something definitely wrong. But I don't know what exactly. How can I pass this value from WindowViewModel to the MainWindowViewModel?
I can achieve the same functionality using a binding each in all the textblocks but that will not serve the purpose of Prism. basically all the text Block click events functionality is same only the value of the textblock will be different.
thanks
Honestly, i don't like my answer, but:
You use Grid, not DataGrid and similar, so what is SelectedItem in you context?!
I cant invent how to use pretty binding in this case, so i changed command
public DelegateCommand<Object> MouseCommand { get; set; }
public WindowViewModel(IEventAggregator eventAggregator)
{
MouseCommand = new DelegateCommand<object>(OnConnection);
}
and
private void OnConnection(object obj)
{
var text = GetTextFromClickOnGrid(obj);
}
private string GetTextFromClickOnGrid(object obj)
{
var grid = obj as Grid;
if (grid != null)
{
var mousePos = Mouse.GetPosition(grid);
var itemUnderMouse = VisualTreeHelper.HitTest(grid, mousePos);
var textBlock = itemUnderMouse.VisualHit as TextBlock;
if (textBlock != null)
{
return textBlock.Text;
}
}
var textBlockUnderMouse = Mouse.DirectlyOver as TextBlock;
if (textBlockUnderMouse != null)
{
return textBlockUnderMouse.Text;
}
return string.Empty;
}
and xaml
<Grid Grid.Row="1" x:Name="Locals1">
<Grid.InputBindings>
<MouseBinding MouseAction="LeftClick"
Gesture="LeftClick"
Command="{Binding MouseCommand,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
CommandParameter="{Binding ElementName=Locals1}"
/>
</Grid.InputBindings>
<TextBlock Focusable="True" Text="textblock1" Height="30" Width="100" Margin="115,45,302,84" />
<TextBlock Focusable="True" Text="textblock2" Height="30" Width="100" Margin="115,10,302,119"/>
<TextBlock Focusable="True" Text="textblock3" Height="30" Width="100" Margin="10,45,407,84"/>
<TextBlock Focusable="True" Text="textblock4" Height="30" Width="100" Margin="10,10,407,119"/>
</Grid>
i think you looking this:
<i:Interaction.Triggers>
<i:EventTrigger EventName="LeftClick">
<command:EventToCommand
Command="{Binding Main.MouseCommand ,
Mode=OneWay,
Source={StaticResource Locator}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
binding what you want. Any event to almost any command.
ViewModelLocator:
public class ViewModelLocator
{
static ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
}
/// <summary>
/// Gets the Main property.
/// </summary>
public MainViewModel Main
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
}
I have a stackpanel with image and button in it. I want to fire event when user clicks on a button in stackPanel. My code in xaml is
<StackPanel x:Uid="TemperatureMonitor" Orientation="Horizontal" HorizontalAlignment="Left" ToolTip="{DynamicResource InstrumentZweiMesswert}" Height="35">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="OnAddUserControl"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<Image Width="35" Height="35" x:Uid="Image_15" Source="/Resources\png\TemperatureMonitor.png"/>
<Button x:Uid="TemperatureMonitor" Content="Temperatur Monitor" x:Name="TemperatureMonitor" IsEnabled="True" Width="135"/>
</StackPanel>
And method OnAddUserControl in my viewModel is
public void OnAddUserControl(object sender, RoutedEventArgs e)
{
//some code
}
The problem it that I don't get into OnAddUserControl. Any ideas why?
I want to fire this event when user makes leftMouseClick on a button. So I don't know why, but RelayCommand also doesn't help and not fires method OnAddUserControl. When I moved iteraction code to my button and it looks like this :
<StackPanel Background="Black" x:Uid="TemperatureMonitor" Orientation="Horizontal" HorizontalAlignment="Left" ToolTip="{DynamicResource InstrumentZweiMesswert}" Height="35">
<Image Width="35" Height="35" x:Uid="Image_15" Source="/Resources\png\TemperatureMonitor.PNG"/>
<Button x:Uid="TemperatureMonitor" Content="Temperatur Monitor" x:Name="TemperatureMonitor" IsEnabled="True" Width="135" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseLeftButtonDown">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="OnAddUserControl"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
i've get during runtime mistake that says "For object Type"DockSite" cannot find methodname "OnAddUserControl"". I will appreciate any ideas or help
You can use RelayCommand for this purpose.
Add RelayCommand.cs to your project.
class RelayCommand : ICommand
{
private Action<object> _action;
public RelayCommand(Action<object> action)
{
_action = action;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter != null)
{
_action(parameter);
}
else
{
_action("Hello World");
}
}
#endregion
}
And this is your ViewModel. I called this MainWindowViewModel. So, add MainWindowViewModel.cs class to your solution.
class MainWindowViewModel
{
private ICommand m_ButtonCommand;
public ICommand ButtonCommand
{
get
{
return m_ButtonCommand;
}
set
{
m_ButtonCommand = value;
}
}
public MainWindowViewModel()
{
ButtonCommand=new RelayCommand(new Action<object>(ShowMessage));
}
public void ShowMessage(object obj)
{
MessageBox.Show(obj.ToString());
}
}
And this is your xaml:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
<Button Width="220" Content="Click me" Command={Binding ButtonCommand} CommandParameter="StackOverflow" />
</StackPanel>
It will show you messageBox after clicking button. So you change your project for handing Button Click event in this way.