How to make everything opacity=0.5 aside from customdialogbox - wpf

Im new in WPF and Im creating a custom dialog (or a message box) in WPF.
My problem is I cant change everything behind my custom dialog to Opacity=0.5. Only the form who called the custom dialog..
I just want to show my dialog box like in Windows 8.1
Anything would be highly appreciated.
TIA!

Set a property in your MainWindow that controls Opacity of the LayoutRoot. This will change the opacity of your App when the dialog is displayed.
Sample code:
<Grid Name="LayoutRoot" Opacity="{Binding MainWindowOpacity}">
<StackPanel>
<Button Click="Button_Click" Content="Click Me to Show Dialog"/>
<TextBlock Text="WPF" FontSize="72" Margin="50" Foreground="Orange" HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
and
public partial class MainWindow : Window
{
public double MainWindowOpacity
{
get { return (double)GetValue(MainWindowOpacityProperty); }
set { SetValue(MainWindowOpacityProperty, value); }
}
// Using a DependencyProperty as the backing store for MainWindowOpacity. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MainWindowOpacityProperty =
DependencyProperty.Register("MainWindowOpacity", typeof(double), typeof(MainWindow), new PropertyMetadata(1.0));
public MainWindow()
{
InitializeComponent();
DataContext = this;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//if (MainWindowOpacity < 1) MainWindowOpacity = 1.0;
//else MainWindowOpacity = 0.5;
MainWindowOpacity = 0.5;
// show dialog
// boilerplate code from http://stackoverflow.com/questions/6417558/modal-dialog-not-showing-on-top-of-other-windows
Window window = new Window()
{
Title = "WPF Modal Dialog",
ShowInTaskbar = false, // don't show the dialog on the taskbar
Topmost = true, // ensure we're Always On Top
ResizeMode = ResizeMode.NoResize, // remove excess caption bar buttons
Owner = Application.Current.MainWindow,
Width = 300,
Height = 200
};
window.ShowDialog();
MainWindowOpacity = 1.0;
}
}
and the result:

You could add a static method to App.xaml like this:
public partial class App : Application
{
public static void SetWindowsToOpacity(double dOpacity, Window ignoreWindow)
{
foreach (Window win in System.Windows.Application.Current.Windows)
{
if (win != ignoreWindow)
{
win.Opacity = dOpacity;
}
}
}
}
Pass in the Dialog window that you do not want to be set to lower opacity and an opacity value for all the rest.
Call the method like this:
private void button_Click(object sender, RoutedEventArgs e)
{
Window1 dialog = new Window1();
App.SetWindowsToOpacity(0.5, dialog);
dialog.ShowDialog();
App.SetWindowsToOpacity(1.0, null);
}

Related

How to execute some code before removing an element from the visual tree?

I am trying to implement some fade-in and fade-out animations for a user control in WPF. For the fade-in animation I was able to use the Loaded event to accomplish that.
public sealed partial class NowPlayingView : UserControl
{
public Duration AnimationDuration
{
get { return (Duration)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
public static readonly DependencyProperty AnimationDurationProperty =
DependencyProperty.Register("AnimationDuration", typeof(Duration), typeof(NowPlayingView), new PropertyMetadata(Duration.Automatic));
public NowPlayingView()
{
Opacity = 0;
InitializeComponent();
Loaded += NowPlayingView_Loaded;
Unloaded += NowPlayingView_Unloaded;
}
private void NowPlayingView_Unloaded(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new(1.0, 0.0, AnimationDuration);
BeginAnimation(OpacityProperty, animation);
}
private void NowPlayingView_Loaded(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new (0.0, 1.0, AnimationDuration);
BeginAnimation(OpacityProperty, animation);
}
}
I attempted to use the Unloaded event for the fade-out effect only to find out that the event is fired after the UserControl is removed from the visual tree (when the UserControl is no longer visible or accessible). Is there a way to run some code right before the UserControl "closes", something like the OnClosing event of a Window?
EDIT:
For a bit more context, the UserControl acts as a component of a more complex window. It is activated whenever the Property NowPlayingViewModel is not null and deactivated when null (which I do in order to hide the UserControl). It is when I set the ViewModel to null that I want to run the fade-out animation and I would like to keep the code-behind decoupled from other ViewModel logic.
<!-- Now playing View-->
<ContentControl Grid.RowSpan="3" Grid.ColumnSpan="2" Content="{Binding NowPlayingViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:NowPlayingViewModel}">
<views:NowPlayingView AnimationDuration="00:00:00.8" />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
From my testing, I couldn't find any good solution to this so far, though I am open to suggestions that lead to similar behavior.
There is no Closing event in UserControl.. but you can get the parent window when UserControl is loaded and implement the fade-out behavior there..
First, Remove Unloaded += NowPlayingView_Unloaded;
Then, modify the Loaded code a bit..
private Window ParentWindow
{
get
{
DependencyObject parentDepObj = this;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
if (parentDepObj is Window parentWindow) return parentWindow;
} while (parentDepObj != null);
return null;
}
}
private void NowPlayingView_Loaded(object sender, RoutedEventArgs e)
{
DoubleAnimation animation = new(0.0, 1.0, AnimationDuration);
BeginAnimation(OpacityProperty, animation);
var parentWindow = this.ParentOfType<Window>();
parentWindow.Closing += WindowClosing;
}
private void WindowClosing(object sender, CancelEventArgs args)
{
var pw = ParentWindow;
pw.Closing -= WindowClosing;
args.Cancel = true;
var anim = new(1.0, 0.0, AnimationDuration);
anim.Completed += (s, _) => pw.Close();
BeginAnimation(OpacityProperty, anim);
}
Optional Note. You could replace the getter of ParentWindow property with a simple call
private Window ParentWindow => this.ParentOfType<Window>();
Where ParentOfType is an extension function in some public static class Utilities..
public static T ParentOfType<T>(this DependencyObject child) where T : DependencyObject
{
var parentDepObj = child;
do
{
parentDepObj = VisualTreeHelper.GetParent(parentDepObj);
if (parentDepObj is T parent) return parent;
} while (parentDepObj != null);
return null;
}

How to make drag-able WPF window without default windows style?

I was making a WPF application with Windows style = None, I managed to create and work exit button in my window but i don't know how to make it drag able while pressing left mouse button.
I have created Mouse left button down event in .cs file as below:
private void see(object sender, MouseButtonEventArgs e)
{
this.DragMove();
}
Then I added border in .xaml file to do dragging of the window as below:
<Grid>
<Border BorderThickness="2" BorderBrush="Black" Height="120" Width="100" MouseLeftButtonDown="see" />
</Grid>
Now i don't understand what is the problem here? I will be very thankful if someone help me in this ?
Use a similar pattern to this Window:
public class DragableWindowNoStyle : Window
{
public DragableWindowNoStyle()
{
WindowStyle = WindowStyle.None;
Grid grid = new Grid() { };
_moveBorder = new Border()
{
BorderThickness = new Thickness(2),
BorderBrush = Brushes.Red,
Background = Brushes.Black,
Width = 50,
Height = 20,
HorizontalAlignment= System.Windows.HorizontalAlignment.Center,
VerticalAlignment = System.Windows.VerticalAlignment.Top,
};
grid.Children.Add(_moveBorder);
this.Content = grid;
_moveBorder.PreviewMouseLeftButtonDown += _moveBorder_PreviewMouseLeftButtonDown;
}
Point _startPoint;
bool _isDragging = false;
Border _moveBorder;
private void _moveBorder_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Mouse.Capture(this))
{
_isDragging = true;
_startPoint = PointToScreen(Mouse.GetPosition(this));
}
}
protected override void OnPreviewMouseMove(MouseEventArgs e)
{
if (_isDragging)
{
Point newPoint = PointToScreen(Mouse.GetPosition(this));
int diffX = (int)(newPoint.X - _startPoint.X);
int diffY = (int)(newPoint.Y - _startPoint.Y);
if (Math.Abs(diffX) > 1 || Math.Abs(diffY) > 1)
{
Left += diffX;
Top += diffY;
InvalidateVisual();
_startPoint = newPoint;
}
}
}
protected override void OnPreviewMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (_isDragging)
{
_isDragging = false;
Mouse.Capture(null);
}
}
}
There is an example of how to create a custom window with resize, drag, minimize, restore and close functionality from scratch available here:
How to create a custom window in WPF: https://blog.magnusmontin.net/2013/03/16/how-to-create-a-custom-window-in-wpf/
You could also customize a window while retaining its standard functionality using the WindowChrome class: https://msdn.microsoft.com/en-us/library/system.windows.shell.windowchrome(v=vs.110).aspx. Then you don't have to implement the resize and drag functionality yourself.

WPF global open/close dialog event?

In my WPF application I would like to subscribe to some event/callbeck/whatever that tells me whenever a dialog window opens (and closes) in my application.
I found the window collection but this is a simple container and it doesn't seem to provide any means of subscription.
I also tried using event handlers but there seems not be an event that tells me what I need.
Any ideas?
One way to do it without a base class is adding a handler to MainWindow deactivated
If a new window is opened, the main window will lose focus = your "new window event"
private readonly List<Window> openWindows = new List<Window>();
public void ApplicationMainWindow_Deactivated(object sender, EventArgs e)
{
foreach (Window window in Application.Current.Windows)
{
if (!openWindows.Contains(window) && window != sender)
{
// Your window code here
window.Closing += PopupWindow_Closing;
openWindows.Add(window);
}
}
}
private void PopupWindow_Closing(object sender, CancelEventArgs e)
{
var window = (Window)sender;
window.Closing -= PopupWindow_Closing;
openWindows.Remove(window);
}
Without creating a Base class for all your windows where you can hook into the opened event (or manually adding the opened event to each window), I'm not sure how you'd be able to know when new windows were create.
There may be a more elegant way, but you could poll the Application.Current.Windows to see if any new windows were created while keeping track of the one's you've found.
Here is a crude example that will demonstrate how to use a DispatchTimer to poll for new windows, keep track of found windows and hook into the closed event.
Code Behind
public partial class MainWindow : Window
{
private DispatcherTimer Timer { get; set; }
public ObservableCollection<Window> Windows { get; private set; }
public MainWindow()
{
InitializeComponent();
// add current Window so we don't add a hook into it
Windows = new ObservableCollection<Window> { this };
Timer = new DispatcherTimer( DispatcherPriority.Background );
Timer.Interval = TimeSpan.FromMilliseconds( 500 );
Timer.Tick += ( _, __ ) => FindNewWindows();
Timer.Start();
this.WindowListBox.ItemsSource = Windows;
this.WindowListBox.DisplayMemberPath = "Title";
}
private void FindNewWindows()
{
foreach( Window window in Application.Current.Windows )
{
if( !Windows.Contains( window ) )
{
window.Closed += OnWatchedWindowClosed;
// inserting at 0 so you can see it in the ListBox
Windows.Insert( 0, window );
Feedback.Text = string.Format( "New Window Found: {0}\r\n{1}",
window.Title, Feedback.Text );
}
}
}
private void OnWatchedWindowClosed( object sender, EventArgs e )
{
var window = (Window)sender;
Windows.Remove( window );
Feedback.Text = string.Format( "Window Closed: {0}\r\n{1}",
window.Title, Feedback.Text );
}
private void CreateWindowButtonClick( object sender, RoutedEventArgs e )
{
string title = string.Format( "New Window {0}", DateTime.Now );
var win = new Window
{
Title = title,
Width = 250,
Height = 250,
Content = title,
};
win.Show();
e.Handled = true;
}
}
XAML
<Grid>
<ListBox Name="WindowListBox"
Width="251"
Height="130"
Margin="12,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
<TextBox Name="Feedback"
Width="479"
Height="134"
Margin="12,148,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
VerticalScrollBarVisibility="Auto" />
<Button Name="CreateWindowButton"
Width="222"
Height="130"
Margin="269,12,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Click="CreateWindowButtonClick"
Content="Create New Window"
FontSize="20" />
</Grid>
Click away and create as many new windows as you want; then close them. You'll see the feedback as it happens. Granted, there will be a 500ms delay whenever a new window is created since the DispatchTimer's interval is set at 500ms.
You could register a class handler in App.cs as demonstrated here
https://gist.github.com/mwisnicki/3104963
...
EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.LoadedEvent, new RoutedEventHandler(OnLoaded), true);
EventManager.RegisterClassHandler(typeof(UIElement), FrameworkElement.UnloadedEvent, new RoutedEventHandler(OnUnloaded), true);
...
private static void OnLoaded(object sender, RoutedEventArgs e)
{
if (sender is Window)
Console.WriteLine("Loaded Window: {0}", sender);
}
private static void OnUnloaded(object sender, RoutedEventArgs e)
{
if (sender is Window)
Console.WriteLine("Unloaded Window: {0}", sender);
}
The link above seems to register an empty handler on instances to make things work properly.
I have never heard of any global open/close event.
It should somehow be possible to do, but that provides that you have control over all windows opening and closing. Like if you build a "base window" (which naturally inherit "Window") that all your dialogs windows inherit from.
Then you clould have a static event on the "base window" which you fire from the base window's opening and closing/closed (or unloaded) events, sending "this" as "sender".
You can attafh to that static event in your App.xaml.cs class.
It's a hack, but it's possible.

WPF Button to have Keyboard focus (dotted border around) during the startup or activation of window

I have a WPF Window in which I have added a button. I want the button should have the keyboard focus with dotted border around it when the application starts up (basically when the window is activated). Normally we see the dotted border when we navigate through controls using Tab key.
I tried the following code but still i think I am missing something.
XAML
<Window x:Class="PropertyChangedTest.TestPropertyChangedWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300" Activated="Window_Activated">
<StackPanel Name="justPanel">
<Button Content="Hello" x:Name="Btn" Height="23" Width="52" Focusable="True" IsDefault="True" Click="Btn_Click"></Button>
</StackPanel>
</Window>
.cs file
private void Window_Activated(object sender, EventArgs e)
{
if (!bActivatedOnce)
{
bool bVisible = Btn.IsVisible;
UIElement elementWithFo = Keyboard.Focus(Btn) as UIElement;
bActivatedOnce = true;
}
}
The button has the keyboard focus but it doesnt have the dotted border around it. When i press Alt key the dotted border appears around the button.
This problem is quite the same as this. Please see my answer there.
The problem is that the dotted border only appears if you are navigating by keyboard.
By editing the KeyboardNavigationEx file from ControlzEx I managed to solve the issue (full credit goes, as always, to punker76).
Just call the KeyboardHelper.Focus method passing the UIElement that shoud be focused. Here's how it'd look in your case:
private void Window_Activated(object sender, EventArgs e)
{
if (!bActivatedOnce && Btn.IsVisible)
{
KeyboardHelper.Focus(Btn);
bActivatedOnce = true;
}
}
And here's the KeyboardHelper class:
public sealed class KeyboardHelper
{
private static KeyboardHelper _Instance;
private readonly PropertyInfo _AlwaysShowFocusVisual;
private readonly MethodInfo _ShowFocusVisual;
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static KeyboardHelper()
{
}
private KeyboardHelper()
{
var type = typeof(KeyboardNavigation);
_AlwaysShowFocusVisual = type.GetProperty("AlwaysShowFocusVisual", BindingFlags.NonPublic | BindingFlags.Static);
_ShowFocusVisual = type.GetMethod("ShowFocusVisual", BindingFlags.NonPublic | BindingFlags.Static);
}
internal static KeyboardHelper Instance => _Instance ?? (_Instance = new KeyboardHelper());
internal void ShowFocusVisualInternal()
{
_ShowFocusVisual.Invoke(null, null);
}
internal bool AlwaysShowFocusVisualInternal
{
get { return (bool)_AlwaysShowFocusVisual.GetValue(null, null); }
set { _AlwaysShowFocusVisual.SetValue(null, value, null); }
}
public static void Focus(UIElement element)
{
element?.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
var keybHack = KeyboardHelper.Instance;
var oldValue = keybHack.AlwaysShowFocusVisualInternal;
keybHack.AlwaysShowFocusVisualInternal = true;
try
{
Keyboard.Focus(element);
keybHack.ShowFocusVisualInternal();
}
finally
{
keybHack.AlwaysShowFocusVisualInternal = oldValue;
}
}));
}
}

Refresh Silverlight UserControl via XAML

I'm using the lastest version of Silverlight 2.0 within Visual Studio 2008. I have a simple Silverlight UserControl with the following code:
public partial class SilverlightControl1 : UserControl
{
public SilverlightControl1()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(SilverlightControl1_Loaded);
Composite = new Composite();
}
void SilverlightControl1_Loaded(object sender, RoutedEventArgs e)
{
Composite.Width = this.Width / 2.0;
Composite.Height = this.Height / 2.0;
if (!this.LayoutRoot.Children.Contains(Composite))
this.LayoutRoot.Children.Add(Composite);
}
public Composite Composite
{
get;
set;
}
}
public class Composite : ContentControl
{
private Grid grid;
private Canvas canvas;
public Composite()
{
if (grid == null) grid = new Grid();
if (canvas == null) canvas = new Canvas();
if (!grid.Children.Contains(canvas))
grid.Children.Add(canvas);
Content = grid;
this.Loaded += new RoutedEventHandler(Composite_Loaded);
}
private Rectangle rectangle;
void Composite_Loaded(object sender, RoutedEventArgs e)
{
if (rectangle == null) rectangle = new Rectangle();
Canvas.SetTop(rectangle, 0);
Canvas.SetLeft(rectangle, 0);
rectangle.Fill = new SolidColorBrush(Color);
rectangle.Width = Width;
rectangle.Height = Height;
if (!canvas.Children.Contains(rectangle))
canvas.Children.Add(rectangle);
}
public Color Color
{
get;
set;
}
}
I then use this UserControl in a Silverlight application, the XAML of the page looking like this:
<UserControl x:Class="SilverlightApplication1.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:SilverlightClassLibrary1;assembly=SilverlightClassLibrary1"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="Green">
<test:SilverlightControl1 Name="uControl1">
<test:SilverlightControl1.Composite>
<test:Composite Color="Yellow"/>
</test:SilverlightControl1.Composite>
</test:SilverlightControl1>
</Grid>
</UserControl>
My question is: what code do I have to add to the above so that by changing "Composite Color" to something other than Yellow and hitting the return button, the UserControl automatically refreshes? As the code is, the only way to refresh the UserControl is by moving the Slider bar within the VS2008 IDE which changes the percentage zoom of the Silverlight Page. A side question, although of lesser importance to the above question, is: with the code as it is above, why can't I change the "Background" color of the LayoutRoot? If I remove my UserControl it works as expected.
The solution was two-fold. Firstly to make changes in the LayoutUpdated event rather than the Loaded event and secondly to subscribe to the PropertyChangedCallback of the PropertyMetadata. Here's the complete working code:
public partial class SilverlightControl1 : UserControl
{
public SilverlightControl1()
{
InitializeComponent();
this.LayoutUpdated += new EventHandler(SilverlightControl1_LayoutUpdated);
Composite = new Composite();
}
void SilverlightControl1_LayoutUpdated(object sender, EventArgs e)
{
Composite.Width = this.Width / 2.0;
Composite.Height = this.Height / 2.0;
if (!this.LayoutRoot.Children.Contains(Composite)) this.LayoutRoot.Children.Add(Composite);
}
public Composite Composite
{
get;
set;
}
}
public class Composite : ContentControl
{
private Grid grid;
private Canvas canvas;
public Composite()
{
if (grid == null) grid = new Grid();
if (canvas == null) canvas = new Canvas();
if (!grid.Children.Contains(canvas)) grid.Children.Add(canvas);
Content = grid;
this.LayoutUpdated += new EventHandler(Composite_LayoutUpdated);
}
void Composite_LayoutUpdated(object sender, EventArgs e)
{
if (rectangle == null) rectangle = new Rectangle();
Canvas.SetTop(rectangle, 0);
Canvas.SetLeft(rectangle, 0);
rectangle.Fill = new SolidColorBrush(Color);
rectangle.Width = Width;
rectangle.Height = Height;
if (!canvas.Children.Contains(rectangle)) canvas.Children.Add(rectangle);
}
public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(Color), typeof(Composite), new PropertyMetadata(Colors.Red, new PropertyChangedCallback(OnColorPropertyChanged)));
private static void OnColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Composite comp = (Composite)d;
comp.InvalidateArrange();
}
private Rectangle rectangle;
public Color Color
{
get { return (Color)GetValue(ColorProperty); }
set { SetValue(ColorProperty, value); }
}
}
I think you need to make Composite into a Dependency Proprety. Probably will want to do the same thing for Color on Composite so that you'll be able to bind it.

Resources