WPF: Overlapping animations - move a grid behind a toolbar - wpf

I want to move a window within the main window in wpf. In the example below I want to move the window "moving window" to the top of the main window until it is completely off the main window. I do this with a TranslateTransform.Y animation - the moving window is actually a grid. The problem is, that I want to move the window behind the toolbar. Currently if is moved over the toolbar. In other words: I want the moving window to disappear behind the toolbar. The toolbar is part of a dock panel, which has a gradient background, the toolbars background is transparent. It doesn't matter what ZIndex I use, the moving window (grid) always moves over the toolbar and is always only cut off by the windows client area (so it "disappears" behind the windows titlebar). How can I achieve such an animation?
-------------------------------------------
| Toolbar |
-------------------------------------------
| ------------------- |
| | moving window | |
| ------------------- |
| |
-------------------------------------------
I created a little quick and dirty Non-MVVM Demo Project to make it clearer. This is the main Window.xaml:
<Window x:Class="MovingWindowTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Background="SlateGray">
<DockPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Vertical">
<Menu IsMainMenu="True" Background="Transparent" Panel.ZIndex="2">
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel HorizontalAlignment="Stretch"/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem Header="Debug">
<MenuItem Header="Move window in" Click="MoveWindowIn_OnClick" />
<MenuItem Header="Move window out" Click="MoveWindowOut_OnClick" />
</MenuItem>
</Menu>
</StackPanel>
<Grid x:Name="contentGrid" Background="White"/>
</DockPanel>
</Grid>
The Code behind:
public partial class MainWindow : Window
{
private InnerWindow _InnerWindow;
private InnerWindow InnerWindow
{
get { return this._InnerWindow ?? ( this._InnerWindow = new InnerWindow() ); }
}
public MainWindow()
{
InitializeComponent();
this.InnerWindow.Width = this.InnerWindow.Height = 100;
}
private void MoveWindowIn_OnClick( object sender, RoutedEventArgs e )
{
Panel.SetZIndex( this.contentGrid, 1 );
this.InnerWindow.Visibility = Visibility.Visible;
this.contentGrid.Children.Add( this.InnerWindow );
TranslateTransform trans = new TranslateTransform();
DoubleAnimation anim = new DoubleAnimation(-100, 0, new Duration( new TimeSpan( 0, 0, 0, 3 ) ) );
this.InnerWindow.RenderTransform = trans;
trans.BeginAnimation( TranslateTransform.YProperty, anim );
}
private void MoveWindowOut_OnClick( object sender, RoutedEventArgs e )
{
TranslateTransform trans = new TranslateTransform();
DoubleAnimation anim = new DoubleAnimation( 0, -100, new Duration( new TimeSpan( 0, 0, 0, 3 ) ) );
this.InnerWindow.RenderTransform = trans;
anim.Completed += ( o, args ) => this.contentGrid.Children.Remove( this.InnerWindow ); ;
trans.BeginAnimation( TranslateTransform.YProperty, anim );
}
}
The InnerWindow is just a super simple User Control with the following xaml:
<UserControl x:Class="MovingWindowTest.InnerWindow"
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" Background="Red" VerticalAlignment="Top"
d:DesignHeight="100" d:DesignWidth="100">
<Grid>
<TextBlock FontSize="20" VerticalAlignment="Center" HorizontalAlignment="Center">TEST</TextBlock>
</Grid>
When the InnderWindow is moved in, it overlaps the Menu, although the menu has a higher ZIndex.
Note that the InnerWindow is added to the contentGrid which is under the menu. And it makes no difference when I add ClipToBounds to the inner window.

What is the parent of the moving window? The DockPanel that also contains the Toolbar or another control? Have you tried setting the ClipToBounds property on the moving window?

Related

WPF - how to create click through semi transparent layer

I want something like this for a screen recording software.
My sample wpf window looks like this
<Window x:Class="WpfTestApp.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:local="clr-namespace:WpfTestApp"
mc:Ignorable="d"
ShowInTaskbar="False" WindowStyle="None" ResizeMode="NoResize"
AllowsTransparency="True"
UseLayoutRounding="True"
Opacity="1"
Cursor="ScrollAll"
Topmost="True"
WindowState="Maximized"
>
<Window.Background>
<SolidColorBrush Color="#01ffffff" Opacity="0" />
</Window.Background>
<Grid>
<Canvas x:Name="canvas1">
<Path Fill="#CC000000" Cursor="Cross" x:Name="backgroundPath">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,1440,810"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="300,200,800,300" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
</Grid>
Now the problem is I can't make the semi transparent area backgroundPath click through. I have set its IsHitTestVisible property to false, but still no change. I have used SetWindowLong to make the whole window transparent, and that lets me click through the window, but that then all the events of my window and controls in it don't work.
Can any one suggest how can I achieve that?
I was actually curious about this and it doesn't look like there really is a "proper" or "official" way to achieve transparency on only the window but not the controls.
In lieu of this, I came up with a functionally effective solution:
MainWindow XAML (I just added a button)
<Window x:Class="test.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:local="clr-namespace:test"
mc:Ignorable="d"
Title="MainWindow"
WindowStyle="None"
AllowsTransparency="True"
ShowInTaskbar="False"
ResizeMode="NoResize"
UseLayoutRounding="True"
Opacity="1"
Cursor="ScrollAll"
Topmost="True"
WindowState="Maximized">
<Window.Background>
<SolidColorBrush Color="#01ffffff" Opacity="0" />
</Window.Background>
<Grid>
<Canvas x:Name="canvas1">
<Path Fill="#CC000000" Cursor="Cross" x:Name="backgroundPath">
<Path.Data>
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="0,0,1440,810"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="300,200,800,300" />
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</Path.Data>
</Path>
</Canvas>
<Button x:Name="My_Button" Width="100" Height="50" Background="White" IsHitTestVisible="True" HorizontalAlignment="Center" VerticalAlignment="Top" Click="Button_Click"/>
</Grid>
</Window>
MainWindow C#
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Threading;
namespace test
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
const int WS_EX_TRANSPARENT = 0x00000020;
const int GWL_EXSTYLE = (-20);
public const uint WS_EX_LAYERED = 0x00080000;
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool GetCursorPos(ref Win32Point pt);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
private bool _isClickThrough = true;
public MainWindow()
{
InitializeComponent();
// List of controls to make clickable. I'm just adding my button.
List<System.Windows.Controls.Control> controls = new List<System.Windows.Controls.Control>();
controls.Add(My_Button);
Thread globalMouseListener = new Thread(() =>
{
while (true)
{
Point p1 = GetMousePosition();
bool mouseInControl = false;
for (int i = 0; i < controls.Count; i++)
{
Point p2 = new Point();
Rect r = new Rect();
System.Windows.Controls.Control iControl = controls[i];
Dispatcher.BeginInvoke(new Action(() =>
{
// Get control position relative to window
p2 = iControl.TransformToAncestor(this).Transform(new Point(0, 0));
// Add window position to get global control position
r.X = p2.X + this.Left;
r.Y = p2.Y + this.Top;
// Set control width/height
r.Width = iControl.Width;
r.Height = iControl.Height;
if (r.Contains(p1))
{
mouseInControl = true;
}
if (mouseInControl && _isClickThrough)
{
_isClickThrough = false;
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExNotTransparent(hwnd);
}
else if (!mouseInControl && !_isClickThrough)
{
_isClickThrough = true;
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExTransparent(hwnd);
}
}));
}
Thread.Sleep(15);
}
});
globalMouseListener.Start();
}
public static Point GetMousePosition()
{
Win32Point w32Mouse = new Win32Point();
GetCursorPos(ref w32Mouse);
return new Point(w32Mouse.X, w32Mouse.Y);
}
public static void SetWindowExTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT);
}
public static void SetWindowExNotTransparent(IntPtr hwnd)
{
var extendedStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle & ~WS_EX_TRANSPARENT);
}
private void Button_Click(object sender, EventArgs e)
{
System.Windows.Forms.MessageBox.Show("hey it worked");
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
var hwnd = new WindowInteropHelper(this).Handle;
SetWindowExTransparent(hwnd);
}
}
}
Basically If the mouse is over a control, I call SetWindowExNotTransparent to turn it into a normal, non click-through window. If the mouse is not over a control, it switches it back to a click-through state with SetWindowExTransparent.
I have a thread running that continuously checks the global mouse position against global control positions (where you fill a list of controls you want to be able to click). The global control positions are determined by getting the control position relative to MainWindow and then adding the Top and Left attributes of MainWindow.
Sure, this is a somewhat "hacky" solution. But I'll be damned if you find a better one! And it seems to be working fine for me. (Albeit it might get weird to handle oddly shaped controls. This code only handles rectangular controls.)
Also I just threw this together really quick to see if it would work, so it's not very clean. A proof of concept, if you will.
There is no way to have a part of a window both visually semi-transparent and transparent for user interaction (e.g. mouse clicks).
You either have to:
make the whole window transparent for the user interaction (using SetWindowLong, CreateParams etc)
or make the desired window parts fully transparent
A workaround for this is to draw the semi-transparent area manually, without having a window. This is going to be a tough job, and AFAIK there is no reliable method for doing this. The Windows DWM doesn't offer any public API for that, drawing directly on the Desktop's HDC won't do, overlays are not always supported by the graphics hardware, Direct2D won't let you do that either.
You can create two top-most windows and synchronize their size. The first window would only have the controls for resizing and would handle the mouse input, no content inside. The second window would display the semi-transparent grey background with a transparent region inside - just as your current window in your sample - but completely transparent for any mouse interaction.

Dynamically change the width of a WPF window when content gets visible

I want to be able to hide the left hand side of the screen and right hand side of the screen at the beginning of the program.
Then when the user presses create new button the left hand side of the screen becomes available so they can create the new item. Then when they press save it comes back to the middle datagrid only.
Then I want to add an event when the double click on the datagrid row (data is programmed in to the datagrid in the code) the right hand side of the screen becomes visible then when the button allocate is pressed the right hand side disappears again just leaving the datagrid.
I am fairly new to WPF so unsure whether this can be done or not. I am trying to do it in the same window at the moment I am making prototypes for my company and already have some that use separate windows. I had posted an image but am un able to post it as I am a new user.
To hide and show controls I would either recommend using expanders (as your comment says you've done) or Grids and setting their visibility as needed. If you want your side panels to appear over the datagrid then displaying them is just a matter of changing their visibility. If don't want obscure the DataGrid then you will need to change the visibility of the panels as well as the size of the window.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<!-- DataGrid display -->
<Grid>
<StackPanel>
<Button Content="Add New" Click="OnAddNewButtonClick" Width="100"/>
<DataGrid ItemsSource="{Binding GridItems}" IsReadOnly="True" Name="dataGrid">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseDoubleClick" Handler="OnRowDoubleClick"/>
</Style>
</DataGrid.RowStyle>
</DataGrid>
</StackPanel>
</Grid>
<!-- Left column pops up over DataGrid -->
<Grid Name="LeftColumn" Visibility="Collapsed" Background="Red" Width="200" HorizontalAlignment="Left">
<StackPanel VerticalAlignment="Center">
<Button Content="Hide Column" Click="OnLeftColumnButtonClick"/>
</StackPanel>
</Grid>
<!-- Right Column expands screen size-->
<Grid Visibility="Collapsed" Name="RightColumn" Width="200" HorizontalAlignment="Right">
<StackPanel Background="Green" >
<TextBlock Text="Hidden Column"/>
<Button Content="Hide Panel" Click="OnRightColumnButtonClick"/>
</StackPanel>
</Grid>
</Grid>
</Window
C# - I know you're working in VB, but this was quicker for me. The code should be fairly self explanatory, but if you need a VB sample let me know:
public partial class MainWindow : Window
{
public ObservableCollection<Person> GridItems { get; set; }
private const double CollapsedWidth = 500;
private const double ExpandedWidth = 700;
public MainWindow()
{
DataContext = this;
GridItems = new ObservableCollection<Person>();
GridItems.Add(new Person { Name = "Foo", Age = 1 });
GridItems.Add(new Person { Name = "Bar", Age = 2 });
InitializeComponent();
Width = CollapsedWidth;
}
private void OnAddNewButtonClick(object sender, RoutedEventArgs e)
{
LeftColumn.Visibility = Visibility.Visible;
}
private void OnLeftColumnButtonClick(object sender, RoutedEventArgs e)
{
LeftColumn.Visibility = Visibility.Collapsed;
}
private void OnRowDoubleClick(object sender, MouseButtonEventArgs e)
{
Width = ExpandedWidth;
RightColumn.Visibility = Visibility.Visible;
}
private void OnRightColumnButtonClick(object sender, RoutedEventArgs e)
{
RightColumn.Visibility = Visibility.Collapsed;
Width = CollapsedWidth;
}
}

Replace a control within a user control through dependency property

I've made a simple project to illustrate my problem. I have a user control ('ButtonPod') that houses a button and a rectangle:
<UserControl x:Class="SilverlightDependencyProp.ButtonPod"
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"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Rectangle Fill="Blue" />
<Button x:Name="ButtonOnPage" Margin="50" Content="Old button content" />
</Grid>
</UserControl>
I want to use this user control throughout my application, but I need to change the Button in the middle. I need control over all the properties of the Button, so I don't want to just expose DependencyProperties like 'ButtonText' or 'ButtonSize' - I would rather define the entire Button whenever I use the control. So I set up a dependency property ('CenterButton') like this:
public Button CenterButton
{
get { return (Button)GetValue(CenterButtonProperty); }
set { SetValue(CenterButtonProperty, value); }
}
public static readonly DependencyProperty CenterButtonProperty =
DependencyProperty.Register("CenterButton", typeof(Button),
typeof(ButtonPod), new PropertyMetadata(
CenterButtonChanged));
private static void CenterButtonChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
var pod = d as ButtonPod;
pod.ButtonOnPage = e.NewValue as Button;
}
Then I try defining the 'CenterButton' on my MainPage.xaml, within my user control:
<Grid x:Name="LayoutRoot" Background="White">
<local:ButtonPod Width="200" Height="200">
<local:ButtonPod.CenterButton>
<Button Content="New button content" />
</local:ButtonPod.CenterButton>
</local:ButtonPod>
</Grid>
But when I load up the app, all I see is a button that says "Old button content" -- why isn't my button being replaced? Stepping through, I can see that the DependencyProperty gets hit and the 'ButtonOnPage' property gets set, but the visual tree does not update. Any ideas?
Setting a named element does not replace it. You want to name the parent and add the child to that instead (replacing the existing children).

Fullscreen richtextbox in silverlight

I have a dataform with a richtextbox In it. The user can type some text and have some editing capability, but I'd like to give the user the option to expand the editor to fullscreen to have more richtextbox editing options. How can I implement a function that will allow me to fullscreen (or atleast create a bigger window) the richtexteditor so the user has better overview over the document and more editing options?
This is ment to be possible in OOB mode.
Full screen wont work as you have limit keyboard input in fullscreen:
Up Arrow
Down Arrow
Left Arrow
Right Arrow
Spacebar
Tab
Page Up
Page Down
Home
End
Enter
What you can do is for example is make your element fill the whole space of your silverlight application by making it the exact size of your RootVisual and adjusting your margins to place it correctly in your application:
XAML:
<UserControl x:Class="SilverlightApplication1.MyRichTextBox"
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"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button x:Name="FullScreen" Grid.Row="0" Content="FullScreen" Click="FullScreen_Click" />
<RichTextBox Grid.Row="1" />
</Grid>
Code-behind:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SilverlightApplication1
{
public partial class MyRichTextBox : UserControl
{
private Thickness _oldMargin;
private double _oldHeight = double.NaN;
private double _oldWidth = double.NaN;
private HorizontalAlignment _oldHorizontalAlignment;
private VerticalAlignment _oldVerticalAlignment;
private bool _fullScreen = false;
public MyRichTextBox()
{
InitializeComponent();
}
private void FullScreen_Click(object sender, RoutedEventArgs e)
{
if (_fullScreen)
{
_fullScreen = false;
Margin = _oldMargin;
Width = _oldWidth;
Height = _oldHeight;
}
else
{
_fullScreen = true;
_oldMargin = Margin;
_oldWidth = Width;
_oldHeight = Height;
_oldHorizontalAlignment = HorizontalAlignment;
_oldVerticalAlignment = VerticalAlignment;
FrameworkElement rootVisual = Application.Current.RootVisual as FrameworkElement;
GeneralTransform generalTransform = TransformToVisual(rootVisual);
Point position = generalTransform.Transform(new Point(0, 0));
Width = rootVisual.ActualWidth;
Height =rootVisual.ActualHeight;
Margin = new Thickness(-position.X - 1, -position.Y - 1
, (ActualWidth + position.X) - rootVisual.ActualWidth - 1
, (ActualHeight + position.Y) - rootVisual.ActualHeight - 1);
}
}
}
}

Creating controls

I am designing a silverlight application in which i have a image control in the left top corner and when i click the button and drag it to the form i should get duplicate of that control and dragged into my form.
Please help me with the code
I am trying to create the control dynamically in mouseleftbuttondown event but the controls are not being created.
Xaml
<UserControl xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation" xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls" x:Class="Workflow.MainPage"
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" d:DesignWidth="640" d:DesignHeight="480">
<Canvas x:Name="layout" Width="800" Height="600" Background="AliceBlue">
<Image x:Name="MyImage" Source="21.jpg" Canvas.Left="10" Canvas.Top="10" Stretch="Uniform"
MouseLeftButtonDown="MyImage_MouseLeftButtonDown" ></Image>
</Canvas>
</UserControl>
Code
private void MyImage_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
b = cs.LoadControl();
layout.Children.Add(b);
}
List<Ellipse> block = new List<Ellipse>();
public Ellipse LoadControl()
{
Ellipse btn = new Ellipse();
block.Add(btn);
btn.Height = 50; btn.Width = 100;
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush.Color = Color.FromArgb(0, 255, 255, 0);
btn.Fill = mySolidColorBrush;
Canvas.SetTop(btn, 50);
Canvas.SetLeft(btn, 50);
return btn;
}
You are using Color.FromArgb(0, 255, 255, 0) which has 0 for alpha, which makes your control transparent. What if you try Color.FromArgb(255, 255, 255, 0)?

Resources