This one is playing with me, and I cannot seem to figure it out - I need some external help here bro's!
I want a popup window that does not have the title, minimize, maximize, and close buttons, because we want to style it ourselves and add a custom close button on the popup's screen.
So I followed these links to get where I am now:
https://msdn.microsoft.com/en-us/library/ff921081(v=pandp.40).aspx
https://msdn.microsoft.com/en-us/library/gg405494(v=pandp.40).aspx
https://www.codeproject.com/Articles/269364/MVVM-PRISM-Modal-Windows-by-using-Interaction-Requ
But I still can't figure out how to achieve this. The basic windows have "OK" and "Cancel" buttons, but those are defaults, and I do not want that, that is why I went the "Custom View" way.
Here is my Main Window's xaml:
<Window x:Class="Prototype.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:views="clr-namespace:Prototype.Views"
Height="349.146" Width="727.317"
WindowState="Maximized">
<Grid>
<Button Command="{Binding RaiseCustomPopupViewCommand}">Show Popup Window</Button>
<i:Interaction.Triggers>
<prism:InteractionRequestTrigger SourceObject="{Binding CustomPopupViewRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowStyle>
<Style TargetType="Window">
<Setter Property="ShowInTaskbar" Value="False"/>
<Setter Property="WindowStyle" Value="None"/>
<Setter Property="ResizeMode" Value="NoResize"/>
</Style>
</prism:PopupWindowAction.WindowStyle>
<prism:PopupWindowAction.WindowContent>
<views:CustomPopupView />
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
</i:Interaction.Triggers>
</Grid>
</Window>
And here it the Main Window's code:
public class MainWindowViewModel : BindableBase
{
public InteractionRequest<INotification> CustomPopupViewRequest { get; private set; }
public MainWindowViewModel()
{
CustomPopupViewRequest = new InteractionRequest<INotification>();
}
public DelegateCommand RaiseCustomPopupViewCommand => new DelegateCommand(RaiseCustomPopupView, CanRaiseCustomPopupView);
public string InteractionResultMessage { get; private set; }
private void RaiseCustomPopupView()
{
InteractionResultMessage = "";
CustomPopupViewRequest.Raise(new Notification { Content = "Message for the CustomPopupView", Title = "Custom Popup" });
}
private bool CanRaiseCustomPopupView()
{
return true;
}
}
The InteractionRequestTrigger's SourceObject is a UserControl.
Here is it's xaml:
<UserControl x:Class="Prototype.Views.CustomPopupView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:local="clr-namespace:Prototype.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
MinWidth="300" MinHeight="100">
<StackPanel Orientation="Vertical" Background="Gray">
<Button DockPanel.Dock="Right"
Content="Close"/>
</StackPanel>
</UserControl>
So as you can see, I have a "Close" button in the UserControl.
I tried using a Command, but even then I cannot acccess the window or execute something to close the window.
Is there some kind of prism command or something I am not aware of?
Is it possible to close the window the way I want with a button?
Any help will be greatly appreciated! :)
No matter, I just managed to solve this on my own :).
I was thinking of it differently, but what I actually wanted was the CancelCommand.
This is how we implement it in the UserControl, with NO other changes. Everything is still the same as described above, but the CustomPopup now have the following in it's ViewModel:
public class CustomPopupViewModel : BindableBase, IInteractionRequestAware
{
public CustomPopupViewModel()
{
CancelCommand = new DelegateCommand(CancelInteraction);
}
private CustomPopupSelectionNotification notification;
public INotification Notification
{
get
{
return this.notification;
}
set
{
if (value is CustomPopupSelectionNotification)
{
this.notification = value as CustomPopupSelectionNotification;
this.OnPropertyChanged(() => this.Notification);
}
}
}
public Action FinishInteraction { get; set; }
public System.Windows.Input.ICommand CancelCommand { get; private set; }
public void CancelInteraction()
{
if (notification != null)
{
notification.SelectedItem = null;
notification.Confirmed = false;
}
FinishInteraction();
}
}
You will also notice that we have a class called CustomPopupSelectionNotification.
Here is it's code:
public class CustomPopupSelectionNotification : Confirmation
{
public CustomPopupSelectionNotification()
{
Items = new List<string>();
SelectedItem = null;
}
public CustomPopupSelectionNotification(IEnumerable<string> items) : this()
{
foreach (string item in items)
{
Items.Add(item);
}
}
public IList<string> Items { get; private set; }
public string SelectedItem { get; set; }
}
So in short, I am just cancelling the popup, instead of trying to close it.
I then added the command CancelCommand to the "Close" button on my UserControl.
Related
So I have created a button and I link it to an ICommand NavigateToVM but it does not hit the execute function when I try to click the button. I am I doing something wrong. I'll post the code that is relevant. Thanks in advance.
The Profile button is the button I am trying to get to work. It is just a standard Icommand.
{
public class NavigationBarVM : BaseViewModel
{
public ICommand NavigateToVMCmd { get; set; }
public NavigationBarVM()
{
}
public NavigationBarVM( NavigationStore _navigationStore)
{
NavigateToVMCmd = new NavigateToProfileCommand(this, _navigationStore);
}
}
}
namespace WpfNotes.Commands
{
public class NavigateToProfileCommand : CommandBase
{
private readonly NavigationBarVM navBarVM;
private readonly NavigationStore _navigationStore;
public NavigateToProfileCommand(NavigationBarVM VM, NavigationStore navigationStore)
{
navBarVM = VM;
_navigationStore = navigationStore;
}
public override void Execute(object parameter)
{
_navigationStore.CurrentViewModel = new ProfileVM();
Debug.WriteLine("Stuff");
}
}
}
<UserControl x:Class="WpfNotes.View.NavigationBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfNotes.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
xmlns:VM ="clr-namespace:WpfNotes.ViewModel">
<UserControl.Resources>
<VM:NavigationBarVM x:Key="vm"/>
</UserControl.Resources>
<Border BorderBrush="Black" BorderThickness="2">
<UniformGrid Columns="4" Height="40">
<Button BorderThickness="0" Content="Profile" Command="{Binding Source={StaticResource vm}, Path = NavigateToVMCmd }"></Button>
</UniformGrid>
</Border>
</UserControl>
You should instantiate the NavigationBarVM programmatically and inject it with a NavigationStore:
public partial class NavigationBar : UserControl
{
public MainWindow()
{
InitializeComponent();
DataContext = new NavigationBarVM(new NavigationStore());
}
}
You can then bind directly to the command of the view model:
<Button BorderThickness="0" Content="Profile"
Command="{Binding NavigateToVMCmd}" />
The following initializes the NavigationBarVM using the default constructor which is pretty useless and should be removed in your case as it doesn't initialize the command:
<VM:NavigationBarVM x:Key="vm"/>
Sometimes the text of notification message for modal dialog is very long as you can see on the figure below.This is not custom modal dialog but simple notification modal dialog which has a very long text mesage. This is the exception error-message produced by MODBUS protocol library that I use in my application. But SQL Server exceptions can also have such very long text messages about errors. By default, Prism 6.2 modal notification dialod displays a none-wrapped text. As a result of it, the modal dialog is very long and not all of the text of error-message is placed and displayed in the dialog. Below is the XAML markup for this modal dialog:
<prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True"/>
</prism:InteractionRequestTrigger>
And below is View Model C#-code for this dialog:
public InteractionRequest<INotification> NotificationRequest { get; private set; }
public String NotificationStatus
{
get { return this._notificationStatus; }
set { SetProperty(ref this._notificationStatus, value); }
}
The folowing line of code is from View Modal constructor:
this.NotificationRequest = new InteractionRequest<INotification>();
And the following is method displaying modal notification dialog:
private void raiseNotification(string message, string caption)
{
this.NotificationRequest.Raise(
new Notification { Content = message, Title = caption },
n => { this.NotificationStatus = "The user was notified."; });
}
Can I set text-wrapping mode for a long-text mesage (in XAML or in View Model) to transfer the text to the next lines in Prism 6.2. modal dialog?
You can show any view that you want inside the PopupWindowAction, just add content to the PopupWindowAction:
<prism:InteractionRequestTrigger SourceObject="{Binding NotificationRequest, Mode=OneWay}">
<prism:PopupWindowAction IsModal="True" CenterOverAssociatedObject="True">
<prism:PopupWindowAction.WindowContent>
<views:MyFancyErrorPopup/>
</prism:PopupWindowAction.WindowContent>
</prism:PopupWindowAction>
</prism:InteractionRequestTrigger>
Now MyFancyErrorPopup can show your error message as a multi-line textbox or whatever you like...
EDIT:
<UserControl x:Class="ClientModule.Views.MyFancyErrorPopup"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel Orientation="Vertical">
<TextBlock Text={Binding Message}" TextWrapping="Wrap"/>
<Button Content="Ok" Command="{Binding OkCommand}"/>
</StackPanel>
</UserControl>
class MyFancyErrorPopupViewModel : BindableBase, IInteractionRequestAware
{
public MyFancyErrorPopupViewModel()
{
OkCommand = new DelegateCommand( OnOk );
}
public DelegateCommand OkCommand
{
get;
}
public string Message
{
get { return (_notification?.Content as string) ?? "null"; }
}
#region IInteractionRequestAware
public INotification Notification
{
get { return _notification; }
set
{
SetProperty( ref _notification, value as Notification );
OnPropertyChanged( () => Message );
}
}
public Action FinishInteraction
{
get;
set;
}
#endregion
#region private
private Notification _notification;
private void OnOk()
{
FinishInteraction();
}
#endregion
}
Just want to make mvvm dialog using answer from this question Using MVVM show new window and get updates data. I also use example from MVVM survival guide book.
I have this ViewModel class for dialog:
public class OrganizationsViewModel
{
public OrganizationsViewModel()
{
TestProp = "TEST prop";
}
public override string ToString()
{
return "Organization";
}
public string TestProp { get; set; }
}
ShowDialog method in DialogService
public void ShowDialog(OrganizationsViewModel viewModel)
{
var dialog = new DialogView() { DataContext = viewModel };
dialog.Owner = Application.Current.MainWindow;
dialog.ShowInTaskbar = false;
dialog.ShowDialog();
}
DialogView.xaml:
<Window x:Class="testlayout.DialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DialogView" Height="300" Width="300">
<StackPanel>
<ContentControl Content="{Binding}" />
</StackPanel>
</Window>
DataTemplate in Application.xaml
<Application.Resources>
<DataTemplate x:Key="OrganizationsTemplate" DataType="{x:Type vm:OrganizationsViewModel}">
<vw:OrganizationsView/>
</DataTemplate>
</Application.Resources>
I call ShowDialog from MainWindowViewModel
DialogService.Instance.ShowDialog(new OrganizationsViewModel());
And I can see only Organization string in dialog, but don't see OrganizationsView. Don't understand what is wrong.
I've been working at this for awhile and seeming to not be able to find any good answers to my problem. I'm using a custom control that has custom dependency properties and in my main app I am binding to those propertys with my viewmodel that is seen through a viewmodel locator using mvvmlight. my question is why is the binding not updating nor seeing the correct datacontext?
Code:
User Control Xaml:
<UserControl x:Name="zKeyBoard"
x:Class="ZLibrary.ZKeyBoard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
d:DesignHeight="768" d:DesignWidth="1024">
<Grid>
<TextBox TextWrapping="Wrap" Text="{Binding zDisplayText}" />
<Label Content="{Binding zBoxToEdit}"/>
</Grid>
</UserControl>
Things I have Tried In The User Control Xaml Already:
<TextBox TextWrapping="Wrap" Text="{Binding zDisplayText, ElementName=zKeyBoard}" />
<Label Content="{Binding zBoxToEdit, ElementName=zKeyBoard}"/>
User Control C#:
using System.ComponentModel;
namespace ZLibrary
{
public partial class ZKeyBoard : UserControl, INotifyPropertyChanged
{
public ZKeyBoard()
{
InitializeComponent();
}
public string zBoxToEdit
{
get { return (string)GetValue(zBoxToEditProperty); }
set { SetValue(zBoxToEditProperty, value); }
}
public static readonly DependencyProperty zBoxToEditProperty =
DependencyProperty.Register("zBoxToEdit", typeof(string), typeof(ZKeyBoard), new UIPropertyMetadata(""));
public string zDisplayText
{
get { return (string)GetValue(zDisplayTextProperty); }
set { SetValue(zDisplayTextProperty, value); }
}
public static readonly DependencyProperty zDisplayTextProperty =
DependencyProperty.Register("zDisplayText", typeof(string), typeof(ZKeyBoard), new UIPropertyMetadata(""));
}
}
Things I have already tried in the user control C#:
public string zBoxToEdit
{
get;
set;
}
public string zDisplayText
{
get;
set;
}
Here is the Project Files Where the User Control Is Being Used:
APP.xaml:
<Application x:Class="WpfApplication1.App"
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:vm="clr-namespace:Sandstorm.ViewModel"
mc:Ignorable="d"
StartupUri="Main.xaml">
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>
The ViewModel Locator:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
namespace Sandstorm.ViewModel
{
class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<KeyBoardViewModel>(() =>
{
return new KeyBoardViewModel();
});
}
public KeyBoardViewModel KeyBoardViewModel
{
get { return ServiceLocator.Current.GetInstance<KeyBoardViewModel>(); }
}
public static void Cleanup()
{
// TODO Clear the ViewModels
}
}
}
The Xaml The User Control Is Being Used In:
<Page x:Name="keyboard_Frame"
x:Class="Sandstorm.keyBoard"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:ZControls="clr-namespace:ZLibrary;assembly=ZLibrary"
DataContext="{Binding KeyBoardViewModel, Source={StaticResource Locator}}"
mc:Ignorable="d"
d:DesignHeight="768" d:DesignWidth="1024"
ShowsNavigationUI="False"
Title="KeyBoard">
<Grid>
<ZControls:ZKeyBoard zBoxToEdit="{Binding boxToEdit}" zDisplayText="{Binding keyboardEntry}" />
</Grid>
</Page>
When This Xaml is ran as is This Way It Throws an error in the console that says it can not find the binding property of either boxToEdit or keyboarEntry and it refrences the original ZKeyBoard Name as The place it can not be found... So I added this:
<ZControls:ZKeyBoard zBoxToEdit="{Binding boxToEdit, RelativeSource={RelativeSource Mode=TemplatedParent}}" zDisplayText="{Binding keyboardEntry, RelativeSource={RelativeSource Mode=TemplatedParent}}" />
Which caused the error to go away which I assume meant that it could find the viewmodel yet still nothing happened.
And Finally The View Model:
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.ComponentModel;
namespace Sandstorm.ViewModel
{
class KeyBoardViewModel : ViewModelBase, INotifyPropertyChanged
{
private string _keyboardEntry;
private string _boxToEdit;
public KeyBoardViewModel()
{
_boxToEdit = "yay";
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
public string keyboardEntry
{
get { return this._keyboardEntry; }
set
{
if (this._keyboardEntry != value)
{
this._keyboardEntry = value;
this.OnPropertyChanged("keyboardEntry");
Console.Out.WriteLine(this._keyboardEntry);
}
}
}
public string boxToEdit
{
get { return this._boxToEdit; }
set
{
if (this._boxToEdit != value)
{
this._boxToEdit = value;
this.OnPropertyChanged("boxToEdit");
Console.Out.WriteLine(this._boxToEdit);
}
}
}
}
}
One Thing I noticed was that I can Not See The Console.out.writeline doing anything which to me means it is not setting at all. so lots of big questions as to why this is not working. Any Help on this would be amazing! it probably is something small and stupid but a second pair of eyes on this will probably notice it faster than me.
Simple answer:
Don't set the DataContext to self.
Problem resolved
I'm trying to put together a very simple example app, but it is not working as intented.
Here is the scenario:
Caliburn.Micro, MVVM, Silverlight 5.0 - simple Conductor example from https://caliburnmicro.codeplex.com/wikipage?title=Screens%2c%20Conductors%20and%20Composition&referringTitle=Documentation (Simple Navigation)
I just put together a live example:
https://db.tt/kTIjKvRx
-> hit enter in textbox (messagebox displays 1x)
-> go to master and go back to login
-> hit enter in textbox (messagebox displays 2x!)
ShellViewModel
public class ShellViewModel : Conductor<object> {
public ShellViewModel() {
ShowLogin();
}
public void ShowLogin() {
ActivateItem(new LoginViewModel());
}
public void ShowMaster() {
ActivateItem(new MasterViewModel());
}
}
EDIT:
Same results with this:
public class ShellViewModel : Conductor<object> {
public ShellViewModel() {
LoginModel = new LoginViewModel();
MasterModel = new MasterViewModel();
ShowLogin();
}
public LoginViewModel LoginModel { get; set; }
public MasterViewModel MasterModel { get; set; }
public void ShowLogin() {
ActiveItem = LoginViewModel;
}
public void ShowMaster() {
ActiveItem = MasterViewModel;
}
}
ShellView
<UserControl
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"
mc:Ignorable="d" x:Class="Hele.ShellView"
d:DesignWidth="438" d:DesignHeight="200">
<Grid>
<StackPanel Orientation="Vertical" HorizontalAlignment="Left" VerticalAlignment="Top">
<Button x:Name="ShowLogin" Width="100" Height="30" Content="Login"/>
<Button x:Name="ShowMaster" Width="100" Height="30" Content="Master"/>
<ContentControl x:Name="ActiveItem" " />
</Grid>
</UserControl>
LoginView
<UserControl ... >
<Grid x:Name="LayoutRoot">
<StackPanel>
<TextBlock>Login</TextBlock>
<TextBox x:Name="Message" Text="{Binding Message, Mode=TwoWay}" >
<i:Interaction.Triggers>
<iex:KeyTrigger Key="Enter">
<cal:ActionMessage MethodName="Login" />
</iex:KeyTrigger>
</i:Interaction.Triggers>
</TextBox>
</StackPanel>
</Grid>
</UserControl>
LoginViewModel
public class LoginViewModel : Screen
{
public string Message { get; set; }
public void Login()
{
MessageBox.Show("login messagebox");
}
}
MasterView and MasterViewModel are just empty, nothing interesting there.
The above example just works fine, after clicking on Login button shows login view, on Master shows master view.
In the Login View there is a textbox which has an event trigger. After hitting Enter key, it calls a method from viewmodel and displays a messagebox.
The problem:
When going to master view and going back to login end hitting Enter - it shows the messagebox twice!
Going to master and again back -> it will display it 3x.. and so on.
I think the Trigger should fire only once. How can we achieve this behavior?
I think it's because your doing the ActivateItem each time your loading the view which rebinds the event handlers to the view. Try setting the ActiveItem property instead (which the ContentControl with x:Name="ActiveItem" is bound to). Also try using public variables to hold your view models:
public class ShellViewModel : Conductor<object> {
public ShellViewModel() {
ShowLogin();
}
public LoginViewModel { get; set; }
public MasterViewModel { get; set; }
public void ShowLogin() {
ActiveItem = LoginViewModel;
}
public void ShowMaster() {
ActiveItem = MasterViewModel;
}
}
EDIT
I was able to reproduce this and it seems to be an issue with the Expression Interactions. If I use a regular EventTrigger attached to key down it works fine:
<TextBox Width="50" Text="{Binding Message, Mode=TwoWay}" >
<i:Interaction.Triggers>
<!--<iex:KeyTrigger Key="Enter">
<cm:ActionMessage MethodName="Page1KeyPress" />
</iex:KeyTrigger>-->
<i:EventTrigger EventName="KeyDown">
<cm:ActionMessage MethodName="Page1KeyPress" >
<cm:Parameter Value="$source" />
<cm:Parameter Value="$eventArgs" />
</cm:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
public void Page1KeyPress(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
MessageBox.Show("Page 1 Key Press");
}