I have MaterialWindows from MaterialDesignInXaml which can be minimized in the notification bar.
I hide the windows by setting the Visibility to Collapsed.
The double click on the icon launches a command to restore the windows:
public RelayCommand OpenWindowsCommand => new RelayCommand(ExecuteOpenWindowsCommand);
private void ExecuteOpenWindowsCommand(object o)
{
WindowState = WindowState.Normal;
//Activated = true;
//IsInForeground = true;
//IsInForeground = false;
//IsFocus = true;
}
My property WindowState changed the visibility in it and notify the change with OnPropertyChanded.
The windows appear in the task bar but doesn't come on the foreground of my screen.
As you can see in comment, I tried different ways to get it without success like :
Focusable property
TopMost property
Top property
Even a behaviour to activate the windows
Is there something special to put a windows in the foreground of the screen?
Edit to add a sample repo :
MainWindow.xaml
<controls:MaterialWindow
x:Class="MinimiedWindows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Behaviors="clr-namespace:MinimiedWindows.Behavior"
xmlns:controls="clr-namespace:MaterialDesignExtensions.Controls;assembly=MaterialDesignExtensions"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:MinimiedWindows"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:tb="http://www.hardcodet.net/taskbar"
Title="MainWindow"
Width="800"
Height="450"
Focusable="{Binding IsFocus}"
Icon="app.ico"
Topmost="{Binding IsFocus}"
Visibility="{Binding WindowsVisibility, Mode=TwoWay}"
WindowState="{Binding WindowState}"
mc:Ignorable="d">
<i:Interaction.Behaviors>
<Behaviors:ActivateBehavior Activated="{Binding Activated, Mode=TwoWay}" />
</i:Interaction.Behaviors>
<Grid>
<tb:TaskbarIcon
x:Name="myNotifyIcon"
DoubleClickCommand="{Binding OpenWindowsCommand}"
IconSource="{Binding NotifyIcon}"
MenuActivation="LeftOrRightClick"
PopupActivation="DoubleClick"
ToolTip="aa"
Visibility="{Binding NotifyIconVisibility}" />
</Grid>
</controls:MaterialWindow>
MainWindows constructor set the datacontext to a new MainViewModel.
MainViewModel.cs :
(ViewModelBase is the classic one with the property OnPropertyChanged)
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
}
#region Windows properties
private WindowState _WindowState = WindowState.Normal;
public WindowState WindowState
{
get { return _WindowState; }
set
{
_WindowState = value;
OnPropertyChanged();
OnPropertyChanged("WindowsVisibility");
OnPropertyChanged("NotifyIconVisibility");
}
}
private Visibility _WindowsVisibility = Visibility.Visible;
public Visibility WindowsVisibility
{
get
{
if (WindowState == WindowState.Minimized)
{
return Visibility.Collapsed;
}
return Visibility.Visible;
}
set { _WindowsVisibility = value; }
}
private bool _Activated;
public bool Activated
{
get { return _Activated; }
set
{
_Activated = value;
OnPropertyChanged();
}
}
private bool _IsInForeground;
public bool IsInForeground
{
get { return _IsInForeground; }
set
{
_IsInForeground = value;
OnPropertyChanged();
}
}
private bool _IsFocus;
public bool IsFocus
{
get { return _IsFocus; }
set
{
_IsFocus = value;
OnPropertyChanged();
}
}
#endregion
#region NotifyBar
public string NotifyIcon
{
get { return "app.ico"; }
}
public Visibility NotifyIconVisibility
{
get
{
if (WindowState == WindowState.Minimized)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
}
public RelayCommand OpenWindowsCommand => new RelayCommand(ExecuteOpenWindowsCommand);
private void ExecuteOpenWindowsCommand(object o)
{
WindowState = WindowState.Normal;
Activated = true;
}
#endregion
}
And finally my ActivateBehavior.cs
public class ActivateBehavior : Behavior<MaterialWindow>
{
Boolean isActivated;
public static readonly DependencyProperty ActivatedProperty =
DependencyProperty.Register(
"Activated",
typeof(Boolean),
typeof(ActivateBehavior),
new PropertyMetadata(OnActivatedChanged)
);
public Boolean Activated
{
get { return (Boolean)GetValue(ActivatedProperty); }
set { SetValue(ActivatedProperty, value); }
}
static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var behavior = (ActivateBehavior)dependencyObject;
if (!behavior.Activated || behavior.isActivated)
return;
// The Activated property is set to true but the Activated event (tracked by the
// isActivated field) hasn't been fired. Go ahead and activate the window.
if (behavior.AssociatedObject.WindowState == WindowState.Minimized)
behavior.AssociatedObject.WindowState = WindowState.Normal;
behavior.AssociatedObject.Activate();
}
protected override void OnAttached()
{
AssociatedObject.Activated += OnActivated;
AssociatedObject.Deactivated += OnDeactivated;
}
protected override void OnDetaching()
{
AssociatedObject.Activated -= OnActivated;
AssociatedObject.Deactivated -= OnDeactivated;
}
void OnActivated(Object sender, EventArgs eventArgs)
{
this.isActivated = true;
Activated = true;
}
void OnDeactivated(Object sender, EventArgs eventArgs)
{
this.isActivated = false;
Activated = false;
}
}
For the packages, I have installed Hardcodet.NotifyIcon.Wpf, MaterialDesignExtensionss 3.1.0 (and the materialDesign dependencies) and System.Windows.Interactivity.WPF.
Change your ExecuteOpenWindowsCommand to only set the Activated property.
private void ExecuteOpenWindowsCommand()
{
Activated = true;
}
Adapt the OnActivatedChanged method in the ActivateBehavior like this.
private static void OnActivatedChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var behavior = (ActivateBehavior)dependencyObject;
if (!behavior.Activated || behavior.isActivated)
return;
// The Activated property is set to true but the Activated event (tracked by the
// isActivated field) hasn't been fired. Go ahead and activate the window.
var window = behavior.AssociatedObject;
if (window.WindowState == WindowState.Minimized)
{
window.WindowState = WindowState.Normal;
SystemCommands.RestoreWindow(window);
}
window.Activate();
}
The essential part is restoring the window, which did not happen before, it was hidden. Then it needs to be activated to come to foreground. The window state asssignment is not needed, but makes the restore transition of the window more pleasant.
Related
So I'm working on a C#/WPF Application and I'm relatively new to the language/environment. A couple weeks ago, I asked this question: How can I mimic this behavior in WPF?
I wanted to create a sort of pseudo modal popup that would appear/disappear with a click of a button. Adapting the answer given to me, I created the following two classes:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace RelayC
{
public partial class PopupBase : UserControl
{
public PopupBase()
{
this.Opacity = 0.0;
this.Visibility = Visibility.Hidden;
}
private void OpenPopup()
{
this.Opacity = 1.0;
this.Visibility = Visibility.Visible;
}
private void ClosePopup()
{
this.Opacity = 0.0;
this.Visibility = Visibility.Hidden;
}
public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register(nameof(IsOpen),
typeof(bool),
typeof(PopupBase),
new PropertyMetadata(false,
new PropertyChangedCallback((s, e) =>
{
if (s is PopupBase popupBase && e.NewValue is bool boolean)
{
if (boolean)
{
popupBase.OpenPopup();
}
else
{
popupBase.ClosePopup();
}
}
})));
}
public class PopupAttach
{
public static PopupBase GetPopup(ButtonBase button)
=> (PopupBase)button.GetValue(PopupProperty);
public static void SetPopup(ButtonBase button, PopupBase value)
=> button.SetValue(PopupProperty, value);
public static readonly DependencyProperty PopupProperty =
DependencyProperty.RegisterAttached("Popup",
typeof(PopupBase),
typeof(PopupAttach),
new PropertyMetadata(null,
new PropertyChangedCallback((s, e) =>
{
if (s is ButtonBase button && e.NewValue is PopupBase newPopup)
{
if (Application.Current.MainWindow.Content is Grid grid)
{
if (e.OldValue is PopupBase oldPopup)
{
grid.Children.Remove(oldPopup);
}
grid.Children.Add(newPopup);
button.Click -= buttonClick;
button.Click += buttonClick;
}
else
{
throw new Exception($"{nameof(Application.Current.MainWindow)} must have a root layout panel of type {nameof(Grid)} in order to use attachable Flyout.");
}
void buttonClick(object sender, RoutedEventArgs routedEventArgs)
{
newPopup.IsOpen = true;
}
}
})));
}
}
And then I have any UserControl I create inherit from PopupBase so my Popup UI's look sort of like this:
<local:PopupBase x:Class="RelayC.AddServer"
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:RelayC"
mc:Ignorable="d"
Width="500"
Height="375">
UI CONTENT INSERTED HERE
</local:PopupBase>
The problem is when I want to attach it to a button, I have to do something like this:
<Window.Resources>
<local:AddServer Grid.Row="1" x:Key="ISERCpm"/>
</Window.Resources>
in my MainWindow.xaml and then call it from a button using
<Button local:PopupAttach.Popup="{StaticResource ISERCpm}" />
Is there anyway to skip the Window Resource declaration and straight just call my PopupBase User Control?
PopupAttach.Popup is an attached dependency property. It is possible to use property element syntax instead of attribute syntax:
<Button>
<local:PopupAttach.Popup>
<local:AddServer Grid.Row="1"/>
</local:PopupAttach.Popup>
</Button>
I have an Image element that's bound to an ImageSource element inside a class that I've created. The ImageSource gets updated every time a slider is changed. When I first instantiate my window, the ImageSource is blank until the user loads a file. Once the file is loaded, the image appears and the user can scroll the slider and see the image change. They can then select "OK" on the dialog to save this pattern. This all works fine.
However, if they double-click on the item in the ListView then it will re-open this dialog to make further edits. So, it creates a new dialog and then reloads the pertinent info about the image. However, for whatever reason... the image binding no longer works. I can put a breakpoint on the ImageSource getter and everytime I change the slider, the image does get updated... However, it just doesn't appear the be binding correctly. Why would it bind correctly on the first time the window is opened, but not on subsequent openings. I'll try to lay out my code.
In my .XAML code:
<UserControl x:Class="MyControls.CreatePattern"
x:Name="PatternCreation"
...
d:DesignHeight="160" d:DesignWidth="350">
<Slider Value="{Binding ElementName=PatternCreation, Path=Pattern.ZNorm, Mode=TwoWay}" Maximum="1" Name="Slider" VerticalAlignment="Stretch" />
<Image Name="PatternPreview" Source="{Binding ElementName=PatternCreation, Path=Pattern.WPFSlice}" Stretch="Uniform"></Image>
</UserControl
In my code behind I define the Pattern to be bound:
protected PatternVoxelBased mPattern = new PatternVoxelBased();
public PatternVoxelBased Pattern
{
get { return mPattern ; }
set { mPattern = value; }
}
In my PatternVoxelBased class, I have a WPFSlice and ZNorm properties defined like this:
protected ImageSource mWPFSlice;
public ImageSource WPFSlice
{
get { return mWPFSlice; }
set
{
mWPFSlice = value;
NotifyPropertyChanged("WPFSlice");
}
}
protected double mZNorm = 0.5;
public double ZNorm
{
get { return mZNorm; }
set
{
if (mZNorm == value) return;
mZNorm = value;
NotifyPropertyChanged("ZNorm");
WPFSlice = BuildImageAtZ(mZNorm);
}
}
I have an event to load the dialog window the first time:
private void CreatePattern_Click(object sender, RoutedEventArgs e)
{
CCreateVoxelPattern dlg = new CCreateVoxelPattern();
dlg.DataContext = DataContext;
dlg.CShow(PatternLibraryMenu);
}
My ListView Double-Click function to reload the dialog window:
private void ListViewPatternLibrary_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
PatternVoxelBased item = ((ListView)sender).SelectedValue as PatternVoxelBased;
CCreateVoxelPattern dlg = new CCreateVoxelPattern();
dlg.DataContext = DataContext;
dlg.Main.Pattern = item;
dlg.Main.LoadPattern();
dlg.CShow(PatternLibraryMenu);
}
public void LoadPattern()
{
if (Pattern == null) return;
Pattern.WPFSlice = Pattern.BuildImageAtZ(Pattern.ZNorm);
}
In your class where this is
protected PatternVoxelBased mPattern = new PatternVoxelBased();
public PatternVoxelBased Pattern
{
get { return mPattern ; }
set { mPattern = value; }
}
you have to implement INotifyPropertyChanged.
Example
public class YourClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
protected PatternVoxelBased mPattern = new PatternVoxelBased();
public PatternVoxelBased Pattern
{
get { return mPattern ; }
set { mPattern = value; OnPropertyChanged(new PropertyChangedEventArgs("Pattern"));}
}
}
EDIT
In your Pattern-class, you have to implement that too on every Property.
How can I cancel exiting from particular form after Cancel button (or X at the top right corner, or Esc) was clicked?
WPF:
<Window
...
x:Class="MyApp.MyView"
...
/>
<Button Content="Cancel" Command="{Binding CancelCommand}" IsCancel="True"/>
</Window>
ViewModel:
public class MyViewModel : Screen {
private CancelCommand cancelCommand;
public CancelCommand CancelCommand {
get { return cancelCommand; }
}
public MyViewModel() {
cancelCommand = new CancelCommand(this);
}
}
public class CancelCommand : ICommand {
public CancelCommand(MyViewModel viewModel) {
this.viewModel = viewModel;
}
public override void Execute(object parameter) {
if (true) { // here is a real condition
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(
"Really close?", "Warning",
System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.No) { return; }
}
viewModel.TryClose(false);
}
public override bool CanExecute(object parameter) {
return true;
}
}
Current code doesn't work. I want user to stay on current form if it chooses 'No' in popup dialog.
Also, overriding CanExecute doesn't help. It just disables the button. I want to allow user to hit the button, but then notify him/her, that data will be lost.
Maybe I should assign an event listener on button?
EDIT:
I managed showing popup on Cancel button. But I still can't manage Esc or X button (top right). It seems I was confused with Cancel button, because Execute method is executed when I click X button or Esc.
EDIT2:
I changed the question. It was 'how cancel Cancel button'. However, it wasn't what I was looking for. I need to cancel Esc or X button.
In 'MyViewModel' I add:
protected override void OnViewAttached(object view, object context) {
base.OnViewAttached(view, context);
(view as MyView).Closing += MyViewModel_Closing;
}
void MyViewModel_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
if (true) {
MessageBoxResult messageBoxResult = System.Windows.MessageBox.Show(
"Really close?", "Warning",
System.Windows.MessageBoxButton.YesNo);
if (messageBoxResult == MessageBoxResult.No) {
e.Cancel = true;
}
}
}
This solved my problem. However, I need ICommand to understand, which button was clicked, Save or Cancel. Is there any way to eliminate usage of event?
You are trying to do View's work in ViewModel class. Let your View class to handle the closing request and whether it should be canceled or not.
To cancel closing of a window you can subscribe to the Closing event of view and set CancelEventArgs.Cancel to true after showing a MessageBox.
Here is an example:
<Window
...
x:Class="MyApp.MyView"
Closing="OnClosing"
...
/>
</Window>
Code behind:
private void OnClosing(object sender, CancelEventArgs e)
{
var result = MessageBox.Show("Really close?", "Warning", MessageBoxButton.YesNo);
if (result != MessageBoxResult.Yes)
{
e.Cancel = true;
}
// OR, if triggering dialog via view-model:
bool shouldClose = ((MyViewModel) DataContext).TryClose();
if(!shouldClose)
{
e.Cancel = true;
}
}
I'm not an MVVM expert, but in my opinion Yusufs' answer isn't quite MVVM. On the other hand Torpederos answer is a bit complicated for only close cancellation. Here is my approach.
In this example I subscribed to the closing event, but it is always cancelled
private void OnClosing(object sender, CancelEventArgs e)
{
e.Cancel = true;
return;
}
In the XAML I added this
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<i:InvokeCommandAction Command="{Binding Close}" />
</i:EventTrigger>
</i:Interaction.Triggers>
And finally in the view model
public ICommand Close { get; set; }
Close = new RelayCommand(CommandClose);
private void CommandClose(object sender)
{
if (Dirty)
{
// Save your data here
}
Environment.Exit(0);
}
In this approach the the closing event is triggered first. That cancels the closing. After that the interaction trigger is invoked and triggers the code in the view model via the RelayCommand.
In the view model I can use the Dirty flag that is not accessible in the view.
Very good example of doing this in the View Model way can be found in the article of Nish Nishant, where he's using attached properties to hook up window events with commands.
Sample code of attached behaviour (author of the code: Nish Nishant)
public class WindowClosingBehavior {
public static ICommand GetClosed(DependencyObject obj) {
return (ICommand)obj.GetValue(ClosedProperty);
}
public static void SetClosed(DependencyObject obj, ICommand value) {
obj.SetValue(ClosedProperty, value);
}
public static readonly DependencyProperty ClosedProperty
= DependencyProperty.RegisterAttached(
"Closed", typeof(ICommand), typeof(WindowClosingBehavior),
new UIPropertyMetadata(new PropertyChangedCallback(ClosedChanged)));
private static void ClosedChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) {
Window window = target as Window;
if (window != null) {
if (e.NewValue != null) {
window.Closed += Window_Closed;
}
else {
window.Closed -= Window_Closed;
}
}
}
public static ICommand GetClosing(DependencyObject obj) {
return (ICommand)obj.GetValue(ClosingProperty);
}
public static void SetClosing(DependencyObject obj, ICommand value) {
obj.SetValue(ClosingProperty, value);
}
public static readonly DependencyProperty ClosingProperty
= DependencyProperty.RegisterAttached(
"Closing", typeof(ICommand), typeof(WindowClosingBehavior),
new UIPropertyMetadata(new PropertyChangedCallback(ClosingChanged)));
private static void ClosingChanged(DependencyObject target, DependencyPropertyChangedEventArgs e) {
Window window = target as Window;
if (window != null) {
if (e.NewValue != null) {
window.Closing += Window_Closing;
}
else {
window.Closing -= Window_Closing;
}
}
}
public static ICommand GetCancelClosing(DependencyObject obj) {
return (ICommand)obj.GetValue(CancelClosingProperty);
}
public static void SetCancelClosing(DependencyObject obj, ICommand value) {
obj.SetValue(CancelClosingProperty, value);
}
public static readonly DependencyProperty CancelClosingProperty
= DependencyProperty.RegisterAttached(
"CancelClosing", typeof(ICommand), typeof(WindowClosingBehavior));
static void Window_Closed(object sender, EventArgs e) {
ICommand closed = GetClosed(sender as Window);
if (closed != null) {
closed.Execute(null);
}
}
static void Window_Closing(object sender, CancelEventArgs e) {
ICommand closing = GetClosing(sender as Window);
if (closing != null) {
if (closing.CanExecute(null)) {
closing.Execute(null);
}
else {
ICommand cancelClosing = GetCancelClosing(sender as Window);
if (cancelClosing != null) {
cancelClosing.Execute(null);
}
e.Cancel = true;
}
}
}
}
Example how to bind commands:
<Window
x:Class="WindowClosingDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:nsmvvm="clr-namespace:NS.MVVM"
nsmvvm:WindowClosingBehavior.Closed="{Binding ClosedCommand}"
nsmvvm:WindowClosingBehavior.Closing="{Binding ClosingCommand}"
nsmvvm:WindowClosingBehavior.CancelClosing="{Binding CancelClosingCommand}">
Commands "ClosedCommand", "ClosingCommand" and "CancelClosingCommand" should be defined in the separate View-Model.
internal class MainViewModel : ViewModelBase {
private ObservableCollection<string> log = new ObservableCollection<string>();
public ObservableCollection<string> Log {
get { return log; }
}
private DelegateCommand exitCommand;
public ICommand ExitCommand {
get {
if (exitCommand == null) {
exitCommand = new DelegateCommand(Exit);
}
return exitCommand;
}
}
private void Exit() {
Application.Current.Shutdown();
}
private DelegateCommand closedCommand;
public ICommand ClosedCommand {
get {
if (closedCommand == null) {
closedCommand = new DelegateCommand(Closed);
}
return closedCommand;
}
}
private void Closed() {
log.Add("You won't see this of course! Closed command executed");
MessageBox.Show("Closed");
}
private DelegateCommand closingCommand;
public ICommand ClosingCommand {
get {
if (closingCommand == null) {
closingCommand = new DelegateCommand(ExecuteClosing, CanExecuteClosing);
}
return closingCommand;
}
}
private void ExecuteClosing() {
log.Add("Closing command executed");
MessageBox.Show("Closing");
}
private bool CanExecuteClosing() {
log.Add("Closing command execution check");
return MessageBox.Show("OK to close?", "Confirm", MessageBoxButton.YesNo) == MessageBoxResult.Yes;
}
private DelegateCommand cancelClosingCommand;
public ICommand CancelClosingCommand {
get {
if (cancelClosingCommand == null) {
cancelClosingCommand = new DelegateCommand(CancelClosing);
}
return cancelClosingCommand;
}
}
private void CancelClosing() {
log.Add("CancelClosing command executed");
MessageBox.Show("CancelClosing");
}
}
This is another example of canceling the close window directly from ViewModel.
View:
<Window x:Class="WpfApplicationMvvmLight.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Platform"
Title="MainWindow" Height="350" Width="525">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding Path=ClosingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid>
<TextBlock>content...</TextBlock>
</Grid>
ViewModel:
using GalaSoft.MvvmLight.CommandWpf;
using System.ComponentModel;
using System.Windows;
namespace WpfApplicationMvvmLight
{
class SampleViewModel
{
public SampleViewModel() {
_closingCommand = new RelayCommand<CancelEventArgs>(OnClosingCommand);
}
private RelayCommand<CancelEventArgs> _closingCommand;
public RelayCommand<CancelEventArgs> ClosingCommand {
get {
return _closingCommand;
}
}
private void OnClosingCommand(CancelEventArgs e) {
//display your custom message box here..
var result = MessageBox.Show("Do you want to close?", "", MessageBoxButton.YesNoCancel);
//set e.Cancel to true to prevent the window from closing
e.Cancel = result != MessageBoxResult.Yes;
}
}
}
Code behind:
using System.Windows;
namespace WpfApplicationMvvmLight
{
public partial class MainWindow : Window
{
public MainWindow() {
InitializeComponent();
this.DataContext = new SampleViewModel();
}
}
}
This is the reference. MVVM close window event
ViewModel:
public FloatingToolbarWindowViewModel(GuiItems guiItems)
{
GuiItemsInstance = guiItems;
GuiItemsInstance.Host = Host;
GuiItemsInstance.RefreshVisibility = RefreshVisibility;
}
private Visibility _windowVisibility;
public Visibility WindowVisibility
{
get { return _windowVisibility; }
set
{
//raises PropertyChanged-event
SetValue(ref _windowVisibility, value);
}
}
// this check if any of the toolbars should be in a window and then sets visibility
public void RefreshVisibility(int RoleId)
{
if (GuiItemsInstance.ToolbarItems.Any(i => i.ToolbarLocation == ToolbarLocation.Float && i.RoleId == RoleId))
WindowVisibility = Visibility.Visible;
else
WindowVisibility = Visibility.Hidden;
}
XAML:
Visibility="{Binding WindowVisibility, Mode=TwoWay}"
This means it can never work because in the end the ShowWindow updates the property to Visible even though the initialization would have "decided" it should be Hidden.
So what I do is a hack in code behind file:
public partial class FloatingToolbarWindow : Window
{
public FloatingToolbarWindow()
{
InitializeComponent();
ContentRendered += FloatingToolbarWindow_ContentRendered;
}
private void FloatingToolbarWindow_ContentRendered(object sender, EventArgs e)
{
((FloatingToolbarWindowViewModel)DataContext).RefreshWindowVisibility();
}
ViewModel extra Hack-method:
public void RefreshVisibility()
{
RefreshVisibility(GuiItemsInstance.ActiveRoleId);
}
Is there a way to do this without this terrible hack. Moreover shouldn't this work with Mode=OneWay binding in the 1st place?
I'd make WindowVisibility a readonly property:
public Visibility WindowVisibility
{
get
{
if (GuiItemsInstance.ToolbarItems.Any(i => i.ToolbarLocation == ToolbarLocation.Float && i.RoleId == RoleId))
return Visibility.Visible;
else
return Visibility.Hidden;
}
}
bind the visibility OneWay:
Visibility="{Binding WindowVisibility, Mode=OneWay}"
and then whenever you have to "update" the visibility just raise the property changed... I don't know what you are using, could either
OnPropertyChanged("WindowVisibility");
or
OnPropertyChanged(() => WindowVisibility);
I am trying to make a usercontrol with some commands. If I wire up the commands in xaml using the approach shown here http://msdn.microsoft.com/en-us/library/vstudio/ms771361(v=vs.90).aspx it works but if I use the DelegateCommand from the Prism library CanExecuteChanged doesn't fire on the usercontrol and I cannot figure out why. I apologize I realize this is a lot of code. Execute fires correctly but CanExecute never does.
Thanks in advance.
Custom Control Xaml
<UserControl x:Class="Controls.LinkButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock>
<Hyperlink x:Name="hyperLink" Click="Hyperlink_Click">
<Run x:Name="textRun"
Text="Click Me"/>
</Hyperlink>
</TextBlock>
</UserControl>
Custom Control Code Behind
public partial class LinkButton : UserControl, ICommandSource
{
public LinkButton()
: base()
{
InitializeComponent();
textRun.DataContext = this;
hyperLink.DataContext = this;
}
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(LinkButton), new PropertyMetadata(null, new PropertyChangedCallback(CommandChanged)));
public object CommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.Register("CommandParameter", typeof(object), typeof(LinkButton), new PropertyMetadata(null));
public IInputElement CommandTarget
{
get { return (IInputElement)GetValue(CommandTargetProperty); }
set { SetValue(CommandTargetProperty, value); }
}
public static readonly DependencyProperty CommandTargetProperty =
DependencyProperty.Register("CommandTarget", typeof(IInputElement), typeof(LinkButton), new PropertyMetadata(null));
private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
if (Command != null)
{
RoutedCommand command = Command as RoutedCommand;
if (command != null)
{
command.Execute(CommandParameter, CommandTarget);
}
else
{
((ICommand)Command).Execute(CommandParameter);
}
}
}
public static EventHandler canExecuteChangedHandler;
private static void CommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LinkButton lb = (LinkButton)d;
lb.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
}
// Add a new command to the Command Property.
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
// If oldCommand is not null, then we need to remove the handlers.
if (oldCommand != null)
{
RemoveCommand(oldCommand, newCommand);
}
AddCommand(oldCommand, newCommand);
}
// Remove an old command from the Command Property.
private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = CanExecuteChanged;
oldCommand.CanExecuteChanged -= handler;
}
// Add the command.
private void AddCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = new EventHandler(CanExecuteChanged);
canExecuteChangedHandler = handler;
if (newCommand != null)
{
newCommand.CanExecuteChanged += canExecuteChangedHandler;
}
}
private void CanExecuteChanged(object sender, EventArgs e)
{
if (this.Command != null)
{
RoutedCommand command = this.Command as RoutedCommand;
// If a RoutedCommand.
if (command != null)
{
if (command.CanExecute(CommandParameter, CommandTarget))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
// If a not RoutedCommand.
else
{
if (Command.CanExecute(CommandParameter))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
}
}
}
Window
<ctrl:LinkButton Command="{Binding LinkClicked}"/>
public partial class MainWindow : Window
{
public MainWindow()
{
LinkClicked = new DelegateCommand(DoSomething, CanDoSomething);
InitializeComponent();
DataContext = this;
LinkClicked.RaiseCanExecuteChanged();
}
public DelegateCommand LinkClicked { get; set; }
public void DoSomething()
{
MessageBox.Show("Did Something");
}
public bool CanDoSomething()
{
return false;
}
}
The problem here is you are calling LinkClicked.RaiseCanExecuteChanged(); immediately after setting DataContext and till then the LinkedCommand is not binded and hence CanExecuteChanged event of the DelegateCommand is null and hence RaiseCanExecuteChanged() does not do anything. so to avoid this call LinkClicked.RaiseCanExecuteChanged() in the loaded event of Window because till then binding will be updated. Though this is a dirty solution because you will have to do this everywhere where you will use this LinkButton and bind its Command.
The implementation of RaiseCanExecuteChanged is something like this
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null) //CanExecuteChanged is coming null in your case so the event is not fired.
CanExecuteChanged(this, new EventArgs());
}
Or the better solution is that you havent called CanExecute in AddCommand method, In actual Command implementation CanExecute is called
if (newCommand != null)
{
newCommand.CanExecuteChanged += CanExecuteChanged;
newCommand.CanExecute(CommandParameter); //you are missing this.
}
If you do this then there is no need to call RaiseCanExecuteChanged.